diff --git a/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/ClassReader.java b/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/ClassReader.java index 69986a9b..df770d38 100644 --- a/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/ClassReader.java +++ b/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/ClassReader.java @@ -188,13 +188,14 @@ public ClassReader( * @param classFileOffset the offset in byteBuffer of the first byte of the ClassFile to be read. * @param checkClassVersion whether to check the class version or not. */ + @SuppressWarnings("PMD.ConstructorCallsOverridableMethod") ClassReader( final byte[] classFileBuffer, final int classFileOffset, final boolean checkClassVersion) { this.classFileBuffer = classFileBuffer; this.b = classFileBuffer; // Check the class' major_version. This field is after the magic and minor_version fields, which // use 4 and 2 bytes respectively. - if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V21) { + if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V22) { throw new IllegalArgumentException( "Unsupported class file major version " + readShort(classFileOffset + 6)); } diff --git a/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/ClassWriter.java b/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/ClassWriter.java index 88f814d6..c173b3ae 100644 --- a/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/ClassWriter.java +++ b/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/ClassWriter.java @@ -217,6 +217,7 @@ public class ClassWriter extends ClassVisitor { /** * Indicates what must be automatically computed in {@link MethodWriter}. Must be one of {@link * MethodWriter#COMPUTE_NOTHING}, {@link MethodWriter#COMPUTE_MAX_STACK_AND_LOCAL}, {@link + * MethodWriter#COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES}, {@link * MethodWriter#COMPUTE_INSERTED_FRAMES}, or {@link MethodWriter#COMPUTE_ALL_FRAMES}. */ private int compute; diff --git a/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/Frame.java b/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/Frame.java index fb97309d..bb816286 100644 --- a/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/Frame.java +++ b/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/Frame.java @@ -64,8 +64,8 @@ * right shift of {@link #DIM_SHIFT}. *
  • the KIND field, stored in 4 bits, indicates the kind of VALUE used. These 4 bits can be * retrieved with {@link #KIND_MASK} and, without any shift, must be equal to {@link - * #CONSTANT_KIND}, {@link #REFERENCE_KIND}, {@link #UNINITIALIZED_KIND}, {@link #LOCAL_KIND} - * or {@link #STACK_KIND}. + * #CONSTANT_KIND}, {@link #REFERENCE_KIND}, {@link #UNINITIALIZED_KIND}, {@link + * #FORWARD_UNINITIALIZED_KIND},{@link #LOCAL_KIND} or {@link #STACK_KIND}. *
  • the FLAGS field, stored in 2 bits, contains up to 2 boolean flags. Currently only one flag * is defined, namely {@link #TOP_IF_LONG_OR_DOUBLE_FLAG}. *
  • the VALUE field, stored in the remaining 20 bits, contains either @@ -78,7 +78,10 @@ *
  • the index of a {@link Symbol#TYPE_TAG} {@link Symbol} in the type table of a {@link * SymbolTable}, if KIND is equal to {@link #REFERENCE_KIND}. *
  • the index of an {@link Symbol#UNINITIALIZED_TYPE_TAG} {@link Symbol} in the type - * table of a SymbolTable, if KIND is equal to {@link #UNINITIALIZED_KIND}. + * table of a {@link SymbolTable}, if KIND is equal to {@link #UNINITIALIZED_KIND}. + *
  • the index of a {@link Symbol#FORWARD_UNINITIALIZED_TYPE_TAG} {@link Symbol} in the + * type table of a {@link SymbolTable}, if KIND is equal to {@link + * #FORWARD_UNINITIALIZED_KIND}. *
  • the index of a local variable in the input stack frame, if KIND is equal to {@link * #LOCAL_KIND}. *
  • a position relatively to the top of the stack of the input stack frame, if KIND is @@ -88,10 +91,10 @@ * *

    Output frames can contain abstract types of any kind and with a positive or negative array * dimension (and even unassigned types, represented by 0 - which does not correspond to any valid - * abstract type value). Input frames can only contain CONSTANT_KIND, REFERENCE_KIND or - * UNINITIALIZED_KIND abstract types of positive or {@literal null} array dimension. In all cases - * the type table contains only internal type names (array type descriptors are forbidden - array - * dimensions must be represented through the DIM field). + * abstract type value). Input frames can only contain CONSTANT_KIND, REFERENCE_KIND, + * UNINITIALIZED_KIND or FORWARD_UNINITIALIZED_KIND abstract types of positive or {@literal null} + * array dimension. In all cases the type table contains only internal type names (array type + * descriptors are forbidden - array dimensions must be represented through the DIM field). * *

    The LONG and DOUBLE types are always represented by using two slots (LONG + TOP or DOUBLE + * TOP), for local variables as well as in the operand stack. This is necessary to be able to @@ -159,8 +162,9 @@ class Frame { private static final int CONSTANT_KIND = 1 << KIND_SHIFT; private static final int REFERENCE_KIND = 2 << KIND_SHIFT; private static final int UNINITIALIZED_KIND = 3 << KIND_SHIFT; - private static final int LOCAL_KIND = 4 << KIND_SHIFT; - private static final int STACK_KIND = 5 << KIND_SHIFT; + private static final int FORWARD_UNINITIALIZED_KIND = 4 << KIND_SHIFT; + private static final int LOCAL_KIND = 5 << KIND_SHIFT; + private static final int STACK_KIND = 6 << KIND_SHIFT; // Possible flags for the FLAGS field of an abstract type. @@ -220,13 +224,13 @@ class Frame { /** * The abstract types that are initialized in the basic block. A constructor invocation on an - * UNINITIALIZED or UNINITIALIZED_THIS abstract type must replace every occurrence of this - * type in the local variables and in the operand stack. This cannot be done during the first step - * of the algorithm since, during this step, the local variables and the operand stack types are - * still abstract. It is therefore necessary to store the abstract types of the constructors which - * are invoked in the basic block, in order to do this replacement during the second step of the - * algorithm, where the frames are fully computed. Note that this array can contain abstract types - * that are relative to the input locals or to the input stack. + * UNINITIALIZED, FORWARD_UNINITIALIZED or UNINITIALIZED_THIS abstract type must replace every + * occurrence of this type in the local variables and in the operand stack. This cannot be + * done during the first step of the algorithm since, during this step, the local variables and + * the operand stack types are still abstract. It is therefore necessary to store the abstract + * types of the constructors which are invoked in the basic block, in order to do this replacement + * during the second step of the algorithm, where the frames are fully computed. Note that this + * array can contain abstract types that are relative to the input locals or to the input stack. */ private int[] initializations; @@ -284,8 +288,12 @@ static int getAbstractTypeFromApiFormat(final SymbolTable symbolTable, final Obj String descriptor = Type.getObjectType((String) type).getDescriptor(); return getAbstractTypeFromDescriptor(symbolTable, descriptor, 0); } else { - return UNINITIALIZED_KIND - | symbolTable.addUninitializedType("", ((Label) type).bytecodeOffset); + Label label = (Label) type; + if ((label.flags & Label.FLAG_RESOLVED) != 0) { + return UNINITIALIZED_KIND | symbolTable.addUninitializedType("", label.bytecodeOffset); + } else { + return FORWARD_UNINITIALIZED_KIND | symbolTable.addForwardUninitializedType("", label); + } } } @@ -637,12 +645,14 @@ private void addInitializedType(final int abstractType) { * @param symbolTable the type table to use to lookup and store type {@link Symbol}. * @param abstractType an abstract type. * @return the REFERENCE_KIND abstract type corresponding to abstractType if it is - * UNINITIALIZED_THIS or an UNINITIALIZED_KIND abstract type for one of the types on which a - * constructor is invoked in the basic block. Otherwise returns abstractType. + * UNINITIALIZED_THIS or an UNINITIALIZED_KIND or FORWARD_UNINITIALIZED_KIND abstract type for + * one of the types on which a constructor is invoked in the basic block. Otherwise returns + * abstractType. */ private int getInitializedType(final SymbolTable symbolTable, final int abstractType) { if (abstractType == UNINITIALIZED_THIS - || (abstractType & (DIM_MASK | KIND_MASK)) == UNINITIALIZED_KIND) { + || (abstractType & (DIM_MASK | KIND_MASK)) == UNINITIALIZED_KIND + || (abstractType & (DIM_MASK | KIND_MASK)) == FORWARD_UNINITIALIZED_KIND) { for (int i = 0; i < initializationCount; ++i) { int initializedType = initializations[i]; int dim = initializedType & DIM_MASK; @@ -1253,11 +1263,12 @@ final boolean merge( * * @param symbolTable the type table to use to lookup and store type {@link Symbol}. * @param sourceType the abstract type with which the abstract type array element must be merged. - * This type should be of {@link #CONSTANT_KIND}, {@link #REFERENCE_KIND} or {@link - * #UNINITIALIZED_KIND} kind, with positive or {@literal null} array dimensions. + * This type should be of {@link #CONSTANT_KIND}, {@link #REFERENCE_KIND}, {@link + * #UNINITIALIZED_KIND} or {@link #FORWARD_UNINITIALIZED_KIND} kind, with positive or + * {@literal null} array dimensions. * @param dstTypes an array of abstract types. These types should be of {@link #CONSTANT_KIND}, - * {@link #REFERENCE_KIND} or {@link #UNINITIALIZED_KIND} kind, with positive or {@literal - * null} array dimensions. + * {@link #REFERENCE_KIND}, {@link #UNINITIALIZED_KIND} or {@link #FORWARD_UNINITIALIZED_KIND} + * kind, with positive or {@literal null} array dimensions. * @param dstIndex the index of the type that must be merged in dstTypes. * @return {@literal true} if the type array has been modified by this operation. */ @@ -1400,7 +1411,8 @@ final void accept(final MethodWriter methodWriter) { * * @param symbolTable the type table to use to lookup and store type {@link Symbol}. * @param abstractType an abstract type, restricted to {@link Frame#CONSTANT_KIND}, {@link - * Frame#REFERENCE_KIND} or {@link Frame#UNINITIALIZED_KIND} types. + * Frame#REFERENCE_KIND}, {@link Frame#UNINITIALIZED_KIND} or {@link + * Frame#FORWARD_UNINITIALIZED_KIND} types. * @param output where the abstract type must be put. * @see JVMS * 4.7.4 @@ -1422,6 +1434,10 @@ static void putAbstractType( case UNINITIALIZED_KIND: output.putByte(ITEM_UNINITIALIZED).putShort((int) symbolTable.getType(typeValue).data); break; + case FORWARD_UNINITIALIZED_KIND: + output.putByte(ITEM_UNINITIALIZED); + symbolTable.getForwardUninitializedLabel(typeValue).put(output); + break; default: throw new AssertionError(); } diff --git a/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/Label.java b/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/Label.java index 28b175b7..0aa4be27 100644 --- a/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/Label.java +++ b/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/Label.java @@ -116,6 +116,13 @@ public class Label { */ static final int FORWARD_REFERENCE_TYPE_WIDE = 0x20000000; + /** + * The type of forward references stored in two bytes in the stack map table. This is the + * case of the labels of {@link Frame#ITEM_UNINITIALIZED} stack map frame elements, when the NEW + * instruction is after the <init> constructor call (in bytecode offset order). + */ + static final int FORWARD_REFERENCE_TYPE_STACK_MAP = 0x30000000; + /** * The bit mask to extract the 'handle' of a forward reference to this label. The extracted handle * is the bytecode offset where the forward reference value is stored (using either 2 or 4 bytes, @@ -404,6 +411,20 @@ final void put( } } + /** + * Puts a reference to this label in the stack map table of a method. If the bytecode + * offset of the label is known, it is written directly. Otherwise, a null relative offset is + * written and a new forward reference is declared for this label. + * + * @param stackMapTableEntries the stack map table where the label offset must be added. + */ + final void put(final ByteVector stackMapTableEntries) { + if ((flags & FLAG_RESOLVED) == 0) { + addForwardReference(0, FORWARD_REFERENCE_TYPE_STACK_MAP, stackMapTableEntries.length); + } + stackMapTableEntries.putShort(bytecodeOffset); + } + /** * Adds a forward reference to this label. This method must be called only for a true forward * reference, i.e. only if this label is not resolved yet. For backward references, the relative @@ -436,9 +457,12 @@ private void addForwardReference( * Sets the bytecode offset of this label to the given value and resolves the forward references * to this label, if any. This method must be called when this label is added to the bytecode of * the method, i.e. when its bytecode offset becomes known. This method fills in the blanks that - * where left in the bytecode by each forward reference previously added to this label. + * where left in the bytecode (and optionally in the stack map table) by each forward reference + * previously added to this label. * * @param code the bytecode of the method. + * @param stackMapTableEntries the 'entries' array of the StackMapTable code attribute of the + * method. Maybe {@literal null}. * @param bytecodeOffset the bytecode offset of this label. * @return {@literal true} if a blank that was left for this label was too small to store the * offset. In such a case the corresponding jump instruction is replaced with an equivalent @@ -446,7 +470,8 @@ private void addForwardReference( * instructions are later replaced with standard bytecode instructions with wider offsets (4 * bytes instead of 2), in ClassReader. */ - final boolean resolve(final byte[] code, final int bytecodeOffset) { + final boolean resolve( + final byte[] code, final ByteVector stackMapTableEntries, final int bytecodeOffset) { this.flags |= FLAG_RESOLVED; this.bytecodeOffset = bytecodeOffset; if (forwardReferences == null) { @@ -476,11 +501,14 @@ final boolean resolve(final byte[] code, final int bytecodeOffset) { } code[handle++] = (byte) (relativeOffset >>> 8); code[handle] = (byte) relativeOffset; - } else { + } else if ((reference & FORWARD_REFERENCE_TYPE_MASK) == FORWARD_REFERENCE_TYPE_WIDE) { code[handle++] = (byte) (relativeOffset >>> 24); code[handle++] = (byte) (relativeOffset >>> 16); code[handle++] = (byte) (relativeOffset >>> 8); code[handle] = (byte) relativeOffset; + } else { + stackMapTableEntries.data[handle++] = (byte) (bytecodeOffset >>> 8); + stackMapTableEntries.data[handle] = (byte) bytecodeOffset; } } return hasAsmInstructions; diff --git a/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/MethodWriter.java b/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/MethodWriter.java index 3663b502..523bace1 100644 --- a/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/MethodWriter.java +++ b/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/MethodWriter.java @@ -534,8 +534,9 @@ final class MethodWriter extends MethodVisitor { * the number of stack elements. The local variables start at index 3 and are followed by the * operand stack elements. In summary frame[0] = offset, frame[1] = numLocal, frame[2] = numStack. * Local variables and operand stack entries contain abstract types, as defined in {@link Frame}, - * but restricted to {@link Frame#CONSTANT_KIND}, {@link Frame#REFERENCE_KIND} or {@link - * Frame#UNINITIALIZED_KIND} abstract types. Long and double types use only one array entry. + * but restricted to {@link Frame#CONSTANT_KIND}, {@link Frame#REFERENCE_KIND}, {@link + * Frame#UNINITIALIZED_KIND} or {@link Frame#FORWARD_UNINITIALIZED_KIND} abstract types. Long and + * double types use only one array entry. */ private int[] currentFrame; @@ -693,7 +694,7 @@ public AnnotationVisitor visitParameterAnnotation( if (visible) { if (lastRuntimeVisibleParameterAnnotations == null) { lastRuntimeVisibleParameterAnnotations = - new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; + new AnnotationWriter[Type.getArgumentCount(descriptor)]; } return lastRuntimeVisibleParameterAnnotations[parameter] = AnnotationWriter.create( @@ -701,7 +702,7 @@ public AnnotationVisitor visitParameterAnnotation( } else { if (lastRuntimeInvisibleParameterAnnotations == null) { lastRuntimeInvisibleParameterAnnotations = - new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; + new AnnotationWriter[Type.getArgumentCount(descriptor)]; } return lastRuntimeInvisibleParameterAnnotations[parameter] = AnnotationWriter.create( @@ -1199,7 +1200,7 @@ public void visitJumpInsn(final int opcode, final Label label) { @Override public void visitLabel(final Label label) { // Resolve the forward references to this label, if any. - hasAsmInstructions |= label.resolve(code.data, code.length); + hasAsmInstructions |= label.resolve(code.data, stackMapTableEntries, code.length); // visitLabel starts a new basic block (except for debug only labels), so we need to update the // previous and current block references and list of successors. if ((label.flags & Label.FLAG_DEBUG_ONLY) != 0) { @@ -1795,7 +1796,7 @@ private void endCurrentBasicBlockWithNoSuccessor() { if (compute == COMPUTE_ALL_FRAMES) { Label nextBasicBlock = new Label(); nextBasicBlock.frame = new Frame(nextBasicBlock); - nextBasicBlock.resolve(code.data, code.length); + nextBasicBlock.resolve(code.data, stackMapTableEntries, code.length); lastBasicBlock.nextBasicBlock = nextBasicBlock; lastBasicBlock = nextBasicBlock; currentBasicBlock = null; @@ -1979,9 +1980,8 @@ private void putFrameType(final Object type) { .putByte(Frame.ITEM_OBJECT) .putShort(symbolTable.addConstantClass((String) type).index); } else { - stackMapTableEntries - .putByte(Frame.ITEM_UNINITIALIZED) - .putShort(((Label) type).bytecodeOffset); + stackMapTableEntries.putByte(Frame.ITEM_UNINITIALIZED); + ((Label) type).put(stackMapTableEntries); } } diff --git a/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/Opcodes.java b/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/Opcodes.java index 824009b1..9ce86b6f 100644 --- a/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/Opcodes.java +++ b/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/Opcodes.java @@ -287,6 +287,7 @@ public interface Opcodes { int V19 = 0 << 16 | 63; int V20 = 0 << 16 | 64; int V21 = 0 << 16 | 65; + int V22 = 0 << 16 | 66; /** * Version flag indicating that the class is using 'preview' features. diff --git a/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/Symbol.java b/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/Symbol.java index db6f0c11..c4969ac6 100644 --- a/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/Symbol.java +++ b/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/Symbol.java @@ -103,12 +103,25 @@ abstract class Symbol { static final int TYPE_TAG = 128; /** - * The tag value of an {@link Frame#ITEM_UNINITIALIZED} type entry in the type table of a class. + * The tag value of an uninitialized type entry in the type table of a class. This type is used + * for the normal case where the NEW instruction is before the <init> constructor call (in + * bytecode offset order), i.e. when the label of the NEW instruction is resolved when the + * constructor call is visited. If the NEW instruction is after the constructor call, use the + * {@link #FORWARD_UNINITIALIZED_TYPE_TAG} tag value instead. */ static final int UNINITIALIZED_TYPE_TAG = 129; + /** + * The tag value of an uninitialized type entry in the type table of a class. This type is used + * for the unusual case where the NEW instruction is after the <init> constructor call (in + * bytecode offset order), i.e. when the label of the NEW instruction is not resolved when the + * constructor call is visited. If the NEW instruction is before the constructor call, use the + * {@link #UNINITIALIZED_TYPE_TAG} tag value instead. + */ + static final int FORWARD_UNINITIALIZED_TYPE_TAG = 130; + /** The tag value of a merged type entry in the (ASM specific) type table of a class. */ - static final int MERGED_TYPE_TAG = 130; + static final int MERGED_TYPE_TAG = 131; // Instance fields. @@ -151,8 +164,8 @@ abstract class Symbol { * #CONSTANT_INVOKE_DYNAMIC_TAG} symbols, *

  • an arbitrary string for {@link #CONSTANT_UTF8_TAG} and {@link #CONSTANT_STRING_TAG} * symbols, - *
  • an internal class name for {@link #CONSTANT_CLASS_TAG}, {@link #TYPE_TAG} and {@link - * #UNINITIALIZED_TYPE_TAG} symbols, + *
  • an internal class name for {@link #CONSTANT_CLASS_TAG}, {@link #TYPE_TAG}, {@link + * #UNINITIALIZED_TYPE_TAG} and {@link #FORWARD_UNINITIALIZED_TYPE_TAG} symbols, *
  • {@literal null} for the other types of symbol. * */ @@ -172,6 +185,9 @@ abstract class Symbol { * {@link #CONSTANT_DYNAMIC_TAG} or {@link #BOOTSTRAP_METHOD_TAG} symbols, *
  • the bytecode offset of the NEW instruction that created an {@link * Frame#ITEM_UNINITIALIZED} type for {@link #UNINITIALIZED_TYPE_TAG} symbols, + *
  • the index of the {@link Label} (in the {@link SymbolTable#labelTable} table) of the NEW + * instruction that created an {@link Frame#ITEM_UNINITIALIZED} type for {@link + * #FORWARD_UNINITIALIZED_TYPE_TAG} symbols, *
  • the indices (in the class' type table) of two {@link #TYPE_TAG} source types for {@link * #MERGED_TYPE_TAG} symbols, *
  • 0 for the other types of symbol. diff --git a/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/SymbolTable.java b/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/SymbolTable.java index 3e96d467..0d71aeea 100644 --- a/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/SymbolTable.java +++ b/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/SymbolTable.java @@ -108,11 +108,35 @@ final class SymbolTable { * An ASM specific type table used to temporarily store internal names that will not necessarily * be stored in the constant pool. This type table is used by the control flow and data flow * analysis algorithm used to compute stack map frames from scratch. This array stores {@link - * Symbol#TYPE_TAG} and {@link Symbol#UNINITIALIZED_TYPE_TAG}) Symbol. The type symbol at index - * {@code i} has its {@link Symbol#index} equal to {@code i} (and vice versa). + * Symbol#TYPE_TAG}, {@link Symbol#UNINITIALIZED_TYPE_TAG},{@link + * Symbol#FORWARD_UNINITIALIZED_TYPE_TAG} and {@link Symbol#MERGED_TYPE_TAG} entries. The type + * symbol at index {@code i} has its {@link Symbol#index} equal to {@code i} (and vice versa). */ private Entry[] typeTable; + /** + * The actual number of {@link LabelEntry} in {@link #labelTable}. These elements are stored from + * index 0 to labelCount (excluded). The other array entries are empty. These label entries are + * also stored in the {@link #labelEntries} hash set. + */ + private int labelCount; + + /** + * The labels corresponding to the "forward uninitialized" types in the ASM specific {@link + * typeTable} (see {@link Symbol#FORWARD_UNINITIALIZED_TYPE_TAG}). The label entry at index {@code + * i} has its {@link LabelEntry#index} equal to {@code i} (and vice versa). + */ + private LabelEntry[] labelTable; + + /** + * A hash set of all the {@link LabelEntry} elements in the {@link #labelTable}. Each {@link + * LabelEntry} instance is stored at the array index given by its hash code modulo the array size. + * If several entries must be stored at the same array index, they are linked together via their + * {@link LabelEntry#next} field. The {@link #getOrAddLabelEntry(Label)} method ensures that this + * table does not contain duplicated entries. + */ + private LabelEntry[] labelEntries; + /** * Constructs a new, empty SymbolTable for the given ClassWriter. * @@ -1129,6 +1153,18 @@ Symbol getType(final int typeIndex) { return typeTable[typeIndex]; } + /** + * Returns the label corresponding to the "forward uninitialized" type table element whose index + * is given. + * + * @param typeIndex the type table index of a "forward uninitialized" type table element. + * @return the label corresponding of the NEW instruction which created this "forward + * uninitialized" type. + */ + Label getForwardUninitializedLabel(final int typeIndex) { + return labelTable[(int) typeTable[typeIndex].data].label; + } + /** * Adds a type in the type table of this symbol table. Does nothing if the type table already * contains a similar type. @@ -1149,13 +1185,13 @@ int addType(final String value) { } /** - * Adds an {@link Frame#ITEM_UNINITIALIZED} type in the type table of this symbol table. Does - * nothing if the type table already contains a similar type. + * Adds an uninitialized type in the type table of this symbol table. Does nothing if the type + * table already contains a similar type. * * @param value an internal class name. - * @param bytecodeOffset the bytecode offset of the NEW instruction that created this {@link - * Frame#ITEM_UNINITIALIZED} type value. - * @return the index of a new or already existing type Symbol with the given value. + * @param bytecodeOffset the bytecode offset of the NEW instruction that created this + * uninitialized type value. + * @return the index of a new or already existing type #@link Symbol} with the given value. */ int addUninitializedType(final String value, final int bytecodeOffset) { int hashCode = hash(Symbol.UNINITIALIZED_TYPE_TAG, value, bytecodeOffset); @@ -1173,6 +1209,32 @@ int addUninitializedType(final String value, final int bytecodeOffset) { new Entry(typeCount, Symbol.UNINITIALIZED_TYPE_TAG, value, bytecodeOffset, hashCode)); } + /** + * Adds a "forward uninitialized" type in the type table of this symbol table. Does nothing if the + * type table already contains a similar type. + * + * @param value an internal class name. + * @param label the label of the NEW instruction that created this uninitialized type value. If + * the label is resolved, use the {@link #addUninitializedType} method instead. + * @return the index of a new or already existing type {@link Symbol} with the given value. + */ + int addForwardUninitializedType(final String value, final Label label) { + int labelIndex = getOrAddLabelEntry(label).index; + int hashCode = hash(Symbol.FORWARD_UNINITIALIZED_TYPE_TAG, value, labelIndex); + Entry entry = get(hashCode); + while (entry != null) { + if (entry.tag == Symbol.FORWARD_UNINITIALIZED_TYPE_TAG + && entry.hashCode == hashCode + && entry.data == labelIndex + && entry.value.equals(value)) { + return entry.index; + } + entry = entry.next; + } + return addTypeInternal( + new Entry(typeCount, Symbol.FORWARD_UNINITIALIZED_TYPE_TAG, value, labelIndex, hashCode)); + } + /** * Adds a merged type in the type table of this symbol table. Does nothing if the type table * already contains a similar type. @@ -1225,6 +1287,59 @@ private int addTypeInternal(final Entry entry) { return put(entry).index; } + /** + * Returns the {@link LabelEntry} corresponding to the given label. Creates a new one if there is + * no such entry. + * + * @param label the {@link Label} of a NEW instruction which created an uninitialized type, in the + * case where this NEW instruction is after the <init> constructor call (in bytecode + * offset order). See {@link Symbol#FORWARD_UNINITIALIZED_TYPE_TAG}. + * @return the {@link LabelEntry} corresponding to {@code label}. + */ + private LabelEntry getOrAddLabelEntry(final Label label) { + if (labelEntries == null) { + labelEntries = new LabelEntry[16]; + labelTable = new LabelEntry[16]; + } + int hashCode = System.identityHashCode(label); + LabelEntry labelEntry = labelEntries[hashCode % labelEntries.length]; + while (labelEntry != null && labelEntry.label != label) { + labelEntry = labelEntry.next; + } + if (labelEntry != null) { + return labelEntry; + } + + if (labelCount > (labelEntries.length * 3) / 4) { + int currentCapacity = labelEntries.length; + int newCapacity = currentCapacity * 2 + 1; + LabelEntry[] newLabelEntries = new LabelEntry[newCapacity]; + for (int i = currentCapacity - 1; i >= 0; --i) { + LabelEntry currentEntry = labelEntries[i]; + while (currentEntry != null) { + int newCurrentEntryIndex = System.identityHashCode(currentEntry.label) % newCapacity; + LabelEntry nextEntry = currentEntry.next; + currentEntry.next = newLabelEntries[newCurrentEntryIndex]; + newLabelEntries[newCurrentEntryIndex] = currentEntry; + currentEntry = nextEntry; + } + } + labelEntries = newLabelEntries; + } + if (labelCount == labelTable.length) { + LabelEntry[] newLabelTable = new LabelEntry[2 * labelTable.length]; + System.arraycopy(labelTable, 0, newLabelTable, 0, labelTable.length); + labelTable = newLabelTable; + } + + labelEntry = new LabelEntry(labelCount, label); + int index = hashCode % labelEntries.length; + labelEntry.next = labelEntries[index]; + labelEntries[index] = labelEntry; + labelTable[labelCount++] = labelEntry; + return labelEntry; + } + // ----------------------------------------------------------------------------------------------- // Static helper methods to compute hash codes. // ----------------------------------------------------------------------------------------------- @@ -1275,7 +1390,7 @@ private static int hash( * * @author Eric Bruneton */ - private static class Entry extends Symbol { + private static final class Entry extends Symbol { /** The hash code of this entry. */ final int hashCode; @@ -1319,4 +1434,30 @@ private static class Entry extends Symbol { this.hashCode = hashCode; } } + + /** + * A label corresponding to a "forward uninitialized" type in the ASM specific {@link + * SymbolTable#typeTable} (see {@link Symbol#FORWARD_UNINITIALIZED_TYPE_TAG}). + * + * @author Eric Bruneton + */ + private static final class LabelEntry { + + /** The index of this label entry in the {@link SymbolTable#labelTable} array. */ + final int index; + + /** The value of this label entry. */ + final Label label; + + /** + * Another entry (and so on recursively) having the same hash code (modulo the size of {@link + * SymbolTable#labelEntries}}) as this one. + */ + LabelEntry next; + + LabelEntry(final int index, final Label label) { + this.index = index; + this.label = label; + } + } } diff --git a/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/Type.java b/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/Type.java index 00c56001..ea32d4e3 100644 --- a/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/Type.java +++ b/org.eclipse.sisu.inject/src/main/java/org/eclipse/sisu/space/asm/Type.java @@ -295,26 +295,12 @@ public Type[] getArgumentTypes() { */ public static Type[] getArgumentTypes(final String methodDescriptor) { // First step: compute the number of argument types in methodDescriptor. - int numArgumentTypes = 0; - // Skip the first character, which is always a '('. - int currentOffset = 1; - // Parse the argument types, one at a each loop iteration. - while (methodDescriptor.charAt(currentOffset) != ')') { - while (methodDescriptor.charAt(currentOffset) == '[') { - currentOffset++; - } - if (methodDescriptor.charAt(currentOffset++) == 'L') { - // Skip the argument descriptor content. - int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset); - currentOffset = Math.max(currentOffset, semiColumnOffset + 1); - } - ++numArgumentTypes; - } + int numArgumentTypes = getArgumentCount(methodDescriptor); // Second step: create a Type instance for each argument type. Type[] argumentTypes = new Type[numArgumentTypes]; // Skip the first character, which is always a '('. - currentOffset = 1; + int currentOffset = 1; // Parse and create the argument types, one at each loop iteration. int currentArgumentTypeIndex = 0; while (methodDescriptor.charAt(currentOffset) != ')') { @@ -702,6 +688,43 @@ public int getSize() { } } + /** + * Returns the number of arguments of this method type. This method should only be used for method + * types. + * + * @return the number of arguments of this method type. Each argument counts for 1, even long and + * double ones. The implicit @literal{this} argument is not counted. + */ + public int getArgumentCount() { + return getArgumentCount(getDescriptor()); + } + + /** + * Returns the number of arguments in the given method descriptor. + * + * @param methodDescriptor a method descriptor. + * @return the number of arguments in the given method descriptor. Each argument counts for 1, + * even long and double ones. The implicit @literal{this} argument is not counted. + */ + public static int getArgumentCount(final String methodDescriptor) { + int argumentCount = 0; + // Skip the first character, which is always a '('. + int currentOffset = 1; + // Parse the argument types, one at a each loop iteration. + while (methodDescriptor.charAt(currentOffset) != ')') { + while (methodDescriptor.charAt(currentOffset) == '[') { + currentOffset++; + } + if (methodDescriptor.charAt(currentOffset++) == 'L') { + // Skip the argument descriptor content. + int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset); + currentOffset = Math.max(currentOffset, semiColumnOffset + 1); + } + ++argumentCount; + } + return argumentCount; + } + /** * Returns the size of the arguments and of the return value of methods of this type. This method * should only be used for method types. @@ -709,7 +732,8 @@ public int getSize() { * @return the size of the arguments of the method (plus one for the implicit this argument), * argumentsSize, and the size of its return value, returnSize, packed into a single int i = * {@code (argumentsSize << 2) | returnSize} (argumentsSize is therefore equal to {@code - * i >> 2}, and returnSize to {@code i & 0x03}). + * i >> 2}, and returnSize to {@code i & 0x03}). Long and double values have size 2, + * the others have size 1. */ public int getArgumentsAndReturnSizes() { return getArgumentsAndReturnSizes(getDescriptor()); @@ -722,7 +746,8 @@ public int getArgumentsAndReturnSizes() { * @return the size of the arguments of the method (plus one for the implicit this argument), * argumentsSize, and the size of its return value, returnSize, packed into a single int i = * {@code (argumentsSize << 2) | returnSize} (argumentsSize is therefore equal to {@code - * i >> 2}, and returnSize to {@code i & 0x03}). + * i >> 2}, and returnSize to {@code i & 0x03}). Long and double values have size 2, + * the others have size 1. */ public static int getArgumentsAndReturnSizes(final String methodDescriptor) { int argumentsSize = 1;