Beispiel #1
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 #2
0
 /**
  * Verify that a local variable index for a given type is not out of bounds.
  *
  * @param type the type of the local variable at <code>index</code> in the local variables array
  * @param index the index of a local variable
  */
 public void verifyLocalVariableIndex(Klass type, int index) {
   if (index < 0) {
     throw codeParser.verifyError("invalid local variable index");
   }
   if (type.isDoubleWord()) {
     index++;
   }
   if (index >= localTypes.length) {
     throw codeParser.verifyError("invalid local variable index");
   }
 }
Beispiel #3
0
  /**
   * Get a <code>Local</code> instance to represent a value of a given type that will be
   * stored/loaded to/from a given local variable.
   *
   * @param type the type of the value
   * @param index the index of the local variable
   * @param isParameter true if the local is a parameter
   * @return the variable at index <code>index</code> in which values of type <code>type</code> are
   *     stored
   */
  private Local allocateLocalPrim(Klass type, int index, boolean isParameter) {
    Assert.that(localTypes.length < 0xFFFF);
    Assert.that(
        index >= 0 && index < localTypes.length,
        "index=" + index + " localTypes.length=" + localTypes.length);

    Klass localType = getLocalTypeFor(type);

    int key = localType.getSuiteID();

    /* We need a hard partition between uses of a slot as a reference vs. an Address, Offset, or UWord.
     * The partitioning of java primitives and objects is accomplished not only by the type passed in here, but
     * by the bytecode verifier. We can't be sure that some bytecodes are refering to the same local variable
     * as both a reference and as a Squawk primitive. Without that kind of support we are conservative here
     * and force a clash whenever javac uses the same local index for a reference and a Squawk primitive.
     */
    if (localType.isSquawkPrimitive()) {
      key = Klass.REFERENCE.getSuiteID();
    }
    key = key << 16 | index;
    if (localValues == null) {
      localValues = new IntHashtable();
    }
    Local local = (Local) localValues.get(key);
    if (local == null) {
      local = new Local(localType, index, isParameter);
      localValues.put(key, local);
    }

    /*
     * Ensure that the original class file does not use the same local variable
     * for both a Squawk primitive value and any other reference value. This prevents the
     * translator from having to do a complete liveness analysis to de-multiplex
     * such a local variable slot. Such de-multiplexing is required as Squawk primitives
     * are 'magically' translated into integers (or longs on a 64 bit system).
     */
    if (localType.isSquawkPrimitive() || local.getType().isSquawkPrimitive()) {
      if (localType != local.getType()) {
        throw codeParser.verifyError(
            getBadAddressLocalVariableMessage(index, localType, local.getType()));
      }
    }

    // System.out.println("allocated: "+local+" index "+index);
    /// *if[SCOPEDLOCALVARIABLES]*/
    codeParser.localVariableAllocated(codeParser.getCurrentIP(), local);
    /// *end[SCOPEDLOCALVARIABLES]*/
    return local;
  }
Beispiel #4
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 #5
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 #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);
    }
  }
Beispiel #7
0
  /**
   * Pushes a value to the operand stack.
   *
   * @param producer the instruction producing the value being pushed
   */
  public void push(StackProducer producer) {
    Klass type = producer.getType();
    Assert.that(type != Klass.VOID);

    /*
     * Check for overflow and push the producer.
     */
    if (sp == maxStack) {
      throw codeParser.verifyError("operand stack overflow");
    }
    stack[sp++] = producer;

    /*
     * For long and double check for overflow and then add a null word to the stack.
     */
    if (type.isDoubleWord()) {
      if (sp == maxStack) {
        throw codeParser.verifyError("operand stack overflow");
      }
      stack[sp++] = null;
    }
  }
Beispiel #8
0
 /**
  * Creates and returns the detailed error message when a local variable is used as a Squawk
  * primitive as well as some value not of exactly the same type. The message includes information
  * derived from the LocalVariableTable attribute so that the source code can be easily changed.
  *
  * @param index the local variable index that is (mis)used
  * @return the detailed error message
  */
 private String getBadAddressLocalVariableMessage(int index, Klass type1, Klass type2) {
   Assert.that(type1.isSquawkPrimitive() || type2.isSquawkPrimitive());
   if (type2.isSquawkPrimitive()) {
     Klass otherType = type1;
     type1 = type2;
     type2 = otherType;
   }
   StringBuffer buf =
       new StringBuffer(
           "Stack location "
               + index
               + " cannot be used for both a local variable of type "
               + type1.getName()
               + " and of type "
               + type2.getName()
               + ". Try moving the variable of type "
               + type1.getName()
               + " to the top-most scope of the method.");
   Enumeration e = codeParser.getLocalVariableTableEntries();
   if (e != null) {
     buf.append(" (source code usage: ");
     while (e.hasMoreElements()) {
       LocalVariableTableEntry entry = (LocalVariableTableEntry) e.nextElement();
       if (entry.getIndex() == index) {
         int start = codeParser.getSourceLineNumber(entry.getStart().getBytecodeOffset());
         int end = codeParser.getSourceLineNumber(entry.getEnd().getBytecodeOffset());
         buf.append(entry.getType().getName())
             .append(' ')
             .append(entry.getName())
             .append(" from line ")
             .append(start)
             .append(" to line ")
             .append(end)
             .append(';');
       }
     }
   }
   return buf.toString();
 }
Beispiel #9
0
 /**
  * Tests two given types to ensure that they are both {@link Klass#isSquawkPrimitive() Squawk
  * primitives} or both not Squawk primitives. If they are both Squawk primitives, then they must
  * be exactly the same type. This enforces the constraint that Squawk primitive values cannot be
  * assigned to or compared with any other type.
  *
  * @param type1 the first type to compare
  * @param type2 the second type to compare
  */
 public void verifyUseOfSquawkPrimitive(Klass type1, Klass type2) {
   if (type1.isSquawkPrimitive() || type2.isSquawkPrimitive()) {
     if (type1 != type2) {
       // Offsets are implemented as Words
       //                if (type1.getClassID() + type2.getClassID() != CID.UWORD + CID.OFFSET) {
       String type = type1.getName();
       throw codeParser.verifyError(
           type
               + " values can only be written to or compared with other "
               + type
               + " values, not with "
               + type2.getName());
       //                }
     }
   }
 }
Beispiel #10
0
 /**
  * Traces the frame state at the current verification address.
  *
  * @param opcode the opcode of the instruction at <code>address</code>
  * @param address the current verification address
  */
 public void traceFrameState(int opcode, int address) {
   /*
    * Trace the recorded and derived types
    */
   if (Translator.TRACING_ENABLED) {
     Target target = null;
     try {
       target = codeParser.getTarget(address);
     } catch (NoClassDefFoundError e) {
       /* Just means there is no stack map at this address */
     }
     Tracer.traceln("Frame state @ " + address + " [ " + Opcode.mnemonics[opcode] + " ]");
     traceLocals(target);
     traceStack(target);
   }
 }
Beispiel #11
0
 /**
  * Resets the max stack limit back to the value specified in the class file for the current
  * method.
  */
 public void resetMaxStack() {
   maxStack = codeParser.getMaxStack();
 }
Beispiel #12
0
  /**
   * Creates a Frame instance to emulate and verify the execution of a single class file method.
   *
   * @param codeParser the parser used to parse the "Code" attribute of the method being emulated
   * @param extraLocals the number of extra local variables needed
   */
  public Frame(CodeParser codeParser, int extraLocals) {
    this.maxStack = codeParser.getMaxStack();
    this.codeParser = codeParser;

    /*
     * Initialize the operand stack
     */
    stack = new StackProducer[maxStack];

    /*
     * Initialize the types in the local variables
     */
    localTypes = new Klass[codeParser.getMaxLocals() + extraLocals];
    Method method = codeParser.getMethod();
    Klass[] parameterTypes = method.getParameterTypes();
    Local[] parameterLocals = null;
    if (Translator.REVERSE_PARAMETERS && method.isInterpreterInvoked()) {
      parameterLocals = new Local[parameterTypes.length];
    }
    int javacIndex = 0;

    /*
     * Initialize 'this' in non-static methods. The type of 'this' is
     * UNINITIALIZED_THIS if this method is a constructor in any class
     * except java.lang.Object otherwise it is the class in which the
     * method was defined.
     */
    if (!method.isStatic() || method.isConstructor()) {
      Assert.that(parameterLocals == null);
      Klass thisType = method.getDefiningClass();
      if (method.isConstructor() && thisType != Klass.OBJECT) {
        thisType = Klass.UNINITIALIZED_THIS;
      }
      Local thisLocal = allocateParameter(thisType, javacIndex);
      store(javacIndex, thisType, thisLocal);
      javacIndex++;
    }

    /*
     * Initialize locals for the parameters.
     */
    int parameterIndex = javacIndex;
    for (int i = 0; i < parameterTypes.length; i++) {
      Klass parameterType = parameterTypes[i];
      Local parameterLocal = allocateParameter(parameterType, javacIndex);
      if (parameterLocals != null) {
        parameterLocals[i] = parameterLocal;
      }
      if (Klass.SQUAWK_64) {
        if (javacIndex != parameterIndex) {
          Assert.that(parameterIndex < javacIndex);
          parameterLocal.setParameterIndex(parameterIndex);
        }
        parameterIndex++;
      }
      store(javacIndex, parameterType, parameterLocal);
      javacIndex += (parameterType.isDoubleWord() ? 2 : 1);
    }
    parameterLocalsCount = javacIndex;

    /*
     * Adjust the parameter offsets for parameter order reversal.
     */
    if (parameterLocals != null) {
      parameterIndex = 0;
      for (int i = parameterTypes.length - 1; i >= 0; i--) {
        Klass parameterType = parameterTypes[i];
        parameterLocals[i].setParameterIndex(parameterIndex++);
        if (!Klass.SQUAWK_64 && parameterType.isDoubleWord()) {
          parameterIndex++;
        }
      }
    }

    /*
     * Initialize the remaining local variables to the TOP type
     */
    while (javacIndex < localTypes.length) {
      localTypes[javacIndex++] = Klass.TOP;
    }
  }