From 22be227ce0ef36915bf0cdd92218c4b01100f847 Mon Sep 17 00:00:00 2001 From: Rafael Winterhalter Date: Thu, 21 Apr 2016 09:56:06 +0200 Subject: [PATCH] Improved support for pre-Java 5 byte code. --- .../main/java/net/bytebuddy/asm/Advice.java | 7 + .../dynamic/scaffold/TypeWriter.java | 2 + .../bytebuddy/implementation/FixedValue.java | 8 +- .../implementation/Implementation.java | 83 ++++++++-- .../bind/annotation/Origin.java | 11 +- .../TargetMethodAnnotationDrivenBinder.java | 12 +- .../bytecode/constant/ClassConstant.java | 8 +- .../constant/JavaInstanceConstant.java | 54 ++++++ .../constant/MethodHandleConstant.java | 155 ------------------ .../bytecode/constant/MethodTypeConstant.java | 87 ---------- .../bytecode/constant/TextConstant.java | 7 +- .../net/bytebuddy/utility/JavaInstance.java | 25 ++- .../scaffold/TypeWriterDefaultTest.java | 14 +- ...ImplementationContextDefaultOtherTest.java | 17 ++ .../ImplementationContextDisabledTest.java | 10 ++ .../bind/annotation/OriginBinderTest.java | 7 +- .../constant/ClassConstantReferenceTest.java | 65 +++++++- .../bytecode/constant/FieldConstantTest.java | 62 ++++++- .../constant/JavaInstanceConstantTest.java | 52 ++++++ .../bytecode/constant/MethodConstantTest.java | 10 +- .../constant/MethodHandleConstantTest.java | 150 ----------------- .../constant/MethodTypeConstantTest.java | 63 ------- ...avaInstanceMethodHandleDispatcherTest.java | 7 + .../utility/JavaInstanceMethodHandleTest.java | 6 +- 24 files changed, 400 insertions(+), 522 deletions(-) create mode 100644 byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bytecode/constant/JavaInstanceConstant.java delete mode 100644 byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bytecode/constant/MethodHandleConstant.java delete mode 100644 byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bytecode/constant/MethodTypeConstant.java create mode 100644 byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bytecode/constant/JavaInstanceConstantTest.java delete mode 100644 byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bytecode/constant/MethodHandleConstantTest.java delete mode 100644 byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bytecode/constant/MethodTypeConstantTest.java diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/asm/Advice.java b/byte-buddy-dep/src/main/java/net/bytebuddy/asm/Advice.java index d50ba6ff3a1..14cbaddb6b7 100644 --- a/byte-buddy-dep/src/main/java/net/bytebuddy/asm/Advice.java +++ b/byte-buddy-dep/src/main/java/net/bytebuddy/asm/Advice.java @@ -76,6 +76,13 @@ * older class file format version. *

*

+ * Note: For the purpose of inlining, Java 5 and Java 6 byte code can be seen as the best candidate for advice methods. These versions do + * no longer allow subroutines, neither do they already allow invokedynamic instructions or method handles. This way, Java 5 and Java 6 byte + * code is compatible to both older and newer versions. One exception for backwards-incompatible byte code is the possibility to load type references + * from the constant pool onto the operand stack. These instructions can however easily be transformerd for classes compiled to Java 4 and older + * by registering a {@link TypeConstantAdjustment} before the advice visitor. + *

+ *

* Note: It is not possible to trigger break points in inlined advice methods as the debugging information of the inlined advice is not * preserved. It is not possible in Java to reference more than one source file per class what makes translating such debugging information * impossible. It is however possible to set break points in advice methods when invoking the original advice target. This allows debugging diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/scaffold/TypeWriter.java b/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/scaffold/TypeWriter.java index c642e2eb77a..1e0ea9912fa 100644 --- a/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/scaffold/TypeWriter.java +++ b/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/scaffold/TypeWriter.java @@ -3014,6 +3014,7 @@ public void visit(int classFileVersionNumber, TypeDescription.OBJECT : instrumentedType.getSuperClass().asErasure()).getInternalName(), instrumentedType.getInterfaces().asErasures().toInternalNames()); + implementationContext.setClassFileVersion(ClassFileVersion.ofMinorMajor(classFileVersionNumber)); typeAttributeAppender.apply(cv, instrumentedType, annotationValueFilterFactory.on(instrumentedType)); if (!ClassFileVersion.ofMinorMajor(classFileVersionNumber).isAtLeast(ClassFileVersion.JAVA_V8) && instrumentedType.isInterface()) { implementationContext.prohibitTypeInitializer(); @@ -3496,6 +3497,7 @@ public byte[] create(Implementation.Context.ExtractableView implementationContex ? TypeDescription.OBJECT : instrumentedType.getSuperClass().asErasure()).getInternalName(), instrumentedType.getInterfaces().asErasures().toInternalNames()); + implementationContext.setClassFileVersion(classFileVersion); typeAttributeAppender.apply(classVisitor, instrumentedType, annotationValueFilterFactory.on(instrumentedType)); for (FieldDescription fieldDescription : instrumentedType.getDeclaredFields()) { fieldPool.target(fieldDescription).apply(classVisitor, annotationValueFilterFactory); diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/FixedValue.java b/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/FixedValue.java index 8a53f78b659..535cfb9343b 100644 --- a/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/FixedValue.java +++ b/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/FixedValue.java @@ -57,6 +57,10 @@ protected FixedValue(Assigner assigner, Assigner.Typing typing) { * When a value is stored in the class's constant pool, its identity is lost. If an object's identity is important, the * {@link FixedValue#reference(Object)} method should be used instead. *

+ *

+ * Important: When supplying a method handle or a method type, all types that are implied must be visible to the instrumented + * type or an {@link IllegalAccessException} will be thrown at runtime. + *

* * @param fixedValue The fixed value to return from the method. * @return An implementation for the given {@code fixedValue}. @@ -114,12 +118,12 @@ public static AssignerConfigurable value(Object fixedValue) { Assigner.DEFAULT, Assigner.Typing.STATIC); } else if (JavaType.METHOD_HANDLE.getTypeStub().isAssignableFrom(type)) { - return new ForPoolValue(MethodHandleConstant.of(JavaInstance.MethodHandle.ofLoaded(fixedValue)), + return new ForPoolValue(new JavaInstanceConstant(JavaInstance.MethodHandle.ofLoaded(fixedValue)), new TypeDescription.ForLoadedType(type), Assigner.DEFAULT, Assigner.Typing.STATIC); } else if (JavaType.METHOD_TYPE.getTypeStub().represents(type)) { - return new ForPoolValue(MethodTypeConstant.of(JavaInstance.MethodType.ofLoaded(fixedValue)), + return new ForPoolValue(new JavaInstanceConstant(JavaInstance.MethodType.ofLoaded(fixedValue)), new TypeDescription.ForLoadedType(type), Assigner.DEFAULT, Assigner.Typing.STATIC); diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/Implementation.java b/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/Implementation.java index 69b6fe45097..606911e2fb4 100644 --- a/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/Implementation.java +++ b/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/Implementation.java @@ -406,12 +406,34 @@ interface Context { */ FieldDescription.InDefinedShape cache(StackManipulation fieldValue, TypeDescription fieldType); + /** + * Returns the instrumented type of the current implementation. The instrumented type is exposed with the intend of allowing optimal + * byte code generation and not for implementing checks or changing the behavior of a {@link StackManipulation}. + * + * @return The instrumented type of the current implementation. + */ + TypeDescription getInstrumentedType(); + + /** + * Returns the class file version of the current implementation. + * + * @return The class file version of the current implementation. + */ + ClassFileVersion getClassFileVersion(); + /** * Represents an extractable view of an {@link Implementation.Context} which * allows the retrieval of any registered auxiliary type. */ interface ExtractableView extends Context { + /** + * Sets the class file version this implementation context should represent. + * + * @param classFileVersion The class file version to represent. + */ + void setClassFileVersion(ClassFileVersion classFileVersion); + /** * Determines if this implementation context allows for the retention of a static type initializer. * @@ -496,6 +518,49 @@ public String toString() { } } } + + /** + * An abstract base implementation of an extractable view of an implementation context. + */ + abstract class AbstractBase implements ExtractableView { + + /** + * The instrumented type. + */ + protected final TypeDescription instrumentedType; + + /** + * The class file version of the instrumented type. + */ + private ClassFileVersion classFileVersion; + + /** + * Create a new extractable view. + * + * @param instrumentedType The instrumented type. + */ + protected AbstractBase(TypeDescription instrumentedType) { + this.instrumentedType = instrumentedType; + } + + @Override + public void setClassFileVersion(ClassFileVersion classFileVersion) { + this.classFileVersion = classFileVersion; + } + + @Override + public TypeDescription getInstrumentedType() { + return instrumentedType; + } + + @Override + public ClassFileVersion getClassFileVersion() { + if (classFileVersion == null) { + throw new IllegalStateException("Cannot read class file version before it was set"); + } + return classFileVersion; + } + } } /** @@ -523,12 +588,7 @@ ExtractableView make(TypeDescription instrumentedType, * redefining a class when it is not allowed to add methods to a class what is an implicit requirement when copying the static * initializer block into another method. */ - class Disabled implements ExtractableView { - - /** - * The instrumented type. - */ - private final TypeDescription instrumentedType; + class Disabled extends ExtractableView.AbstractBase { /** * Creates a new disabled implementation context. @@ -536,7 +596,7 @@ class Disabled implements ExtractableView { * @param instrumentedType The instrumented type. */ protected Disabled(TypeDescription instrumentedType) { - this.instrumentedType = instrumentedType; + super(instrumentedType); } @Override @@ -624,7 +684,7 @@ public String toString() { * A default implementation of an {@link Implementation.Context.ExtractableView} * which serves as its own {@link net.bytebuddy.implementation.auxiliary.AuxiliaryType.MethodAccessorFactory}. */ - class Default implements ExtractableView, AuxiliaryType.MethodAccessorFactory { + class Default extends ExtractableView.AbstractBase implements AuxiliaryType.MethodAccessorFactory { /** * The name suffix to be appended to an accessor method. @@ -636,11 +696,6 @@ class Default implements ExtractableView, AuxiliaryType.MethodAccessorFactory { */ public static final String FIELD_CACHE_PREFIX = "cachedValue"; - /** - * The instrumented type that this instance represents. - */ - private final TypeDescription instrumentedType; - /** * The type initializer of the created instrumented type. */ @@ -714,7 +769,7 @@ protected Default(TypeDescription instrumentedType, AuxiliaryType.NamingStrategy auxiliaryTypeNamingStrategy, TypeInitializer typeInitializer, ClassFileVersion classFileVersion) { - this.instrumentedType = instrumentedType; + super(instrumentedType); this.auxiliaryTypeNamingStrategy = auxiliaryTypeNamingStrategy; this.typeInitializer = typeInitializer; this.classFileVersion = classFileVersion; diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bind/annotation/Origin.java b/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bind/annotation/Origin.java index e0eccf765a0..2c76fbcceef 100644 --- a/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bind/annotation/Origin.java +++ b/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bind/annotation/Origin.java @@ -8,6 +8,7 @@ import net.bytebuddy.implementation.bind.MethodDelegationBinder; import net.bytebuddy.implementation.bytecode.assign.Assigner; import net.bytebuddy.implementation.bytecode.constant.*; +import net.bytebuddy.utility.JavaInstance; import net.bytebuddy.utility.JavaType; import java.lang.annotation.*; @@ -15,6 +16,7 @@ import java.lang.reflect.Method; /** + *

* The origin annotation provides some meta information about the source method that is bound to this method where * the binding is dependant of the parameter's type: *

    @@ -36,6 +38,11 @@ * is injected. Method type descriptions are only supported for byte code versions starting from Java 7. *
* Any other parameter type will cause an {@link java.lang.IllegalStateException}. + *

+ *

+ * Important: A method handle or method type reference can only be used if the referenced method's types are all visible + * to the instrumented type or an {@link IllegalAccessError} will be thrown at runtime. + *

* * @see net.bytebuddy.implementation.MethodDelegation * @see net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder @@ -104,9 +111,9 @@ public MethodDelegationBinder.ParameterBinding bind(AnnotationDescription.Loa } else if (parameterType.represents(int.class)) { return new MethodDelegationBinder.ParameterBinding.Anonymous(IntegerConstant.forValue(source.getModifiers())); } else if (parameterType.equals(JavaType.METHOD_HANDLE.getTypeStub())) { - return new MethodDelegationBinder.ParameterBinding.Anonymous(MethodHandleConstant.of(source.asDefined())); + return new MethodDelegationBinder.ParameterBinding.Anonymous(JavaInstance.MethodHandle.of(source.asDefined()).asStackManipulation()); } else if (parameterType.equals(JavaType.METHOD_TYPE.getTypeStub())) { - return new MethodDelegationBinder.ParameterBinding.Anonymous(MethodTypeConstant.of(source.asDefined())); + return new MethodDelegationBinder.ParameterBinding.Anonymous(JavaInstance.MethodType.of(source.asDefined()).asStackManipulation()); } else { throw new IllegalStateException("The " + target + " method's " + target.getIndex() + " parameter is annotated with a Origin annotation with an argument not representing a Class," + diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bind/annotation/TargetMethodAnnotationDrivenBinder.java b/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bind/annotation/TargetMethodAnnotationDrivenBinder.java index 37feca40e7f..7f08177a436 100644 --- a/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bind/annotation/TargetMethodAnnotationDrivenBinder.java +++ b/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bind/annotation/TargetMethodAnnotationDrivenBinder.java @@ -197,6 +197,10 @@ ParameterBinding bind(AnnotationDescription.Loadable annotation, * instances or method handles and method types for classes of a version at least of Java 7. The latter instances can also be * expressed as unloaded {@link JavaInstance} representations. *

+ *

+ * Important: When supplying a method handle or a method type, all types that are implied must be visible to the instrumented + * type or an {@link IllegalAccessException} will be thrown at runtime. + *

* * @param The bound annotation's type. */ @@ -248,16 +252,16 @@ public ParameterBinding bind(AnnotationDescription.Loadable annotation, stackManipulation = ClassConstant.of((TypeDescription) value); suppliedType = TypeDescription.CLASS; } else if (JavaType.METHOD_HANDLE.getTypeStub().isInstance(value)) { - stackManipulation = MethodHandleConstant.of(JavaInstance.MethodHandle.ofLoaded(value)); + stackManipulation = JavaInstance.MethodHandle.ofLoaded(value).asStackManipulation(); suppliedType = JavaType.METHOD_HANDLE.getTypeStub(); } else if (value instanceof JavaInstance.MethodHandle) { - stackManipulation = MethodHandleConstant.of((JavaInstance.MethodHandle) value); + stackManipulation = new JavaInstanceConstant((JavaInstance.MethodHandle) value); suppliedType = JavaType.METHOD_HANDLE.getTypeStub(); } else if (JavaType.METHOD_TYPE.getTypeStub().isInstance(value)) { - stackManipulation = MethodTypeConstant.of(JavaInstance.MethodType.ofLoaded(value)); + stackManipulation = new JavaInstanceConstant(JavaInstance.MethodType.ofLoaded(value)); suppliedType = JavaType.METHOD_HANDLE.getTypeStub(); } else if (value instanceof JavaInstance.MethodType) { - stackManipulation = MethodTypeConstant.of((JavaInstance.MethodType) value); + stackManipulation = new JavaInstanceConstant((JavaInstance.MethodType) value); suppliedType = JavaType.METHOD_HANDLE.getTypeStub(); } else { throw new IllegalStateException("Not able to save in class's constant pool: " + value); diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bytecode/constant/ClassConstant.java b/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bytecode/constant/ClassConstant.java index 66c49423e0e..f4e8ace0e2f 100644 --- a/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bytecode/constant/ClassConstant.java +++ b/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bytecode/constant/ClassConstant.java @@ -1,5 +1,6 @@ package net.bytebuddy.implementation.bytecode.constant; +import net.bytebuddy.ClassFileVersion; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.implementation.Implementation; import net.bytebuddy.implementation.bytecode.StackManipulation; @@ -160,7 +161,12 @@ public boolean isValid() { @Override public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) { - methodVisitor.visitLdcInsn(Type.getType(typeDescription.getDescriptor())); + if (implementationContext.getClassFileVersion().isAtLeast(ClassFileVersion.JAVA_V5) && typeDescription.isVisibleTo(implementationContext.getInstrumentedType())) { + methodVisitor.visitLdcInsn(Type.getType(typeDescription.getDescriptor())); + } else { + methodVisitor.visitLdcInsn(typeDescription.getName()); + methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;", false); + } return SIZE; } diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bytecode/constant/JavaInstanceConstant.java b/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bytecode/constant/JavaInstanceConstant.java new file mode 100644 index 00000000000..6a767a1fb69 --- /dev/null +++ b/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bytecode/constant/JavaInstanceConstant.java @@ -0,0 +1,54 @@ +package net.bytebuddy.implementation.bytecode.constant; + +import net.bytebuddy.implementation.Implementation; +import net.bytebuddy.implementation.bytecode.StackManipulation; +import net.bytebuddy.implementation.bytecode.StackSize; +import net.bytebuddy.utility.JavaInstance; +import org.objectweb.asm.MethodVisitor; + +/** + * A constant representing a {@link JavaInstance}. + */ +public class JavaInstanceConstant implements StackManipulation { + + /** + * The instance to load onto the operand stack. + */ + private final JavaInstance javaInstance; + + /** + * Creates a constant pool value representing a {@link JavaInstance}. + * + * @param javaInstance The instance to load onto the operand stack. + */ + public JavaInstanceConstant(JavaInstance javaInstance) { + this.javaInstance = javaInstance; + } + + @Override + public boolean isValid() { + return true; + } + + @Override + public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) { + methodVisitor.visitLdcInsn(javaInstance.asConstantPoolValue()); + return StackSize.SINGLE.toIncreasingSize(); + } + + @Override + public boolean equals(Object other) { + return this == other || !(other == null || getClass() != other.getClass()) + && javaInstance.equals(((JavaInstanceConstant) other).javaInstance); + } + + @Override + public int hashCode() { + return javaInstance.hashCode(); + } + + @Override + public String toString() { + return "JavaInstanceConstant{javaInstance=" + javaInstance + '}'; + } +} diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bytecode/constant/MethodHandleConstant.java b/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bytecode/constant/MethodHandleConstant.java deleted file mode 100644 index d1079727510..00000000000 --- a/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bytecode/constant/MethodHandleConstant.java +++ /dev/null @@ -1,155 +0,0 @@ -package net.bytebuddy.implementation.bytecode.constant; - -import net.bytebuddy.description.field.FieldDescription; -import net.bytebuddy.description.method.MethodDescription; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.implementation.Implementation; -import net.bytebuddy.implementation.bytecode.StackManipulation; -import net.bytebuddy.implementation.bytecode.StackSize; -import net.bytebuddy.utility.JavaInstance; -import org.objectweb.asm.Handle; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; - -/** - * A constant for a Java 7 {@code java.lang.invoke.MethodHandle}. - */ -public class MethodHandleConstant implements StackManipulation { - - /** - * The size of a {@code java.lang.invoke.MethodHandle} on the operand stack. - */ - private static final Size SIZE = StackSize.SINGLE.toIncreasingSize(); - - /** - * The ASM handle for the creating the given method handle. - */ - private final Handle handle; - - /** - * Creates a new method handle constant. - * - * @param handle The ASM handle for loading the handle onto the operand stack. - */ - private MethodHandleConstant(Handle handle) { - this.handle = handle; - } - - /** - * Creates a method handle for a method. - * - * @param methodDescription The method for which a method handle is to be put onto the operand stack. - * @return A stack manipulation that represents the loading of the handle. - */ - public static StackManipulation of(MethodDescription.InDefinedShape methodDescription) { - return methodDescription.isTypeInitializer() - ? Illegal.INSTANCE - : new MethodHandleConstant(new Handle(tagFor(methodDescription), - methodDescription.getDeclaringType().asErasure().getInternalName(), - methodDescription.getInternalName(), - methodDescription.getDescriptor(), - methodDescription.getDeclaringType().isInterface())); - } - - /** - * Creates stack manipulation for loading the provided method handle onto the operand stack. - * - * @param methodHandle The method handle to load onto the operand stack. - * @return A stack manipulation for loading the provided method handle onto the operand stack. - */ - public static StackManipulation of(JavaInstance.MethodHandle methodHandle) { - Type[] parameterType = new Type[methodHandle.getParameterTypes().size()]; - int index = 0; - for (TypeDescription typeDescription : methodHandle.getParameterTypes()) { - parameterType[index++] = Type.getType(typeDescription.getDescriptor()); - } - return new MethodHandleConstant(new Handle(methodHandle.getHandleType().getIdentifier(), - methodHandle.getOwnerType().getInternalName(), - methodHandle.getName(), - Type.getMethodDescriptor(Type.getType(methodHandle.getReturnType().getDescriptor()), parameterType), - methodHandle.getOwnerType().isInterface())); - } - - /** - * Looks up the handle tag for the given method. - * - * @param methodDescription The method for which a method handle is to be put onto the operand stack. - * @return The tag for the handle of this method. - */ - private static int tagFor(MethodDescription.InDefinedShape methodDescription) { - if (methodDescription.isConstructor()) { - return Opcodes.H_NEWINVOKESPECIAL; - } else if (methodDescription.isStatic()) { - return Opcodes.H_INVOKESTATIC; - } else if (methodDescription.isPrivate() || methodDescription.isDefaultMethod()) { - return Opcodes.H_INVOKESPECIAL; - } else if (methodDescription.getDeclaringType().isInterface()) { - return Opcodes.H_INVOKEINTERFACE; - } else { - return Opcodes.H_INVOKEVIRTUAL; - } - } - - /** - * Creates a method handle for a field getter. - * - * @param fieldDescription The field for which a get handle is to be put onto the operand stack. - * @return A stack manipulation that represents the loading of the handle. - */ - public static StackManipulation ofGetter(FieldDescription.InDefinedShape fieldDescription) { - return of(fieldDescription, fieldDescription.isStatic() ? Opcodes.H_GETSTATIC : Opcodes.H_GETFIELD); - } - - /** - * Creates a method handle for a field putter. - * - * @param fieldDescription The field for which a put handle is to be put onto the operand stack. - * @return A stack manipulation that represents the loading of the handle. - */ - public static StackManipulation ofPutter(FieldDescription.InDefinedShape fieldDescription) { - return of(fieldDescription, fieldDescription.isStatic() ? Opcodes.H_PUTSTATIC : Opcodes.H_PUTFIELD); - } - - /** - * Creates a method handle for a field. - * - * @param fieldDescription The field for which a handle is to be put onto the operand stack. - * @param tag The tag for this handle. - * @return A stack manipulation that represents the loading of the handle. - */ - private static StackManipulation of(FieldDescription.InDefinedShape fieldDescription, int tag) { - return new MethodHandleConstant(new Handle(tag, - fieldDescription.getDeclaringType().getInternalName(), - fieldDescription.getInternalName(), - fieldDescription.getDescriptor(), - fieldDescription.getDeclaringType().isInterface())); - } - - @Override - public boolean isValid() { - return true; - } - - @Override - public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) { - methodVisitor.visitLdcInsn(handle); - return SIZE; - } - - @Override - public boolean equals(Object other) { - return this == other || !(other == null || getClass() != other.getClass()) - && handle.equals(((MethodHandleConstant) other).handle); - } - - @Override - public int hashCode() { - return handle.hashCode(); - } - - @Override - public String toString() { - return "MethodHandleConstant{handle=" + handle + '}'; - } -} diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bytecode/constant/MethodTypeConstant.java b/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bytecode/constant/MethodTypeConstant.java deleted file mode 100644 index b4b44109882..00000000000 --- a/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bytecode/constant/MethodTypeConstant.java +++ /dev/null @@ -1,87 +0,0 @@ -package net.bytebuddy.implementation.bytecode.constant; - -import net.bytebuddy.description.method.MethodDescription; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.implementation.Implementation; -import net.bytebuddy.implementation.bytecode.StackManipulation; -import net.bytebuddy.implementation.bytecode.StackSize; -import net.bytebuddy.utility.JavaInstance; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Type; - -/** - * A constant for a Java 7 {@code java.lang.invoke.MethodType}. - */ -public class MethodTypeConstant implements StackManipulation { - - /** - * The size of a {@code java.lang.invoke.MethodType} on the operand stack. - */ - private static final Size SIZE = StackSize.SINGLE.toIncreasingSize(); - - /** - * The represented method type. - */ - private final Type methodType; - - /** - * Creates a method type constant for the given method type. - * - * @param methodType The represented method type. - */ - protected MethodTypeConstant(Type methodType) { - this.methodType = methodType; - } - - /** - * Transforms the given method into a stack manipulation that loads its type onto the operand stack. - * - * @param methodDescription The method of which the method type should be loaded onto the operand stack. - * @return A stack manipulation that loads the method type of the given method onto the operand stack. - */ - public static StackManipulation of(MethodDescription.InDefinedShape methodDescription) { - return new MethodTypeConstant(Type.getMethodType(methodDescription.getDescriptor())); - } - - /** - * Transforms the given method type into a stack manipulation that loads its type onto the operand stack. - * - * @param methodType The method type that should be loaded onto the operand stack. - * @return A stack manipulation that loads the given method type. - */ - public static StackManipulation of(JavaInstance.MethodType methodType) { - Type[] parameterType = new Type[methodType.getParameterTypes().size()]; - int index = 0; - for (TypeDescription typeDescription : methodType.getParameterTypes()) { - parameterType[index++] = Type.getType(typeDescription.getDescriptor()); - } - return new MethodTypeConstant(Type.getMethodType(Type.getType(methodType.getReturnType().getDescriptor()), parameterType)); - } - - @Override - public boolean isValid() { - return true; - } - - @Override - public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) { - methodVisitor.visitLdcInsn(methodType); - return SIZE; - } - - @Override - public boolean equals(Object other) { - return this == other || !(other == null || getClass() != other.getClass()) - && methodType.equals(((MethodTypeConstant) other).methodType); - } - - @Override - public int hashCode() { - return methodType.hashCode(); - } - - @Override - public String toString() { - return "MethodTypeConstant{methodType=" + methodType + '}'; - } -} diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bytecode/constant/TextConstant.java b/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bytecode/constant/TextConstant.java index b2c42aa001a..c5dae9df2d4 100644 --- a/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bytecode/constant/TextConstant.java +++ b/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bytecode/constant/TextConstant.java @@ -10,11 +10,6 @@ */ public class TextConstant implements StackManipulation { - /** - * The size impact of loading a {@link String} onto the operand stack. - */ - private static final Size SIZE = StackSize.SINGLE.toIncreasingSize(); - /** * The text value to load onto the operand stack. */ @@ -37,7 +32,7 @@ public boolean isValid() { @Override public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) { methodVisitor.visitLdcInsn(text); - return SIZE; + return StackSize.SINGLE.toIncreasingSize(); } @Override diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/utility/JavaInstance.java b/byte-buddy-dep/src/main/java/net/bytebuddy/utility/JavaInstance.java index 381b051549a..940f21b70d8 100644 --- a/byte-buddy-dep/src/main/java/net/bytebuddy/utility/JavaInstance.java +++ b/byte-buddy-dep/src/main/java/net/bytebuddy/utility/JavaInstance.java @@ -5,8 +5,7 @@ import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeList; import net.bytebuddy.implementation.bytecode.StackManipulation; -import net.bytebuddy.implementation.bytecode.constant.MethodHandleConstant; -import net.bytebuddy.implementation.bytecode.constant.MethodTypeConstant; +import net.bytebuddy.implementation.bytecode.constant.JavaInstanceConstant; import org.objectweb.asm.Handle; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; @@ -273,7 +272,7 @@ public Object asConstantPoolValue() { @Override public StackManipulation asStackManipulation() { - return MethodTypeConstant.of(this); + return new JavaInstanceConstant(this); } @Override @@ -611,7 +610,7 @@ public static MethodHandle of(Constructor constructor) { * @param methodDescription The method ro represent. * @return A method handle representing the given method. */ - public static MethodHandle of(MethodDescription methodDescription) { + public static MethodHandle of(MethodDescription.InDefinedShape methodDescription) { return new MethodHandle(HandleType.of(methodDescription), methodDescription.getDeclaringType().asErasure(), methodDescription.getInternalName(), @@ -637,7 +636,7 @@ public static MethodHandle ofSpecial(Method method, Class type) { * @param typeDescription The type on which the method is to be invoked on as a special method invocation. * @return A method handle representing the given method as special method invocation. */ - public static MethodHandle ofSpecial(MethodDescription methodDescription, TypeDescription typeDescription) { + public static MethodHandle ofSpecial(MethodDescription.InDefinedShape methodDescription, TypeDescription typeDescription) { if (!methodDescription.isSpecializableFor(typeDescription)) { throw new IllegalArgumentException("Cannot specialize " + methodDescription + " for " + typeDescription); } @@ -664,7 +663,7 @@ public static MethodHandle ofGetter(Field field) { * @param fieldDescription The field to represent. * @return A method handle for a setter of the given field. */ - public static MethodHandle ofGetter(FieldDescription fieldDescription) { + public static MethodHandle ofGetter(FieldDescription.InDefinedShape fieldDescription) { return new MethodHandle(HandleType.ofGetter(fieldDescription), fieldDescription.getDeclaringType().asErasure(), fieldDescription.getInternalName(), @@ -688,7 +687,7 @@ public static MethodHandle ofSetter(Field field) { * @param fieldDescription The field to represent. * @return A method handle for a getter of the given field. */ - public static MethodHandle ofSetter(FieldDescription fieldDescription) { + public static MethodHandle ofSetter(FieldDescription.InDefinedShape fieldDescription) { return new MethodHandle(HandleType.ofSetter(fieldDescription), fieldDescription.getDeclaringType().asErasure(), fieldDescription.getInternalName(), @@ -708,7 +707,7 @@ public Object asConstantPoolValue() { @Override public StackManipulation asStackManipulation() { - return MethodHandleConstant.of(this); + return new JavaInstanceConstant(this); } @Override @@ -1305,7 +1304,7 @@ public Object publicLookup() { @Override public Class lookupType(Object lookup) { - throw new IllegalStateException(); // TODO + throw new IllegalStateException("Unsupported type on current JVM: java.lang.invoke.MethodHandle"); } @Override @@ -1385,7 +1384,7 @@ public enum HandleType { * @param methodDescription The method for which a handle type should be found. * @return The handle type for the given method. */ - protected static HandleType of(MethodDescription methodDescription) { + protected static HandleType of(MethodDescription.InDefinedShape methodDescription) { if (methodDescription.isStatic()) { return INVOKE_STATIC; } else if (methodDescription.isPrivate()) { @@ -1420,7 +1419,7 @@ protected static HandleType of(int identifier) { * @param methodDescription The method for which a handle type should be found. * @return The handle type for the given method. */ - protected static HandleType ofSpecial(MethodDescription methodDescription) { + protected static HandleType ofSpecial(MethodDescription.InDefinedShape methodDescription) { if (methodDescription.isStatic() || methodDescription.isAbstract()) { throw new IllegalArgumentException("Cannot invoke " + methodDescription + " via invokespecial"); } @@ -1435,7 +1434,7 @@ protected static HandleType ofSpecial(MethodDescription methodDescription) { * @param fieldDescription The field for which to create a getter handle. * @return The corresponding handle type. */ - protected static HandleType ofGetter(FieldDescription fieldDescription) { + protected static HandleType ofGetter(FieldDescription.InDefinedShape fieldDescription) { return fieldDescription.isStatic() ? GET_STATIC_FIELD : GET_FIELD; @@ -1447,7 +1446,7 @@ protected static HandleType ofGetter(FieldDescription fieldDescription) { * @param fieldDescription The field for which to create a setter handle. * @return The corresponding handle type. */ - protected static HandleType ofSetter(FieldDescription fieldDescription) { + protected static HandleType ofSetter(FieldDescription.InDefinedShape fieldDescription) { return fieldDescription.isStatic() ? PUT_STATIC_FIELD : PUT_FIELD; diff --git a/byte-buddy-dep/src/test/java/net/bytebuddy/dynamic/scaffold/TypeWriterDefaultTest.java b/byte-buddy-dep/src/test/java/net/bytebuddy/dynamic/scaffold/TypeWriterDefaultTest.java index 650ed5b9493..805f5b60d47 100644 --- a/byte-buddy-dep/src/test/java/net/bytebuddy/dynamic/scaffold/TypeWriterDefaultTest.java +++ b/byte-buddy-dep/src/test/java/net/bytebuddy/dynamic/scaffold/TypeWriterDefaultTest.java @@ -377,21 +377,11 @@ public void testTypeInitializerOnRebasedLegacyInterface() throws Exception { .make(); } - @Test(expected = IllegalStateException.class) - public void testTypeInLegacyConstantPool() throws Exception { - new ByteBuddy(ClassFileVersion.JAVA_V4) - .subclass(Object.class) - .defineMethod(FOO, Object.class) - .intercept(FixedValue.value(Object.class)) - .make(); - } - @Test public void testTypeInLegacyConstantPoolRemapped() throws Exception { Class dynamicType = new ByteBuddy(ClassFileVersion.JAVA_V4) .with(TypeValidation.DISABLED) .subclass(Object.class) - .visit(TypeConstantAdjustment.INSTANCE) .defineMethod(FOO, Object.class, Visibility.PUBLIC) .intercept(FixedValue.value(Object.class)) .make() @@ -405,7 +395,6 @@ public void testArrayTypeInLegacyConstantPoolRemapped() throws Exception { Class dynamicType = new ByteBuddy(ClassFileVersion.JAVA_V4) .with(TypeValidation.DISABLED) .subclass(Object.class) - .visit(TypeConstantAdjustment.INSTANCE) .defineMethod(FOO, Object.class, Visibility.PUBLIC) .intercept(FixedValue.value(Object[].class)) .make() @@ -419,7 +408,6 @@ public void testPrimitiveTypeInLegacyConstantPoolRemapped() throws Exception { Class dynamicType = new ByteBuddy(ClassFileVersion.JAVA_V4) .with(TypeValidation.DISABLED) .subclass(Object.class) - .visit(TypeConstantAdjustment.INSTANCE) .defineMethod(FOO, Object.class, Visibility.PUBLIC) .intercept(FixedValue.value(int.class)) .make() @@ -428,6 +416,8 @@ public void testPrimitiveTypeInLegacyConstantPoolRemapped() throws Exception { assertThat(dynamicType.getDeclaredMethod(FOO).invoke(dynamicType.newInstance()), is((Object) int.class)); } + // TODO: Add tests for type adjustment with pre-compiling legacy type + @Test(expected = IllegalStateException.class) public void testMethodTypeInLegacyConstantPool() throws Exception { new ByteBuddy(ClassFileVersion.JAVA_V4) diff --git a/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/ImplementationContextDefaultOtherTest.java b/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/ImplementationContextDefaultOtherTest.java index f8e4c881dc6..9cb10300b6c 100644 --- a/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/ImplementationContextDefaultOtherTest.java +++ b/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/ImplementationContextDefaultOtherTest.java @@ -63,6 +63,23 @@ public void testFrozenTypeInitializerFrozenThrowsExceptionOnDrain() throws Excep mock(AnnotationValueFilter.Factory.class)); } + @Test(expected = IllegalStateException.class) + public void testPrematureUse() throws Exception { + new Implementation.Context.Default(mock(TypeDescription.class), + mock(AuxiliaryType.NamingStrategy.class), + mock(TypeInitializer.class), + mock(ClassFileVersion.class)).getClassFileVersion(); + } + + @Test + public void testInstrumentationGetter() throws Exception { + TypeDescription instrumentedType = mock(TypeDescription.class); + assertThat(new Implementation.Context.Default(instrumentedType, + mock(AuxiliaryType.NamingStrategy.class), + mock(TypeInitializer.class), + mock(ClassFileVersion.class)).getInstrumentedType(), is(instrumentedType)); + } + @Test public void testObjectProperties() throws Exception { ObjectPropertyAssertion.of(Implementation.Context.Default.class).applyBasic(); diff --git a/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/ImplementationContextDisabledTest.java b/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/ImplementationContextDisabledTest.java index f2e171b3fd0..6db379e0b2c 100644 --- a/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/ImplementationContextDisabledTest.java +++ b/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/ImplementationContextDisabledTest.java @@ -108,6 +108,16 @@ public void testDrainWithMatchedCode() throws Exception { mock(AnnotationValueFilter.Factory.class)); } + @Test(expected = IllegalStateException.class) + public void testPrematureUse() throws Exception { + new Implementation.Context.Disabled(instrumentedType).getClassFileVersion(); + } + + @Test + public void testInstrumentationGetter() throws Exception { + assertThat(new Implementation.Context.Disabled(instrumentedType).getInstrumentedType(), is(instrumentedType)); + } + @Test public void testObjectProperties() throws Exception { ObjectPropertyAssertion.of(Implementation.Context.Disabled.class).apply(); diff --git a/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bind/annotation/OriginBinderTest.java b/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bind/annotation/OriginBinderTest.java index 461a2c1deb5..a5bec096b40 100644 --- a/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bind/annotation/OriginBinderTest.java +++ b/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bind/annotation/OriginBinderTest.java @@ -1,6 +1,8 @@ package net.bytebuddy.implementation.bind.annotation; import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.method.ParameterDescription; +import net.bytebuddy.description.method.ParameterList; import net.bytebuddy.description.type.TypeDefinition; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.implementation.bind.MethodDelegationBinder; @@ -127,6 +129,8 @@ public void testModifierBinding() throws Exception { @JavaVersionRule.Enforce(7) public void testMethodHandleBinding() throws Exception { when(genericTargetType.asErasure()).thenReturn(new TypeDescription.ForLoadedType(JavaType.METHOD_HANDLE.load())); + when(methodDescription.getReturnType()).thenReturn(TypeDescription.Generic.VOID); + when(methodDescription.getParameters()).thenReturn(new ParameterList.Empty()); TypeDescription typeDescription = mock(TypeDescription.class); when(typeDescription.asErasure()).thenReturn(typeDescription); when(methodDescription.getDeclaringType()).thenReturn(typeDescription); @@ -139,7 +143,8 @@ public void testMethodHandleBinding() throws Exception { @JavaVersionRule.Enforce(7) public void testMethodTypeBinding() throws Exception { when(genericTargetType.asErasure()).thenReturn(new TypeDescription.ForLoadedType(JavaType.METHOD_TYPE.load())); - when(methodDescription.getDescriptor()).thenReturn(FOO); + when(methodDescription.getReturnType()).thenReturn(TypeDescription.Generic.VOID); + when(methodDescription.getParameters()).thenReturn(new ParameterList.Empty()); MethodDelegationBinder.ParameterBinding parameterBinding = Origin.Binder.INSTANCE .bind(annotationDescription, source, target, implementationTarget, assigner); assertThat(parameterBinding.isValid(), is(true)); diff --git a/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bytecode/constant/ClassConstantReferenceTest.java b/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bytecode/constant/ClassConstantReferenceTest.java index 9b4ff4aa3be..6472ab8f8df 100644 --- a/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bytecode/constant/ClassConstantReferenceTest.java +++ b/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bytecode/constant/ClassConstantReferenceTest.java @@ -1,15 +1,18 @@ package net.bytebuddy.implementation.bytecode.constant; +import net.bytebuddy.ClassFileVersion; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.implementation.Implementation; import net.bytebuddy.implementation.bytecode.StackManipulation; import net.bytebuddy.test.utility.MockitoRule; import net.bytebuddy.test.utility.ObjectPropertyAssertion; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; import org.mockito.Mock; import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import static org.hamcrest.CoreMatchers.is; @@ -24,7 +27,10 @@ public class ClassConstantReferenceTest { public TestRule mockitoRule = new MockitoRule(this); @Mock - private TypeDescription typeDescription; + private TypeDescription typeDescription, instrumentedType; + + @Mock + private ClassFileVersion classFileVersion; @Mock private MethodVisitor methodVisitor; @@ -32,8 +38,16 @@ public class ClassConstantReferenceTest { @Mock private Implementation.Context implementationContext; + @Before + public void setUp() throws Exception { + when(implementationContext.getInstrumentedType()).thenReturn(instrumentedType); + when(implementationContext.getClassFileVersion()).thenReturn(classFileVersion); + } + @Test - public void testClassConstant() throws Exception { + public void testClassConstantModernVisible() throws Exception { + when(typeDescription.isVisibleTo(instrumentedType)).thenReturn(true); + when(classFileVersion.isAtLeast(ClassFileVersion.JAVA_V5)).thenReturn(true); when(typeDescription.getDescriptor()).thenReturn(FOO); StackManipulation stackManipulation = ClassConstant.of(typeDescription); assertThat(stackManipulation.isValid(), is(true)); @@ -41,11 +55,56 @@ public void testClassConstant() throws Exception { assertThat(size.getSizeImpact(), is(1)); assertThat(size.getMaximalSize(), is(1)); verify(typeDescription).getDescriptor(); + verify(typeDescription).isVisibleTo(instrumentedType); verify(typeDescription, times(9)).represents(any(Class.class)); verifyNoMoreInteractions(typeDescription); verify(methodVisitor).visitLdcInsn(Type.getType(FOO)); verifyNoMoreInteractions(methodVisitor); - verifyZeroInteractions(implementationContext); + } + + @Test + public void testClassConstantModernInvisible() throws Exception { + when(typeDescription.isVisibleTo(instrumentedType)).thenReturn(false); + when(classFileVersion.isAtLeast(ClassFileVersion.JAVA_V5)).thenReturn(true); + when(typeDescription.getName()).thenReturn(FOO); + StackManipulation stackManipulation = ClassConstant.of(typeDescription); + assertThat(stackManipulation.isValid(), is(true)); + StackManipulation.Size size = stackManipulation.apply(methodVisitor, implementationContext); + assertThat(size.getSizeImpact(), is(1)); + assertThat(size.getMaximalSize(), is(1)); + verify(typeDescription).getName(); + verify(typeDescription).isVisibleTo(instrumentedType); + verify(typeDescription, times(9)).represents(any(Class.class)); + verifyNoMoreInteractions(typeDescription); + verify(methodVisitor).visitLdcInsn(FOO); + verify(methodVisitor).visitMethodInsn(Opcodes.INVOKESTATIC, + Type.getInternalName(Class.class), + "forName", + Type.getMethodDescriptor(Type.getType(Class.class), Type.getType(String.class)), + false); + verifyNoMoreInteractions(methodVisitor); + } + + @Test + public void testClassConstantLegacy() throws Exception { + when(typeDescription.isVisibleTo(instrumentedType)).thenReturn(true); + when(classFileVersion.isAtLeast(ClassFileVersion.JAVA_V5)).thenReturn(false); + when(typeDescription.getName()).thenReturn(FOO); + StackManipulation stackManipulation = ClassConstant.of(typeDescription); + assertThat(stackManipulation.isValid(), is(true)); + StackManipulation.Size size = stackManipulation.apply(methodVisitor, implementationContext); + assertThat(size.getSizeImpact(), is(1)); + assertThat(size.getMaximalSize(), is(1)); + verify(typeDescription).getName(); + verify(typeDescription, times(9)).represents(any(Class.class)); + verifyNoMoreInteractions(typeDescription); + verify(methodVisitor).visitLdcInsn(FOO); + verify(methodVisitor).visitMethodInsn(Opcodes.INVOKESTATIC, + Type.getInternalName(Class.class), + "forName", + Type.getMethodDescriptor(Type.getType(Class.class), Type.getType(String.class)), + false); + verifyNoMoreInteractions(methodVisitor); } @Test diff --git a/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bytecode/constant/FieldConstantTest.java b/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bytecode/constant/FieldConstantTest.java index bb8ea0796d1..ad17c8d06d2 100644 --- a/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bytecode/constant/FieldConstantTest.java +++ b/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bytecode/constant/FieldConstantTest.java @@ -1,5 +1,6 @@ package net.bytebuddy.implementation.bytecode.constant; +import net.bytebuddy.ClassFileVersion; import net.bytebuddy.description.field.FieldDescription; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.implementation.Implementation; @@ -33,7 +34,7 @@ public class FieldConstantTest { private FieldDescription.InDefinedShape fieldDescription, cacheField; @Mock - private TypeDescription declaringType, cacheDeclaringType, cacheFieldType; + private TypeDescription declaringType, cacheDeclaringType, cacheFieldType, instrumentedType; @Mock private TypeDescription.Generic genericCacheFieldType; @@ -41,6 +42,9 @@ public class FieldConstantTest { @Mock private MethodVisitor methodVisitor; + @Mock + private ClassFileVersion classFileVersion; + @Mock private Implementation.Context implementationContext; @@ -54,6 +58,7 @@ public void setUp() throws Exception { .thenReturn(cacheField); when(cacheField.getDeclaringType()).thenReturn(cacheDeclaringType); when(cacheField.isStatic()).thenReturn(true); + when(declaringType.getName()).thenReturn(BAZ); when(cacheDeclaringType.getInternalName()).thenReturn(BAZ); when(cacheField.getName()).thenReturn(FOO + BAR); when(cacheField.getType()).thenReturn(genericCacheFieldType); @@ -61,10 +66,14 @@ public void setUp() throws Exception { when(genericCacheFieldType.getStackSize()).thenReturn(StackSize.SINGLE); when(cacheField.getInternalName()).thenReturn(FOO + BAR); when(cacheField.getDescriptor()).thenReturn(QUX + BAZ); + when(implementationContext.getClassFileVersion()).thenReturn(classFileVersion); + when(implementationContext.getInstrumentedType()).thenReturn(instrumentedType); } @Test - public void testConstantCreation() throws Exception { + public void testConstantCreationModernVisible() throws Exception { + when(classFileVersion.isAtLeast(ClassFileVersion.JAVA_V5)).thenReturn(true); + when(declaringType.isVisibleTo(instrumentedType)).thenReturn(true); StackManipulation stackManipulation = new FieldConstant(fieldDescription); assertThat(stackManipulation.isValid(), is(true)); StackManipulation.Size size = stackManipulation.apply(methodVisitor, implementationContext); @@ -78,7 +87,54 @@ public void testConstantCreation() throws Exception { "(Ljava/lang/String;)Ljava/lang/reflect/Field;", false); verifyNoMoreInteractions(methodVisitor); - verifyZeroInteractions(implementationContext); + } + + @Test + public void testConstantCreationModernInvisible() throws Exception { + when(classFileVersion.isAtLeast(ClassFileVersion.JAVA_V5)).thenReturn(true); + when(declaringType.isVisibleTo(instrumentedType)).thenReturn(false); + StackManipulation stackManipulation = new FieldConstant(fieldDescription); + assertThat(stackManipulation.isValid(), is(true)); + StackManipulation.Size size = stackManipulation.apply(methodVisitor, implementationContext); + assertThat(size.getSizeImpact(), is(1)); + assertThat(size.getMaximalSize(), is(2)); + verify(methodVisitor).visitLdcInsn(BAZ); + verify(methodVisitor).visitMethodInsn(Opcodes.INVOKESTATIC, + Type.getInternalName(Class.class), + "forName", + Type.getMethodDescriptor(Type.getType(Class.class), Type.getType(String.class)), + false); + verify(methodVisitor).visitLdcInsn(BAR); + verify(methodVisitor).visitMethodInsn(Opcodes.INVOKEVIRTUAL, + "java/lang/Class", + "getDeclaredField", + "(Ljava/lang/String;)Ljava/lang/reflect/Field;", + false); + verifyNoMoreInteractions(methodVisitor); + } + + @Test + public void testConstantCreationLegacy() throws Exception { + when(classFileVersion.isAtLeast(ClassFileVersion.JAVA_V5)).thenReturn(false); + when(declaringType.isVisibleTo(instrumentedType)).thenReturn(true); + StackManipulation stackManipulation = new FieldConstant(fieldDescription); + assertThat(stackManipulation.isValid(), is(true)); + StackManipulation.Size size = stackManipulation.apply(methodVisitor, implementationContext); + assertThat(size.getSizeImpact(), is(1)); + assertThat(size.getMaximalSize(), is(2)); + verify(methodVisitor).visitLdcInsn(BAZ); + verify(methodVisitor).visitMethodInsn(Opcodes.INVOKESTATIC, + Type.getInternalName(Class.class), + "forName", + Type.getMethodDescriptor(Type.getType(Class.class), Type.getType(String.class)), + false); + verify(methodVisitor).visitLdcInsn(BAR); + verify(methodVisitor).visitMethodInsn(Opcodes.INVOKEVIRTUAL, + "java/lang/Class", + "getDeclaredField", + "(Ljava/lang/String;)Ljava/lang/reflect/Field;", + false); + verifyNoMoreInteractions(methodVisitor); } @Test diff --git a/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bytecode/constant/JavaInstanceConstantTest.java b/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bytecode/constant/JavaInstanceConstantTest.java new file mode 100644 index 00000000000..f44b7d32be8 --- /dev/null +++ b/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bytecode/constant/JavaInstanceConstantTest.java @@ -0,0 +1,52 @@ +package net.bytebuddy.implementation.bytecode.constant; + +import net.bytebuddy.implementation.Implementation; +import net.bytebuddy.implementation.bytecode.StackManipulation; +import net.bytebuddy.test.utility.MockitoRule; +import net.bytebuddy.test.utility.ObjectPropertyAssertion; +import net.bytebuddy.utility.JavaInstance; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.mockito.Mock; +import org.objectweb.asm.MethodVisitor; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.*; + +public class JavaInstanceConstantTest { + + private static final String FOO = "foo"; + + @Rule + public TestRule mockitoRule = new MockitoRule(this); + + @Mock + private JavaInstance javaInstance; + + @Mock + private MethodVisitor methodVisitor; + + @Mock + private Implementation.Context implementationContext; + + @Test + public void testMethodHandle() throws Exception { + when(javaInstance.asConstantPoolValue()).thenReturn(FOO); + StackManipulation stackManipulation = new JavaInstanceConstant(javaInstance); + StackManipulation.Size size = stackManipulation.apply(methodVisitor, implementationContext); + assertThat(size.getSizeImpact(), is(1)); + assertThat(size.getMaximalSize(), is(1)); + verify(javaInstance).asConstantPoolValue(); + verifyNoMoreInteractions(javaInstance); + verify(methodVisitor).visitLdcInsn(FOO); + verifyNoMoreInteractions(methodVisitor); + verifyZeroInteractions(implementationContext); + } + + @Test + public void testObjectProperties() throws Exception { + ObjectPropertyAssertion.of(JavaInstanceConstant.class).apply(); + } +} diff --git a/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bytecode/constant/MethodConstantTest.java b/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bytecode/constant/MethodConstantTest.java index 17f5ee2c531..1889e7bb091 100644 --- a/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bytecode/constant/MethodConstantTest.java +++ b/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bytecode/constant/MethodConstantTest.java @@ -1,5 +1,6 @@ package net.bytebuddy.implementation.bytecode.constant; +import net.bytebuddy.ClassFileVersion; import net.bytebuddy.description.field.FieldDescription; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.method.ParameterList; @@ -38,7 +39,10 @@ public class MethodConstantTest { private MethodDescription.InDefinedShape methodDescription; @Mock - private TypeDescription declaringType, parameterType, fieldType; + private TypeDescription declaringType, parameterType, fieldType, instrumentedType; + + @Mock + private ClassFileVersion classFileVersion; @Mock private TypeDescription.Generic genericFieldType; @@ -82,6 +86,8 @@ public void setUp() throws Exception { when(fieldDescription.getInternalName()).thenReturn(FOO); when(fieldDescription.getDescriptor()).thenReturn(QUX); when(fieldDescription.asDefined()).thenReturn(fieldDescription); + when(implementationContext.getClassFileVersion()).thenReturn(classFileVersion); + when(implementationContext.getInstrumentedType()).thenReturn(instrumentedType); } @Test @@ -94,7 +100,6 @@ public void testMethod() throws Exception { "getDeclaredMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", false); - verifyZeroInteractions(implementationContext); } @Test @@ -120,7 +125,6 @@ public void testConstructor() throws Exception { "getDeclaredConstructor", "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;", false); - verifyZeroInteractions(implementationContext); } @Test diff --git a/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bytecode/constant/MethodHandleConstantTest.java b/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bytecode/constant/MethodHandleConstantTest.java deleted file mode 100644 index 570fa6c3eba..00000000000 --- a/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bytecode/constant/MethodHandleConstantTest.java +++ /dev/null @@ -1,150 +0,0 @@ -package net.bytebuddy.implementation.bytecode.constant; - -import net.bytebuddy.description.field.FieldDescription; -import net.bytebuddy.description.method.MethodDescription; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.implementation.Implementation; -import net.bytebuddy.implementation.bytecode.StackManipulation; -import net.bytebuddy.test.utility.MockitoRule; -import net.bytebuddy.test.utility.ObjectPropertyAssertion; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestRule; -import org.mockito.Mock; -import org.objectweb.asm.Handle; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; - -import java.util.Arrays; -import java.util.Iterator; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.mockito.Mockito.*; - -public class MethodHandleConstantTest { - - private static final String FOO = "foo", BAR = "bar", QUX = "qux"; - - @Rule - public TestRule mockitoRule = new MockitoRule(this); - - @Mock - private MethodDescription.InDefinedShape methodDescription; - - @Mock - private FieldDescription.InDefinedShape fieldDescription; - - @Mock - private TypeDescription typeDescription; - - @Mock - private MethodVisitor methodVisitor; - - @Mock - private Implementation.Context implementationContext; - - @Before - public void setUp() throws Exception { - when(typeDescription.asErasure()).thenReturn(typeDescription); - when(methodDescription.getDeclaringType()).thenReturn(typeDescription); - when(typeDescription.getInternalName()).thenReturn(FOO); - when(methodDescription.getInternalName()).thenReturn(BAR); - when(methodDescription.getDescriptor()).thenReturn(QUX); - when(fieldDescription.getDeclaringType()).thenReturn(typeDescription); - when(fieldDescription.getInternalName()).thenReturn(FOO); - when(fieldDescription.getInternalName()).thenReturn(BAR); - when(fieldDescription.getDescriptor()).thenReturn(QUX); - } - - @After - public void tearDown() throws Exception { - verifyZeroInteractions(implementationContext); - } - - @Test - public void testMethodHandleForStaticMethod() throws Exception { - when(methodDescription.isStatic()).thenReturn(true); - testMethodHandle(MethodHandleConstant.of(methodDescription), Opcodes.H_INVOKESTATIC, false); - } - - @Test - public void testMethodHandleForVirtualMethod() throws Exception { - testMethodHandle(MethodHandleConstant.of(methodDescription), Opcodes.H_INVOKEVIRTUAL, false); - } - - @Test - public void testMethodHandleForPrivateMethod() throws Exception { - when(methodDescription.isPrivate()).thenReturn(true); - testMethodHandle(MethodHandleConstant.of(methodDescription), Opcodes.H_INVOKESPECIAL, false); - } - - @Test - public void testMethodHandleForDefaultMethod() throws Exception { - when(methodDescription.isDefaultMethod()).thenReturn(true); - testMethodHandle(MethodHandleConstant.of(methodDescription), Opcodes.H_INVOKESPECIAL, false); - } - - @Test - public void testMethodHandleForInterfaceMethod() throws Exception { - when(typeDescription.isInterface()).thenReturn(true); - testMethodHandle(MethodHandleConstant.of(methodDescription), Opcodes.H_INVOKEINTERFACE, true); - } - - @Test - public void testMethodHandleForConstructorMethod() throws Exception { - when(methodDescription.isConstructor()).thenReturn(true); - testMethodHandle(MethodHandleConstant.of(methodDescription), Opcodes.H_NEWINVOKESPECIAL, false); - } - - @Test - public void testMethodHandleForMemberFieldGetter() throws Exception { - testMethodHandle(MethodHandleConstant.ofGetter(fieldDescription), Opcodes.H_GETFIELD, false); - } - - @Test - public void testMethodHandleForMemberFieldPutter() throws Exception { - testMethodHandle(MethodHandleConstant.ofPutter(fieldDescription), Opcodes.H_PUTFIELD, false); - } - - @Test - public void testMethodHandleForStaticFieldGetter() throws Exception { - when(fieldDescription.isStatic()).thenReturn(true); - testMethodHandle(MethodHandleConstant.ofGetter(fieldDescription), Opcodes.H_GETSTATIC, false); - } - - @Test - public void testMethodHandleForStaticFieldPutter() throws Exception { - when(fieldDescription.isStatic()).thenReturn(true); - testMethodHandle(MethodHandleConstant.ofPutter(fieldDescription), Opcodes.H_PUTSTATIC, false); - } - - private void testMethodHandle(StackManipulation stackManipulation, int handleCode, boolean handleInterface) throws Exception { - assertThat(stackManipulation.isValid(), is(true)); - StackManipulation.Size size = stackManipulation.apply(methodVisitor, implementationContext); - assertThat(size.getSizeImpact(), is(1)); - assertThat(size.getMaximalSize(), is(1)); - verify(methodVisitor).visitLdcInsn(new Handle(handleCode, FOO, BAR, QUX, handleInterface)); - verifyNoMoreInteractions(methodVisitor); - } - - @Test - public void testMethodHandleForTypeInitializer() throws Exception { - when(methodDescription.isTypeInitializer()).thenReturn(true); - StackManipulation stackManipulation = MethodHandleConstant.of(methodDescription); - assertThat(stackManipulation.isValid(), is(false)); - } - - @Test - public void testObjectProperties() throws Exception { - final Iterator iterator = Arrays.asList(FOO, BAR).iterator(); - ObjectPropertyAssertion.of(MethodHandleConstant.class).create(new ObjectPropertyAssertion.Creator() { - @Override - public Handle create() { - return new Handle(Opcodes.H_GETFIELD, FOO, BAR, iterator.next(), false); - } - }).apply(); - } -} diff --git a/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bytecode/constant/MethodTypeConstantTest.java b/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bytecode/constant/MethodTypeConstantTest.java deleted file mode 100644 index f52d268a001..00000000000 --- a/byte-buddy-dep/src/test/java/net/bytebuddy/implementation/bytecode/constant/MethodTypeConstantTest.java +++ /dev/null @@ -1,63 +0,0 @@ -package net.bytebuddy.implementation.bytecode.constant; - -import net.bytebuddy.description.method.MethodDescription; -import net.bytebuddy.implementation.Implementation; -import net.bytebuddy.implementation.bytecode.StackManipulation; -import net.bytebuddy.test.utility.MockitoRule; -import net.bytebuddy.test.utility.ObjectPropertyAssertion; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestRule; -import org.mockito.Mock; -import org.mockito.asm.Type; -import org.objectweb.asm.MethodVisitor; - -import java.util.Arrays; -import java.util.Iterator; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.mockito.Mockito.when; - -public class MethodTypeConstantTest { - - private static final String FOO = "foo", BAR = "bar"; - - @Rule - public TestRule mockitoRule = new MockitoRule(this); - - @Mock - private MethodDescription.InDefinedShape methodDescription; - - @Mock - private MethodVisitor methodVisitor; - - @Mock - private Implementation.Context implementationContext; - - @Before - public void setUp() throws Exception { - when(methodDescription.getDescriptor()).thenReturn(FOO); - } - - @Test - public void testApplication() throws Exception { - StackManipulation stackManipulation = MethodTypeConstant.of(methodDescription); - assertThat(stackManipulation.isValid(), is(true)); - StackManipulation.Size size = stackManipulation.apply(methodVisitor, implementationContext); - assertThat(size.getSizeImpact(), is(1)); - assertThat(size.getMaximalSize(), is(1)); - } - - @Test - public void testObjectProperties() throws Exception { - final Iterator iterator = Arrays.asList(Type.getDescriptor(Void.class), Type.getDescriptor(String.class)).iterator(); - ObjectPropertyAssertion.of(MethodTypeConstant.class).refine(new ObjectPropertyAssertion.Refinement() { - @Override - public void apply(MethodDescription mock) { - when(mock.getDescriptor()).thenReturn(iterator.next()); - } - }).apply(); - } -} diff --git a/byte-buddy-dep/src/test/java/net/bytebuddy/utility/JavaInstanceMethodHandleDispatcherTest.java b/byte-buddy-dep/src/test/java/net/bytebuddy/utility/JavaInstanceMethodHandleDispatcherTest.java index 53f1cada427..0009f831c0f 100644 --- a/byte-buddy-dep/src/test/java/net/bytebuddy/utility/JavaInstanceMethodHandleDispatcherTest.java +++ b/byte-buddy-dep/src/test/java/net/bytebuddy/utility/JavaInstanceMethodHandleDispatcherTest.java @@ -9,6 +9,8 @@ import java.util.Arrays; import java.util.Iterator; +import static org.mockito.Mockito.mock; + public class JavaInstanceMethodHandleDispatcherTest { @Test(expected = IllegalStateException.class) @@ -21,6 +23,11 @@ public void testLegacyVmPunlicLookup() throws Exception { JavaInstance.MethodHandle.Dispatcher.ForLegacyVm.INSTANCE.publicLookup(); } + @Test(expected = IllegalStateException.class) + public void testLegacyVmLookupType() throws Exception { + JavaInstance.MethodHandle.Dispatcher.ForLegacyVm.INSTANCE.lookupType(mock(Object.class)); + } + @Test public void testObjectProperties() throws Exception { final Iterator methods1 = Arrays.asList(Foo.class.getDeclaredMethods()).iterator(); diff --git a/byte-buddy-dep/src/test/java/net/bytebuddy/utility/JavaInstanceMethodHandleTest.java b/byte-buddy-dep/src/test/java/net/bytebuddy/utility/JavaInstanceMethodHandleTest.java index 5b990f1c676..65cb59598e2 100644 --- a/byte-buddy-dep/src/test/java/net/bytebuddy/utility/JavaInstanceMethodHandleTest.java +++ b/byte-buddy-dep/src/test/java/net/bytebuddy/utility/JavaInstanceMethodHandleTest.java @@ -165,7 +165,7 @@ public void testIllegalParameterThrowsException() throws Exception { @Test(expected = IllegalArgumentException.class) public void testStaticMethodNotSpecial() throws Exception { - MethodDescription methodDescription = mock(MethodDescription.class); + MethodDescription.InDefinedShape methodDescription = mock(MethodDescription.InDefinedShape.class); TypeDescription typeDescription = mock(TypeDescription.class); when(methodDescription.isStatic()).thenReturn(true); when(methodDescription.isSpecializableFor(typeDescription)).thenReturn(true); @@ -174,7 +174,7 @@ public void testStaticMethodNotSpecial() throws Exception { @Test(expected = IllegalArgumentException.class) public void testAbstractMethodNotSpecial() throws Exception { - MethodDescription methodDescription = mock(MethodDescription.class); + MethodDescription.InDefinedShape methodDescription = mock(MethodDescription.InDefinedShape.class); TypeDescription typeDescription = mock(TypeDescription.class); when(methodDescription.isAbstract()).thenReturn(true); when(methodDescription.isSpecializableFor(typeDescription)).thenReturn(true); @@ -183,7 +183,7 @@ public void testAbstractMethodNotSpecial() throws Exception { @Test(expected = IllegalArgumentException.class) public void testMethodNotSpecializable() throws Exception { - MethodDescription methodDescription = mock(MethodDescription.class); + MethodDescription.InDefinedShape methodDescription = mock(MethodDescription.InDefinedShape.class); TypeDescription typeDescription = mock(TypeDescription.class); when(methodDescription.isSpecializableFor(typeDescription)).thenReturn(false); JavaInstance.MethodHandle.ofSpecial(methodDescription, typeDescription);