/** * Returns a sequence that creates an object of type compatible the given class. Wraps the object * in a list, and returns the list. * * <p>CURRENTLY, will return a sequence (i.e. a non-empty list) only if cls is an array. */ public static SimpleList<Sequence> createSequence(ComponentManager components, Class<?> cls) { // Class<?> cls = statement.getInputTypes().get(i); if (!cls.isArray()) { return new ArrayListSimpleList<Sequence>(); } Sequence s = null; if (cls.getComponentType().isPrimitive()) { s = randPrimitiveArray(cls.getComponentType()); } else { SimpleList<Sequence> candidates = components.getSequencesForType(cls.getComponentType(), false); if (candidates.isEmpty()) { if (GenInputsAbstract.forbid_null) { // No sequences that produce appropriate component values found, and null forbidden. // Return the empty array. ArrayDeclaration decl = new ArrayDeclaration(cls.getComponentType(), 0); s = new Sequence(); s = s.extend(decl); } else { // No sequences that produce appropriate component values found, and null allowed. // TODO: We should also randomly return the empty array--it's a perfectly good case // even if null is allowed. // Return the array [ null ]. ArrayDeclaration decl = new ArrayDeclaration(cls.getComponentType(), 1); s = new Sequence(); s = s.extend(PrimitiveOrStringOrNullDecl.nullOrZeroDecl(cls.getComponentType())); List<Variable> ins = new ArrayList<Variable>(); ins.add(s.getVariable(0)); s = s.extend(decl, ins); } } else { // Return the array [ x ] where x is the last value in the sequence. ArrayDeclaration decl = new ArrayDeclaration(cls.getComponentType(), 1); s = candidates.get(Randomness.nextRandomInt(candidates.size())); List<Variable> ins = new ArrayList<Variable>(); // XXX IS THIS OLD COMMENT TRUE? : this assumes that last statement will have such a var, // which I know is currently true because of SequenceCollection implementation. ins.add( s.randomVariableForTypeLastStatement(cls.getComponentType(), Match.COMPATIBLE_TYPE)); s = s.extend(decl, ins); } } assert s != null; ArrayListSimpleList<Sequence> l = new ArrayListSimpleList<Sequence>(); l.add(s); return l; }
private static Sequence randPrimitiveArray(Class<?> componentType) { assert componentType.isPrimitive(); Set<Object> potentialElts = SeedSequences.getSeeds(componentType); int length = Randomness.nextRandomInt(4); Sequence s = new Sequence(); List<Variable> emptylist = new ArrayList<Variable>(); for (int i = 0; i < length; i++) { Object elt = Randomness.randomSetMember(potentialElts); s = s.extend(new PrimitiveOrStringOrNullDecl(componentType, elt), emptylist); } List<Variable> inputs = new ArrayList<Variable>(); for (int i = 0; i < length; i++) { inputs.add(s.getVariable(i)); } s = s.extend(new ArrayDeclaration(componentType, length), inputs); return s; }
// 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); }
/** * 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); }