boolean computeSubtypeOf(Klass k) { if (k.isInterface()) { return implementsInterface(k); } else { return super.computeSubtypeOf(k); } }
/** * 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; } } }
public boolean implementsInterface(Klass k) { if (Assert.ASSERTS_ENABLED) { Assert.that(k.isInterface(), "should not reach here"); } ObjArray interfaces = getTransitiveInterfaces(); final int len = (int) interfaces.getLength(); for (int i = 0; i < len; i++) { if (interfaces.getObjAt(i).equals(k)) return true; } return false; }
/** * 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); } }