All Downloads are FREE. Search and download functionalities are using the official Maven repository.

mockit.external.asm.FrameAndStackComputation Maven / Gradle / Ivy

Go to download

JMockit is a Java toolkit for automated developer testing. It contains APIs for the creation of the objects to be tested, for mocking dependencies, and for faking external APIs; JUnit (4 & 5) and TestNG test runners are supported. It also contains an advanced code coverage tool.

There is a newer version: 1.49
Show newest version
package mockit.external.asm;

import mockit.external.asm.Frame.*;

final class FrameAndStackComputation
{
   interface FrameType
   {
      /**
       * An expanded frame.
       */
      int NEW = -1;

      /**
       * A compressed frame with complete frame data.
       */
      int FULL = 0;

      /**
       * A compressed frame where locals are the same as the locals in the previous frame, except that additional 1-3
       * locals are defined, and with an empty stack.
       */
      int APPEND = 1;

      /**
       * A compressed frame where locals are the same as the locals in the previous frame, except that the last 1-3
       * locals are absent and with an empty stack.
       */
      int CHOP = 2;

      /**
       * A compressed frame with exactly the same locals as the previous frame and with an empty stack.
       */
      int SAME = 3;

      /**
       * A compressed frame with exactly the same locals as the previous frame and with a single value on the stack.
       */
      int SAME1 = 4;
   }

   /**
    * Constants that identify how many locals and stack items a frame has, with respect to its previous frame.
    */
   interface LocalsAndStackItemsDiff
   {
      /**
       * Same locals as the previous frame, number of stack items is zero.
       */
      int SAME_FRAME = 0; // to 63 (0-3f)

      /**
       * Same locals as the previous frame, number of stack items is 1.
       */
      int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; // to 127 (40-7f)

      /**
       * Same locals as the previous frame, number of stack items is 1. Offset is bigger then 63.
       */
      int SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247; // f7

      /**
       * Current locals are the same as the locals in the previous frame, except that the k last locals are absent.
       * The value of k is given by the formula 251-frame_type.
       */
      int CHOP_FRAME = 248; // to 250 (f8-fA)

      /**
       * Same locals as the previous frame, number of stack items is zero. Offset is bigger then 63.
       */
      int SAME_FRAME_EXTENDED = 251; // fb

      /**
       * Current locals are the same as the locals in the previous frame, except that k additional locals are defined.
       * The value of k is given by the formula frame_type-251.
       */
      int APPEND_FRAME = 252; // to 254 // fc-fe

      /**
       * Full frame.
       */
      int FULL_FRAME = 255; // ff
   }

   private final MethodWriter mw;
   private final ClassWriter cw;
   private final ConstantPoolGeneration cp;

   /**
    * Maximum stack size of this method.
    */
   private int maxStack;

   /**
    * Maximum number of local variables for this method.
    */
   private int maxLocals;

   /**
    * Number of local variables in the current stack map frame.
    */
   private int currentLocals;

   /**
    * Number of stack map frames in the StackMapTable attribute.
    */
   private int frameCount;

   /**
    * The StackMapTable attribute.
    */
   private ByteVector stackMap;

   /**
    * The offset of the last frame that was written in the StackMapTable attribute.
    */
   private int previousFrameOffset;

   /**
    * The last frame that was written in the StackMapTable attribute.
    *
    * @see #frameDefinition
    */
   private int[] previousFrame;

   /**
    * The current stack map frame.
    * 

* The first element contains the offset of the instruction to which the frame corresponds (frameDefinition[0] = * offset), the second element is the number of locals (frameDefinition[1] = nLocal) and the third one is the number * of stack elements (frameDefinition[2] = nStack). * The local variables start at index 3 (frameDefinition[3 to 3+nLocal-1]) and are followed by the operand stack * values (frameDefinition[3+nLocal...]). *

* All types are encoded as integers, with the same format as the one used in {@link Label}, but limited to BASE * types. */ private int[] frameDefinition; /** * The current index in {@link #frameDefinition}, when writing new values into the array. */ private int frameIndex; FrameAndStackComputation(MethodWriter mw, int methodAccess, String methodDesc) { this.mw = mw; cw = mw.cw; cp = cw.cp; int size = Type.getArgumentsAndReturnSizes(methodDesc) >> 2; if ((methodAccess & Access.STATIC) != 0) { size--; } maxLocals = size; currentLocals = size; } void setMaxStack(int maxStack) { this.maxStack = maxStack; } void updateMaxLocals(int n) { if (n > maxLocals) { maxLocals = n; } } void putMaxStackAndLocals(ByteVector out) { out.putShort(maxStack).putShort(maxLocals); } void readFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) { if (type == FrameType.NEW) { readExpandedFrame(nLocal, local, nStack, stack); } else { int delta = getDeltaForType(type); if (delta < 0) { return; } switch (type) { case FrameType.FULL: readFullCompressedFrame(nLocal, local, nStack, stack, delta); break; case FrameType.APPEND: readCompressedFrame(nLocal, local, delta); break; case FrameType.CHOP: readCompressedFrameWithChoppedLocals(nLocal, delta); break; case FrameType.SAME: readCompressedFrameWithEmptyStack(delta); break; case FrameType.SAME1: readCompressedWithSingleValueOnStack(stack[0], delta); break; } previousFrameOffset = mw.code.length; frameCount++; } maxStack = Math.max(maxStack, nStack); maxLocals = Math.max(maxLocals, currentLocals); } private int getDeltaForType(int type) { int codeLength = mw.code.length; if (stackMap == null) { stackMap = new ByteVector(); return codeLength; } int delta = codeLength - previousFrameOffset - 1; if (delta < 0) { if (type == FrameType.SAME) { return delta; } throw new IllegalStateException("Unexpected frame type: " + type); } return delta; } private void readFullCompressedFrame(int nLocal, Object[] local, int nStack, Object[] stack, int delta) { currentLocals = nLocal; stackMap.putByte(LocalsAndStackItemsDiff.FULL_FRAME).putShort(delta).putShort(nLocal); for (int i = 0; i < nLocal; ++i) { writeFrameType(local[i]); } stackMap.putShort(nStack); for (int i = 0; i < nStack; ++i) { writeFrameType(stack[i]); } } private void readCompressedFrame(int nLocal, Object[] local, int delta) { currentLocals += nLocal; stackMap.putByte(LocalsAndStackItemsDiff.SAME_FRAME_EXTENDED + nLocal).putShort(delta); for (int i = 0; i < nLocal; ++i) { writeFrameType(local[i]); } } private void readCompressedFrameWithChoppedLocals(int nLocal, int delta) { currentLocals -= nLocal; stackMap.putByte(LocalsAndStackItemsDiff.SAME_FRAME_EXTENDED - nLocal).putShort(delta); } private void readCompressedFrameWithEmptyStack(int delta) { if (delta < 64) { stackMap.putByte(delta); } else { stackMap.putByte(LocalsAndStackItemsDiff.SAME_FRAME_EXTENDED).putShort(delta); } } private void readCompressedWithSingleValueOnStack(Object type, int delta) { if (delta < 64) { stackMap.putByte(LocalsAndStackItemsDiff.SAME_LOCALS_1_STACK_ITEM_FRAME + delta); } else { stackMap.putByte(LocalsAndStackItemsDiff.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED).putShort(delta); } writeFrameType(type); } private void readExpandedFrame(int nLocal, Object[] local, int nStack, Object[] stack) { if (previousFrame == null) { visitImplicitFirstFrame(); } currentLocals = nLocal; startFrame(mw.code.length, nLocal, nStack); for (int i = 0; i < nLocal; ++i) { Object localType = local[i]; int frame; if (localType instanceof String) { frame = TypeMask.OBJECT | cp.addType((String) localType); } else if (localType instanceof Integer) { frame = (Integer) localType; } else { frame = TypeMask.UNINITIALIZED | cp.addUninitializedType("", ((Label) localType).position); } writeFrameDefinition(frame); } for (int i = 0; i < nStack; ++i) { Object stackType = stack[i]; int frame; if (stackType instanceof String) { frame = TypeMask.OBJECT | cp.addType((String) stackType); } else if (stackType instanceof Integer) { frame = (Integer) stackType; } else { frame = TypeMask.UNINITIALIZED | cp.addUninitializedType("", ((Label) stackType).position); } writeFrameDefinition(frame); } endFrame(); } private void visitImplicitFirstFrame() { int access = mw.access; String desc = mw.descriptor; // There can be at most descriptor.length() + 1 locals. startFrame(0, desc.length() + 1, 0); if ((access & Access.STATIC) == 0) { int frame = Access.isConstructor(access) ? 6 /* Frame.UNINITIALIZED_THIS */ : TypeMask.OBJECT | cp.addType(cw.thisName); writeFrameDefinition(frame); } int i = 1; loop: while (true) { int j = i; char typeCode = desc.charAt(i++); switch (typeCode) { case 'Z': case 'C': case 'B': case 'S': case 'I': writeFrameDefinition(1); // INTEGER break; case 'F': writeFrameDefinition(2); // FLOAT break; case 'J': writeFrameDefinition(4); // LONG break; case 'D': writeFrameDefinition(3); // DOUBLE break; case '[': i = writeArrayType(desc, i, j); break; case 'L': i = writeReferenceType(desc, i, j); break; default: break loop; } } setNumLocals(frameIndex - 3); endFrame(); } private int getInstructionOffset() { return frameDefinition[0]; } private void setInstructionOffset(int offset) { frameDefinition[0] = offset; } private int getNumLocals() { return frameDefinition[1]; } private void setNumLocals(int numLocals) { frameDefinition[1] = numLocals; } private int getStackSize() { return frameDefinition[2]; } private void setStackSize(int stackSize) { frameDefinition[2] = stackSize; } private void writeFrameDefinition(int value) { frameDefinition[frameIndex++] = value; } private int writeArrayType(String desc, int i, int j) { while (desc.charAt(i) == '[') { i++; } if (desc.charAt(i) == 'L') { i++; while (desc.charAt(i) != ';') { i++; } } int frameValue = TypeMask.OBJECT | cp.addType(desc.substring(j, ++i)); writeFrameDefinition(frameValue); return i; } private int writeReferenceType(String desc, int i, int j) { while (desc.charAt(i) != ';') { i++; } int frameValue = TypeMask.OBJECT | cp.addType(desc.substring(j + 1, i++)); writeFrameDefinition(frameValue); return i; } private void writeFrameType(Object type) { if (type instanceof String) { stackMap.putByte(7).putShort(cp.newClass((String) type)); } else if (type instanceof Integer) { stackMap.putByte((Integer) type); } else { stackMap.putByte(8).putShort(((Label) type).position); } } boolean hasStackMap() { return stackMap != null; } /** * Starts the visit of a stack map frame. * Sets {@link #frameIndex} to the index of the next element to be written in this frame. * * @param offset the offset of the instruction to which the frame corresponds. * @param nLocals the number of local variables in the frame. * @param nStack the number of stack elements in the frame. */ private void startFrame(int offset, int nLocals, int nStack) { int n = 3 + nLocals + nStack; if (frameDefinition == null || frameDefinition.length < n) { frameDefinition = new int[n]; } setInstructionOffset(offset); setNumLocals(nLocals); setStackSize(nStack); frameIndex = 3; } /** * Checks if the visit of the current {@link #frameDefinition frame} is finished, and if yes, write it in the * StackMapTable attribute. */ private void endFrame() { if (previousFrame != null) { // do not write the first frame if (stackMap == null) { stackMap = new ByteVector(); } writeFrame(); frameCount++; } previousFrame = frameDefinition; frameDefinition = null; } /** * Compress and writes the current {@link #frameDefinition frame} in the StackMapTable attribute. */ private void writeFrame() { int currentFrameLocalsSize = getNumLocals(); int currentFrameStackSize = getStackSize(); if (cw.getClassVersion() < ClassVersion.V1_6) { writeFrameForOldVersionOfJava(currentFrameLocalsSize, currentFrameStackSize); return; } int type = LocalsAndStackItemsDiff.FULL_FRAME; int localsSize = previousFrame[1]; int k = currentFrameStackSize == 0 ? currentFrameLocalsSize - localsSize : 0; int delta = getDelta(); if (currentFrameStackSize == 0) { switch (k) { case -3: case -2: case -1: type = LocalsAndStackItemsDiff.CHOP_FRAME; localsSize = currentFrameLocalsSize; break; case 0: type = delta < 64 ? LocalsAndStackItemsDiff.SAME_FRAME : LocalsAndStackItemsDiff.SAME_FRAME_EXTENDED; break; case 1: case 2: case 3: type = LocalsAndStackItemsDiff.APPEND_FRAME; break; } } else if (currentFrameLocalsSize == localsSize && currentFrameStackSize == 1) { type = delta < 63 ? LocalsAndStackItemsDiff.SAME_LOCALS_1_STACK_ITEM_FRAME : LocalsAndStackItemsDiff.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED; } type = chooseTypeAsFullFrameIfApplicable(localsSize, type); switch (type) { case LocalsAndStackItemsDiff.SAME_FRAME: stackMap.putByte(delta); break; case LocalsAndStackItemsDiff.SAME_LOCALS_1_STACK_ITEM_FRAME: stackMap.putByte(LocalsAndStackItemsDiff.SAME_LOCALS_1_STACK_ITEM_FRAME + delta); writeFrameTypes(3 + currentFrameLocalsSize, 4 + currentFrameLocalsSize); break; case LocalsAndStackItemsDiff.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED: stackMap.putByte(LocalsAndStackItemsDiff.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED).putShort(delta); writeFrameTypes(3 + currentFrameLocalsSize, 4 + currentFrameLocalsSize); break; case LocalsAndStackItemsDiff.SAME_FRAME_EXTENDED: stackMap.putByte(LocalsAndStackItemsDiff.SAME_FRAME_EXTENDED).putShort(delta); break; case LocalsAndStackItemsDiff.CHOP_FRAME: stackMap.putByte(LocalsAndStackItemsDiff.SAME_FRAME_EXTENDED + k).putShort(delta); break; case LocalsAndStackItemsDiff.APPEND_FRAME: stackMap.putByte(LocalsAndStackItemsDiff.SAME_FRAME_EXTENDED + k).putShort(delta); writeFrameTypes(3 + localsSize, 3 + currentFrameLocalsSize); break; // case FULL_FRAME: default: stackMap.putByte(LocalsAndStackItemsDiff.FULL_FRAME).putShort(delta).putShort(currentFrameLocalsSize); writeFrameTypes(3, 3 + currentFrameLocalsSize); stackMap.putShort(currentFrameStackSize); writeFrameTypes(3 + currentFrameLocalsSize, 3 + currentFrameLocalsSize + currentFrameStackSize); } } private void writeFrameForOldVersionOfJava(int currentFrameLocalsSize, int currentFrameStackSize) { stackMap.putShort(getInstructionOffset()).putShort(currentFrameLocalsSize); writeFrameTypes(3, 3 + currentFrameLocalsSize); stackMap.putShort(currentFrameStackSize); writeFrameTypes(3 + currentFrameLocalsSize, 3 + currentFrameLocalsSize + currentFrameStackSize); } private int getDelta() { int offset = getInstructionOffset(); return frameCount == 0 ? offset : offset - previousFrame[0] - 1; } private int chooseTypeAsFullFrameIfApplicable(int localsSize, int type) { if (type != LocalsAndStackItemsDiff.FULL_FRAME) { // Verify if locals are the same. int l = 3; for (int j = 0; j < localsSize; j++) { if (frameDefinition[l] != previousFrame[l]) { return LocalsAndStackItemsDiff.FULL_FRAME; } l++; } } return type; } /** * Writes some types of the current {@link #frameDefinition frame} into the StackMapTableAttribute. This method * converts types from the format used in {@link Label} to the format used in StackMapTable attributes. In * particular, it converts type table indexes to constant pool indexes. * * @param start index of the first type in {@link #frameDefinition} to write. * @param end index of last type in {@link #frameDefinition} to write (exclusive). */ private void writeFrameTypes(int start, int end) { for (int i = start; i < end; i++) { int type = frameDefinition[i]; int dimensions = type & TypeMask.DIM; if (dimensions == 0) { writeFrameOfRegularType(type); } else { writeFrameOfArrayType(dimensions, type); } } } private void writeFrameOfRegularType(int type) { int v = type & TypeMask.BASE_VALUE; switch (type & TypeMask.BASE_KIND) { case TypeMask.OBJECT: String classDesc = cp.getInternalName(v); stackMap.putByte(7).putShort(cp.newClass(classDesc)); break; case TypeMask.UNINITIALIZED: int typeDesc = cp.getIntegerItemValue(v); stackMap.putByte(8).putShort(typeDesc); break; default: stackMap.putByte(v); } } private void writeFrameOfArrayType(int arrayDimensions, int arrayElementType) { StringBuilder sb = new StringBuilder(); arrayDimensions >>= 28; while (arrayDimensions-- > 0) { sb.append('['); } if ((arrayElementType & TypeMask.BASE_KIND) == TypeMask.OBJECT) { String arrayElementTypeDesc = cp.getInternalName(arrayElementType & TypeMask.BASE_VALUE); sb.append('L').append(arrayElementTypeDesc).append(';'); } else { switch (arrayElementType & 0xF) { case 1: sb.append('I'); break; case 2: sb.append('F'); break; case 3: sb.append('D'); break; case 9: sb.append('Z'); break; case 10: sb.append('B'); break; case 11: sb.append('C'); break; case 12: sb.append('S'); break; default: sb.append('J'); } } String arrayElementTypeDesc = sb.toString(); stackMap.putByte(7).putShort(cp.newClass(arrayElementTypeDesc)); } // Creates and visits the first (implicit) frame. void createAndVisitFirstFrame(Frame frame) { Type[] args = Type.getArgumentTypes(mw.descriptor); frame.initInputFrame(cw.thisName, cp, mw.access, args, maxLocals); visitFrame(frame); } /** * Visits a frame that has been computed from scratch. */ void visitFrame(Frame f) { int[] locals = f.inputLocals; int nLocal = computeNumberOfLocals(locals); int[] stacks = f.inputStack; int nStack = computeStackSize(stacks); startFrame(f.owner.position, nLocal, nStack); putLocalsOrStackElements(locals, nLocal); putLocalsOrStackElements(stacks, nStack); endFrame(); } // Computes the number of locals (ignores TOP types that are just after a LONG or a DOUBLE, and all trailing TOP // types). private int computeNumberOfLocals(int[] locals) { int nLocal = 0; int nTop = 0; for (int i = 0; i < locals.length; i++) { int t = locals[i]; if (t == TypeMask.TOP) { nTop++; } else { nLocal += nTop + 1; nTop = 0; } if (t == TypeMask.LONG || t == TypeMask.DOUBLE) { i++; } } return nLocal; } // Computes the stack size (ignores TOP types that are just after a LONG or a DOUBLE). private int computeStackSize(int[] stacks) { int nStack = 0; for (int i = 0; i < stacks.length; i++) { int t = stacks[i]; nStack++; if (t == TypeMask.LONG || t == TypeMask.DOUBLE) { i++; } } return nStack; } private void putLocalsOrStackElements(int[] itemIndices, int nItems) { for (int i = 0; nItems > 0; i++, nItems--) { int itemType = itemIndices[i]; writeFrameDefinition(itemType); if (itemType == TypeMask.LONG || itemType == TypeMask.DOUBLE) { i++; } } } void emitFrameForUnreachableBlock(int startOffset) { startFrame(startOffset, 0, 1); int frameValue = TypeMask.OBJECT | cp.addType("java/lang/Throwable"); writeFrameDefinition(frameValue); endFrame(); } int getSize() { return stackMap == null ? 0 : 8 + stackMap.length; } int getSizeWhileAddingConstantPoolItem() { int size = getSize(); if (size > 0) { boolean zip = cw.getClassVersion() >= ClassVersion.V1_6; cp.newUTF8(zip ? "StackMapTable" : "StackMap"); } return size; } void put(ByteVector out) { if (stackMap != null) { boolean zip = cw.getClassVersion() >= ClassVersion.V1_6; out.putShort(cp.newUTF8(zip ? "StackMapTable" : "StackMap")); out.putInt(stackMap.length + 2).putShort(frameCount); out.putByteVector(stackMap); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy