/** * Reset the state of the operand stack based on the stack state recorded for a given target. * * @param target the target whose recorded state will be used to reset the state of this frame's * operand stack * @param isForCatch specifies if target corresponds to an exception handler entry */ public void resetStack(Target target, boolean isForCatch) { Klass[] recordedTypes = target.getStack(); if (!isForCatch) { sp = 0; if (recordedTypes.length != 0) { StackProducer[] derivedStack = target.getDerivedStack(); boolean isBackwardBranchTarget = (derivedStack[0] == null); if (isBackwardBranchTarget) { for (int i = 0; i < recordedTypes.length; i++) { Klass recordedType = recordedTypes[i]; if (recordedType != Klass.LONG2 && recordedType != Klass.DOUBLE2) { StackMerge merge = new StackMerge(recordedType); // Sometimes we end up with old derivedStack[0] == a StackMerge with no producers. Assert.that(derivedStack[0] == null || !derivedStack[0].isOnStack()); derivedStack[0] = merge; push(merge); } } } else { while (sp != derivedStack.length) { StackProducer producer = derivedStack[sp]; stack[sp++] = producer; } } } } else { // Assert.that(sp == 0); sp = 0; } }
/** * Reset the state of the local variable array based on the local variable state recorded for a * given target. * * @param target the target whose recorded state will be used to reset the state of this frame's * local variable array */ public void resetLocals(Target target) { Klass[] recordedTypes = target.getLocals(); int i; for (i = 0; i < recordedTypes.length; i++) { localTypes[i] = recordedTypes[i]; } while (i < localTypes.length) { localTypes[i++] = Klass.TOP; } // There is at least one TCK test that has a stack map in a synchronized method // at the entry to some unreachable code: // javasoft.sqe.tests.vm.instr.athrow.athrow013.athrow01301m1.athrow01301m1_wrapper // In this case, the stack map will be invalid with respect to the synthesized // stack map at the entry to the exception handler wrapping the whole method // to implement the correct semantics for method synchronization. To make such // code pass the translator, the reset state of the locals includes the extra state // derived from the synthesized stack map. if (finallyTargetForSynchronizedMethod != null) { Klass[] types = finallyTargetForSynchronizedMethod.getLocals(); if (types.length > recordedTypes.length) { for (i = recordedTypes.length; i != types.length; ++i) { localTypes[i] = types[i]; } } } }
/** * 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; } } }
/** * 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); } }
/** * Traces the state of the local variables at the current verification address. * * @param target the stack map (if any) at the current verification address */ private void traceLocals(Target target) { if (Translator.TRACING_ENABLED) { Klass[] map = (target == null) ? Klass.NO_CLASSES : target.getLocals(); int i = 0; int l = 0; while (i < map.length || l < localTypes.length) { Klass derived = (l < localTypes.length) ? localTypes[l] : null; Klass recorded = (i < map.length) ? map[i] : null; String prefix = " local[" + l + "]: "; traceType(derived, prefix, true); if (recorded != null) { traceType(recorded, prefix, false); } i++; l++; } } }
/** * Traces the state of the operand stack at the current verification address. * * @param target the stack map (if any) at the current verification address */ private void traceStack(Target target) { if (Translator.TRACING_ENABLED) { Klass[] map = target == null ? Klass.NO_CLASSES : target.getStack(); int r = 0; // index into recorded stack (i.e. from stack map) int d = 0; // index into derived stack while (r < map.length || d < sp) { Klass derived = (d < sp) ? getStackTypeAt(d) : null; Klass recorded = (r < map.length) ? map[r] : null; String prefix = " stack[" + r + "]: "; traceType(derived, prefix, true); if (recorded != null) { traceType(recorded, prefix, false); } ++r; ++d; } } }