package net.bytebuddy.dynamic.scaffold; import net.bytebuddy.ByteBuddy; import net.bytebuddy.ClassFileVersion; import net.bytebuddy.asm.AsmVisitorWrapper; import net.bytebuddy.description.annotation.AnnotationDescription; import net.bytebuddy.description.field.FieldDescription; import net.bytebuddy.description.field.FieldList; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.method.MethodList; import net.bytebuddy.description.modifier.*; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy; import net.bytebuddy.implementation.FixedValue; import net.bytebuddy.implementation.StubMethod; import net.bytebuddy.implementation.SuperMethodCall; import net.bytebuddy.implementation.bytecode.ByteCodeAppender; import net.bytebuddy.test.utility.JavaVersionRule; import net.bytebuddy.test.utility.ObjectPropertyAssertion; import net.bytebuddy.utility.JavaConstant; import org.junit.Rule; import org.junit.Test; import org.junit.rules.MethodRule; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import java.io.File; import java.io.Serializable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Field; import java.util.Collections; import java.util.List; import static net.bytebuddy.matcher.ElementMatchers.*; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class TypeWriterDefaultTest { private static final String FOO = "foo", BAR = "bar"; private static final String LEGACY_INTERFACE = "net.bytebuddy.test.precompiled.LegacyInterface"; private static final String JAVA_8_INTERFACE = "net.bytebuddy.test.precompiled.SingleDefaultMethodInterface"; @Rule public MethodRule javaVersionRule = new JavaVersionRule(); @Test(expected = IllegalStateException.class) public void testConstructorOnInterfaceAssertion() throws Exception { new ByteBuddy() .makeInterface() .defineConstructor(Visibility.PUBLIC) .intercept(SuperMethodCall.INSTANCE) .make(); } @Test(expected = IllegalStateException.class) public void testConstructorOnAnnotationAssertion() throws Exception { new ByteBuddy() .makeAnnotation() .defineConstructor(Visibility.PUBLIC) .intercept(SuperMethodCall.INSTANCE) .make(); } @Test(expected = IllegalStateException.class) public void testAbstractConstructorAssertion() throws Exception { new ByteBuddy() .subclass(Object.class, ConstructorStrategy.Default.NO_CONSTRUCTORS) .defineConstructor(Visibility.PUBLIC) .withoutCode() .make(); } @Test(expected = IllegalStateException.class) public void testStaticAbstractMethodAssertion() throws Exception { new ByteBuddy() .subclass(Object.class) .defineMethod(FOO, void.class, Ownership.STATIC) .withoutCode() .make(); } @Test(expected = IllegalStateException.class) public void testPrivateAbstractMethodAssertion() throws Exception { new ByteBuddy() .subclass(Object.class) .defineMethod(FOO, void.class, Visibility.PRIVATE) .withoutCode() .make(); } @Test(expected = IllegalStateException.class) public void testAbstractMethodOnNonAbstractClassAssertion() throws Exception { new ByteBuddy() .subclass(Object.class) .defineMethod(FOO, String.class) .withoutCode() .make(); } @Test(expected = IllegalStateException.class) public void testNonPublicFieldOnInterfaceAssertion() throws Exception { new ByteBuddy() .makeInterface() .defineField(FOO, String.class, Ownership.STATIC, FieldManifestation.FINAL) .make(); } @Test(expected = IllegalStateException.class) public void testNonPublicFieldOnAnnotationAssertion() throws Exception { new ByteBuddy() .makeAnnotation() .defineField(FOO, String.class, Ownership.STATIC, FieldManifestation.FINAL) .make(); } @Test(expected = IllegalStateException.class) public void testNonStaticFieldOnInterfaceAssertion() throws Exception { new ByteBuddy() .makeInterface() .defineField(FOO, String.class, Visibility.PUBLIC, FieldManifestation.FINAL) .make(); } @Test(expected = IllegalStateException.class) public void testNonStaticFieldOnAnnotationAssertion() throws Exception { new ByteBuddy() .makeAnnotation() .defineField(FOO, String.class, Visibility.PUBLIC, FieldManifestation.FINAL) .make(); } @Test(expected = IllegalStateException.class) public void testNonFinalFieldOnInterfaceAssertion() throws Exception { new ByteBuddy() .makeInterface() .defineField(FOO, String.class, Visibility.PUBLIC, Ownership.STATIC) .make(); } @Test(expected = IllegalStateException.class) public void testNonFinalFieldOnAnnotationAssertion() throws Exception { new ByteBuddy() .makeAnnotation() .defineField(FOO, String.class, Visibility.PUBLIC, Ownership.STATIC) .make(); } @Test(expected = IllegalStateException.class) public void testStaticFieldWithIncompatibleConstantValue() throws Exception { new ByteBuddy() .subclass(Object.class) .defineField(FOO, String.class, Ownership.STATIC) .value(0) .make(); } @Test(expected = IllegalArgumentException.class) public void testStaticFieldWithNullConstantValue() throws Exception { new ByteBuddy() .subclass(Object.class) .defineField(FOO, String.class, Ownership.STATIC) .value(null); } @Test(expected = IllegalStateException.class) public void testStaticNumericFieldWithIncompatibleConstantValue() throws Exception { new ByteBuddy() .subclass(Object.class) .defineField(FOO, boolean.class, Ownership.STATIC) .value(Integer.MAX_VALUE) .make(); } @Test(expected = IllegalStateException.class) public void testStaticFieldWithNonDefinableConstantValue() throws Exception { new ByteBuddy() .subclass(Object.class) .defineField(FOO, Object.class, Ownership.STATIC) .value(FOO) .make(); } @Test(expected = IllegalStateException.class) public void testNonPublicMethodOnInterfaceAssertion() throws Exception { new ByteBuddy(ClassFileVersion.JAVA_V6) .makeInterface() .defineMethod(FOO, void.class) .withoutCode() .make(); } @Test public void testNonPublicMethodOnInterfaceAssertionJava8() throws Exception { new ByteBuddy(ClassFileVersion.JAVA_V8) .makeInterface() .defineMethod(FOO, void.class) .withoutCode() .make(); } @Test(expected = IllegalStateException.class) public void testNonPublicMethodOnAnnotationAssertion() throws Exception { new ByteBuddy() .makeAnnotation() .defineMethod(FOO, void.class) .withoutCode() .make(); } @Test(expected = IllegalStateException.class) public void testStaticMethodOnInterfaceAssertion() throws Exception { new ByteBuddy(ClassFileVersion.JAVA_V6) .makeInterface() .defineMethod(FOO, String.class, Visibility.PUBLIC, Ownership.STATIC) .withoutCode() .make(); } @Test @JavaVersionRule.Enforce(8) public void testStaticMethodOnAnnotationAssertionJava8() throws Exception { new ByteBuddy() .makeInterface() .defineMethod(FOO, String.class, Visibility.PUBLIC, Ownership.STATIC) .intercept(StubMethod.INSTANCE) .make(); } @Test(expected = IllegalStateException.class) public void testStaticMethodOnAnnotationAssertion() throws Exception { new ByteBuddy(ClassFileVersion.JAVA_V6) .makeAnnotation() .defineMethod(FOO, String.class, Visibility.PUBLIC, Ownership.STATIC) .intercept(StubMethod.INSTANCE) .make(); } @Test @JavaVersionRule.Enforce(8) public void testStaticMethodOnInterfaceAssertionJava8() throws Exception { new ByteBuddy() .makeAnnotation() .defineMethod(FOO, String.class, Visibility.PUBLIC, Ownership.STATIC) .intercept(StubMethod.INSTANCE) .make(); } @Test(expected = IllegalStateException.class) public void testAnnotationDefaultValueOnClassAssertion() throws Exception { new ByteBuddy() .subclass(Object.class) .merge(TypeManifestation.ABSTRACT) .defineMethod(FOO, String.class) .defaultValue(BAR, String.class) .make(); } @Test(expected = IllegalStateException.class) public void testAnnotationDefaultValueOnInterfaceClassAssertion() throws Exception { new ByteBuddy() .makeInterface() .defineMethod(FOO, String.class) .defaultValue(BAR, String.class) .make(); } @Test(expected = IllegalStateException.class) public void testAnnotationPropertyWithVoidReturnAssertion() throws Exception { new ByteBuddy() .makeAnnotation() .defineMethod(FOO, void.class, Visibility.PUBLIC) .withoutCode() .make(); } @Test(expected = IllegalStateException.class) public void testAnnotationPropertyWithParametersAssertion() throws Exception { new ByteBuddy() .makeAnnotation() .defineMethod(FOO, String.class, Visibility.PUBLIC).withParameters(Void.class) .withoutCode() .make(); } @Test(expected = IllegalStateException.class) public void testPackageDescriptionWithModifiers() throws Exception { new ByteBuddy() .makePackage(FOO) .modifiers(Visibility.PRIVATE) .make(); } @Test(expected = IllegalStateException.class) public void testPackageDescriptionWithInterfaces() throws Exception { new ByteBuddy() .makePackage(FOO) .implement(Serializable.class) .make(); } @Test(expected = IllegalStateException.class) public void testPackageDescriptionWithField() throws Exception { new ByteBuddy() .makePackage(FOO) .defineField(FOO, Void.class) .make(); } @Test(expected = IllegalStateException.class) public void testPackageDescriptionWithMethod() throws Exception { new ByteBuddy() .makePackage(FOO) .defineMethod(FOO, void.class) .withoutCode() .make(); } @Test(expected = IllegalStateException.class) public void testAnnotationPreJava5TypeAssertion() throws Exception { new ByteBuddy(ClassFileVersion.JAVA_V4) .makeAnnotation() .make(); } @Test(expected = IllegalStateException.class) public void testAnnotationOnTypePreJava5TypeAssertion() throws Exception { new ByteBuddy(ClassFileVersion.JAVA_V4) .subclass(Object.class) .annotateType(AnnotationDescription.Builder.ofType(Foo.class).build()) .make(); } @Test(expected = IllegalStateException.class) public void testAnnotationOnFieldPreJava5TypeAssertion() throws Exception { new ByteBuddy(ClassFileVersion.JAVA_V4) .subclass(Object.class) .defineField(FOO, Void.class) .annotateField(AnnotationDescription.Builder.ofType(Foo.class).build()) .make(); } @Test(expected = IllegalStateException.class) public void testAnnotationOnMethodPreJava5TypeAssertion() throws Exception { new ByteBuddy(ClassFileVersion.JAVA_V4) .subclass(Object.class) .defineMethod(FOO, void.class) .intercept(StubMethod.INSTANCE) .annotateMethod(AnnotationDescription.Builder.ofType(Foo.class).build()) .make(); } @Test public void testTypeInitializerOnInterface() throws Exception { assertThat(new ByteBuddy() .makeInterface() .invokable(isTypeInitializer()) .intercept(StubMethod.INSTANCE) .make() .load(ClassLoadingStrategy.BOOTSTRAP_LOADER, ClassLoadingStrategy.Default.WRAPPER) .getLoaded(), notNullValue(Class.class)); } @Test public void testTypeInitializerOnAnnotation() throws Exception { assertThat(new ByteBuddy() .makeAnnotation() .invokable(isTypeInitializer()) .intercept(StubMethod.INSTANCE) .make() .load(ClassLoadingStrategy.BOOTSTRAP_LOADER, ClassLoadingStrategy.Default.WRAPPER) .getLoaded(), notNullValue(Class.class)); } @Test @JavaVersionRule.Enforce(8) public void testTypeInitializerOnRebasedModernInterface() throws Exception { assertThat(new ByteBuddy() .rebase(Class.forName(JAVA_8_INTERFACE)) .invokable(isTypeInitializer()) .intercept(StubMethod.INSTANCE) .make(), notNullValue(DynamicType.class)); } @Test public void testTypeInitializerOnRebasedLegacyInterface() throws Exception { assertThat(new ByteBuddy() .rebase(Class.forName(LEGACY_INTERFACE)) .invokable(isTypeInitializer()) .intercept(StubMethod.INSTANCE) .make(), notNullValue(DynamicType.class)); } @Test public void testTypeInitializerOnRebasedInterfaceWithFrameComputation() throws Exception { assertThat(new ByteBuddy() .makeInterface() .visit(new AsmVisitorWrapper.ForDeclaredMethods().writerFlags(ClassWriter.COMPUTE_FRAMES)) .invokable(isTypeInitializer()) .intercept(StubMethod.INSTANCE) .make(), notNullValue(DynamicType.class)); } @Test public void testTypeInitializerOnRebasedInterfaceWithFrameExpansion() throws Exception { assertThat(new ByteBuddy() .makeInterface() .visit(new AsmVisitorWrapper.ForDeclaredMethods().readerFlags(ClassReader.EXPAND_FRAMES)) .invokable(isTypeInitializer()) .intercept(StubMethod.INSTANCE) .make(), notNullValue(DynamicType.class)); } @Test public void testTypeInitializerOnRebasedInterfaceWithInitializer() throws Exception { assertThat(new ByteBuddy() .makeInterface() .initializer(new ByteCodeAppender.Simple()) .invokable(isTypeInitializer()) .intercept(StubMethod.INSTANCE) .make(), notNullValue(DynamicType.class)); } @Test public void testTypeInLegacyConstantPoolRemapped() throws Exception { Class<?> dynamicType = new ByteBuddy(ClassFileVersion.JAVA_V4) .with(TypeValidation.DISABLED) .subclass(Object.class) .defineMethod(FOO, Object.class, Visibility.PUBLIC) .intercept(FixedValue.value(Object.class)) .make() .load(ClassLoadingStrategy.BOOTSTRAP_LOADER, ClassLoadingStrategy.Default.WRAPPER) .getLoaded(); assertThat(dynamicType.getDeclaredMethod(FOO).invoke(dynamicType.getDeclaredConstructor().newInstance()), is((Object) Object.class)); } @Test public void testArrayTypeInLegacyConstantPoolRemapped() throws Exception { Class<?> dynamicType = new ByteBuddy(ClassFileVersion.JAVA_V4) .with(TypeValidation.DISABLED) .subclass(Object.class) .defineMethod(FOO, Object.class, Visibility.PUBLIC) .intercept(FixedValue.value(Object[].class)) .make() .load(ClassLoadingStrategy.BOOTSTRAP_LOADER, ClassLoadingStrategy.Default.WRAPPER) .getLoaded(); assertThat(dynamicType.getDeclaredMethod(FOO).invoke(dynamicType.getDeclaredConstructor().newInstance()), is((Object) Object[].class)); } @Test public void testPrimitiveTypeInLegacyConstantPoolRemapped() throws Exception { Class<?> dynamicType = new ByteBuddy(ClassFileVersion.JAVA_V4) .with(TypeValidation.DISABLED) .subclass(Object.class) .defineMethod(FOO, Object.class, Visibility.PUBLIC) .intercept(FixedValue.value(int.class)) .make() .load(ClassLoadingStrategy.BOOTSTRAP_LOADER, ClassLoadingStrategy.Default.WRAPPER) .getLoaded(); assertThat(dynamicType.getDeclaredMethod(FOO).invoke(dynamicType.getDeclaredConstructor().newInstance()), is((Object) int.class)); } @Test public void testLegacyTypeRedefinitionIsDiscovered() throws Exception { Class<?> dynamicType = new ByteBuddy() .with(TypeValidation.DISABLED) .redefine(Class.forName("net.bytebuddy.test.precompiled.TypeConstantSample")) .method(named(BAR)) .intercept(FixedValue.value(int.class)) .make() .load(ClassLoadingStrategy.BOOTSTRAP_LOADER, ClassLoadingStrategy.Default.WRAPPER) .getLoaded(); assertThat(dynamicType.getDeclaredMethod(BAR).invoke(null), is((Object) int.class)); } @Test(expected = IllegalStateException.class) public void testMethodTypeInLegacyConstantPool() throws Exception { new ByteBuddy(ClassFileVersion.JAVA_V4) .subclass(Object.class) .defineMethod(FOO, Object.class) .intercept(FixedValue.value(JavaConstant.MethodType.of(Object.class, Object.class))) .make(); } @Test(expected = IllegalStateException.class) public void testMethodHandleInLegacyConstantPool() throws Exception { new ByteBuddy(ClassFileVersion.JAVA_V4) .subclass(Object.class) .defineMethod(FOO, Object.class) .intercept(FixedValue.value(JavaConstant.MethodHandle.of(new MethodDescription.ForLoadedMethod(Object.class.getDeclaredMethod("toString"))))) .make(); } @Test(expected = IllegalStateException.class) @JavaVersionRule.Enforce(8) public void testDefaultMethodCallFromLegacyType() throws Exception { new ByteBuddy(ClassFileVersion.JAVA_V7) .subclass(Class.forName("net.bytebuddy.test.precompiled.SingleDefaultMethodInterface")) .method(isDefaultMethod()) .intercept(SuperMethodCall.INSTANCE) .make(); } @Test public void testBridgeNonLegacyType() throws Exception { Class<?> base = new ByteBuddy(ClassFileVersion.JAVA_V5) .subclass(Object.class) .modifiers(Visibility.PACKAGE_PRIVATE) .defineMethod("foo", void.class, Visibility.PUBLIC).intercept(StubMethod.INSTANCE) .defineMethod("bar", Object.class).intercept(StubMethod.INSTANCE) .defineMethod("bar", String.class).intercept(StubMethod.INSTANCE) .make() .load(null, ClassLoadingStrategy.Default.WRAPPER) .getLoaded(); Class<?> subclass = new ByteBuddy(ClassFileVersion.JAVA_V5) .subclass(base) .modifiers(Visibility.PUBLIC) .method(named("bar")).intercept(StubMethod.INSTANCE) .make() .load(base.getClassLoader(), ClassLoadingStrategy.Default.INJECTION) .getLoaded(); assertThat(subclass.getDeclaredMethods().length, is(3)); assertThat(subclass.getDeclaredMethod("foo").isBridge(), is(true)); assertThat(subclass.getDeclaredMethod("bar").isBridge(), is(false)); assertThat(subclass.getDeclaredMethod("bar").getReturnType(), is((Object) String.class)); } @Test public void testNoBridgeLegacyType() throws Exception { Class<?> base = new ByteBuddy(ClassFileVersion.JAVA_V4) .subclass(Object.class, ConstructorStrategy.Default.NO_CONSTRUCTORS) .modifiers(Visibility.PACKAGE_PRIVATE) .defineConstructor(Visibility.PUBLIC).intercept(SuperMethodCall.INSTANCE) .defineMethod(FOO, void.class, Visibility.PUBLIC).intercept(StubMethod.INSTANCE) .defineMethod(BAR, Object.class).intercept(StubMethod.INSTANCE) .defineMethod(BAR, String.class).intercept(StubMethod.INSTANCE) .make() .load(null, ClassLoadingStrategy.Default.WRAPPER) .getLoaded(); Class<?> subclass = new ByteBuddy(ClassFileVersion.JAVA_V4) .subclass(base) .modifiers(Visibility.PUBLIC) .method(named(BAR)).intercept(StubMethod.INSTANCE) .make() .load(base.getClassLoader(), ClassLoadingStrategy.Default.INJECTION) .getLoaded(); assertThat(subclass.getDeclaredMethods().length, is(1)); assertThat(subclass.getDeclaredMethod(BAR).isBridge(), is(false)); assertThat(subclass.getDeclaredMethod(BAR).getReturnType(), is((Object) String.class)); } @Test public void testIncompatibleBridgeMethodIsFiltered() throws Exception { Class<?> base = new ByteBuddy() .subclass(Object.class) .defineMethod(FOO, Object.class, Visibility.PUBLIC).intercept(StubMethod.INSTANCE) .defineMethod(FOO, void.class, Visibility.PUBLIC, MethodManifestation.BRIDGE).intercept(StubMethod.INSTANCE) .make() .load(null, ClassLoadingStrategy.Default.WRAPPER) .getLoaded(); Class<?> subclass = new ByteBuddy() .subclass(base) .method(named(FOO)).intercept(StubMethod.INSTANCE) .make() .load(base.getClassLoader(), ClassLoadingStrategy.Default.INJECTION) .getLoaded(); assertThat(subclass.getDeclaredMethods().length, is(1)); assertThat(subclass.getDeclaredMethod(FOO).isBridge(), is(false)); assertThat(subclass.getDeclaredMethod(FOO).getReturnType(), is((Object) Object.class)); } @Test public void testClassDump() throws Exception { TypeDescription instrumentedType = mock(TypeDescription.class); byte[] binaryRepresentation = new byte[]{1, 2, 3}; File file = File.createTempFile(FOO, BAR); assertThat(file.delete(), is(true)); file = new File(file.getParentFile(), "temp" + System.currentTimeMillis()); assertThat(file.mkdir(), is(true)); when(instrumentedType.getName()).thenReturn(FOO + "." + BAR); new TypeWriter.Default.ClassDumpAction(file.getAbsolutePath(), instrumentedType, binaryRepresentation).run(); File[] child = file.listFiles(); assertThat(child, notNullValue(File[].class)); assertThat(child.length, is(1)); assertThat(child[0].length(), is(3L)); assertThat(child[0].delete(), is(true)); assertThat(file.delete(), is(true)); } @Test public void testObjectProperties() throws Exception { ObjectPropertyAssertion.of(TypeWriter.Default.UnresolvedType.class).apply(); ObjectPropertyAssertion.of(TypeWriter.Default.ClassDumpAction.class).apply(); ObjectPropertyAssertion.of(TypeWriter.Default.ForCreation.class).apply(); ObjectPropertyAssertion.of(TypeWriter.Default.ForInlining.class).apply(); ObjectPropertyAssertion.of(TypeWriter.Default.ForInlining.InitializationHandler.Creating.class).apply(); ObjectPropertyAssertion.of(TypeWriter.Default.ValidatingClassVisitor.Constraint.ForAnnotation.class).apply(); ObjectPropertyAssertion.of(TypeWriter.Default.ValidatingClassVisitor.Constraint.ForInterface.class).apply(); ObjectPropertyAssertion.of(TypeWriter.Default.ValidatingClassVisitor.Constraint.ForClass.class).apply(); ObjectPropertyAssertion.of(TypeWriter.Default.ValidatingClassVisitor.Constraint.ForPackageType.class).apply(); ObjectPropertyAssertion.of(TypeWriter.Default.ValidatingClassVisitor.Constraint.ForClassFileVersion.class).apply(); ObjectPropertyAssertion.of(TypeWriter.Default.ValidatingClassVisitor.Constraint.Compound.class).create(new ObjectPropertyAssertion.Creator<List<?>>() { @Override public List<?> create() { return Collections.singletonList(mock(TypeWriter.Default.ValidatingClassVisitor.Constraint.class)); } }).apply(); } @Retention(RetentionPolicy.RUNTIME) public @interface Foo { /* empty */ } }