private void checkBinary(ExecutableSequence s, ObjectContract c, Set<Integer> values, int idx) { for (Integer i : values) { for (Integer j : values) { ExecutionOutcome result1 = s.getResult(i); assert result1 instanceof NormalExecution : s; ExecutionOutcome result2 = s.getResult(j); assert result2 instanceof NormalExecution : s; if (Log.isLoggingOn()) Log.logLine("Checking contract " + c.getClass() + " on " + i + ", " + j); ExecutionOutcome exprOutcome = ObjectContractUtils.execute( c, ((NormalExecution) result1).getRuntimeValue(), ((NormalExecution) result2).getRuntimeValue()); Check obs = null; if (exprOutcome instanceof NormalExecution) { NormalExecution e = (NormalExecution) exprOutcome; if (e.getRuntimeValue().equals(true)) { if (Log.isLoggingOn()) Log.logLine("Contract returned true."); continue; // Behavior ok. } else { if (Log.isLoggingOn()) Log.logLine("Contract returned false. Will add ExpressionEqFalse check"); // Create an check that records the actual value // returned by the expression, marking it as invalid // behavior. obs = new ObjectCheck(c, i, s.sequence.getVariable(i), s.sequence.getVariable(j)); s.addCheck(idx, obs, false); } } else { if (Log.isLoggingOn()) Log.logLine("Contract threw exception."); // Execution of contract resulted in exception. Do not create // a contract-violation decoration. assert exprOutcome instanceof ExceptionalExecution; } } } }
/** * Tries to create and execute a new sequence. If the sequence is new (not already in the * specified component manager), then it is executed and added to the manager's sequences. If the * sequence created is already in the manager's sequences, this method has no effect, and returns * null. */ private ExecutableSequence createNewUniqueSequence() { Tracer.trace("createNewUniqueSequence"); if (Log.isLoggingOn()) Log.logLine("-------------------------------------------"); StatementKind statement = null; if (this.statements.isEmpty()) return null; // Select a StatementInfo statement = Randomness.randomMember(this.statements); if (Log.isLoggingOn()) Log.logLine("Selected statement: " + statement.toString()); // jhp: add flags here InputsAndSuccessFlag sequences = selectInputs(statement); if (!sequences.success) { if (Log.isLoggingOn()) Log.logLine("Failed to find inputs for statement."); return null; } Sequence concatSeq = Sequence.concatenate(sequences.sequences); // Figure out input variables. List<Variable> inputs = new ArrayList<Variable>(); for (Integer oneinput : sequences.indices) { Variable v = concatSeq.getVariable(oneinput); inputs.add(v); } Sequence newSequence = concatSeq.extend(statement, inputs); // With .5 probability, do a primitive value heuristic. Tracer.trace("heuristic-uniquesequence"); if (GenInputsAbstract.repeat_heuristic && Randomness.nextRandomInt(10) == 0) { Tracer.trace("repeat_heuristic@heuristic-uniquesequence"); int times = Randomness.nextRandomInt(100); newSequence = newSequence.repeatLast(times); if (Log.isLoggingOn()) Log.log(">>>" + times + newSequence.toCodeString()); } Tracer.trace("NO_repeat_heuristic@heuristic-uniquesequence"); // If parameterless statement, subsequence inputs // will all be redundant, so just remove it from list of statements. if (statement.getInputTypes().size() == 0) { statements.remove(statement); } // If sequence is larger than size limit, try again. Tracer.trace("evaluating-maxsize"); if (newSequence.size() > GenInputsAbstract.maxsize) { Tracer.trace(">maxsize@evaluating-maxsize"); if (Log.isLoggingOn()) Log.logLine( "Sequence discarded because size " + newSequence.size() + " exceeds maximum allowed size " + GenInputsAbstract.maxsize); return null; } Tracer.trace("<maxsize@evaluating-maxsize"); randoopConsistencyTests(newSequence); if (this.allSequences.contains(newSequence)) { Tracer.trace("discard existing"); if (Log.isLoggingOn()) Log.logLine("Sequence discarded because the same sequence was previously created."); return null; } this.allSequences.add(newSequence); for (Sequence s : sequences.sequences) { s.lastTimeUsed = java.lang.System.currentTimeMillis(); } randoopConsistencyTest2(newSequence); if (Log.isLoggingOn()) { Log.logLine("Successfully created new unique sequence:" + newSequence.toString()); } // System.out.println("###" + statement.toStringVerbose() + "###" + statement.getClass()); // Keep track of any input sequences that are used in this sequence // Tests that contain only these sequences are probably redundant for (Sequence is : sequences.sequences) { subsumed_sequences.add(is); } return new ExecutableSequence(newSequence); }
// This method is responsible for doing two things: // // 1. Selecting at random a collection of sequences that can be used to // create input values for the given statement, and // // 2. Selecting at random valid indices to the above sequence specifying // the values to be used as input to the statement. // // The selected sequences and indices are wrapped in an InputsAndSuccessFlag // object and returned. If an appropriate collection of sequences and indices // was not found (e.g. because there are no sequences in the componentManager // that create values of some type required by the statement), the success flag // of the returned object is false. @SuppressWarnings("unchecked") private InputsAndSuccessFlag selectInputs(StatementKind statement) { Tracer.trace("selectInputs"); // Variable inputTypes containsthe values required as input to the // statement given as a parameter to the selectInputs method. List<Class<?>> inputTypes = statement.getInputTypes(); // The rest of the code in this method will attempt to create // a sequence that creates at least one value of type T for // every type T in inputTypes, and thus can be used to create all the // inputs for the statement. // We denote this goal sequence as "S". We don't create S explicitly, but // define it as the concatenation of the following list of sequences. // In other words, S = sequences[0] + ... + sequences[sequences.size()-1]. // (This representation choice is for efficiency: it is cheaper to perform // a single concatenation of the subsequences in the end than repeatedly // extending S.) List<Sequence> sequences = new ArrayList<Sequence>(); // We store the total size of S in the following variable. int totStatements = 0; // The method also returns a list of randomly-selected variables to // be used as inputs to the statement, represented as indices into S. // For example, given as statement a method M(T1)/T2 that takes as input // a value of type T1 and returns a value of type T2, this method might // return, for example, the sequence // // T0 var0 = new T0(); T1 var1 = var0.getT1()" // // and the singleton list [0] that represents variable var1. The variable // indices are stored in the following list. Upon successful completion // of this method, variables will contain inputTypes.size() variables. // Note additionally that for every i in variables, 0 <= i < |S|. List<Integer> variables = new ArrayList<Integer>(); // [Optimization] // The following two variables are used in the loop below only when // an alias ratio is present (GenInputsAbstract.alias_ratio != null). // Their purpose is purely to improve efficiency. For a given loop iteration // i, "types" contains the types of all variables in S, and "typesToVars" // maps each type to all variable indices of the given type. SubTypeSet types = new SubTypeSet(false); MultiMap<Class<?>, Integer> typesToVars = new MultiMap<Class<?>, Integer>(); for (int i = 0; i < inputTypes.size(); i++) { Class<?> t = inputTypes.get(i); // TODO Does this ever happen? if (!Reflection.isVisible(t)) return new InputsAndSuccessFlag(false, null, null); // true if statement st represents an instance method, and we are currently // selecting a value to act as the receiver for the method. boolean isReceiver = (i == 0 && (statement instanceof RMethod) && (!((RMethod) statement).isStatic())); // If alias ratio is given, attempt with some probability to use a variable already in S. if (GenInputsAbstract.alias_ratio != 0 && Randomness.weighedCoinFlip(GenInputsAbstract.alias_ratio)) { Tracer.trace("alias_ratio@selectInputs"); // candidateVars will store the indices that can serve as input to the i-th input in st. List<SimpleList<Integer>> candidateVars = new ArrayList<SimpleList<Integer>>(); // For each type T in S compatible with inputTypes[i], add all the indices in S of type T. for (Class<?> match : types.getMatches(t)) { // Sanity check: the domain of typesToVars contains all the types in variable types. assert typesToVars.keySet().contains(match); candidateVars.add( new ArrayListSimpleList<Integer>( new ArrayList<Integer>(typesToVars.getValues(match)))); } // If any type-compatible variables found, pick one at random as the i-th input to st. SimpleList<Integer> candidateVars2 = new ListOfLists<Integer>(candidateVars); if (candidateVars2.size() > 0) { int randVarIdx = Randomness.nextRandomInt(candidateVars2.size()); Integer randVar = candidateVars2.get(randVarIdx); variables.add(randVar); continue; } } Tracer.trace("NO alias_ratio@selectInputs"); // If we got here, it means we will not attempt to use a value already defined in S, // so we will have to augment S with new statements that yield a value of type inputTypes[i]. // We will do this by assembling a list of candidate sequences n(stored in the list declared // immediately below) that create one or more values of the appropriate type, // randomly selecting a single sequence from this list, and appending it to S. SimpleList<Sequence> l = null; // We use one of three ways to gather candidate sequences, but the third case below // is by far the most common. if (GenInputsAbstract.always_use_ints_as_objects && t.equals(Object.class)) { Tracer.trace("always_use_ints_as_objects@selectInputs"); // 1. OBSCURE, applicable only for branch-directed generation project. Get all // sequences that create one or more integer. Applicable only when inputTypes[i] // is "Object" and always_use_ints_as_objects option is specified. if (Log.isLoggingOn()) Log.logLine("Integer-as-object heuristic: will use random Integer."); l = componentManager.getSequencesForType(int.class, false); } else if (t.isArray()) { // 2. If T=inputTypes[i] is an array type, ask the component manager for all sequences // of type T (list l1), but also try to directly build some sequences that create arrays // (list l2). SimpleList<Sequence> l1 = componentManager.getSequencesForType(statement, i); if (Log.isLoggingOn()) Log.logLine("Array creation heuristic: will create helper array of type " + t); SimpleList<Sequence> l2 = HelperSequenceCreator.createSequence(componentManager, t); l = new ListOfLists<Sequence>(l1, l2); } else { // 3. COMMON CASE: ask the component manager for all sequences that yield the required type. if (Log.isLoggingOn()) Log.logLine("Will query component set for objects of type" + t); l = componentManager.getSequencesForType(statement, i); } assert l != null; if (Log.isLoggingOn()) Log.logLine("components: " + l.size()); // If we were not able to find (or create) any sequences of type inputTypes[i], and we are // allowed the use null values, use null. If we're not allowed, then return with failure. if (l.size() == 0) { Tracer.trace("selectinputs-evalforbid"); if (isReceiver || GenInputsAbstract.forbid_null) { if (!isReceiver) { if (GenInputsAbstract.forbid_null) { Tracer.trace("forbid_null@selectinputs-evalforbid"); } } if (Log.isLoggingOn()) Log.logLine("forbid-null option is true. Failed to create new sequence."); return new InputsAndSuccessFlag(false, null, null); } else { if (!GenInputsAbstract.forbid_null) { Tracer.trace("NOT forbid_null@selectinputs-evalforbid"); } if (Log.isLoggingOn()) Log.logLine("Will use null as " + i + "-th input"); StatementKind st = PrimitiveOrStringOrNullDecl.nullOrZeroDecl(t); Sequence seq = new Sequence().extend(st, new ArrayList<Variable>()); variables.add(totStatements); sequences.add(seq); assert seq.size() == 1; totStatements++; // Null is not an interesting value to add to the set of // possible values to reuse, so we don't update typesToVars or types. continue; } } // At this point, we have one or more sequences that create non-null values of type // inputTypes[i]. // However, the user may have requested that we use null values as inputs with some given // frequency. // If this is the case, then use null instead with some probability. Tracer.trace("selectinputs-null-ratio"); if (!isReceiver && GenInputsAbstract.null_ratio != 0 && Randomness.weighedCoinFlip(GenInputsAbstract.null_ratio)) { Tracer.trace("null_ratio@selectinputs-null-ratio"); if (Log.isLoggingOn()) Log.logLine("null-ratio option given. Randomly decided to use null as input."); StatementKind st = PrimitiveOrStringOrNullDecl.nullOrZeroDecl(t); Sequence seq = new Sequence().extend(st, new ArrayList<Variable>()); variables.add(totStatements); sequences.add(seq); assert seq.size() == 1; totStatements++; continue; } Tracer.trace("NOT null_ratio@selectinputs-null-ratio"); // At this point, we have a list of candidate sequences and need to select a // randomly-chosen sequence from the list. Sequence chosenSeq = null; Tracer.trace("selectInputs-smalltests"); if (GenInputsAbstract.small_tests) { Tracer.trace("small_tests@selectInputs-smalltests"); chosenSeq = Randomness.randomMemberWeighted(l); } else { Tracer.trace("NO small_tests@selectInputs-smalltests"); chosenSeq = Randomness.randomMember(l); } // Now, find values that satisfy the constraint set. Match m = Match.COMPATIBLE_TYPE; // if (i == 0 && statement.isInstanceMethod()) m = Match.EXACT_TYPE; Variable randomVariable = chosenSeq.randomVariableForTypeLastStatement(t, m); // We are not done yet: we have chosen a sequence that yields a value of the required // type inputTypes[i], but there may be more than one such value. Our last random // selection step is to select from among all possible values. // if (i == 0 && statement.isInstanceMethod()) m = Match.EXACT_TYPE; if (randomVariable == null) { throw new BugInRandoopException("type: " + t + ", sequence: " + chosenSeq); } // If we were unlucky and selected a null value as the receiver // for a method call, return with failure. if (i == 0 && (statement instanceof RMethod) && (!((RMethod) statement).isStatic()) && chosenSeq.getCreatingStatement(randomVariable) instanceof PrimitiveOrStringOrNullDecl) return new InputsAndSuccessFlag(false, null, null); // [Optimization.] Update optimization-related variables "types" and "typesToVars". Tracer.trace("selectinputs-alias"); if (GenInputsAbstract.alias_ratio != 0) { Tracer.trace("alias_ratio@selectinputs-alias"); // Update types and typesToVars. for (int j = 0; j < chosenSeq.size(); j++) { StatementKind stk = chosenSeq.getStatementKind(j); if (stk instanceof PrimitiveOrStringOrNullDecl) continue; // Prim decl not an interesting candidate for multiple uses. Class<?> outType = stk.getOutputType(); types.add(outType); typesToVars.add(outType, totStatements + j); } } Tracer.trace("NOT alias_ratio@selectinputs-alias"); variables.add(totStatements + randomVariable.index); sequences.add(chosenSeq); totStatements += chosenSeq.size(); } return new InputsAndSuccessFlag(true, sequences, variables); }
/** * Determines what indices in the given sequence are active. An active index i means that the i-th * method call creates an interesting/useful value that can be used as an input to a larger * sequence; inactive indices are never used as inputs. The effect of setting active/inactive * indices is that the SequenceCollection to which the given sequences is added only considers the * active indices when deciding whether the sequence creates values of a given type. * * <p>In addition to determining active indices, this method determines if any primitive values * created during execution of the sequence are new values not encountered before. Such values are * added to the component manager so they can be used during subsequent generation attempts. */ public void processSequence(ExecutableSequence seq) { Tracer.trace("processSequence"); if (GenInputsAbstract.offline) { Tracer.trace("offline@processSequence"); if (Log.isLoggingOn()) { Log.logLine( "Making all indices active (offline generation specified; sequences are not executed)."); } seq.sequence.setAllActiveFlags(); return; } Tracer.trace("NOT offline@processSequence"); if (seq.hasNonExecutedStatements()) { if (Log.isLoggingOn()) { Log.logLine( "Making all indices inactive (sequence has non-executed statements, so judging it inadequate for further extension)."); } seq.sequence.clearAllActiveFlags(); return; } if (seq.hasFailure()) { if (Log.isLoggingOn()) { Log.logLine( "Making all indices inactive (sequence reveals a failure, so judging it inadequate for further extension)"); } seq.sequence.clearAllActiveFlags(); return; } if (!seq.isNormalExecution()) { if (Log.isLoggingOn()) { Log.logLine( "Making all indices inactive (exception thrown, or failure revealed during execution)."); } seq.sequence.clearAllActiveFlags(); return; } // If runtime value is a primitive value, clear active flag, and // if the value is new, add a sequence corresponding to that value. for (int i = 0; i < seq.sequence.size(); i++) { // type ensured by isNormalExecution clause ealier in this method. NormalExecution e = (NormalExecution) seq.getResult(i); Object runtimeValue = e.getRuntimeValue(); if (runtimeValue == null) { if (Log.isLoggingOn()) { Log.logLine("Making index " + i + " inactive (value is null)"); } seq.sequence.clearActiveFlag(i); continue; } Class<?> objectClass = runtimeValue.getClass(); Tracer.trace("processSequence-useobjcache"); if (PrimitiveTypes.isBoxedOrPrimitiveOrStringType(objectClass)) { if (Log.isLoggingOn()) { Log.logLine("Making index " + i + " inactive (value is a primitive)"); } seq.sequence.clearActiveFlag(i); boolean looksLikeObjToString = (runtimeValue instanceof String) && PrimitiveTypes.looksLikeObjectToString((String) runtimeValue); boolean tooLongString = (runtimeValue instanceof String) && !PrimitiveTypes.stringLengthOK((String) runtimeValue); if (!looksLikeObjToString && !tooLongString && runtimePrimitivesSeen.add(runtimeValue)) { // Have not seen this value before; add it to the component set. componentManager.addGeneratedSequence( PrimitiveOrStringOrNullDecl.sequenceForPrimitive(runtimeValue)); } } else if (GenInputsAbstract.use_object_cache) { Tracer.trace("use_object_cache@processSequence-useobjcache"); objectCache.setActiveFlags(seq, i); } else { if (Log.isLoggingOn()) { Log.logLine("Making index " + i + " active."); } } } }