Beispiel #1
0
 /**
  * Determines if the operand stack or local variable array currently contains an entry whose type
  * is equal to or a subtype of a given type.
  *
  * @param type the type to search for
  * @return true if an entry was found
  */
 public boolean containsType(Klass type) {
   for (int i = 0; i < localTypes.length; i++) {
     if (type.isAssignableFrom(localTypes[i])) {
       return true;
     }
   }
   for (int i = 0; i < sp; i++) {
     StackProducer producer = stack[i];
     if (producer != null && type.isAssignableFrom(producer.getType())) {
       return true;
     }
   }
   return false;
 }
Beispiel #2
0
  /**
   * Verifies that the current state of the local variables array matches the state specified by a
   * stack map entry.
   *
   * @param target the target encapsulating a stack map entry specifying what the current state of
   *     the local variables array should be
   * @param replaceWithTarget if true, then the current state of the local variable array is updated
   *     to reflect the state recorded in the stack map entry
   */
  public void mergeLocals(Target target, boolean replaceWithTarget) {
    Klass[] recordedTypes = target.getLocals();
    if (recordedTypes.length > localTypes.length) {
      throw codeParser.verifyError("size of recorded and derived local variable array differs");
    }

    /*
     * Check the locals
     */
    for (int i = 0; i < recordedTypes.length; i++) {
      Klass recordedType = recordedTypes[i];
      Klass derivedType = localTypes[i];
      if (!recordedType.isAssignableFrom(derivedType)) {
        /*
         * For some reason, the preverifier occasionally generates
         * stack map entries for local variables even though the
         * local variable is dead. What's more, in these cases,
         * it determines that the type resulting from merging an
         * object type and an interface type is the interface
         * type. This makes no sense to me, but the case must be
         * allowed.
         */
        if (!recordedType.isInterface() || derivedType.isPrimitive()) {
          throw codeParser.verifyError("invalid type in local variable");
        }
      }
      if (replaceWithTarget) {
        localTypes[i] = recordedType;
      }
    }
  }
Beispiel #3
0
  /**
   * Emulates loading a value of a given type from a local variable.
   *
   * @param index the index of the local variable being loaded from
   * @param localType the expected type of the variable from which the value is loaded
   * @return the variable from which the value is loaded
   */
  public Local load(int index, Klass localType) {
    verifyLocalVariableIndex(localType, index);
    Klass derivedType = localTypes[index];
    if (!localType.isAssignableFrom(derivedType)) {
      throw codeParser.verifyError("incompatible type in local variable");
    }

    if (localType.isDoubleWord()) {
      Klass secondWordType = Klass.getSecondWordType(localType);
      if (!secondWordType.isAssignableFrom(localTypes[index + 1])) {
        throw codeParser.verifyError("incompatible type in local variable");
      }
    }

    if (derivedType.isSquawkPrimitive()) {
      localType = derivedType;
    }
    return allocateLocal(localType, index);
  }
Beispiel #4
0
 /**
  * Emulates the storing of a value to a local variable.
  *
  * @param index the index of the local variable being stored to
  * @param type the type of the value
  * @param local the variable to which the value is stored
  */
 public void store(int index, Klass type, Local local) {
   Klass localType = local.getType();
   Assert.that(localType.isAssignableFrom(type) || localType == getLocalTypeFor(type));
   verifyLocalVariableIndex(localType, index);
   localTypes[index] = type;
   if (localType.isDoubleWord()) {
     localTypes[index + 1] = Klass.getSecondWordType(localType);
   } else {
     verifyUseOfSquawkPrimitive(localType, type);
   }
 }
Beispiel #5
0
  /**
   * Pops a value off the operand stack.
   *
   * @param type the type that the value popped off the operand stack must be assignable to
   * @return the instruction that produced the popped value
   */
  public StackProducer pop(Klass type) {
    StackProducer producer;
    if (type.isDoubleWord()) {
      if (sp < 2) {
        throw codeParser.verifyError("operand stack underflow");
      }
      if (!isTopDoubleWord()) {
        throw codeParser.verifyError("incompatible type on operand stack " + tosKlassName());
      }
      sp -= 2;
      producer = stack[sp];
    } else {
      if (sp < 1) {
        throw codeParser.verifyError("operand stack underflow");
      }
      if (isTopDoubleWord()) {
        throw codeParser.verifyError("incompatible type on operand stack " + tosKlassName());
      }
      producer = stack[--sp];

      /*
       * The primitive one-word, non-float types are all assignment compatible with each other
       */
      if (type.isPrimitive() && type != Klass.FLOAT) {

        type = Klass.INT;
      }
    }

    Assert.that(producer != null);

    /*
     * Interfaces are treated as java.lang.Object in the verifier.
     */
    if (type.isInterface()) {
      type = Klass.OBJECT;
    }

    /*
     * Verify that the instruction is producing the correct type.
     */
    if (!type.isAssignableFrom(producer.getType())) {
      throw codeParser.verifyError(
          "incompatible type: '" + type + "' is not assignable from '" + producer.getType() + "'");
    }

    return producer;
  }
Beispiel #6
0
  /**
   * Merges the current state of the operand stack into the saved state at a control flow target.
   * This method also verifies that the current state of the operand stack matches the expected
   * state of the operand stack at the target as pecified by a stack map entry.
   *
   * @param target the target encapsulting the merged state of the operand stack at a distinct
   *     address
   * @param replaceWithTarget if true, then the current state of the operand stack is updated to
   *     reflect the state recorded in the stack map entry
   */
  public void mergeStack(Target target, boolean replaceWithTarget) {
    Klass[] recordedTypes = target.getStack();

    /*
     * Fail if the map sp is different
     */
    if (recordedTypes.length != getStackSize()) {
      throw codeParser.verifyError("size of recorded and derived stack differs");
    }

    /*
     * Check the stack items
     */
    for (int r = 0, d = 0; r < recordedTypes.length; ++r, ++d) {
      Klass recordedType = recordedTypes[r];
      Klass derivedType = getStackTypeAt(d);
      if (!recordedType.isAssignableFrom(derivedType)) {

        // Interfaces are treated like java.lang.Object in the verifier according to the CLDC spec.
        if (!recordedType.isInterface() || !Klass.OBJECT.isAssignableFrom(derivedType)) {
          throw codeParser.verifyError(
              "invalid type on operand stack @ "
                  + d
                  + ": expected "
                  + recordedType
                  + ", received "
                  + derivedType);
        }
      }
    }

    /*
     * Merge the instructions on the stack
     */
    target.merge(this);

    if (replaceWithTarget) {
      resetStack(target, false);
    }
  }