From 31084618f4728fcd2416583a3fa90a71b6aa896a Mon Sep 17 00:00:00 2001 From: Col-E Date: Wed, 5 Oct 2022 23:25:37 -0400 Subject: [PATCH 1/2] Add support for Java 11+ Change bytecode handling to explicitly fail rather than assume a type in switch cases Use objenesis instead of direct sun reflection-factory for instance creation Remove junit platform exclusion, resolving some failures with IntelliJ integration --- .gitignore | 4 +- main/pom.xml | 7 +- .../asm/classes/BootstrapMethodsWriter.java | 2 +- main/src/mockit/asm/classes/ClassWriter.java | 4 +- .../asm/classes/ConstantPoolCopying.java | 38 +++++++--- .../constantPool/ConstantPoolGeneration.java | 20 +++-- .../mockit/asm/constantPool/DynamicItem.java | 4 +- main/src/mockit/asm/constantPool/IntItem.java | 2 +- .../mockit/asm/constantPool/ModuleItem.java | 41 ++++++++++ .../mockit/asm/constantPool/PackageItem.java | 41 ++++++++++ .../mockit/asm/controlFlow/CFGAnalysis.java | 3 +- main/src/mockit/asm/controlFlow/Frame.java | 74 +++++++++++++++++-- .../asm/controlFlow/StackMapTableWriter.java | 5 +- main/src/mockit/asm/jvmConstants/Access.java | 2 +- .../mockit/asm/jvmConstants/ClassVersion.java | 20 ++++- .../asm/jvmConstants/ConstantPoolTypes.java | 32 ++++---- main/src/mockit/asm/methods/MethodReader.java | 2 +- main/src/mockit/asm/util/BytecodeReader.java | 61 +++++++++++---- main/src/mockit/asm/util/MethodHandle.java | 14 ++-- .../mockit/internal/BaseClassModifier.java | 4 +- .../invocation/ExpectedInvocation.java | 8 +- .../mocking/BaseTypeRedefinition.java | 2 +- .../expectations/mocking/InstanceFactory.java | 26 +------ .../InvocationBlockModifier.java | 5 +- 24 files changed, 317 insertions(+), 104 deletions(-) create mode 100644 main/src/mockit/asm/constantPool/ModuleItem.java create mode 100644 main/src/mockit/asm/constantPool/PackageItem.java diff --git a/.gitignore b/.gitignore index 593f30637..0d8076f96 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -/.shelf/ -/.idea/ +.shelf/ +.idea/ /main/target/ /coverageTests/target/ /samples/java8testing/target/ diff --git a/main/pom.xml b/main/pom.xml index 70994d7cb..fff1760b1 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -164,7 +164,7 @@ - org.jacocojacoco-maven-plugin0.8.4 + org.jacocojacoco-maven-plugin0.8.8 default-prepare-agentprepare-agent default-reportreporttest @@ -206,8 +206,6 @@ org.apiguardianapiguardian-api org.opentest4jopentest4j - org.junit.platformjunit-platform-engine - org.junit.platformjunit-platform-commons @@ -233,5 +231,8 @@ org.springframeworkspring-expression + + org.objenesisobjenesis3.3 + \ No newline at end of file diff --git a/main/src/mockit/asm/classes/BootstrapMethodsWriter.java b/main/src/mockit/asm/classes/BootstrapMethodsWriter.java index 2176f429e..0678e14f6 100644 --- a/main/src/mockit/asm/classes/BootstrapMethodsWriter.java +++ b/main/src/mockit/asm/classes/BootstrapMethodsWriter.java @@ -84,7 +84,7 @@ DynamicItem addInvokeDynamicReference( methods.setLength(position); // revert to old position BootstrapMethodItem bsmItem = getBSMItem(hashCode); - DynamicItem result = cp.createDynamicItem(INDY, name, desc, bsmItem.index); + DynamicItem result = cp.createDynamicItem(INVOKE_DYNAMIC, name, desc, bsmItem.index); return result; } diff --git a/main/src/mockit/asm/classes/ClassWriter.java b/main/src/mockit/asm/classes/ClassWriter.java index 92caa377a..553ada7f6 100644 --- a/main/src/mockit/asm/classes/ClassWriter.java +++ b/main/src/mockit/asm/classes/ClassWriter.java @@ -162,7 +162,7 @@ public FieldVisitor visitField( public MethodWriter visitMethod( int access, @Nonnull String name, @Nonnull String desc, @Nullable String signature, @Nullable String[] exceptions ) { - boolean computeFrames = classVersion >= ClassVersion.V1_7; + boolean computeFrames = classVersion >= ClassVersion.V7; MethodWriter method = new MethodWriter(this, access, name, desc, signature, exceptions, computeFrames); methods.add(method); return method; @@ -268,5 +268,5 @@ public DynamicItem addInvokeDynamicReference( return bootstrapMethodsWriter.addInvokeDynamicReference(name, desc, bsm, bsmArgs); } - public boolean isJava6OrNewer() { return classVersion >= ClassVersion.V1_6; } + public boolean isJava6OrNewer() { return classVersion >= ClassVersion.V6; } } \ No newline at end of file diff --git a/main/src/mockit/asm/classes/ConstantPoolCopying.java b/main/src/mockit/asm/classes/ConstantPoolCopying.java index 07c7321aa..14a8bedd0 100644 --- a/main/src/mockit/asm/classes/ConstantPoolCopying.java +++ b/main/src/mockit/asm/classes/ConstantPoolCopying.java @@ -46,19 +46,23 @@ void copyPool(@Nullable BootstrapMethodsWriter bootstrapMethods) { private Item copyItem(int itemType) { switch (itemType) { case UTF8: return copyUTF8Item(); - case INT: return copyIntItem(); + case INTEGER: return copyIntItem(); case FLOAT: return copyFloatItem(); case LONG: return copyLongItem(); case DOUBLE: return copyDoubleItem(); - case FIELD: - case METH: - case IMETH: return copyFieldOrMethodReferenceItem(itemType); - case NAME_TYPE: return copyNameAndTypeItem(); - case HANDLE: return copyHandleItem(); - case CONDY: - case INDY: return copyDynamicItem(itemType); - // case STR|CLASS|MTYPE: - default: return copyNameReferenceItem(itemType); + case FIELD_REF: + case METHOD_REF: + case IMETHOD_REF: return copyFieldOrMethodReferenceItem(itemType); + case NAME_TYPE: return copyNameAndTypeItem(); + case METHOD_HANDLE: return copyHandleItem(); + case DYNAMIC: + case INVOKE_DYNAMIC:return copyDynamicItem(itemType); + case STRING: + case CLASS: + case METHOD_TYPE: return copyNameReferenceItem(itemType); + case MODULE: return copyModule(); + case PACKAGE: return copyPackage(); + default: throw new IllegalArgumentException("Unknown CP type, cannot copy: " + itemType); } } @@ -158,4 +162,18 @@ private Item copyDynamicItem(int type) { item.set(type, name, desc, bsmIndex); return item; } + + @Nonnull + private Item copyModule() { + int nameIndex = source.readItem(); + String name = source.readNonnullUTF8(nameIndex); + return new ModuleItem(itemIndex, MODULE, name); + } + + @Nonnull + private Item copyPackage() { + int nameIndex = source.readItem(); + String name = source.readNonnullUTF8(nameIndex); + return new PackageItem(itemIndex, PACKAGE, name); + } } \ No newline at end of file diff --git a/main/src/mockit/asm/constantPool/ConstantPoolGeneration.java b/main/src/mockit/asm/constantPool/ConstantPoolGeneration.java index f252f2503..92ad775ee 100644 --- a/main/src/mockit/asm/constantPool/ConstantPoolGeneration.java +++ b/main/src/mockit/asm/constantPool/ConstantPoolGeneration.java @@ -138,7 +138,7 @@ public StringItem newClassItem(@Nonnull String internalName) { * Adds a string to the constant pool of the class being built. * Does nothing if the constant pool already contains a similar item. * - * @param type one of {@link ConstantPoolTypes#STR}, {@link ConstantPoolTypes#CLASS} or {@link ConstantPoolTypes#MTYPE} + * @param type one of {@link ConstantPoolTypes#STRING}, {@link ConstantPoolTypes#CLASS} or {@link ConstantPoolTypes#METHOD_TYPE} * @param value the String value. * @return a new or already existing string item. */ @@ -173,9 +173,9 @@ public MethodHandleItem newMethodHandleItem(@Nonnull MethodHandle methodHandle) if (result == null) { int tag = methodHandle.tag; - int memberType = tag == MethodHandle.Tag.INVOKEINTERFACE ? IMETH : METH; + int memberType = tag == MethodHandle.Tag.TAG_INVOKEINTERFACE ? IMETHOD_REF : METHOD_REF; ClassMemberItem memberItem = newClassMemberItem(memberType, methodHandle.owner, methodHandle.name, methodHandle.desc); - pool.put11(HANDLE, tag).putShort(memberItem.index); + pool.put11(METHOD_HANDLE, tag).putShort(memberItem.index); result = new MethodHandleItem(index++, reusableMethodHandleItem); put(result); @@ -213,7 +213,7 @@ private ClassMemberItem newClassMemberItem(int type, @Nonnull String owner, @Non */ @Nonnull public ClassMemberItem newFieldItem(@Nonnull String owner, @Nonnull String name, @Nonnull String desc) { - return newClassMemberItem(FIELD, owner, name, desc); + return newClassMemberItem(FIELD_REF, owner, name, desc); } /** @@ -228,7 +228,7 @@ public ClassMemberItem newFieldItem(@Nonnull String owner, @Nonnull String name, */ @Nonnull public ClassMemberItem newMethodItem(@Nonnull String owner, @Nonnull String name, @Nonnull String desc, boolean itf) { - return newClassMemberItem(itf ? IMETH : METH, owner, name, desc); + return newClassMemberItem(itf ? IMETHOD_REF : METHOD_REF, owner, name, desc); } /** @@ -245,7 +245,7 @@ public IntItem newInteger(int value) { IntItem result = get(reusableIntItem); if (result == null) { - pool.putByte(INT).putInt(value); + pool.putByte(INTEGER).putInt(value); result = new IntItem(index++, reusableIntItem); put(result); @@ -361,7 +361,7 @@ private int newNameType(@Nonnull String name, @Nonnull String desc) { @Nonnull public Item newConstItem(@Nonnull Object cst) { if (cst instanceof String) { - return newStringItem(STR, (String) cst); + return newStringItem(STRING, (String) cst); } if (cst instanceof Number) { @@ -379,7 +379,7 @@ public Item newConstItem(@Nonnull Object cst) { if (cst instanceof ReferenceType) { String typeDesc = ((ReferenceType) cst).getInternalName(); - return cst instanceof MethodType ? newStringItem(MTYPE, typeDesc) : newClassItem(typeDesc); + return cst instanceof MethodType ? newStringItem(METHOD_TYPE, typeDesc) : newClassItem(typeDesc); } if (cst instanceof PrimitiveType) { @@ -391,6 +391,10 @@ public Item newConstItem(@Nonnull Object cst) { return newMethodHandleItem((MethodHandle) cst); } + if(cst instanceof DynamicItem) { + DynamicItem dynamicItem = (DynamicItem) cst; + return createDynamicItem(dynamicItem.type, dynamicItem.name, dynamicItem.desc, dynamicItem.bsmIndex); + } throw new IllegalArgumentException("value " + cst); } diff --git a/main/src/mockit/asm/constantPool/DynamicItem.java b/main/src/mockit/asm/constantPool/DynamicItem.java index 2832de91b..052674931 100644 --- a/main/src/mockit/asm/constantPool/DynamicItem.java +++ b/main/src/mockit/asm/constantPool/DynamicItem.java @@ -6,7 +6,7 @@ public final class DynamicItem extends TypeOrMemberItem { - @Nonnegative private int bsmIndex; + @Nonnegative int bsmIndex; public DynamicItem(@Nonnegative int index) { super(index); } @@ -18,7 +18,7 @@ public final class DynamicItem extends TypeOrMemberItem /** * Sets the type, name, desc, and index of the constant or invoke dynamic item. * - * @param type one of {@link ConstantPoolTypes#INDY} or {@link ConstantPoolTypes#CONDY}, for invoke or constant dynamic, respectively + * @param type one of {@link ConstantPoolTypes#INVOKE_DYNAMIC} or {@link ConstantPoolTypes#DYNAMIC}, for invoke or constant dynamic, respectively * @param name the item name * @param desc the item type descriptor * @param index zero based index into the class attribute "BootstrapMethods". diff --git a/main/src/mockit/asm/constantPool/IntItem.java b/main/src/mockit/asm/constantPool/IntItem.java index 1863f909c..43d8d5c36 100644 --- a/main/src/mockit/asm/constantPool/IntItem.java +++ b/main/src/mockit/asm/constantPool/IntItem.java @@ -8,7 +8,7 @@ public final class IntItem extends IntValueItem { public IntItem(@Nonnegative int index) { super(index); - type = INT; + type = INTEGER; } IntItem(@Nonnegative int index, @Nonnull IntItem item) { super(index, item); } diff --git a/main/src/mockit/asm/constantPool/ModuleItem.java b/main/src/mockit/asm/constantPool/ModuleItem.java new file mode 100644 index 000000000..18080ca4c --- /dev/null +++ b/main/src/mockit/asm/constantPool/ModuleItem.java @@ -0,0 +1,41 @@ +package mockit.asm.constantPool; + +import javax.annotation.Nonnegative; +import javax.annotation.Nonnull; + +public final class ModuleItem extends Item +{ + @Nonnull @SuppressWarnings("NullableProblems") String strVal; + + ModuleItem() { + super(0); + strVal = ""; + } + + public ModuleItem(@Nonnegative int index, int type, @Nonnull String strVal) { + super(index); + set(type, strVal); + } + + ModuleItem(@Nonnegative int index, @Nonnull ModuleItem item) { + super(index, item); + strVal = item.strVal; + } + + @Nonnull + public String getValue() { return strVal; } + + /** + * Sets this module name value. + */ + void set(int type, @Nonnull String strVal) { + this.type = type; + this.strVal = strVal; + setHashCode(strVal.hashCode()); + } + + @Override + boolean isEqualTo(@Nonnull Item item) { + return ((ModuleItem) item).strVal.equals(strVal); + } +} diff --git a/main/src/mockit/asm/constantPool/PackageItem.java b/main/src/mockit/asm/constantPool/PackageItem.java new file mode 100644 index 000000000..e957b5c6d --- /dev/null +++ b/main/src/mockit/asm/constantPool/PackageItem.java @@ -0,0 +1,41 @@ +package mockit.asm.constantPool; + +import javax.annotation.Nonnegative; +import javax.annotation.Nonnull; + +public final class PackageItem extends Item +{ + @Nonnull @SuppressWarnings("NullableProblems") String strVal; + + PackageItem() { + super(0); + strVal = ""; + } + + public PackageItem(@Nonnegative int index, int type, @Nonnull String strVal) { + super(index); + set(type, strVal); + } + + PackageItem(@Nonnegative int index, @Nonnull PackageItem item) { + super(index, item); + strVal = item.strVal; + } + + @Nonnull + public String getValue() { return strVal; } + + /** + * Sets this package name value. + */ + void set(int type, @Nonnull String strVal) { + this.type = type; + this.strVal = strVal; + setHashCode(strVal.hashCode()); + } + + @Override + boolean isEqualTo(@Nonnull Item item) { + return ((PackageItem) item).strVal.equals(strVal); + } +} diff --git a/main/src/mockit/asm/controlFlow/CFGAnalysis.java b/main/src/mockit/asm/controlFlow/CFGAnalysis.java index 75d24387e..1d70a7a5e 100644 --- a/main/src/mockit/asm/controlFlow/CFGAnalysis.java +++ b/main/src/mockit/asm/controlFlow/CFGAnalysis.java @@ -188,7 +188,8 @@ private static int computeSizeVariationForFieldAccess(int fieldAccessOpcode, cha case GETSTATIC: return doubleSizeType ? 2 : 1; case PUTSTATIC: return doubleSizeType ? -2 : -1; case GETFIELD: return doubleSizeType ? 1 : 0; - case PUTFIELD: default: return doubleSizeType ? -3 : -2; + case PUTFIELD: return doubleSizeType ? -3 : -2; + default: throw new IllegalArgumentException("Unknown field access opcode: " + fieldAccessOpcode); } } diff --git a/main/src/mockit/asm/controlFlow/Frame.java b/main/src/mockit/asm/controlFlow/Frame.java index ea0e471ad..da57cd43e 100644 --- a/main/src/mockit/asm/controlFlow/Frame.java +++ b/main/src/mockit/asm/controlFlow/Frame.java @@ -709,7 +709,7 @@ private void executeSWAP() { */ void executeLDC(@Nonnull Item item) { switch (item.getType()) { - case ConstantPoolTypes.INT: + case ConstantPoolTypes.INTEGER: push(INTEGER); break; case ConstantPoolTypes.LONG: @@ -726,15 +726,79 @@ void executeLDC(@Nonnull Item item) { case ConstantPoolTypes.CLASS: push(OBJECT | cp.addNormalType("java/lang/Class")); break; - case ConstantPoolTypes.STR: + case ConstantPoolTypes.STRING: push(OBJECT | cp.addNormalType("java/lang/String")); break; - case ConstantPoolTypes.MTYPE: + case ConstantPoolTypes.METHOD_TYPE: push(OBJECT | cp.addNormalType("java/lang/invoke/MethodType")); break; - // case Item.Type.HANDLE_BASE + [1..9]: - default: + case ConstantPoolTypes.METHOD_HANDLE: push(OBJECT | cp.addNormalType("java/lang/invoke/MethodHandle")); + break; + case ConstantPoolTypes.DYNAMIC: + DynamicItem dynamicItem = (DynamicItem) item; + String desc = dynamicItem.getDesc(); + if (desc.length() == 1) { + // primitive + char descChar = desc.charAt(0); + switch(descChar) { + case 'Z': + case 'B': + case 'S': + case 'I': + push(INTEGER); + break; + case 'F': + push(FLOAT); + break; + case 'D': + push(DOUBLE); + break; + case 'J': + push(LONG); + break; + default: + throw new IllegalArgumentException("Unsupported dynamic local 'primitive' type: " + desc); + } + } else if (desc.charAt(0) == '['){ + // array + ArrayType arrayType = ArrayType.create(desc); + JavaType elementType = arrayType.getElementType(); + int mask; + if (elementType instanceof PrimitiveType) { + PrimitiveType primitiveElementType = (PrimitiveType) elementType; + char descChar = primitiveElementType.getTypeCode(); + switch(descChar) { + case 'Z': + case 'B': + case 'C': + case 'S': + case 'I': + mask = INTEGER; + break; + case 'F': + mask = FLOAT; + break; + case 'D': + mask = DOUBLE; + break; + case 'J': + mask = LONG; + break; + default: + throw new IllegalArgumentException("Unsupported array element 'primitive' type: " + desc); + } + } else { + mask = OBJECT; + } + push(ARRAY_OF | mask); + } else { + // object, substring 'L' and ';' + push(OBJECT | cp.addNormalType(desc.substring(1, desc.length() - 1))); + } + break; + default: + throw new IllegalArgumentException("Unknown item type, cannot execute frame: " + item.getType()); } } diff --git a/main/src/mockit/asm/controlFlow/StackMapTableWriter.java b/main/src/mockit/asm/controlFlow/StackMapTableWriter.java index 83894c1a4..06a1c3518 100644 --- a/main/src/mockit/asm/controlFlow/StackMapTableWriter.java +++ b/main/src/mockit/asm/controlFlow/StackMapTableWriter.java @@ -246,8 +246,11 @@ private void writeFrameForJava6OrNewer(@Nonnegative int currentLocalsSize, @Nonn case APPEND_FRAME: writeAppendedFrame(currentLocalsSize, previousLocalsSize, k, delta); break; - default: // FULL_FRAME + case FULL_FRAME: writeFullFrame(delta, currentLocalsSize, currentStackSize); + break; + default: + throw new IllegalArgumentException("Unknown frame type: " + type); } } diff --git a/main/src/mockit/asm/jvmConstants/Access.java b/main/src/mockit/asm/jvmConstants/Access.java index 0bf05914f..9b524d76c 100644 --- a/main/src/mockit/asm/jvmConstants/Access.java +++ b/main/src/mockit/asm/jvmConstants/Access.java @@ -48,7 +48,7 @@ private Access() {} public static boolean isSynthetic(int access) { return (access & SYNTHETIC) != 0; } public static boolean isSynthetic(int access, int classVersion) { - return isSynthetic(access) && ((access & SYNTHETIC_ATTRIBUTE) != 0 || classVersion < V1_5); + return isSynthetic(access) && ((access & SYNTHETIC_ATTRIBUTE) != 0 || classVersion < V5); } public static boolean isConstructor(int access) { return (access & CONSTRUCTOR) != 0; } diff --git a/main/src/mockit/asm/jvmConstants/ClassVersion.java b/main/src/mockit/asm/jvmConstants/ClassVersion.java index 5c4d024d0..e55bcc9f7 100644 --- a/main/src/mockit/asm/jvmConstants/ClassVersion.java +++ b/main/src/mockit/asm/jvmConstants/ClassVersion.java @@ -5,8 +5,20 @@ */ public interface ClassVersion { - int V1_5 = 49; - int V1_6 = 50; - int V1_7 = 51; - int V1_8 = 52; + int V5 = 49; + int V6 = 50; + int V7 = 51; + int V8 = 52; + int V9 = 53; + int V10 = 54; + int V11 = 55; + int V12 = 56; + int V13 = 57; + int V14 = 58; + int V15 = 59; + int V16 = 60; + int V17 = 61; + int V18 = 62; + int V19 = 63; + int V20 = 64; } diff --git a/main/src/mockit/asm/jvmConstants/ConstantPoolTypes.java b/main/src/mockit/asm/jvmConstants/ConstantPoolTypes.java index e504480c2..d0cfa16c6 100644 --- a/main/src/mockit/asm/jvmConstants/ConstantPoolTypes.java +++ b/main/src/mockit/asm/jvmConstants/ConstantPoolTypes.java @@ -6,21 +6,23 @@ */ public interface ConstantPoolTypes { - int CLASS = 7; // CONSTANT_Class - int FIELD = 9; // CONSTANT_Fieldref - int METH = 10; // CONSTANT_Methodref - int IMETH = 11; // CONSTANT_InterfaceMethodref - int STR = 8; // CONSTANT_String - int INT = 3; // CONSTANT_Integer - int FLOAT = 4; // CONSTANT_Float - int LONG = 5; // CONSTANT_Long - int DOUBLE = 6; // CONSTANT_Double - int NAME_TYPE = 12; // CONSTANT_NameAndType - int UTF8 = 1; // CONSTANT_Utf8 - int HANDLE = 15; // CONSTANT_MethodHandle - int MTYPE = 16; // CONSTANT_MethodType - int CONDY = 17; // CONSTANT_Dynamic - int INDY = 18; // CONSTANT_InvokeDynamic + int CLASS = 7; // CONSTANT_Class + int FIELD_REF = 9; // CONSTANT_Fieldref + int METHOD_REF = 10; // CONSTANT_Methodref + int IMETHOD_REF = 11; // CONSTANT_InterfaceMethodref + int STRING = 8; // CONSTANT_String + int INTEGER = 3; // CONSTANT_Integer + int FLOAT = 4; // CONSTANT_Float + int LONG = 5; // CONSTANT_Long + int DOUBLE = 6; // CONSTANT_Double + int NAME_TYPE = 12; // CONSTANT_NameAndType + int UTF8 = 1; // CONSTANT_Utf8 + int METHOD_HANDLE = 15; // CONSTANT_MethodHandle + int METHOD_TYPE = 16; // CONSTANT_MethodType + int DYNAMIC = 17; // CONSTANT_Dynamic + int INVOKE_DYNAMIC = 18; // CONSTANT_InvokeDynamic + int MODULE = 19; // CONSTANT_Module + int PACKAGE = 20; // CONSTANT_Package /** * The base value for all CONSTANT_MethodHandle constant pool items. diff --git a/main/src/mockit/asm/methods/MethodReader.java b/main/src/mockit/asm/methods/MethodReader.java index 76b0e455e..1fc544b46 100644 --- a/main/src/mockit/asm/methods/MethodReader.java +++ b/main/src/mockit/asm/methods/MethodReader.java @@ -574,7 +574,7 @@ private void readFieldOrInvokeInstruction(int opcode) { mv.visitFieldInsn(opcode, owner, memberName, memberDesc); } else { - boolean itf = code[ownerCodeIndex - 1] == ConstantPoolTypes.IMETH; + boolean itf = code[ownerCodeIndex - 1] == ConstantPoolTypes.IMETHOD_REF; mv.visitMethodInsn(opcode, owner, memberName, memberDesc, itf); if (opcode == INVOKEINTERFACE) { diff --git a/main/src/mockit/asm/util/BytecodeReader.java b/main/src/mockit/asm/util/BytecodeReader.java index 40524b3c4..e5530543e 100644 --- a/main/src/mockit/asm/util/BytecodeReader.java +++ b/main/src/mockit/asm/util/BytecodeReader.java @@ -2,6 +2,8 @@ import javax.annotation.*; +import mockit.asm.constantPool.DynamicItem; +import mockit.asm.jvmConstants.ConstantPoolTypes; import mockit.asm.types.*; import static mockit.asm.jvmConstants.ConstantPoolTypes.*; @@ -71,12 +73,31 @@ else if (itemType == UTF8 && itemSize > maxStringSize) { @Nonnegative private int getItemSize(int itemType) { - switch (itemType) { - case FIELD: case METH: case IMETH: case INT: case FLOAT: case NAME_TYPE: case CONDY: case INDY: return 5; - case LONG: case DOUBLE: return 9; - case UTF8: return 3 + readUnsignedShort(codeIndex); - case HANDLE: return 4; - default: return 3; // CLASS|STR|MTYPE + switch(itemType) { + case FIELD_REF: + case METHOD_REF: + case IMETHOD_REF: + case INTEGER: + case FLOAT: + case NAME_TYPE: + case DYNAMIC: + case INVOKE_DYNAMIC: + return 5; + case LONG: + case DOUBLE: + return 9; + case UTF8: + return 3 + readUnsignedShort(codeIndex); + case METHOD_HANDLE: + return 4; + case MODULE: + case PACKAGE: + case CLASS: + case STRING: + case METHOD_TYPE: + return 3; + default: + throw new IllegalArgumentException("Unknown item type, cannot determine size: " + itemType); } } @@ -383,20 +404,30 @@ protected final Object readConst(@Nonnegative int itemIndex) { byte itemType = code[constCodeIndex - 1]; switch (itemType) { - case INT: return readInt(constCodeIndex); - case FLOAT: return readFloat(constCodeIndex); - case LONG: return readLong(constCodeIndex); - case DOUBLE: return readDouble(constCodeIndex); - case STR: return readNonnullUTF8(constCodeIndex); + case INTEGER: return readInt(constCodeIndex); + case FLOAT: return readFloat(constCodeIndex); + case LONG: return readLong(constCodeIndex); + case DOUBLE: return readDouble(constCodeIndex); + case STRING: return readNonnullUTF8(constCodeIndex); case CLASS: String typeDesc = readNonnullUTF8(constCodeIndex); return ReferenceType.createFromInternalName(typeDesc); - case MTYPE: + case METHOD_TYPE: String methodDesc = readNonnullUTF8(constCodeIndex); return MethodType.create(methodDesc); - // case HANDLE_BASE + [1..9]: - default: + case DYNAMIC: { + int bsmStartIndex = readUnsignedShort(constCodeIndex); + int nameIndex = readItem(constCodeIndex + 2); + String name = readNonnullUTF8(nameIndex); + String desc = readNonnullUTF8(nameIndex + 2); + DynamicItem dynamicItem = new DynamicItem(itemIndex); + dynamicItem.set(ConstantPoolTypes.DYNAMIC, name, desc, bsmStartIndex); + return dynamicItem; + } + case METHOD_HANDLE: return readMethodHandle(constCodeIndex); + default: + throw new IllegalArgumentException("Unknown const item type code: " + itemType); } } @@ -415,6 +446,8 @@ protected final MethodHandle readMethodHandleItem(@Nonnegative int bsmCodeIndex) @Nonnull private MethodHandle readMethodHandle(@Nonnegative int bsmCodeIndex) { int tag = readUnsignedByte(bsmCodeIndex); + if (tag < MethodHandle.Tag.TAG_GETFIELD || tag > MethodHandle.Tag.TAG_INVOKEINTERFACE) + throw new IllegalArgumentException("Illegal method-handle tag: " + tag); int classIndex = readItem(bsmCodeIndex + 1); String owner = readNonnullClass(classIndex); diff --git a/main/src/mockit/asm/util/MethodHandle.java b/main/src/mockit/asm/util/MethodHandle.java index 855926724..b17e2a0da 100644 --- a/main/src/mockit/asm/util/MethodHandle.java +++ b/main/src/mockit/asm/util/MethodHandle.java @@ -8,11 +8,15 @@ public final class MethodHandle { public interface Tag { -// int INVOKEVIRTUAL = 5; -// int INVOKESTATIC = 6; -// int INVOKESPECIAL = 7; -// int NEWINVOKESPECIAL = 8; - int INVOKEINTERFACE = 9; + int TAG_GETFIELD = 1; + int TAG_GETSTATIC = 2; + int TAG_PUTFIELD = 3; + int TAG_PUTSTATIC = 4; + int TAG_INVOKEVIRTUAL = 5; + int TAG_INVOKESTATIC = 6; + int TAG_INVOKESPECIAL = 7; + int TAG_NEWINVOKESPECIAL = 8; + int TAG_INVOKEINTERFACE = 9; } /** diff --git a/main/src/mockit/internal/BaseClassModifier.java b/main/src/mockit/internal/BaseClassModifier.java index c30c83c35..7e5eaf10b 100644 --- a/main/src/mockit/internal/BaseClassModifier.java +++ b/main/src/mockit/internal/BaseClassModifier.java @@ -49,10 +49,10 @@ public void visit(int version, int access, @Nonnull String name, @Nonnull ClassI int modifiedVersion = version; int originalVersion = version & 0xFFFF; - if (originalVersion < ClassVersion.V1_5) { + if (originalVersion < ClassVersion.V5) { // LDC instructions (see MethodVisitor#visitLdcInsn) are more capable in JVMs with support for class files of // version 49 (Java 5) or newer, so we "upgrade" it to avoid a VerifyError: - modifiedVersion = ClassVersion.V1_5; + modifiedVersion = ClassVersion.V5; } cw.visit(modifiedVersion, access, name, additionalInfo); diff --git a/main/src/mockit/internal/expectations/invocation/ExpectedInvocation.java b/main/src/mockit/internal/expectations/invocation/ExpectedInvocation.java index 1dea784f2..bef98071a 100644 --- a/main/src/mockit/internal/expectations/invocation/ExpectedInvocation.java +++ b/main/src/mockit/internal/expectations/invocation/ExpectedInvocation.java @@ -113,7 +113,7 @@ private boolean isMatchingGenericMethod(@Nullable Object mock, @Nonnull String i if (mockedClass != instance.getClass()) { GenericTypeReflection typeReflection = new GenericTypeReflection(mockedClass, null); GenericSignature parsedSignature = typeReflection.parseSignature(genericSignature); - return parsedSignature.satisfiesSignature(invokedMethod); + return parsedSignature.satisfiesSignature(invokedMethod) && isMatchingMethodName(invokedMethod); } } } @@ -137,6 +137,12 @@ private boolean isMatchingMethod(@Nonnull String invokedMethod) { return isReturnTypeOfRecordedMethodAssignableToReturnTypeOfInvokedMethod(invokedMethod, returnTypeStartPos); } + private boolean isMatchingMethodName(@Nonnull String invokedMethod) { + int methodNameEndPos = invokedMethod.indexOf('('); + String methodName = invokedMethod.substring(0, methodNameEndPos + 1); + return getMethodNameAndDescription().startsWith(methodName); + } + // Returns -1 if the method names or parameters are different. private int getReturnTypePosition(@Nonnull String invokedMethod) { String recordedMethod = getMethodNameAndDescription(); diff --git a/main/src/mockit/internal/expectations/mocking/BaseTypeRedefinition.java b/main/src/mockit/internal/expectations/mocking/BaseTypeRedefinition.java index c7fab6c51..b9ec93581 100644 --- a/main/src/mockit/internal/expectations/mocking/BaseTypeRedefinition.java +++ b/main/src/mockit/internal/expectations/mocking/BaseTypeRedefinition.java @@ -134,7 +134,7 @@ private Object createMockInterfaceImplementationDirectly(@Nonnull Type interface private void redefineClass(@Nonnull Class realClass) { ClassReader classReader = ClassFile.createReaderOrGetFromCache(realClass); - if (realClass.isInterface() && classReader.getVersion() < ClassVersion.V1_8) { + if (realClass.isInterface() && classReader.getVersion() < ClassVersion.V8) { return; } diff --git a/main/src/mockit/internal/expectations/mocking/InstanceFactory.java b/main/src/mockit/internal/expectations/mocking/InstanceFactory.java index 614bcf7c5..d4b585691 100644 --- a/main/src/mockit/internal/expectations/mocking/InstanceFactory.java +++ b/main/src/mockit/internal/expectations/mocking/InstanceFactory.java @@ -4,12 +4,10 @@ */ package mockit.internal.expectations.mocking; -import java.lang.reflect.*; import javax.annotation.*; import mockit.internal.util.*; - -import sun.reflect.ReflectionFactory; +import org.objenesis.ObjenesisHelper; /** * Factory for the creation of new mocked instances, and for obtaining/clearing the last instance created. @@ -17,13 +15,6 @@ */ public abstract class InstanceFactory { - @SuppressWarnings("UseOfSunClasses") - private static final ReflectionFactory REFLECTION_FACTORY = ReflectionFactory.getReflectionFactory(); - private static final Constructor OBJECT_CONSTRUCTOR; - static { - try { OBJECT_CONSTRUCTOR = Object.class.getConstructor(); } - catch (NoSuchMethodException e) { throw new RuntimeException(e); } - } @Nonnull private final Class concreteClass; @Nullable Object lastInstance; @@ -31,25 +22,16 @@ public abstract class InstanceFactory InstanceFactory(@Nonnull Class concreteClass) { this.concreteClass = concreteClass; } @Nonnull + @SuppressWarnings("unchecked") final T newUninitializedConcreteClassInstance() { try { - Constructor fakeConstructor = REFLECTION_FACTORY.newConstructorForSerialization(concreteClass, OBJECT_CONSTRUCTOR); - - if (fakeConstructor == null) { // can happen on Java 9 - //noinspection ConstantConditions - return null; - } - - @SuppressWarnings("unchecked") T newInstance = (T) fakeConstructor.newInstance(); - return newInstance; + return (T) ObjenesisHelper.newInstance(concreteClass); } - catch (NoClassDefFoundError | ExceptionInInitializerError e) { + catch (Exception e) { StackTrace.filterStackTrace(e); e.printStackTrace(); throw e; } - catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } - catch (InvocationTargetException e) { throw new RuntimeException(e.getCause()); } } @Nonnull public abstract Object create(); diff --git a/main/src/mockit/internal/expectations/transformation/InvocationBlockModifier.java b/main/src/mockit/internal/expectations/transformation/InvocationBlockModifier.java index 207e36c84..f708af657 100644 --- a/main/src/mockit/internal/expectations/transformation/InvocationBlockModifier.java +++ b/main/src/mockit/internal/expectations/transformation/InvocationBlockModifier.java @@ -90,8 +90,9 @@ private static int stackSizeVariationForFieldAccess(@Nonnegative int opcode, @No switch (opcode) { case GETSTATIC: return twoByteType ? 2 : 1; case PUTSTATIC: return twoByteType ? -2 : -1; - case GETFIELD: return twoByteType ? 1 : 0; - default: return twoByteType ? -3 : -2; + case GETFIELD: return twoByteType ? 1 : 0; + case PUTFIELD: return twoByteType ? -3 : -2; + default: throw new IllegalArgumentException("Invalid field access opcode: " + opcode); } } From 99a9231648bb9f03dffcd3cc4df57d8b5e4ea94d Mon Sep 17 00:00:00 2001 From: Col-E Date: Fri, 7 Oct 2022 17:40:17 -0400 Subject: [PATCH 2/2] Re-apply changes to pom.xml from merge, add objenesis dependency. Tweak config to resolve intelliJ integrations failures. --- main/pom.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/main/pom.xml b/main/pom.xml index f428970ee..97a3fb3f7 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -84,7 +84,7 @@ maven-compiler-plugin UTF-8 - 1.71.7 + 1.81.8 -Xlint:none false @@ -199,6 +199,9 @@ + + org.objenesisobjenesis3.3 + com.github.spotbugsspotbugs-annotations4.7.2 @@ -212,8 +215,6 @@ org.apiguardianapiguardian-api org.opentest4jopentest4j - org.junit.platformjunit-platform-engine - org.junit.platformjunit-platform-commons