/** * 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; }
/** * 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; } } }
/** * 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); }
/** * 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); } }
/** * 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; }
/** * 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); } }