Esempio n. 1
0
 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;
 }
Esempio n. 2
0
  /**
   * 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;
  }
  // 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);
  }