From 2ed62daa6295e0a35f9025bf351a19c5b82f4eec Mon Sep 17 00:00:00 2001 From: Rafael Winterhalter Date: Mon, 25 Jan 2016 17:20:26 +0100 Subject: [PATCH] Small renaming in agent builder classes. --- .../bytebuddy/agent/builder/AgentBuilder.java | 3301 ++++++++--------- .../AgentBuilderDefaultApplicationTest.java | 14 +- .../builder/AgentBuilderDefaultTest.java | 67 +- 3 files changed, 1671 insertions(+), 1711 deletions(-) diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/agent/builder/AgentBuilder.java b/byte-buddy-dep/src/main/java/net/bytebuddy/agent/builder/AgentBuilder.java index 6ab90d096c6..f7201efcf6b 100644 --- a/byte-buddy-dep/src/main/java/net/bytebuddy/agent/builder/AgentBuilder.java +++ b/byte-buddy-dep/src/main/java/net/bytebuddy/agent/builder/AgentBuilder.java @@ -177,6 +177,30 @@ public interface AgentBuilder { */ AgentBuilder with(RedefinitionStrategy redefinitionStrategy); + /** + *

+ * Enables or disables management of the JVM's {@code LambdaMetafactory} which is responsible for creating classes that + * implement lambda expressions. Without this feature enabled, classes that are represented by lambda expressions are + * not instrumented by the JVM such that Java agents have no effect on them when a lambda expression's class is loaded + * for the first time. + *

+ *

+ * When activating this feature, Byte Buddy instruments the {@code LambdaMetafactory} and takes over the responsibility + * of creating classes that represent lambda expressions. In doing so, Byte Buddy has the opportunity to apply the built + * class file transformer. If the current VM does not support lambda expressions, activating this feature has no effect. + *

+ *

+ * Important: If this feature is active, it is important to release the built class file transformer when + * deactivating it. Normally, it is sufficient to call {@link Instrumentation#removeTransformer(ClassFileTransformer)}. + * When this feature is enabled, it is however also required to invoke {@link Default#releaseLambdaTransformer(ClassFileTransformer, Instrumentation)}. + * Otherwise, the executing VMs class loader retains a reference to the class file transformer what can cause a memory leak. + *

+ * + * @param lambdaInstrumentationStrategy {@code true} if this feature should be enabled. + * @return A new instance of this agent builder where this feature is explicitly enabled or disabled. + */ + AgentBuilder with(LambdaInstrumentationStrategy lambdaInstrumentationStrategy); + /** * Enables class injection of auxiliary classes into the bootstrap class loader. * @@ -211,47 +235,6 @@ public interface AgentBuilder { */ AgentBuilder disableBootstrapInjection(); - /** - * Enables the use of the given native method prefix for instrumented methods. Note that this prefix is also - * applied when preserving non-native methods. The use of this prefix is also registered when installing the - * final agent with an {@link java.lang.instrument.Instrumentation}. - * - * @param prefix The prefix to be used. - * @return A new instance of this agent builder which uses the given native method prefix. - */ - AgentBuilder withNativeMethodPrefix(String prefix); - - /** - * Disables the use of a native method prefix for instrumented methods. - * - * @return A new instance of this agent builder which does not use a native method prefix. - */ - AgentBuilder withoutNativeMethodPrefix(); - - /** - *

- * Enables or disables management of the JVM's {@code LambdaMetafactory} which is responsible for creating classes that - * implement lambda expressions. Without this feature enabled, classes that are represented by lambda expressions are - * not instrumented by the JVM such that Java agents have no effect on them when a lambda expression's class is loaded - * for the first time. - *

- *

- * When activating this feature, Byte Buddy instruments the {@code LambdaMetafactory} and takes over the responsibility - * of creating classes that represent lambda expressions. In doing so, Byte Buddy has the opportunity to apply the built - * class file transformer. If the current VM does not support lambda expressions, activating this feature has no effect. - *

- *

- * Important: If this feature is active, it is important to release the built class file transformer when - * deactivating it. Normally, it is sufficient to call {@link Instrumentation#removeTransformer(ClassFileTransformer)}. - * When this feature is enabled, it is however also required to invoke {@link Default#releaseLambdaTransformer(ClassFileTransformer, Instrumentation)}. - * Otherwise, the executing VMs class loader retains a reference to the class file transformer what can cause a memory leak. - *

- * - * @param enable {@code true} if this feature should be enabled. - * @return A new instance of this agent builder where this feature is explicitly enabled or disabled. - */ - AgentBuilder enableLambdaInstrumentation(boolean enable); - /** * Creates a {@link java.lang.instrument.ClassFileTransformer} that implements the configuration of this * agent builder. @@ -1933,1434 +1916,1806 @@ public String toString() { } /** - * The default implementation of an {@link net.bytebuddy.agent.builder.AgentBuilder}. + * Implements the instrumentation of the {@code LambdaMetafactory} if this feature is enabled. */ - class Default implements AgentBuilder { + enum LambdaInstrumentationStrategy implements Callable> { /** - * The name of the Byte Buddy {@code net.bytebuddy.agent.Installer} class. + * A strategy that enables instrumentation of the {@code LambdaMetafactory} if such a factory exists on the current VM. */ - private static final String INSTALLER_TYPE = "net.bytebuddy.agent.Installer"; + ENABLED { + @Override + protected void apply(ByteBuddy byteBuddy, Instrumentation instrumentation, ClassFileTransformer classFileTransformer) { + if (LambdaFactory.register(classFileTransformer, new LambdaInstanceFactory(byteBuddy), this)) { + Class lambdaMetaFactory; + try { + lambdaMetaFactory = Class.forName("java.lang.invoke.LambdaMetafactory"); + } catch (ClassNotFoundException ignored) { + return; + } + byteBuddy.with(Implementation.Context.Disabled.Factory.INSTANCE) + .redefine(lambdaMetaFactory) + .visit(new AsmVisitorWrapper.ForDeclaredMethods() + .method(named("metafactory"), MetaFactoryRedirection.INSTANCE) + .method(named("altMetafactory"), AlternativeMetaFactoryRedirection.INSTANCE)) + .make() + .load(lambdaMetaFactory.getClassLoader(), ClassReloadingStrategy.of(instrumentation)); + } + } - /** - * The name of the {@code net.bytebuddy.agent.Installer} field containing an installed {@link Instrumentation}. - */ - private static final String INSTRUMENTATION_FIELD = "instrumentation"; + @Override + public Class call() throws Exception { + TypeDescription lambdaFactory = new TypeDescription.ForLoadedType(LambdaFactory.class); + return ClassInjector.UsingReflection.ofSystemClassLoader() + .inject(Collections.singletonMap(lambdaFactory, ClassFileLocator.ForClassLoader.read(LambdaFactory.class).resolve())) + .get(lambdaFactory); + } + }, /** - * Indicator for access to a static member via reflection to make the code more readable. + * A strategy that does not instrument the {@code LambdaMetafactory}. */ - private static final Object STATIC_FIELD = null; + DISABLED { + @Override + protected void apply(ByteBuddy byteBuddy, Instrumentation instrumentation, ClassFileTransformer classFileTransformer) { + /* do nothing */ + } - /** - * The value that is to be returned from a {@link java.lang.instrument.ClassFileTransformer} to indicate - * that no class file transformation is to be applied. - */ - private static final byte[] NO_TRANSFORMATION = null; + @Override + public Class call() throws Exception { + throw new IllegalStateException("Cannot inject LambdaFactory from disabled instrumentation strategy"); + } + }; /** - * The {@link net.bytebuddy.ByteBuddy} instance to be used. + * Indicates that an original implementation can be ignored when redefining a method. */ - private final ByteBuddy byteBuddy; + protected static final MethodVisitor IGNORE_ORIGINAL = null; /** - * The binary locator to use. + * Applies a transformation to lambda instances if applicable. + * + * @param byteBuddy The Byte Buddy instance to use. + * @param instrumentation The instrumentation instance for applying a redefinition. + * @param classFileTransformer The class file transformer to apply. */ - private final BinaryLocator binaryLocator; + protected abstract void apply(ByteBuddy byteBuddy, Instrumentation instrumentation, ClassFileTransformer classFileTransformer); - /** - * The definition handler to use. - */ - private final TypeStrategy typeStrategy; + @Override + public String toString() { + return "AgentBuilder.LambdaInstrumentationStrategy." + name(); + } /** - * The listener to notify on transformations. + * A factory that creates instances that represent lambda expressions. */ - private final Listener listener; + protected static class LambdaInstanceFactory { - /** - * The native method strategy to use. - */ - private final NativeMethodStrategy nativeMethodStrategy; + /** + * The name of a factory for a lambda expression. + */ + private static final String LAMBDA_FACTORY = "get$Lambda"; - /** - * The access control context to use for loading classes. - */ - private final AccessControlContext accessControlContext; + /** + * A prefix for a field that represents a property of a lambda expression. + */ + private static final String FIELD_PREFIX = "arg$"; - /** - * The initialization strategy to use for creating classes. - */ - private final InitializationStrategy initializationStrategy; + /** + * The infix to use for naming classes that represent lambda expression. The additional prefix + * is necessary because the subsequent counter is not sufficient to keep names unique compared + * to the original factory. + */ + private static final String LAMBDA_TYPE_INFIX = "$$Lambda$ByteBuddy$"; - /** - * The redefinition strategy to apply. - */ - private final RedefinitionStrategy redefinitionStrategy; + /** + * A type-safe constant to express that a class is not already loaded when applying a class file transformer. + */ + private static final Class NOT_PREVIOUSLY_DEFINED = null; - /** - * The injection strategy for injecting classes into the bootstrap class loader. - */ - private final BootstrapInjectionStrategy bootstrapInjectionStrategy; + /** + * A counter for naming lambda expressions randomly. + */ + private static final AtomicInteger LAMBDA_NAME_COUNTER = new AtomicInteger(); - /** - * A strategy to determine of the {@code LambdaMetfactory} should be instrumented to allow for the instrumentation - * of classes that represent lambda expressions. - */ - private final LambdaInstrumentationStrategy lambdaInstrumentationStrategy; + /** + * The Byte Buddy instance to use for creating lambda objects. + */ + private final ByteBuddy byteBuddy; - /** - * The transformation object for handling type transformations. - */ - private final Transformation transformation; + /** + * Creates a new lambda instance factory. + * + * @param byteBuddy The Byte Buddy instance to use for creating lambda objects. + */ + protected LambdaInstanceFactory(ByteBuddy byteBuddy) { + this.byteBuddy = byteBuddy; + } - /** - * Creates a new default agent builder that uses a default {@link net.bytebuddy.ByteBuddy} instance for - * creating classes. - */ - public Default() { - this(new ByteBuddy()); - } + /** + * Applies this lambda meta factory. + * + * @param targetTypeLookup A lookup context representing the creating class of this lambda expression. + * @param lambdaMethodName The name of the lambda expression's represented method. + * @param factoryMethodType The type of the lambda expression's represented method. + * @param lambdaMethodType The type of the lambda expression's factory method. + * @param targetMethodHandle A handle representing the target of the lambda expression's method. + * @param specializedLambdaMethodType A specialization of the type of the lambda expression's represented method. + * @param serializable {@code true} if the lambda expression should be serializable. + * @param markerInterfaces A list of interfaces for the lambda expression to represent. + * @param additionalBridges A list of additional bridge methods to be implemented by the lambda expression. + * @param classFileTransformers A collection of class file transformers to apply when creating the class. + * @return A binary representation of the transformed class file. + */ + public byte[] make(Object targetTypeLookup, + String lambdaMethodName, + Object factoryMethodType, + Object lambdaMethodType, + Object targetMethodHandle, + Object specializedLambdaMethodType, + boolean serializable, + List> markerInterfaces, + List additionalBridges, + Collection classFileTransformers) { + JavaInstance.MethodType factoryMethod = JavaInstance.MethodType.of(factoryMethodType); + JavaInstance.MethodType lambdaMethod = JavaInstance.MethodType.of(lambdaMethodType); + JavaInstance.MethodHandle targetMethod = JavaInstance.MethodHandle.of(targetMethodHandle, targetTypeLookup); + JavaInstance.MethodType specializedLambdaMethod = JavaInstance.MethodType.of(specializedLambdaMethodType); + Class targetType = JavaInstance.MethodHandle.lookupType(targetTypeLookup); + String lambdaClassName = targetType.getName() + LAMBDA_TYPE_INFIX + LAMBDA_NAME_COUNTER.incrementAndGet(); + DynamicType.Builder builder = byteBuddy + .subclass(factoryMethod.getReturnType(), ConstructorStrategy.Default.NO_CONSTRUCTORS) + .modifiers(SyntheticState.SYNTHETIC, TypeManifestation.FINAL, Visibility.PUBLIC) + .implement(markerInterfaces) + .name(lambdaClassName) + .defineConstructor(Visibility.PUBLIC) + .withParameters(factoryMethod.getParameterTypes()) + .intercept(ConstructorImplementation.INSTANCE) + .method(named(lambdaMethodName) + .and(takesArguments(lambdaMethod.getParameterTypes())) + .and(returns(lambdaMethod.getReturnType()))) + .intercept(new LambdaMethodImplementation(targetMethod, specializedLambdaMethod)); + int index = 0; + for (TypeDescription capturedType : factoryMethod.getParameterTypes()) { + builder = builder.defineField(FIELD_PREFIX + ++index, capturedType, Visibility.PRIVATE, FieldManifestation.FINAL); + } + if (!factoryMethod.getParameterTypes().isEmpty()) { + builder = builder.defineMethod(LAMBDA_FACTORY, factoryMethod.getReturnType(), Visibility.PRIVATE, Ownership.STATIC) + .withParameters(factoryMethod.getParameterTypes()) + .intercept(FactoryImplementation.INSTANCE); + } + if (serializable) { + if (!markerInterfaces.contains(Serializable.class)) { + builder = builder.implement(Serializable.class); + } + builder = builder.defineMethod("writeReplace", Object.class, Visibility.PRIVATE) + .intercept(new SerializationImplementation(new TypeDescription.ForLoadedType(targetType), + factoryMethod.getReturnType(), + lambdaMethodName, + lambdaMethod, + targetMethod, + JavaInstance.MethodType.of(specializedLambdaMethodType))); + } else if (factoryMethod.getReturnType().isAssignableTo(Serializable.class)) { + builder = builder.defineMethod("readObject", void.class, Visibility.PRIVATE) + .withParameters(ObjectInputStream.class) + .throwing(NotSerializableException.class) + .intercept(ExceptionMethod.throwing(NotSerializableException.class, "Non-serializable lambda")) + .defineMethod("writeObject", void.class, Visibility.PRIVATE) + .withParameters(ObjectOutputStream.class) + .throwing(NotSerializableException.class) + .intercept(ExceptionMethod.throwing(NotSerializableException.class, "Non-serializable lambda")); + } + for (Object additionalBridgeType : additionalBridges) { + JavaInstance.MethodType additionalBridge = JavaInstance.MethodType.of(additionalBridgeType); + builder = builder.defineMethod(lambdaMethodName, additionalBridge.getReturnType(), MethodManifestation.BRIDGE, Visibility.PUBLIC) + .withParameters(additionalBridge.getParameterTypes()) + .intercept(new BridgeMethodImplementation(lambdaMethodName, lambdaMethod)); + } + byte[] classFile = builder.make().getBytes(); + for (ClassFileTransformer classFileTransformer : classFileTransformers) { + try { + byte[] transformedClassFile = classFileTransformer.transform(targetType.getClassLoader(), + lambdaClassName.replace('.', '/'), + NOT_PREVIOUSLY_DEFINED, + targetType.getProtectionDomain(), + classFile); + classFile = transformedClassFile == null + ? classFile + : transformedClassFile; + } catch (Exception ignored) { + /* do nothing */ + } + } + return classFile; + } - /** - * Creates a new agent builder with default settings. - * - * @param byteBuddy The Byte Buddy instance to be used. - */ - public Default(ByteBuddy byteBuddy) { - this(byteBuddy, - BinaryLocator.Default.FAST, - TypeStrategy.Default.REBASE, - Listener.NoOp.INSTANCE, - NativeMethodStrategy.Disabled.INSTANCE, - AccessController.getContext(), - InitializationStrategy.SelfInjection.SPLIT, - RedefinitionStrategy.DISABLED, - BootstrapInjectionStrategy.Disabled.INSTANCE, - LambdaInstrumentationStrategy.DISABLED, - Transformation.Ignored.INSTANCE); - } + @Override + public boolean equals(Object other) { + return this == other || !(other == null || getClass() != other.getClass()) + && byteBuddy.equals(((LambdaInstanceFactory) other).byteBuddy); + } - /** - * Creates a new default agent builder. - * - * @param byteBuddy The Byte Buddy instance to be used. - * @param binaryLocator The binary locator to use. - * @param typeStrategy The definition handler to use. - * @param listener The listener to notify on transformations. - * @param nativeMethodStrategy The native method strategy to apply. - * @param accessControlContext The access control context to use for loading classes. - * @param initializationStrategy The initialization strategy to use for transformed types. - * @param redefinitionStrategy The redefinition strategy to apply. - * @param bootstrapInjectionStrategy The injection strategy for injecting classes into the bootstrap class loader. - * @param lambdaInstrumentationStrategy A strategy to determine of the {@code LambdaMetfactory} should be instrumented to allow for the - * instrumentation of classes that represent lambda expressions. - * @param transformation The transformation object for handling type transformations. - */ - protected Default(ByteBuddy byteBuddy, - BinaryLocator binaryLocator, - TypeStrategy typeStrategy, - Listener listener, - NativeMethodStrategy nativeMethodStrategy, - AccessControlContext accessControlContext, - InitializationStrategy initializationStrategy, - RedefinitionStrategy redefinitionStrategy, - BootstrapInjectionStrategy bootstrapInjectionStrategy, - LambdaInstrumentationStrategy lambdaInstrumentationStrategy, - Transformation transformation) { - this.byteBuddy = byteBuddy; - this.binaryLocator = binaryLocator; - this.typeStrategy = typeStrategy; - this.listener = listener; - this.nativeMethodStrategy = nativeMethodStrategy; - this.accessControlContext = accessControlContext; - this.initializationStrategy = initializationStrategy; - this.redefinitionStrategy = redefinitionStrategy; - this.bootstrapInjectionStrategy = bootstrapInjectionStrategy; - this.lambdaInstrumentationStrategy = lambdaInstrumentationStrategy; - this.transformation = transformation; - } + @Override + public int hashCode() { + return byteBuddy.hashCode(); + } - /** - * Releases the supplied class file transformer when it was built with {@link AgentBuilder#enableLambdaInstrumentation(boolean)} enabled. - * Subsequently, the class file transformer is no longer applied when a class that represents a lambda expression is created. - * - * @param classFileTransformer The class file transformer to release. - * @param instrumentation The instrumentation instance that is used to potentially rollback the instrumentation of the {@code LambdaMetafactory}. - */ - public static void releaseLambdaTransformer(ClassFileTransformer classFileTransformer, Instrumentation instrumentation) { - if (LambdaFactory.release(classFileTransformer)) { - try { - ClassReloadingStrategy.of(instrumentation).reset(Class.forName("java.lang.invoke.LambdaMetafactory")); - } catch (Exception exception) { - throw new IllegalStateException("Could not release lambda transformer", exception); - } + @Override + public String toString() { + return "AgentBuilder.LambdaInstrumentationStrategy.LambdaInstanceFactory{" + + "byteBuddy=" + byteBuddy + + '}'; } - } - @Override - public Identified type(RawMatcher matcher) { - return new Matched(matcher, Transformer.NoOp.INSTANCE); - } + /** + * Implements a lambda class's constructor. + */ + protected enum ConstructorImplementation implements Implementation { - @Override - public Identified type(ElementMatcher typeMatcher) { - return type(typeMatcher, any()); - } + /** + * The singleton instance. + */ + INSTANCE; - @Override - public Identified type(ElementMatcher typeMatcher, ElementMatcher classLoaderMatcher) { - return type(new RawMatcher.ForElementMatcherPair(typeMatcher, classLoaderMatcher)); - } + /** + * A reference to the {@link Object} class's default constructor. + */ + protected final MethodDescription.InDefinedShape objectConstructor; - @Override - public AgentBuilder with(ByteBuddy byteBuddy) { - return new Default(byteBuddy, - binaryLocator, - typeStrategy, - listener, - nativeMethodStrategy, - accessControlContext, - initializationStrategy, - redefinitionStrategy, - bootstrapInjectionStrategy, - lambdaInstrumentationStrategy, - transformation); - } + /** + * Creates a new constructor implementation. + */ + ConstructorImplementation() { + objectConstructor = TypeDescription.OBJECT.getDeclaredMethods().filter(isConstructor()).getOnly(); + } - @Override - public AgentBuilder with(Listener listener) { - return new Default(byteBuddy, - binaryLocator, - typeStrategy, - new Listener.Compound(this.listener, listener), - nativeMethodStrategy, - accessControlContext, - initializationStrategy, - redefinitionStrategy, - bootstrapInjectionStrategy, - lambdaInstrumentationStrategy, - transformation); - } + @Override + public ByteCodeAppender appender(Target implementationTarget) { + return new Appender(implementationTarget.getInstrumentedType().getDeclaredFields()); + } - @Override - public AgentBuilder with(TypeStrategy typeStrategy) { - return new Default(byteBuddy, - binaryLocator, - typeStrategy, - listener, - nativeMethodStrategy, - accessControlContext, - initializationStrategy, - redefinitionStrategy, - bootstrapInjectionStrategy, - lambdaInstrumentationStrategy, - transformation); - } + @Override + public InstrumentedType prepare(InstrumentedType instrumentedType) { + return instrumentedType; + } - @Override - public AgentBuilder with(BinaryLocator binaryLocator) { - return new Default(byteBuddy, - binaryLocator, - typeStrategy, - listener, - nativeMethodStrategy, - accessControlContext, - initializationStrategy, - redefinitionStrategy, - bootstrapInjectionStrategy, - lambdaInstrumentationStrategy, - transformation); - } + @Override + public String toString() { + return "AgentBuilder.LambdaInstrumentationStrategy.LambdaInstanceFactory.ConstructorImplementation." + name(); + } - @Override - public AgentBuilder enableNativeMethodPrefix(String prefix) { - return new Default(byteBuddy, - binaryLocator, - typeStrategy, - listener, - NativeMethodStrategy.ForPrefix.of(prefix), - accessControlContext, - initializationStrategy, - redefinitionStrategy, - bootstrapInjectionStrategy, - lambdaInstrumentationStrategy, - transformation); - } + /** + * An appender to implement the constructor. + */ + protected static class Appender implements ByteCodeAppender { - @Override - public AgentBuilder disableNativeMethodPrefix() { - return new Default(byteBuddy, - binaryLocator, - typeStrategy, - listener, - NativeMethodStrategy.Disabled.INSTANCE, - accessControlContext, - initializationStrategy, - redefinitionStrategy, - bootstrapInjectionStrategy, - lambdaInstrumentationStrategy, - transformation); - } + /** + * The fields that are declared by the instrumented type. + */ + private final List declaredFields; - @Override - public AgentBuilder with(AccessControlContext accessControlContext) { - return new Default(byteBuddy, - binaryLocator, - typeStrategy, - listener, - nativeMethodStrategy, - accessControlContext, - initializationStrategy, - redefinitionStrategy, - bootstrapInjectionStrategy, - lambdaInstrumentationStrategy, - transformation); - } + /** + * Creates a new appender. + * + * @param declaredFields The fields that are declared by the instrumented type. + */ + protected Appender(List declaredFields) { + this.declaredFields = declaredFields; + } - @Override - public AgentBuilder with(RedefinitionStrategy redefinitionStrategy) { - return new Default(byteBuddy, - binaryLocator, - typeStrategy, - listener, - nativeMethodStrategy, - accessControlContext, - initializationStrategy, - redefinitionStrategy, - bootstrapInjectionStrategy, - lambdaInstrumentationStrategy, - transformation); - } + @Override + public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) { + List fieldAssignments = new ArrayList(declaredFields.size() * 3); + for (ParameterDescription parameterDescription : instrumentedMethod.getParameters()) { + fieldAssignments.add(MethodVariableAccess.REFERENCE.loadOffset(0)); + fieldAssignments.add(MethodVariableAccess.of(parameterDescription.getType()).loadOffset(parameterDescription.getOffset())); + fieldAssignments.add(FieldAccess.forField(declaredFields.get(parameterDescription.getIndex())).putter()); + } + return new Size(new StackManipulation.Compound( + MethodVariableAccess.REFERENCE.loadOffset(0), + MethodInvocation.invoke(INSTANCE.objectConstructor), + new StackManipulation.Compound(fieldAssignments), + MethodReturn.VOID + ).apply(methodVisitor, implementationContext).getMaximalSize(), instrumentedMethod.getStackSize()); + } - @Override - public AgentBuilder with(InitializationStrategy initializationStrategy) { - return new Default(byteBuddy, - binaryLocator, - typeStrategy, - listener, - nativeMethodStrategy, - accessControlContext, - initializationStrategy, - redefinitionStrategy, - bootstrapInjectionStrategy, - lambdaInstrumentationStrategy, - transformation); - } + @Override + public boolean equals(Object other) { + return this == other || !(other == null || getClass() != other.getClass()) + && declaredFields.equals(((Appender) other).declaredFields); + } - @Override - public AgentBuilder enableBootstrapInjection(File folder, Instrumentation instrumentation) { - return new Default(byteBuddy, - binaryLocator, - typeStrategy, - listener, - nativeMethodStrategy, - accessControlContext, - initializationStrategy, - redefinitionStrategy, - new BootstrapInjectionStrategy.Enabled(folder, instrumentation), - lambdaInstrumentationStrategy, - transformation); - } + @Override + public int hashCode() { + return declaredFields.hashCode(); + } - @Override - public AgentBuilder disableBootstrapInjection() { - return new Default(byteBuddy, - binaryLocator, - typeStrategy, - listener, - nativeMethodStrategy, - accessControlContext, - initializationStrategy, - redefinitionStrategy, - BootstrapInjectionStrategy.Disabled.INSTANCE, - lambdaInstrumentationStrategy, - transformation); - } + @Override + public String toString() { + return "AgentBuilder.LambdaInstrumentationStrategy.LambdaInstanceFactory.ConstructorImplementation.Appender{" + + "declaredFields=" + declaredFields + + '}'; + } + } + } - @Override - public AgentBuilder enableLambdaInstrumentation(boolean enabled) { - return new Default(byteBuddy, - binaryLocator, - typeStrategy, - listener, - nativeMethodStrategy, - accessControlContext, - initializationStrategy, - redefinitionStrategy, - bootstrapInjectionStrategy, - enabled - ? LambdaInstrumentationStrategy.ENABLED - : LambdaInstrumentationStrategy.DISABLED, - transformation); - } + /** + * An implementation of a instance factory for a lambda expression's class. + */ + protected enum FactoryImplementation implements Implementation { - @Override - public ClassFileTransformer makeRaw() { - return new ExecutingTransformer(byteBuddy, - binaryLocator, - typeStrategy, - listener, - nativeMethodStrategy, - accessControlContext, - initializationStrategy, - bootstrapInjectionStrategy, - transformation); - } + /** + * The singleton instance. + */ + INSTANCE; - @Override - public ClassFileTransformer installOn(Instrumentation instrumentation) { - ClassFileTransformer classFileTransformer = makeRaw(); - instrumentation.addTransformer(classFileTransformer, redefinitionStrategy.isRetransforming(instrumentation)); - if (nativeMethodStrategy.isEnabled(instrumentation)) { - instrumentation.setNativeMethodPrefix(classFileTransformer, nativeMethodStrategy.getPrefix()); - } - lambdaInstrumentationStrategy.apply(byteBuddy, instrumentation, classFileTransformer); - if (redefinitionStrategy.isEnabled()) { - RedefinitionStrategy.Collector collector = redefinitionStrategy.makeCollector(transformation); - for (Class type : instrumentation.getAllLoadedClasses()) { - TypeDescription typeDescription = new TypeDescription.ForLoadedType(type); - try { - if (!instrumentation.isModifiableClass(type) || !collector.consider(type)) { - try { - try { - listener.onIgnored(typeDescription); - } finally { - listener.onComplete(typeDescription.getName()); - } - } catch (Throwable ignored) { - // Ignore exceptions that are thrown by listeners to mimic the behavior of a transformation. - } - } - } catch (Throwable throwable) { - try { - try { - listener.onError(typeDescription.getName(), throwable); - } finally { - listener.onComplete(typeDescription.getName()); - } - } catch (Throwable ignored) { - // Ignore exceptions that are thrown by listeners to mimic the behavior of a transformation. - } - } - } - try { - collector.apply(instrumentation, - byteBuddy, - binaryLocator, - typeStrategy, - listener, - nativeMethodStrategy, - accessControlContext, - initializationStrategy, - bootstrapInjectionStrategy); - } catch (UnmodifiableClassException exception) { - throw new IllegalStateException("Cannot modify at least one class: " + collector, exception); - } catch (ClassNotFoundException exception) { - throw new IllegalStateException("Cannot find at least one class class: " + collector, exception); + @Override + public ByteCodeAppender appender(Target implementationTarget) { + return new Appender(implementationTarget.getInstrumentedType()); } - } - return classFileTransformer; - } - @Override - public ClassFileTransformer installOnByteBuddyAgent() { - try { - Instrumentation instrumentation = (Instrumentation) ClassLoader.getSystemClassLoader() - .loadClass(INSTALLER_TYPE) - .getDeclaredField(INSTRUMENTATION_FIELD) - .get(STATIC_FIELD); - if (instrumentation == null) { - throw new IllegalStateException("The Byte Buddy agent is not installed"); + @Override + public InstrumentedType prepare(InstrumentedType instrumentedType) { + return instrumentedType; } - return installOn(instrumentation); - } catch (RuntimeException exception) { - throw exception; - } catch (Exception exception) { - throw new IllegalStateException("The Byte Buddy agent is not installed or not accessible", exception); - } - } - - @Override - public boolean equals(Object other) { - if (this == other) return true; - if (other == null || getClass() != other.getClass()) return false; - Default aDefault = (Default) other; - return binaryLocator.equals(aDefault.binaryLocator) - && byteBuddy.equals(aDefault.byteBuddy) - && listener.equals(aDefault.listener) - && nativeMethodStrategy.equals(aDefault.nativeMethodStrategy) - && typeStrategy.equals(aDefault.typeStrategy) - && accessControlContext.equals(aDefault.accessControlContext) - && initializationStrategy == aDefault.initializationStrategy - && redefinitionStrategy == aDefault.redefinitionStrategy - && bootstrapInjectionStrategy.equals(aDefault.bootstrapInjectionStrategy) - && lambdaInstrumentationStrategy.equals(aDefault.lambdaInstrumentationStrategy) - && transformation.equals(aDefault.transformation); - } - @Override - public int hashCode() { - int result = byteBuddy.hashCode(); - result = 31 * result + binaryLocator.hashCode(); - result = 31 * result + listener.hashCode(); - result = 31 * result + typeStrategy.hashCode(); - result = 31 * result + nativeMethodStrategy.hashCode(); - result = 31 * result + accessControlContext.hashCode(); - result = 31 * result + initializationStrategy.hashCode(); - result = 31 * result + redefinitionStrategy.hashCode(); - result = 31 * result + bootstrapInjectionStrategy.hashCode(); - result = 31 * result + lambdaInstrumentationStrategy.hashCode(); - result = 31 * result + transformation.hashCode(); - return result; - } + @Override + public String toString() { + return "AgentBuilder.LambdaInstrumentationStrategy.LambdaInstanceFactory.FactoryImplementation." + name(); + } - @Override - public String toString() { - return "AgentBuilder.Default{" + - "byteBuddy=" + byteBuddy + - ", binaryLocator=" + binaryLocator + - ", typeStrategy=" + typeStrategy + - ", listener=" + listener + - ", nativeMethodStrategy=" + nativeMethodStrategy + - ", accessControlContext=" + accessControlContext + - ", initializationStrategy=" + initializationStrategy + - ", redefinitionStrategy=" + redefinitionStrategy + - ", bootstrapInjectionStrategy=" + bootstrapInjectionStrategy + - ", lambdaInstrumentationStrategy=" + lambdaInstrumentationStrategy + - ", transformation=" + transformation + - '}'; - } + /** + * An appender for a lambda expression factory. + */ + protected static class Appender implements ByteCodeAppender { - /** - * An injection strategy for injecting classes into the bootstrap class loader. - */ - protected interface BootstrapInjectionStrategy { + /** + * The instrumented type. + */ + private final TypeDescription instrumentedType; - /** - * Creates an injector for the bootstrap class loader. - * - * @param protectionDomain The protection domain to be used. - * @return A class injector for the bootstrap class loader. - */ - ClassInjector make(ProtectionDomain protectionDomain); + /** + * Creates a new appender. + * + * @param instrumentedType The instrumented type. + */ + protected Appender(TypeDescription instrumentedType) { + this.instrumentedType = instrumentedType; + } - /** - * A disabled bootstrap injection strategy. - */ - enum Disabled implements BootstrapInjectionStrategy { + @Override + public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) { + return new Size(new StackManipulation.Compound( + TypeCreation.of(instrumentedType), + Duplication.SINGLE, + MethodVariableAccess.allArgumentsOf(instrumentedMethod), + MethodInvocation.invoke(instrumentedType.getDeclaredMethods().filter(isConstructor()).getOnly()), + MethodReturn.REFERENCE + ).apply(methodVisitor, implementationContext).getMaximalSize(), instrumentedMethod.getStackSize()); + } - /** - * The singleton instance. - */ - INSTANCE; + @Override + public boolean equals(Object other) { + return this == other || !(other == null || getClass() != other.getClass()) + && instrumentedType.equals(((Appender) other).instrumentedType); + } - @Override - public ClassInjector make(ProtectionDomain protectionDomain) { - throw new IllegalStateException("Injecting classes into the bootstrap class loader was not enabled"); - } + @Override + public int hashCode() { + return instrumentedType.hashCode(); + } - @Override - public String toString() { - return "AgentBuilder.Default.BootstrapInjectionStrategy.Disabled." + name(); + @Override + public String toString() { + return "AgentBuilder.LambdaInstrumentationStrategy.LambdaInstanceFactory.FactoryImplementation.Appender{" + + "instrumentedType=" + instrumentedType + + '}'; + } } } /** - * An enabled bootstrap injection strategy. + * Implements a lambda expression's functional method. */ - class Enabled implements BootstrapInjectionStrategy { + protected static class LambdaMethodImplementation implements Implementation { /** - * The folder in which jar files are to be saved. + * The handle of the target method of the lambda expression. */ - private final File folder; + private final JavaInstance.MethodHandle targetMethod; /** - * The instrumentation to use for appending jar files. + * The specialized type of the lambda method. */ - private final Instrumentation instrumentation; + private final JavaInstance.MethodType specializedLambdaMethod; /** - * Creates a new enabled bootstrap class loader injection strategy. + * Creates a implementation of a lambda expression's functional method. * - * @param folder The folder in which jar files are to be saved. - * @param instrumentation The instrumentation to use for appending jar files. + * @param targetMethod The target method of the lambda expression. + * @param specializedLambdaMethod The specialized type of the lambda method. */ - public Enabled(File folder, Instrumentation instrumentation) { - this.folder = folder; - this.instrumentation = instrumentation; + protected LambdaMethodImplementation(JavaInstance.MethodHandle targetMethod, JavaInstance.MethodType specializedLambdaMethod) { + this.targetMethod = targetMethod; + this.specializedLambdaMethod = specializedLambdaMethod; } @Override - public ClassInjector make(ProtectionDomain protectionDomain) { - return ClassInjector.UsingInstrumentation.of(folder, ClassInjector.UsingInstrumentation.Target.BOOTSTRAP, instrumentation); + public ByteCodeAppender appender(Target implementationTarget) { + return new Appender(targetMethod.getOwnerType() + .getDeclaredMethods() + .filter(named(targetMethod.getName()) + .and(returns(targetMethod.getReturnType())) + .and(takesArguments(targetMethod.getParameterTypes()))) + .getOnly(), + specializedLambdaMethod, + implementationTarget.getInstrumentedType().getDeclaredFields()); + } + + @Override + public InstrumentedType prepare(InstrumentedType instrumentedType) { + return instrumentedType; } @Override public boolean equals(Object other) { if (this == other) return true; if (other == null || getClass() != other.getClass()) return false; - Enabled enabled = (Enabled) other; - return folder.equals(enabled.folder) && instrumentation.equals(enabled.instrumentation); + LambdaMethodImplementation that = (LambdaMethodImplementation) other; + return targetMethod.equals(that.targetMethod) + && specializedLambdaMethod.equals(that.specializedLambdaMethod); } @Override public int hashCode() { - int result = folder.hashCode(); - result = 31 * result + instrumentation.hashCode(); + int result = targetMethod.hashCode(); + result = 31 * result + specializedLambdaMethod.hashCode(); return result; } @Override public String toString() { - return "AgentBuilder.Default.BootstrapInjectionStrategy.Enabled{" + - "folder=" + folder + - ", instrumentation=" + instrumentation + + return "AgentBuilder.LambdaInstrumentationStrategy.LambdaInstanceFactory.LambdaMethodImplementation{" + + "targetMethod=" + targetMethod + + ", specializedLambdaMethod=" + specializedLambdaMethod + '}'; } - } - } - /** - * Implements the instrumentation of the {@code LambdaMetafactory} if this feature is enabled. - */ - protected enum LambdaInstrumentationStrategy implements Callable> { + /** + * An appender for a lambda expression's functional method. + */ + protected static class Appender implements ByteCodeAppender { - /** - * A strategy that enables instrumentation of the {@code LambdaMetafactory} if such a factory exists on the current VM. - */ - ENABLED { - @Override - protected void apply(ByteBuddy byteBuddy, Instrumentation instrumentation, ClassFileTransformer classFileTransformer) { - if (LambdaFactory.register(classFileTransformer, new LambdaInstanceFactory(byteBuddy), this)) { - Class lambdaMetaFactory; - try { - lambdaMetaFactory = Class.forName("java.lang.invoke.LambdaMetafactory"); - } catch (ClassNotFoundException ignored) { - return; - } - byteBuddy.with(Implementation.Context.Disabled.Factory.INSTANCE) - .redefine(lambdaMetaFactory) - .visit(new AsmVisitorWrapper.ForDeclaredMethods() - .method(named("metafactory"), MetaFactoryRedirection.INSTANCE) - .method(named("altMetafactory"), AlternativeMetaFactoryRedirection.INSTANCE)) - .make() - .load(lambdaMetaFactory.getClassLoader(), ClassReloadingStrategy.of(instrumentation)); - } - } + /** + * The target method of the lambda expression. + */ + private final MethodDescription targetMethod; - @Override - public Class call() throws Exception { - TypeDescription lambdaFactory = new TypeDescription.ForLoadedType(LambdaFactory.class); - return ClassInjector.UsingReflection.ofSystemClassLoader() - .inject(Collections.singletonMap(lambdaFactory, ClassFileLocator.ForClassLoader.read(LambdaFactory.class).resolve())) - .get(lambdaFactory); - } - }, + /** + * The specialized type of the lambda method. + */ + private final JavaInstance.MethodType specializedLambdaMethod; - /** - * A strategy that does not instrument the {@code LambdaMetafactory}. - */ - DISABLED { - @Override - protected void apply(ByteBuddy byteBuddy, Instrumentation instrumentation, ClassFileTransformer classFileTransformer) { - /* do nothing */ - } + /** + * The instrumented type's declared fields. + */ + private final List declaredFields; - @Override - public Class call() throws Exception { - throw new IllegalStateException("Cannot inject LambdaFactory from disabled instrumentation strategy"); - } - }; + /** + * Creates an appender of a lambda expression's functional method. + * + * @param targetMethod The target method of the lambda expression. + * @param specializedLambdaMethod The specialized type of the lambda method. + * @param declaredFields The instrumented type's declared fields. + */ + protected Appender(MethodDescription targetMethod, + JavaInstance.MethodType specializedLambdaMethod, + List declaredFields) { + this.targetMethod = targetMethod; + this.specializedLambdaMethod = specializedLambdaMethod; + this.declaredFields = declaredFields; + } - /** - * Indicates that an original implementation can be ignored when redefining a method. - */ - protected static final MethodVisitor IGNORE_ORIGINAL = null; + @Override + public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) { + List fieldAccess = new ArrayList(declaredFields.size() * 2); + for (FieldDescription.InDefinedShape fieldDescription : declaredFields) { + fieldAccess.add(MethodVariableAccess.REFERENCE.loadOffset(0)); + fieldAccess.add(FieldAccess.forField(fieldDescription).getter()); + } + List parameterAccess = new ArrayList(instrumentedMethod.getParameters().size() * 2); + for (ParameterDescription parameterDescription : instrumentedMethod.getParameters()) { + parameterAccess.add(MethodVariableAccess.of(parameterDescription.getType()).loadOffset(parameterDescription.getOffset())); + parameterAccess.add(Assigner.DEFAULT.assign(parameterDescription.getType(), + specializedLambdaMethod.getParameterTypes().get(parameterDescription.getIndex()).asGenericType(), + Assigner.Typing.DYNAMIC)); + } + return new Size(new StackManipulation.Compound( + new StackManipulation.Compound(fieldAccess), + new StackManipulation.Compound(parameterAccess), + MethodInvocation.invoke(targetMethod), + MethodReturn.returning(targetMethod.getReturnType().asErasure()) + ).apply(methodVisitor, implementationContext).getMaximalSize(), instrumentedMethod.getStackSize()); + } - /** - * Applies a transformation to lambda instances if applicable. - * - * @param byteBuddy The Byte Buddy instance to use. - * @param instrumentation The instrumentation instance for applying a redefinition. - * @param classFileTransformer The class file transformer to apply. - */ - protected abstract void apply(ByteBuddy byteBuddy, Instrumentation instrumentation, ClassFileTransformer classFileTransformer); + @Override + public boolean equals(Object other) { + if (this == other) return true; + if (other == null || getClass() != other.getClass()) return false; + Appender appender = (Appender) other; + return targetMethod.equals(appender.targetMethod) + && declaredFields.equals(appender.declaredFields) + && specializedLambdaMethod.equals(appender.specializedLambdaMethod); + } - @Override - public String toString() { - return "AgentBuilder.Default.LambdaInstrumentationStrategy." + name(); + @Override + public int hashCode() { + int result = targetMethod.hashCode(); + result = 31 * result + declaredFields.hashCode(); + result = 31 * result + specializedLambdaMethod.hashCode(); + return result; + } + + @Override + public String toString() { + return "AgentBuilder.LambdaInstrumentationStrategy.LambdaInstanceFactory.LambdaMethodImplementation.Appender{" + + "targetMethod=" + targetMethod + + ", specializedLambdaMethod=" + specializedLambdaMethod + + ", declaredFields=" + declaredFields + + '}'; + } + } } /** - * A factory that creates instances that represent lambda expressions. + * Implements the {@code writeReplace} method for serializable lambda expressions. */ - protected static class LambdaInstanceFactory { + protected static class SerializationImplementation implements Implementation { /** - * The name of a factory for a lambda expression. + * The lambda expression's declaring type. */ - private static final String LAMBDA_FACTORY = "get$Lambda"; + private final TypeDescription targetType; /** - * A prefix for a field that represents a property of a lambda expression. + * The lambda expression's functional type. */ - private static final String FIELD_PREFIX = "arg$"; + private final TypeDescription lambdaType; /** - * The infix to use for naming classes that represent lambda expression. The additional prefix - * is necessary because the subsequent counter is not sufficient to keep names unique compared - * to the original factory. + * The lambda expression's functional method name. */ - private static final String LAMBDA_TYPE_INFIX = "$$Lambda$ByteBuddy$"; + private final String lambdaMethodName; /** - * A type-safe constant to express that a class is not already loaded when applying a class file transformer. + * The method type of the lambda expression's functional method. */ - private static final Class NOT_PREVIOUSLY_DEFINED = null; + private final JavaInstance.MethodType lambdaMethod; /** - * A counter for naming lambda expressions randomly. + * A handle that references the lambda expressions invocation target. */ - private static final AtomicInteger LAMBDA_NAME_COUNTER = new AtomicInteger(); + private final JavaInstance.MethodHandle targetMethod; /** - * The Byte Buddy instance to use for creating lambda objects. + * The specialized method type of the lambda expression's functional method. */ - private final ByteBuddy byteBuddy; + private final JavaInstance.MethodType specializedMethod; /** - * Creates a new lambda instance factory. + * Creates a new implementation for a serializable's lambda expression's {@code writeReplace} method. * - * @param byteBuddy The Byte Buddy instance to use for creating lambda objects. + * @param targetType The lambda expression's declaring type. + * @param lambdaType The lambda expression's functional type. + * @param lambdaMethodName The lambda expression's functional method name. + * @param lambdaMethod The method type of the lambda expression's functional method. + * @param targetMethod A handle that references the lambda expressions invocation target. + * @param specializedMethod The specialized method type of the lambda expression's functional method. */ - protected LambdaInstanceFactory(ByteBuddy byteBuddy) { - this.byteBuddy = byteBuddy; + protected SerializationImplementation(TypeDescription targetType, + TypeDescription lambdaType, + String lambdaMethodName, + JavaInstance.MethodType lambdaMethod, + JavaInstance.MethodHandle targetMethod, + JavaInstance.MethodType specializedMethod) { + this.targetType = targetType; + this.lambdaType = lambdaType; + this.lambdaMethodName = lambdaMethodName; + this.lambdaMethod = lambdaMethod; + this.targetMethod = targetMethod; + this.specializedMethod = specializedMethod; } - /** - * Applies this lambda meta factory. - * - * @param targetTypeLookup A lookup context representing the creating class of this lambda expression. - * @param lambdaMethodName The name of the lambda expression's represented method. - * @param factoryMethodType The type of the lambda expression's represented method. - * @param lambdaMethodType The type of the lambda expression's factory method. - * @param targetMethodHandle A handle representing the target of the lambda expression's method. - * @param specializedLambdaMethodType A specialization of the type of the lambda expression's represented method. - * @param serializable {@code true} if the lambda expression should be serializable. - * @param markerInterfaces A list of interfaces for the lambda expression to represent. - * @param additionalBridges A list of additional bridge methods to be implemented by the lambda expression. - * @param classFileTransformers A collection of class file transformers to apply when creating the class. - * @return A binary representation of the transformed class file. - */ - public byte[] make(Object targetTypeLookup, - String lambdaMethodName, - Object factoryMethodType, - Object lambdaMethodType, - Object targetMethodHandle, - Object specializedLambdaMethodType, - boolean serializable, - List> markerInterfaces, - List additionalBridges, - Collection classFileTransformers) { - JavaInstance.MethodType factoryMethod = JavaInstance.MethodType.of(factoryMethodType); - JavaInstance.MethodType lambdaMethod = JavaInstance.MethodType.of(lambdaMethodType); - JavaInstance.MethodHandle targetMethod = JavaInstance.MethodHandle.of(targetMethodHandle, targetTypeLookup); - JavaInstance.MethodType specializedLambdaMethod = JavaInstance.MethodType.of(specializedLambdaMethodType); - Class targetType = JavaInstance.MethodHandle.lookupType(targetTypeLookup); - String lambdaClassName = targetType.getName() + LAMBDA_TYPE_INFIX + LAMBDA_NAME_COUNTER.incrementAndGet(); - DynamicType.Builder builder = byteBuddy - .subclass(factoryMethod.getReturnType(), ConstructorStrategy.Default.NO_CONSTRUCTORS) - .modifiers(SyntheticState.SYNTHETIC, TypeManifestation.FINAL, Visibility.PUBLIC) - .implement(markerInterfaces) - .name(lambdaClassName) - .defineConstructor(Visibility.PUBLIC) - .withParameters(factoryMethod.getParameterTypes()) - .intercept(ConstructorImplementation.INSTANCE) - .method(named(lambdaMethodName) - .and(takesArguments(lambdaMethod.getParameterTypes())) - .and(returns(lambdaMethod.getReturnType()))) - .intercept(new LambdaMethodImplementation(targetMethod, specializedLambdaMethod)); - int index = 0; - for (TypeDescription capturedType : factoryMethod.getParameterTypes()) { - builder = builder.defineField(FIELD_PREFIX + ++index, capturedType, Visibility.PRIVATE, FieldManifestation.FINAL); + @Override + public ByteCodeAppender appender(Target implementationTarget) { + TypeDescription serializedLambda; + try { + serializedLambda = new TypeDescription.ForLoadedType(Class.forName("java.lang.invoke.SerializedLambda")); + } catch (ClassNotFoundException exception) { + throw new IllegalStateException("Cannot find class for lambda serialization", exception); } - if (!factoryMethod.getParameterTypes().isEmpty()) { - builder = builder.defineMethod(LAMBDA_FACTORY, factoryMethod.getReturnType(), Visibility.PRIVATE, Ownership.STATIC) - .withParameters(factoryMethod.getParameterTypes()) - .intercept(FactoryImplementation.INSTANCE); + List lambdaArguments = new ArrayList(implementationTarget.getInstrumentedType().getDeclaredFields().size()); + for (FieldDescription.InDefinedShape fieldDescription : implementationTarget.getInstrumentedType().getDeclaredFields()) { + lambdaArguments.add(new StackManipulation.Compound(MethodVariableAccess.REFERENCE.loadOffset(0), + FieldAccess.forField(fieldDescription).getter(), + Assigner.DEFAULT.assign(fieldDescription.getType(), TypeDescription.Generic.OBJECT, Assigner.Typing.STATIC))); } - if (serializable) { - if (!markerInterfaces.contains(Serializable.class)) { - builder = builder.implement(Serializable.class); - } - builder = builder.defineMethod("writeReplace", Object.class, Visibility.PRIVATE) - .intercept(new SerializationImplementation(new TypeDescription.ForLoadedType(targetType), - factoryMethod.getReturnType(), - lambdaMethodName, - lambdaMethod, - targetMethod, - JavaInstance.MethodType.of(specializedLambdaMethodType))); - } else if (factoryMethod.getReturnType().isAssignableTo(Serializable.class)) { - builder = builder.defineMethod("readObject", void.class, Visibility.PRIVATE) - .withParameters(ObjectInputStream.class) - .throwing(NotSerializableException.class) - .intercept(ExceptionMethod.throwing(NotSerializableException.class, "Non-serializable lambda")) - .defineMethod("writeObject", void.class, Visibility.PRIVATE) - .withParameters(ObjectOutputStream.class) - .throwing(NotSerializableException.class) - .intercept(ExceptionMethod.throwing(NotSerializableException.class, "Non-serializable lambda")); - } - for (Object additionalBridgeType : additionalBridges) { - JavaInstance.MethodType additionalBridge = JavaInstance.MethodType.of(additionalBridgeType); - builder = builder.defineMethod(lambdaMethodName, additionalBridge.getReturnType(), MethodManifestation.BRIDGE, Visibility.PUBLIC) - .withParameters(additionalBridge.getParameterTypes()) - .intercept(new BridgeMethodImplementation(lambdaMethodName, lambdaMethod)); - } - byte[] classFile = builder.make().getBytes(); - for (ClassFileTransformer classFileTransformer : classFileTransformers) { - try { - byte[] transformedClassFile = classFileTransformer.transform(targetType.getClassLoader(), - lambdaClassName.replace('.', '/'), - NOT_PREVIOUSLY_DEFINED, - targetType.getProtectionDomain(), - classFile); - classFile = transformedClassFile == null - ? classFile - : transformedClassFile; - } catch (Exception ignored) { - /* do nothing */ - } - } - return classFile; + return new ByteCodeAppender.Simple(new StackManipulation.Compound( + TypeCreation.of(serializedLambda), + Duplication.SINGLE, + ClassConstant.of(targetType), + new TextConstant(lambdaType.getInternalName()), + new TextConstant(lambdaMethodName), + new TextConstant(lambdaMethod.getDescriptor()), + IntegerConstant.forValue(targetMethod.getHandleType().getIdentifier()), + new TextConstant(targetMethod.getOwnerType().getInternalName()), + new TextConstant(targetMethod.getName()), + new TextConstant(targetMethod.getDescriptor()), + new TextConstant(specializedMethod.getDescriptor()), + ArrayFactory.forType(TypeDescription.Generic.OBJECT).withValues(lambdaArguments), + MethodInvocation.invoke(serializedLambda.getDeclaredMethods().filter(isConstructor()).getOnly()), + MethodReturn.REFERENCE + )); + } + + @Override + public InstrumentedType prepare(InstrumentedType instrumentedType) { + return instrumentedType; } @Override public boolean equals(Object other) { - return this == other || !(other == null || getClass() != other.getClass()) - && byteBuddy.equals(((LambdaInstanceFactory) other).byteBuddy); + if (this == other) return true; + if (other == null || getClass() != other.getClass()) return false; + SerializationImplementation that = (SerializationImplementation) other; + return targetType.equals(that.targetType) + && lambdaType.equals(that.lambdaType) + && lambdaMethodName.equals(that.lambdaMethodName) + && lambdaMethod.equals(that.lambdaMethod) + && targetMethod.equals(that.targetMethod) + && specializedMethod.equals(that.specializedMethod); } @Override public int hashCode() { - return byteBuddy.hashCode(); + int result = targetType.hashCode(); + result = 31 * result + lambdaType.hashCode(); + result = 31 * result + lambdaMethodName.hashCode(); + result = 31 * result + lambdaMethod.hashCode(); + result = 31 * result + targetMethod.hashCode(); + result = 31 * result + specializedMethod.hashCode(); + return result; } @Override public String toString() { - return "AgentBuilder.Default.LambdaInstrumentationStrategy.LambdaInstanceFactory{" + - "byteBuddy=" + byteBuddy + + return "AgentBuilder.LambdaInstrumentationStrategy.LambdaInstanceFactory.SerializationImplementation{" + + "targetType=" + targetType + + ", lambdaType=" + lambdaType + + ", lambdaMethodName='" + lambdaMethodName + '\'' + + ", lambdaMethod=" + lambdaMethod + + ", targetMethod=" + targetMethod + + ", specializedMethod=" + specializedMethod + '}'; } + } + + /** + * Implements an explicit bridge method for a lambda expression. + */ + protected static class BridgeMethodImplementation implements Implementation { /** - * Implements a lambda class's constructor. + * The name of the lambda expression's functional method. */ - protected enum ConstructorImplementation implements Implementation { - - /** - * The singleton instance. - */ - INSTANCE; - - /** - * A reference to the {@link Object} class's default constructor. - */ - protected final MethodDescription.InDefinedShape objectConstructor; - - /** - * Creates a new constructor implementation. - */ - ConstructorImplementation() { - objectConstructor = TypeDescription.OBJECT.getDeclaredMethods().filter(isConstructor()).getOnly(); - } - - @Override - public ByteCodeAppender appender(Target implementationTarget) { - return new Appender(implementationTarget.getInstrumentedType().getDeclaredFields()); - } - - @Override - public InstrumentedType prepare(InstrumentedType instrumentedType) { - return instrumentedType; - } - - @Override - public String toString() { - return "AgentBuilder.Default.LambdaInstrumentationStrategy.LambdaInstanceFactory.ConstructorImplementation." + name(); - } - - /** - * An appender to implement the constructor. - */ - protected static class Appender implements ByteCodeAppender { - - /** - * The fields that are declared by the instrumented type. - */ - private final List declaredFields; - - /** - * Creates a new appender. - * - * @param declaredFields The fields that are declared by the instrumented type. - */ - protected Appender(List declaredFields) { - this.declaredFields = declaredFields; - } - - @Override - public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) { - List fieldAssignments = new ArrayList(declaredFields.size() * 3); - for (ParameterDescription parameterDescription : instrumentedMethod.getParameters()) { - fieldAssignments.add(MethodVariableAccess.REFERENCE.loadOffset(0)); - fieldAssignments.add(MethodVariableAccess.of(parameterDescription.getType()).loadOffset(parameterDescription.getOffset())); - fieldAssignments.add(FieldAccess.forField(declaredFields.get(parameterDescription.getIndex())).putter()); - } - return new Size(new StackManipulation.Compound( - MethodVariableAccess.REFERENCE.loadOffset(0), - MethodInvocation.invoke(INSTANCE.objectConstructor), - new StackManipulation.Compound(fieldAssignments), - MethodReturn.VOID - ).apply(methodVisitor, implementationContext).getMaximalSize(), instrumentedMethod.getStackSize()); - } - - @Override - public boolean equals(Object other) { - return this == other || !(other == null || getClass() != other.getClass()) - && declaredFields.equals(((Appender) other).declaredFields); - } - - @Override - public int hashCode() { - return declaredFields.hashCode(); - } - - @Override - public String toString() { - return "AgentBuilder.Default.LambdaInstrumentationStrategy.LambdaInstanceFactory.ConstructorImplementation.Appender{" + - "declaredFields=" + declaredFields + - '}'; - } - } - } + private final String lambdaMethodName; /** - * An implementation of a instance factory for a lambda expression's class. + * The actual type of the lambda expression's functional method. */ - protected enum FactoryImplementation implements Implementation { - - /** - * The singleton instance. - */ - INSTANCE; - - @Override - public ByteCodeAppender appender(Target implementationTarget) { - return new Appender(implementationTarget.getInstrumentedType()); - } - - @Override - public InstrumentedType prepare(InstrumentedType instrumentedType) { - return instrumentedType; - } - - @Override - public String toString() { - return "AgentBuilder.Default.LambdaInstrumentationStrategy.LambdaInstanceFactory.FactoryImplementation." + name(); - } - - /** - * An appender for a lambda expression factory. - */ - protected static class Appender implements ByteCodeAppender { + private final JavaInstance.MethodType lambdaMethod; - /** - * The instrumented type. - */ - private final TypeDescription instrumentedType; + /** + * Creates a new bridge method implementation for a lambda expression. + * + * @param lambdaMethodName The name of the lambda expression's functional method. + * @param lambdaMethod The actual type of the lambda expression's functional method. + */ + protected BridgeMethodImplementation(String lambdaMethodName, JavaInstance.MethodType lambdaMethod) { + this.lambdaMethodName = lambdaMethodName; + this.lambdaMethod = lambdaMethod; + } - /** - * Creates a new appender. - * - * @param instrumentedType The instrumented type. - */ - protected Appender(TypeDescription instrumentedType) { - this.instrumentedType = instrumentedType; - } + @Override + public ByteCodeAppender appender(Target implementationTarget) { + return new Appender(implementationTarget.invokeSuper(new MethodDescription.SignatureToken(lambdaMethodName, + lambdaMethod.getReturnType(), + lambdaMethod.getParameterTypes()))); + } - @Override - public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) { - return new Size(new StackManipulation.Compound( - TypeCreation.of(instrumentedType), - Duplication.SINGLE, - MethodVariableAccess.allArgumentsOf(instrumentedMethod), - MethodInvocation.invoke(instrumentedType.getDeclaredMethods().filter(isConstructor()).getOnly()), - MethodReturn.REFERENCE - ).apply(methodVisitor, implementationContext).getMaximalSize(), instrumentedMethod.getStackSize()); - } + @Override + public InstrumentedType prepare(InstrumentedType instrumentedType) { + return instrumentedType; + } - @Override - public boolean equals(Object other) { - return this == other || !(other == null || getClass() != other.getClass()) - && instrumentedType.equals(((Appender) other).instrumentedType); - } + @Override + public boolean equals(Object other) { + if (this == other) return true; + if (other == null || getClass() != other.getClass()) return false; + BridgeMethodImplementation that = (BridgeMethodImplementation) other; + return lambdaMethodName.equals(that.lambdaMethodName) && lambdaMethod.equals(that.lambdaMethod); + } - @Override - public int hashCode() { - return instrumentedType.hashCode(); - } + @Override + public int hashCode() { + int result = lambdaMethodName.hashCode(); + result = 31 * result + lambdaMethod.hashCode(); + return result; + } - @Override - public String toString() { - return "AgentBuilder.Default.LambdaInstrumentationStrategy.LambdaInstanceFactory.FactoryImplementation.Appender{" + - "instrumentedType=" + instrumentedType + - '}'; - } - } + @Override + public String toString() { + return "AgentBuilder.LambdaInstrumentationStrategy.LambdaInstanceFactory.BridgeMethodImplementation{" + + "lambdaMethodName='" + lambdaMethodName + '\'' + + ", lambdaMethod=" + lambdaMethod + + '}'; } /** - * Implements a lambda expression's functional method. + * An appender for implementing a bridge method for a lambda expression. */ - protected static class LambdaMethodImplementation implements Implementation { - - /** - * The handle of the target method of the lambda expression. - */ - private final JavaInstance.MethodHandle targetMethod; + protected static class Appender implements ByteCodeAppender { /** - * The specialized type of the lambda method. + * The invocation of the bridge's target method. */ - private final JavaInstance.MethodType specializedLambdaMethod; + private final SpecialMethodInvocation bridgeTargetInvocation; /** - * Creates a implementation of a lambda expression's functional method. + * Creates a new appender for invoking a lambda expression's bridge method target. * - * @param targetMethod The target method of the lambda expression. - * @param specializedLambdaMethod The specialized type of the lambda method. + * @param bridgeTargetInvocation The invocation of the bridge's target method. */ - protected LambdaMethodImplementation(JavaInstance.MethodHandle targetMethod, JavaInstance.MethodType specializedLambdaMethod) { - this.targetMethod = targetMethod; - this.specializedLambdaMethod = specializedLambdaMethod; + protected Appender(SpecialMethodInvocation bridgeTargetInvocation) { + this.bridgeTargetInvocation = bridgeTargetInvocation; } @Override - public ByteCodeAppender appender(Target implementationTarget) { - return new Appender(targetMethod.getOwnerType() - .getDeclaredMethods() - .filter(named(targetMethod.getName()) - .and(returns(targetMethod.getReturnType())) - .and(takesArguments(targetMethod.getParameterTypes()))) - .getOnly(), - specializedLambdaMethod, - implementationTarget.getInstrumentedType().getDeclaredFields()); - } + public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) { + return new Compound(new Simple( + MethodVariableAccess.allArgumentsOf(instrumentedMethod) + .asBridgeOf(bridgeTargetInvocation.getMethodDescription()) + .prependThisReference(), + bridgeTargetInvocation, + bridgeTargetInvocation.getMethodDescription().getReturnType().asErasure().isAssignableTo(instrumentedMethod.getReturnType().asErasure()) + ? StackManipulation.Trivial.INSTANCE + : TypeCasting.to(instrumentedMethod.getReceiverType().asErasure()), + MethodReturn.returning(instrumentedMethod.getReturnType().asErasure()) - @Override - public InstrumentedType prepare(InstrumentedType instrumentedType) { - return instrumentedType; + )).apply(methodVisitor, implementationContext, instrumentedMethod); } @Override public boolean equals(Object other) { - if (this == other) return true; - if (other == null || getClass() != other.getClass()) return false; - LambdaMethodImplementation that = (LambdaMethodImplementation) other; - return targetMethod.equals(that.targetMethod) - && specializedLambdaMethod.equals(that.specializedLambdaMethod); + return this == other || !(other == null || getClass() != other.getClass()) + && bridgeTargetInvocation.equals(((Appender) other).bridgeTargetInvocation); } @Override public int hashCode() { - int result = targetMethod.hashCode(); - result = 31 * result + specializedLambdaMethod.hashCode(); - return result; + return bridgeTargetInvocation.hashCode(); } @Override public String toString() { - return "AgentBuilder.Default.LambdaInstrumentationStrategy.LambdaInstanceFactory.LambdaMethodImplementation{" + - "targetMethod=" + targetMethod + - ", specializedLambdaMethod=" + specializedLambdaMethod + + return "AgentBuilder.LambdaInstrumentationStrategy.LambdaInstanceFactory.BridgeMethodImplementation.Appender{" + + "bridgeTargetInvocation=" + bridgeTargetInvocation + '}'; } + } + } + } - /** - * An appender for a lambda expression's functional method. - */ - protected static class Appender implements ByteCodeAppender { + /** + * Implements the regular lambda meta factory. The implementation represents the following code: + *
+         * public static CallSite metafactory(MethodHandles.Lookup caller,
+         *     String invokedName,
+         *     MethodType invokedType,
+         *     MethodType samMethodType,
+         *     MethodHandle implMethod,
+         *     MethodType instantiatedMethodType) throws Exception {
+         *   Unsafe unsafe = Unsafe.getUnsafe();
+         *   {@code Class} lambdaClass = unsafe.defineAnonymousClass(caller.lookupClass(),
+         *       (byte[]) ClassLoader.getSystemClassLoader().loadClass("net.bytebuddy.agent.builder.LambdaFactory").getDeclaredMethod("make",
+         *           Object.class,
+         *           String.class,
+         *           Object.class,
+         *           Object.class,
+         *           Object.class,
+         *           Object.class,
+         *           boolean.class,
+         *           List.class,
+         *           List.class).invoke(null,
+         *               caller,
+         *               invokedName,
+         *               invokedType,
+         *               samMethodType,
+         *               implMethod,
+         *               instantiatedMethodType,
+         *               false,
+         *               Collections.emptyList(),
+         *               Collections.emptyList()),
+         *       null);
+         *   unsafe.ensureClassInitialized(lambdaClass);
+         *   return invokedType.parameterCount() == 0
+         *     ? new ConstantCallSite(MethodHandles.constant(invokedType.returnType(), lambdaClass.getDeclaredConstructors()[0].newInstance()))
+         *     : new ConstantCallSite(MethodHandles.Lookup.IMPL_LOOKUP.findStatic(lambdaClass, "get$Lambda", invokedType));
+         * 
+ */ + protected enum MetaFactoryRedirection implements AsmVisitorWrapper.ForDeclaredMethods.MethodVisitorWrapper { - /** - * The target method of the lambda expression. - */ - private final MethodDescription targetMethod; + /** + * The singleton instance. + */ + INSTANCE; - /** - * The specialized type of the lambda method. - */ - private final JavaInstance.MethodType specializedLambdaMethod; + @Override + public MethodVisitor wrap(TypeDescription instrumentedType, MethodDescription.InDefinedShape methodDescription, MethodVisitor methodVisitor) { + methodVisitor.visitCode(); + methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "sun/misc/Unsafe", "getUnsafe", "()Lsun/misc/Unsafe;", false); + methodVisitor.visitVarInsn(Opcodes.ASTORE, 6); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 6); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); + methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "lookupClass", "()Ljava/lang/Class;", false); + methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/ClassLoader", "getSystemClassLoader", "()Ljava/lang/ClassLoader;", false); + methodVisitor.visitLdcInsn("net.bytebuddy.agent.builder.LambdaFactory"); + methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/ClassLoader", "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;", false); + methodVisitor.visitLdcInsn("make"); + methodVisitor.visitIntInsn(Opcodes.BIPUSH, 9); + methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Class"); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitInsn(Opcodes.ICONST_0); + methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/Object;")); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitInsn(Opcodes.ICONST_1); + methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/String;")); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitInsn(Opcodes.ICONST_2); + methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/Object;")); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitInsn(Opcodes.ICONST_3); + methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/Object;")); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitInsn(Opcodes.ICONST_4); + methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/Object;")); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitInsn(Opcodes.ICONST_5); + methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/Object;")); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitIntInsn(Opcodes.BIPUSH, 6); + methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Boolean", "TYPE", "Ljava/lang/Class;"); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitIntInsn(Opcodes.BIPUSH, 7); + methodVisitor.visitLdcInsn(Type.getType("Ljava/util/List;")); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitIntInsn(Opcodes.BIPUSH, 8); + methodVisitor.visitLdcInsn(Type.getType("Ljava/util/List;")); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "getDeclaredMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", false); + methodVisitor.visitInsn(Opcodes.ACONST_NULL); + methodVisitor.visitIntInsn(Opcodes.BIPUSH, 9); + methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitInsn(Opcodes.ICONST_0); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitInsn(Opcodes.ICONST_1); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 1); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitInsn(Opcodes.ICONST_2); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 2); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitInsn(Opcodes.ICONST_3); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 3); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitInsn(Opcodes.ICONST_4); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 4); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitInsn(Opcodes.ICONST_5); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 5); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitIntInsn(Opcodes.BIPUSH, 6); + methodVisitor.visitInsn(Opcodes.ICONST_0); + methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitIntInsn(Opcodes.BIPUSH, 7); + methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Collections", "emptyList", "()Ljava/util/List;", false); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitIntInsn(Opcodes.BIPUSH, 8); + methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Collections", "emptyList", "()Ljava/util/List;", false); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/reflect/Method", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", false); + methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, "[B"); + methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, "[B"); + methodVisitor.visitInsn(Opcodes.ACONST_NULL); + methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "sun/misc/Unsafe", "defineAnonymousClass", "(Ljava/lang/Class;[B[Ljava/lang/Object;)Ljava/lang/Class;", false); + methodVisitor.visitVarInsn(Opcodes.ASTORE, 7); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 6); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 7); + methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "sun/misc/Unsafe", "ensureClassInitialized", "(Ljava/lang/Class;)V", false); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 2); + methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/invoke/MethodType", "parameterCount", "()I", false); + Label conditionalDefault = new Label(); + methodVisitor.visitJumpInsn(Opcodes.IFNE, conditionalDefault); + methodVisitor.visitTypeInsn(Opcodes.NEW, "java/lang/invoke/ConstantCallSite"); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 2); + methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/invoke/MethodType", "returnType", "()Ljava/lang/Class;", false); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 7); + methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;", false); + methodVisitor.visitInsn(Opcodes.ICONST_0); + methodVisitor.visitInsn(Opcodes.AALOAD); + methodVisitor.visitInsn(Opcodes.ICONST_0); + methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); + methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/reflect/Constructor", "newInstance", "([Ljava/lang/Object;)Ljava/lang/Object;", false); + methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/invoke/MethodHandles", "constant", "(Ljava/lang/Class;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;", false); + methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/invoke/ConstantCallSite", "", "(Ljava/lang/invoke/MethodHandle;)V", false); + Label conditionalAlternative = new Label(); + methodVisitor.visitJumpInsn(Opcodes.GOTO, conditionalAlternative); + methodVisitor.visitLabel(conditionalDefault); + methodVisitor.visitFrame(Opcodes.F_APPEND, 2, new Object[]{"sun/misc/Unsafe", "java/lang/Class"}, 0, null); + methodVisitor.visitTypeInsn(Opcodes.NEW, "java/lang/invoke/ConstantCallSite"); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/invoke/MethodHandles$Lookup", "IMPL_LOOKUP", "Ljava/lang/invoke/MethodHandles$Lookup;"); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 7); + methodVisitor.visitLdcInsn("get$Lambda"); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 2); + methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "findStatic", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", false); + methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/invoke/ConstantCallSite", "", "(Ljava/lang/invoke/MethodHandle;)V", false); + methodVisitor.visitLabel(conditionalAlternative); + methodVisitor.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[]{"java/lang/invoke/CallSite"}); + methodVisitor.visitInsn(Opcodes.ARETURN); + methodVisitor.visitMaxs(8, 8); + methodVisitor.visitEnd(); + return IGNORE_ORIGINAL; + } - /** - * The instrumented type's declared fields. - */ - private final List declaredFields; + @Override + public String toString() { + return "AgentBuilder.LambdaInstrumentationStrategy.MetaFactoryRedirection." + name(); + } + } - /** - * Creates an appender of a lambda expression's functional method. - * - * @param targetMethod The target method of the lambda expression. - * @param specializedLambdaMethod The specialized type of the lambda method. - * @param declaredFields The instrumented type's declared fields. - */ - protected Appender(MethodDescription targetMethod, - JavaInstance.MethodType specializedLambdaMethod, - List declaredFields) { - this.targetMethod = targetMethod; - this.specializedLambdaMethod = specializedLambdaMethod; - this.declaredFields = declaredFields; - } + /** + * Implements the alternative lambda meta factory. The implementation represents the following code: + *
+         * public static CallSite altMetafactory(MethodHandles.Lookup caller,
+         *     String invokedName,
+         *     MethodType invokedType,
+         *     Object... args) throws Exception {
+         *   int flags = (Integer) args[3];
+         *   int argIndex = 4;
+         *   {@code Class[]} markerInterface;
+         *   if ((flags & FLAG_MARKERS) != 0) {
+         *     int markerCount = (Integer) args[argIndex++];
+         *     markerInterface = new {@code Class}[markerCount];
+         *     System.arraycopy(args, argIndex, markerInterface, 0, markerCount);
+         *     argIndex += markerCount;
+         *   } else {
+         *     markerInterface = new {@code Class}[0];
+         *   }
+         *   MethodType[] additionalBridge;
+         *   if ((flags & FLAG_BRIDGES) != 0) {
+         *     int bridgeCount = (Integer) args[argIndex++];
+         *     additionalBridge = new MethodType[bridgeCount];
+         *     System.arraycopy(args, argIndex, additionalBridge, 0, bridgeCount);
+         *     // argIndex += bridgeCount;
+         *   } else {
+         *     additionalBridge = new MethodType[0];
+         *   }
+         *   Unsafe unsafe = Unsafe.getUnsafe();
+         *   Class lambdaClass = unsafe.defineAnonymousClass(caller.lookupClass(),
+         *       (byte[]) ClassLoader.getSystemClassLoader().loadClass("net.bytebuddy.agent.builder.LambdaFactory").getDeclaredMethod("make",
+         *           Object.class,
+         *           String.class,
+         *           Object.class,
+         *           Object.class,
+         *           Object.class,
+         *           Object.class,
+         *           boolean.class,
+         *           List.class,
+         *           List.class).invoke(null,
+         *               caller,
+         *               invokedName,
+         *               invokedType,
+         *               args[0],
+         *               args[1],
+         *               args[2],
+         *               (flags & FLAG_SERIALIZABLE) != 0,
+         *               Arrays.asList(markerInterface),
+         *               Arrays.asList(additionalBridge)),
+         *       null);
+         *   unsafe.ensureClassInitialized(lambdaClass);
+         *   return invokedType.parameterCount() == 0
+         *     ? new ConstantCallSite(MethodHandles.constant(invokedType.returnType(), lambdaClass.getDeclaredConstructors()[0].newInstance()))
+         *     : new ConstantCallSite(MethodHandles.Lookup.IMPL_LOOKUP.findStatic(lambdaClass, "get$Lambda", invokedType));
+         * }
+         * 
+ */ + protected enum AlternativeMetaFactoryRedirection implements AsmVisitorWrapper.ForDeclaredMethods.MethodVisitorWrapper { - @Override - public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) { - List fieldAccess = new ArrayList(declaredFields.size() * 2); - for (FieldDescription.InDefinedShape fieldDescription : declaredFields) { - fieldAccess.add(MethodVariableAccess.REFERENCE.loadOffset(0)); - fieldAccess.add(FieldAccess.forField(fieldDescription).getter()); - } - List parameterAccess = new ArrayList(instrumentedMethod.getParameters().size() * 2); - for (ParameterDescription parameterDescription : instrumentedMethod.getParameters()) { - parameterAccess.add(MethodVariableAccess.of(parameterDescription.getType()).loadOffset(parameterDescription.getOffset())); - parameterAccess.add(Assigner.DEFAULT.assign(parameterDescription.getType(), - specializedLambdaMethod.getParameterTypes().get(parameterDescription.getIndex()).asGenericType(), - Assigner.Typing.DYNAMIC)); - } - return new Size(new StackManipulation.Compound( - new StackManipulation.Compound(fieldAccess), - new StackManipulation.Compound(parameterAccess), - MethodInvocation.invoke(targetMethod), - MethodReturn.returning(targetMethod.getReturnType().asErasure()) - ).apply(methodVisitor, implementationContext).getMaximalSize(), instrumentedMethod.getStackSize()); - } + /** + * The singleton instance. + */ + INSTANCE; - @Override - public boolean equals(Object other) { - if (this == other) return true; - if (other == null || getClass() != other.getClass()) return false; - Appender appender = (Appender) other; - return targetMethod.equals(appender.targetMethod) - && declaredFields.equals(appender.declaredFields) - && specializedLambdaMethod.equals(appender.specializedLambdaMethod); - } + @Override + public MethodVisitor wrap(TypeDescription instrumentedType, MethodDescription.InDefinedShape methodDescription, MethodVisitor methodVisitor) { + methodVisitor.visitCode(); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 3); + methodVisitor.visitInsn(Opcodes.ICONST_3); + methodVisitor.visitInsn(Opcodes.AALOAD); + methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Integer"); + methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false); + methodVisitor.visitVarInsn(Opcodes.ISTORE, 4); + methodVisitor.visitInsn(Opcodes.ICONST_4); + methodVisitor.visitVarInsn(Opcodes.ISTORE, 5); + methodVisitor.visitVarInsn(Opcodes.ILOAD, 4); + methodVisitor.visitInsn(Opcodes.ICONST_2); + methodVisitor.visitInsn(Opcodes.IAND); + Label markerInterfaceLoop = new Label(); + methodVisitor.visitJumpInsn(Opcodes.IFEQ, markerInterfaceLoop); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 3); + methodVisitor.visitVarInsn(Opcodes.ILOAD, 5); + methodVisitor.visitIincInsn(5, 1); + methodVisitor.visitInsn(Opcodes.AALOAD); + methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Integer"); + methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false); + methodVisitor.visitVarInsn(Opcodes.ISTORE, 7); + methodVisitor.visitVarInsn(Opcodes.ILOAD, 7); + methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Class"); + methodVisitor.visitVarInsn(Opcodes.ASTORE, 6); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 3); + methodVisitor.visitVarInsn(Opcodes.ILOAD, 5); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 6); + methodVisitor.visitInsn(Opcodes.ICONST_0); + methodVisitor.visitVarInsn(Opcodes.ILOAD, 7); + methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "arraycopy", "(Ljava/lang/Object;ILjava/lang/Object;II)V", false); + methodVisitor.visitVarInsn(Opcodes.ILOAD, 5); + methodVisitor.visitVarInsn(Opcodes.ILOAD, 7); + methodVisitor.visitInsn(Opcodes.IADD); + methodVisitor.visitVarInsn(Opcodes.ISTORE, 5); + Label markerInterfaceExit = new Label(); + methodVisitor.visitJumpInsn(Opcodes.GOTO, markerInterfaceExit); + methodVisitor.visitLabel(markerInterfaceLoop); + methodVisitor.visitFrame(Opcodes.F_APPEND, 2, new Object[]{Opcodes.INTEGER, Opcodes.INTEGER}, 0, null); + methodVisitor.visitInsn(Opcodes.ICONST_0); + methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Class"); + methodVisitor.visitVarInsn(Opcodes.ASTORE, 6); + methodVisitor.visitLabel(markerInterfaceExit); + methodVisitor.visitFrame(Opcodes.F_APPEND, 1, new Object[]{"[Ljava/lang/Class;"}, 0, null); + methodVisitor.visitVarInsn(Opcodes.ILOAD, 4); + methodVisitor.visitInsn(Opcodes.ICONST_4); + methodVisitor.visitInsn(Opcodes.IAND); + Label additionalBridgesLoop = new Label(); + methodVisitor.visitJumpInsn(Opcodes.IFEQ, additionalBridgesLoop); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 3); + methodVisitor.visitVarInsn(Opcodes.ILOAD, 5); + methodVisitor.visitIincInsn(5, 1); + methodVisitor.visitInsn(Opcodes.AALOAD); + methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Integer"); + methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false); + methodVisitor.visitVarInsn(Opcodes.ISTORE, 8); + methodVisitor.visitVarInsn(Opcodes.ILOAD, 8); + methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/invoke/MethodType"); + methodVisitor.visitVarInsn(Opcodes.ASTORE, 7); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 3); + methodVisitor.visitVarInsn(Opcodes.ILOAD, 5); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 7); + methodVisitor.visitInsn(Opcodes.ICONST_0); + methodVisitor.visitVarInsn(Opcodes.ILOAD, 8); + methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "arraycopy", "(Ljava/lang/Object;ILjava/lang/Object;II)V", false); + Label additionalBridgesExit = new Label(); + methodVisitor.visitJumpInsn(Opcodes.GOTO, additionalBridgesExit); + methodVisitor.visitLabel(additionalBridgesLoop); + methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); + methodVisitor.visitInsn(Opcodes.ICONST_0); + methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/invoke/MethodType"); + methodVisitor.visitVarInsn(Opcodes.ASTORE, 7); + methodVisitor.visitLabel(additionalBridgesExit); + methodVisitor.visitFrame(Opcodes.F_APPEND, 1, new Object[]{"[Ljava/lang/invoke/MethodType;"}, 0, null); + methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "sun/misc/Unsafe", "getUnsafe", "()Lsun/misc/Unsafe;", false); + methodVisitor.visitVarInsn(Opcodes.ASTORE, 8); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 8); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); + methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "lookupClass", "()Ljava/lang/Class;", false); + methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/ClassLoader", "getSystemClassLoader", "()Ljava/lang/ClassLoader;", false); + methodVisitor.visitLdcInsn("net.bytebuddy.agent.builder.LambdaFactory"); + methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/ClassLoader", "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;", false); + methodVisitor.visitLdcInsn("make"); + methodVisitor.visitIntInsn(Opcodes.BIPUSH, 9); + methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Class"); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitInsn(Opcodes.ICONST_0); + methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/Object;")); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitInsn(Opcodes.ICONST_1); + methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/String;")); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitInsn(Opcodes.ICONST_2); + methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/Object;")); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitInsn(Opcodes.ICONST_3); + methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/Object;")); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitInsn(Opcodes.ICONST_4); + methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/Object;")); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitInsn(Opcodes.ICONST_5); + methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/Object;")); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitIntInsn(Opcodes.BIPUSH, 6); + methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Boolean", "TYPE", "Ljava/lang/Class;"); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitIntInsn(Opcodes.BIPUSH, 7); + methodVisitor.visitLdcInsn(Type.getType("Ljava/util/List;")); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitIntInsn(Opcodes.BIPUSH, 8); + methodVisitor.visitLdcInsn(Type.getType("Ljava/util/List;")); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "getDeclaredMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", false); + methodVisitor.visitInsn(Opcodes.ACONST_NULL); + methodVisitor.visitIntInsn(Opcodes.BIPUSH, 9); + methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitInsn(Opcodes.ICONST_0); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitInsn(Opcodes.ICONST_1); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 1); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitInsn(Opcodes.ICONST_2); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 2); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitInsn(Opcodes.ICONST_3); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 3); + methodVisitor.visitInsn(Opcodes.ICONST_0); + methodVisitor.visitInsn(Opcodes.AALOAD); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitInsn(Opcodes.ICONST_4); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 3); + methodVisitor.visitInsn(Opcodes.ICONST_1); + methodVisitor.visitInsn(Opcodes.AALOAD); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitInsn(Opcodes.ICONST_5); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 3); + methodVisitor.visitInsn(Opcodes.ICONST_2); + methodVisitor.visitInsn(Opcodes.AALOAD); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitIntInsn(Opcodes.BIPUSH, 6); + methodVisitor.visitVarInsn(Opcodes.ILOAD, 4); + methodVisitor.visitInsn(Opcodes.ICONST_1); + methodVisitor.visitInsn(Opcodes.IAND); + Label callSiteConditional = new Label(); + methodVisitor.visitJumpInsn(Opcodes.IFEQ, callSiteConditional); + methodVisitor.visitInsn(Opcodes.ICONST_1); + Label callSiteAlternative = new Label(); + methodVisitor.visitJumpInsn(Opcodes.GOTO, callSiteAlternative); + methodVisitor.visitLabel(callSiteConditional); + methodVisitor.visitFrame(Opcodes.F_FULL, 9, new Object[]{"java/lang/invoke/MethodHandles$Lookup", "java/lang/String", "java/lang/invoke/MethodType", "[Ljava/lang/Object;", Opcodes.INTEGER, Opcodes.INTEGER, "[Ljava/lang/Class;", "[Ljava/lang/invoke/MethodType;", "sun/misc/Unsafe"}, 7, new Object[]{"sun/misc/Unsafe", "java/lang/Class", "java/lang/reflect/Method", Opcodes.NULL, "[Ljava/lang/Object;", "[Ljava/lang/Object;", Opcodes.INTEGER}); + methodVisitor.visitInsn(Opcodes.ICONST_0); + methodVisitor.visitLabel(callSiteAlternative); + methodVisitor.visitFrame(Opcodes.F_FULL, 9, new Object[]{"java/lang/invoke/MethodHandles$Lookup", "java/lang/String", "java/lang/invoke/MethodType", "[Ljava/lang/Object;", Opcodes.INTEGER, Opcodes.INTEGER, "[Ljava/lang/Class;", "[Ljava/lang/invoke/MethodType;", "sun/misc/Unsafe"}, 8, new Object[]{"sun/misc/Unsafe", "java/lang/Class", "java/lang/reflect/Method", Opcodes.NULL, "[Ljava/lang/Object;", "[Ljava/lang/Object;", Opcodes.INTEGER, Opcodes.INTEGER}); + methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitIntInsn(Opcodes.BIPUSH, 7); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 6); + methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Arrays", "asList", "([Ljava/lang/Object;)Ljava/util/List;", false); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitIntInsn(Opcodes.BIPUSH, 8); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 7); + methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Arrays", "asList", "([Ljava/lang/Object;)Ljava/util/List;", false); + methodVisitor.visitInsn(Opcodes.AASTORE); + methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/reflect/Method", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", false); + methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, "[B"); + methodVisitor.visitInsn(Opcodes.ACONST_NULL); + methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "sun/misc/Unsafe", "defineAnonymousClass", "(Ljava/lang/Class;[B[Ljava/lang/Object;)Ljava/lang/Class;", false); + methodVisitor.visitVarInsn(Opcodes.ASTORE, 9); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 8); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 9); + methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "sun/misc/Unsafe", "ensureClassInitialized", "(Ljava/lang/Class;)V", false); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 2); + methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/invoke/MethodType", "parameterCount", "()I", false); + Label callSiteJump = new Label(); + methodVisitor.visitJumpInsn(Opcodes.IFNE, callSiteJump); + methodVisitor.visitTypeInsn(Opcodes.NEW, "java/lang/invoke/ConstantCallSite"); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 2); + methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/invoke/MethodType", "returnType", "()Ljava/lang/Class;", false); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 9); + methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;", false); + methodVisitor.visitInsn(Opcodes.ICONST_0); + methodVisitor.visitInsn(Opcodes.AALOAD); + methodVisitor.visitInsn(Opcodes.ICONST_0); + methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); + methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/reflect/Constructor", "newInstance", "([Ljava/lang/Object;)Ljava/lang/Object;", false); + methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/invoke/MethodHandles", "constant", "(Ljava/lang/Class;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;", false); + methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/invoke/ConstantCallSite", "", "(Ljava/lang/invoke/MethodHandle;)V", false); + Label callSiteExit = new Label(); + methodVisitor.visitJumpInsn(Opcodes.GOTO, callSiteExit); + methodVisitor.visitLabel(callSiteJump); + methodVisitor.visitFrame(Opcodes.F_APPEND, 1, new Object[]{"java/lang/Class"}, 0, null); + methodVisitor.visitTypeInsn(Opcodes.NEW, "java/lang/invoke/ConstantCallSite"); + methodVisitor.visitInsn(Opcodes.DUP); + methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/invoke/MethodHandles$Lookup", "IMPL_LOOKUP", "Ljava/lang/invoke/MethodHandles$Lookup;"); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 9); + methodVisitor.visitLdcInsn("get$Lambda"); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 2); + methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "findStatic", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", false); + methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/invoke/ConstantCallSite", "", "(Ljava/lang/invoke/MethodHandle;)V", false); + methodVisitor.visitLabel(callSiteExit); + methodVisitor.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[]{"java/lang/invoke/CallSite"}); + methodVisitor.visitInsn(Opcodes.ARETURN); + methodVisitor.visitMaxs(9, 10); + methodVisitor.visitEnd(); + return IGNORE_ORIGINAL; + } - @Override - public int hashCode() { - int result = targetMethod.hashCode(); - result = 31 * result + declaredFields.hashCode(); - result = 31 * result + specializedLambdaMethod.hashCode(); - return result; - } + @Override + public String toString() { + return "AgentBuilder.LambdaInstrumentationStrategy.AlternativeMetaFactoryRedirection." + name(); + } + } + } - @Override - public String toString() { - return "AgentBuilder.Default.LambdaInstrumentationStrategy.LambdaInstanceFactory.LambdaMethodImplementation.Appender{" + - "targetMethod=" + targetMethod + - ", specializedLambdaMethod=" + specializedLambdaMethod + - ", declaredFields=" + declaredFields + - '}'; - } - } - } + /** + * The default implementation of an {@link net.bytebuddy.agent.builder.AgentBuilder}. + */ + class Default implements AgentBuilder { - /** - * Implements the {@code writeReplace} method for serializable lambda expressions. - */ - protected static class SerializationImplementation implements Implementation { + /** + * The name of the Byte Buddy {@code net.bytebuddy.agent.Installer} class. + */ + private static final String INSTALLER_TYPE = "net.bytebuddy.agent.Installer"; - /** - * The lambda expression's declaring type. - */ - private final TypeDescription targetType; + /** + * The name of the {@code net.bytebuddy.agent.Installer} field containing an installed {@link Instrumentation}. + */ + private static final String INSTRUMENTATION_FIELD = "instrumentation"; - /** - * The lambda expression's functional type. - */ - private final TypeDescription lambdaType; + /** + * Indicator for access to a static member via reflection to make the code more readable. + */ + private static final Object STATIC_FIELD = null; - /** - * The lambda expression's functional method name. - */ - private final String lambdaMethodName; + /** + * The value that is to be returned from a {@link java.lang.instrument.ClassFileTransformer} to indicate + * that no class file transformation is to be applied. + */ + private static final byte[] NO_TRANSFORMATION = null; - /** - * The method type of the lambda expression's functional method. - */ - private final JavaInstance.MethodType lambdaMethod; + /** + * The {@link net.bytebuddy.ByteBuddy} instance to be used. + */ + private final ByteBuddy byteBuddy; - /** - * A handle that references the lambda expressions invocation target. - */ - private final JavaInstance.MethodHandle targetMethod; + /** + * The binary locator to use. + */ + private final BinaryLocator binaryLocator; - /** - * The specialized method type of the lambda expression's functional method. - */ - private final JavaInstance.MethodType specializedMethod; + /** + * The definition handler to use. + */ + private final TypeStrategy typeStrategy; - /** - * Creates a new implementation for a serializable's lambda expression's {@code writeReplace} method. - * - * @param targetType The lambda expression's declaring type. - * @param lambdaType The lambda expression's functional type. - * @param lambdaMethodName The lambda expression's functional method name. - * @param lambdaMethod The method type of the lambda expression's functional method. - * @param targetMethod A handle that references the lambda expressions invocation target. - * @param specializedMethod The specialized method type of the lambda expression's functional method. - */ - protected SerializationImplementation(TypeDescription targetType, - TypeDescription lambdaType, - String lambdaMethodName, - JavaInstance.MethodType lambdaMethod, - JavaInstance.MethodHandle targetMethod, - JavaInstance.MethodType specializedMethod) { - this.targetType = targetType; - this.lambdaType = lambdaType; - this.lambdaMethodName = lambdaMethodName; - this.lambdaMethod = lambdaMethod; - this.targetMethod = targetMethod; - this.specializedMethod = specializedMethod; - } + /** + * The listener to notify on transformations. + */ + private final Listener listener; - @Override - public ByteCodeAppender appender(Target implementationTarget) { - TypeDescription serializedLambda; - try { - serializedLambda = new TypeDescription.ForLoadedType(Class.forName("java.lang.invoke.SerializedLambda")); - } catch (ClassNotFoundException exception) { - throw new IllegalStateException("Cannot find class for lambda serialization", exception); - } - List lambdaArguments = new ArrayList(implementationTarget.getInstrumentedType().getDeclaredFields().size()); - for (FieldDescription.InDefinedShape fieldDescription : implementationTarget.getInstrumentedType().getDeclaredFields()) { - lambdaArguments.add(new StackManipulation.Compound(MethodVariableAccess.REFERENCE.loadOffset(0), - FieldAccess.forField(fieldDescription).getter(), - Assigner.DEFAULT.assign(fieldDescription.getType(), TypeDescription.Generic.OBJECT, Assigner.Typing.STATIC))); - } - return new ByteCodeAppender.Simple(new StackManipulation.Compound( - TypeCreation.of(serializedLambda), - Duplication.SINGLE, - ClassConstant.of(targetType), - new TextConstant(lambdaType.getInternalName()), - new TextConstant(lambdaMethodName), - new TextConstant(lambdaMethod.getDescriptor()), - IntegerConstant.forValue(targetMethod.getHandleType().getIdentifier()), - new TextConstant(targetMethod.getOwnerType().getInternalName()), - new TextConstant(targetMethod.getName()), - new TextConstant(targetMethod.getDescriptor()), - new TextConstant(specializedMethod.getDescriptor()), - ArrayFactory.forType(TypeDescription.Generic.OBJECT).withValues(lambdaArguments), - MethodInvocation.invoke(serializedLambda.getDeclaredMethods().filter(isConstructor()).getOnly()), - MethodReturn.REFERENCE - )); - } + /** + * The native method strategy to use. + */ + private final NativeMethodStrategy nativeMethodStrategy; - @Override - public InstrumentedType prepare(InstrumentedType instrumentedType) { - return instrumentedType; - } + /** + * The access control context to use for loading classes. + */ + private final AccessControlContext accessControlContext; - @Override - public boolean equals(Object other) { - if (this == other) return true; - if (other == null || getClass() != other.getClass()) return false; - SerializationImplementation that = (SerializationImplementation) other; - return targetType.equals(that.targetType) - && lambdaType.equals(that.lambdaType) - && lambdaMethodName.equals(that.lambdaMethodName) - && lambdaMethod.equals(that.lambdaMethod) - && targetMethod.equals(that.targetMethod) - && specializedMethod.equals(that.specializedMethod); - } + /** + * The initialization strategy to use for creating classes. + */ + private final InitializationStrategy initializationStrategy; - @Override - public int hashCode() { - int result = targetType.hashCode(); - result = 31 * result + lambdaType.hashCode(); - result = 31 * result + lambdaMethodName.hashCode(); - result = 31 * result + lambdaMethod.hashCode(); - result = 31 * result + targetMethod.hashCode(); - result = 31 * result + specializedMethod.hashCode(); - return result; - } + /** + * The redefinition strategy to apply. + */ + private final RedefinitionStrategy redefinitionStrategy; - @Override - public String toString() { - return "AgentBuilder.Default.LambdaInstrumentationStrategy.LambdaInstanceFactory.SerializationImplementation{" + - "targetType=" + targetType + - ", lambdaType=" + lambdaType + - ", lambdaMethodName='" + lambdaMethodName + '\'' + - ", lambdaMethod=" + lambdaMethod + - ", targetMethod=" + targetMethod + - ", specializedMethod=" + specializedMethod + - '}'; - } + /** + * The injection strategy for injecting classes into the bootstrap class loader. + */ + private final BootstrapInjectionStrategy bootstrapInjectionStrategy; + + /** + * A strategy to determine of the {@code LambdaMetfactory} should be instrumented to allow for the instrumentation + * of classes that represent lambda expressions. + */ + private final LambdaInstrumentationStrategy lambdaInstrumentationStrategy; + + /** + * The transformation object for handling type transformations. + */ + private final Transformation transformation; + + /** + * Creates a new default agent builder that uses a default {@link net.bytebuddy.ByteBuddy} instance for + * creating classes. + */ + public Default() { + this(new ByteBuddy()); + } + + /** + * Creates a new agent builder with default settings. + * + * @param byteBuddy The Byte Buddy instance to be used. + */ + public Default(ByteBuddy byteBuddy) { + this(byteBuddy, + BinaryLocator.Default.FAST, + TypeStrategy.Default.REBASE, + Listener.NoOp.INSTANCE, + NativeMethodStrategy.Disabled.INSTANCE, + AccessController.getContext(), + InitializationStrategy.SelfInjection.SPLIT, + RedefinitionStrategy.DISABLED, + BootstrapInjectionStrategy.Disabled.INSTANCE, + LambdaInstrumentationStrategy.DISABLED, + Transformation.Ignored.INSTANCE); + } + + /** + * Creates a new default agent builder. + * + * @param byteBuddy The Byte Buddy instance to be used. + * @param binaryLocator The binary locator to use. + * @param typeStrategy The definition handler to use. + * @param listener The listener to notify on transformations. + * @param nativeMethodStrategy The native method strategy to apply. + * @param accessControlContext The access control context to use for loading classes. + * @param initializationStrategy The initialization strategy to use for transformed types. + * @param redefinitionStrategy The redefinition strategy to apply. + * @param bootstrapInjectionStrategy The injection strategy for injecting classes into the bootstrap class loader. + * @param lambdaInstrumentationStrategy A strategy to determine of the {@code LambdaMetfactory} should be instrumented to allow for the + * instrumentation of classes that represent lambda expressions. + * @param transformation The transformation object for handling type transformations. + */ + protected Default(ByteBuddy byteBuddy, + BinaryLocator binaryLocator, + TypeStrategy typeStrategy, + Listener listener, + NativeMethodStrategy nativeMethodStrategy, + AccessControlContext accessControlContext, + InitializationStrategy initializationStrategy, + RedefinitionStrategy redefinitionStrategy, + BootstrapInjectionStrategy bootstrapInjectionStrategy, + LambdaInstrumentationStrategy lambdaInstrumentationStrategy, + Transformation transformation) { + this.byteBuddy = byteBuddy; + this.binaryLocator = binaryLocator; + this.typeStrategy = typeStrategy; + this.listener = listener; + this.nativeMethodStrategy = nativeMethodStrategy; + this.accessControlContext = accessControlContext; + this.initializationStrategy = initializationStrategy; + this.redefinitionStrategy = redefinitionStrategy; + this.bootstrapInjectionStrategy = bootstrapInjectionStrategy; + this.lambdaInstrumentationStrategy = lambdaInstrumentationStrategy; + this.transformation = transformation; + } + + /** + * Releases the supplied class file transformer when it was built with {@link AgentBuilder#with(LambdaInstrumentationStrategy)} enabled. + * Subsequently, the class file transformer is no longer applied when a class that represents a lambda expression is created. + * + * @param classFileTransformer The class file transformer to release. + * @param instrumentation The instrumentation instance that is used to potentially rollback the instrumentation of the {@code LambdaMetafactory}. + */ + public static void releaseLambdaTransformer(ClassFileTransformer classFileTransformer, Instrumentation instrumentation) { + if (LambdaFactory.release(classFileTransformer)) { + try { + ClassReloadingStrategy.of(instrumentation).reset(Class.forName("java.lang.invoke.LambdaMetafactory")); + } catch (Exception exception) { + throw new IllegalStateException("Could not release lambda transformer", exception); } + } + } - /** - * Implements an explicit bridge method for a lambda expression. - */ - protected static class BridgeMethodImplementation implements Implementation { + @Override + public Identified type(RawMatcher matcher) { + return new Matched(matcher, Transformer.NoOp.INSTANCE); + } - /** - * The name of the lambda expression's functional method. - */ - private final String lambdaMethodName; + @Override + public Identified type(ElementMatcher typeMatcher) { + return type(typeMatcher, any()); + } - /** - * The actual type of the lambda expression's functional method. - */ - private final JavaInstance.MethodType lambdaMethod; + @Override + public Identified type(ElementMatcher typeMatcher, ElementMatcher classLoaderMatcher) { + return type(new RawMatcher.ForElementMatcherPair(typeMatcher, classLoaderMatcher)); + } - /** - * Creates a new bridge method implementation for a lambda expression. - * - * @param lambdaMethodName The name of the lambda expression's functional method. - * @param lambdaMethod The actual type of the lambda expression's functional method. - */ - protected BridgeMethodImplementation(String lambdaMethodName, JavaInstance.MethodType lambdaMethod) { - this.lambdaMethodName = lambdaMethodName; - this.lambdaMethod = lambdaMethod; - } + @Override + public AgentBuilder with(ByteBuddy byteBuddy) { + return new Default(byteBuddy, + binaryLocator, + typeStrategy, + listener, + nativeMethodStrategy, + accessControlContext, + initializationStrategy, + redefinitionStrategy, + bootstrapInjectionStrategy, + lambdaInstrumentationStrategy, + transformation); + } - @Override - public ByteCodeAppender appender(Target implementationTarget) { - return new Appender(implementationTarget.invokeSuper(new MethodDescription.SignatureToken(lambdaMethodName, - lambdaMethod.getReturnType(), - lambdaMethod.getParameterTypes()))); - } + @Override + public AgentBuilder with(Listener listener) { + return new Default(byteBuddy, + binaryLocator, + typeStrategy, + new Listener.Compound(this.listener, listener), + nativeMethodStrategy, + accessControlContext, + initializationStrategy, + redefinitionStrategy, + bootstrapInjectionStrategy, + lambdaInstrumentationStrategy, + transformation); + } + + @Override + public AgentBuilder with(TypeStrategy typeStrategy) { + return new Default(byteBuddy, + binaryLocator, + typeStrategy, + listener, + nativeMethodStrategy, + accessControlContext, + initializationStrategy, + redefinitionStrategy, + bootstrapInjectionStrategy, + lambdaInstrumentationStrategy, + transformation); + } + + @Override + public AgentBuilder with(BinaryLocator binaryLocator) { + return new Default(byteBuddy, + binaryLocator, + typeStrategy, + listener, + nativeMethodStrategy, + accessControlContext, + initializationStrategy, + redefinitionStrategy, + bootstrapInjectionStrategy, + lambdaInstrumentationStrategy, + transformation); + } + + @Override + public AgentBuilder enableNativeMethodPrefix(String prefix) { + return new Default(byteBuddy, + binaryLocator, + typeStrategy, + listener, + NativeMethodStrategy.ForPrefix.of(prefix), + accessControlContext, + initializationStrategy, + redefinitionStrategy, + bootstrapInjectionStrategy, + lambdaInstrumentationStrategy, + transformation); + } + + @Override + public AgentBuilder disableNativeMethodPrefix() { + return new Default(byteBuddy, + binaryLocator, + typeStrategy, + listener, + NativeMethodStrategy.Disabled.INSTANCE, + accessControlContext, + initializationStrategy, + redefinitionStrategy, + bootstrapInjectionStrategy, + lambdaInstrumentationStrategy, + transformation); + } + + @Override + public AgentBuilder with(AccessControlContext accessControlContext) { + return new Default(byteBuddy, + binaryLocator, + typeStrategy, + listener, + nativeMethodStrategy, + accessControlContext, + initializationStrategy, + redefinitionStrategy, + bootstrapInjectionStrategy, + lambdaInstrumentationStrategy, + transformation); + } + + @Override + public AgentBuilder with(RedefinitionStrategy redefinitionStrategy) { + return new Default(byteBuddy, + binaryLocator, + typeStrategy, + listener, + nativeMethodStrategy, + accessControlContext, + initializationStrategy, + redefinitionStrategy, + bootstrapInjectionStrategy, + lambdaInstrumentationStrategy, + transformation); + } + + @Override + public AgentBuilder with(InitializationStrategy initializationStrategy) { + return new Default(byteBuddy, + binaryLocator, + typeStrategy, + listener, + nativeMethodStrategy, + accessControlContext, + initializationStrategy, + redefinitionStrategy, + bootstrapInjectionStrategy, + lambdaInstrumentationStrategy, + transformation); + } + + @Override + public AgentBuilder enableBootstrapInjection(File folder, Instrumentation instrumentation) { + return new Default(byteBuddy, + binaryLocator, + typeStrategy, + listener, + nativeMethodStrategy, + accessControlContext, + initializationStrategy, + redefinitionStrategy, + new BootstrapInjectionStrategy.Enabled(folder, instrumentation), + lambdaInstrumentationStrategy, + transformation); + } - @Override - public InstrumentedType prepare(InstrumentedType instrumentedType) { - return instrumentedType; - } + @Override + public AgentBuilder disableBootstrapInjection() { + return new Default(byteBuddy, + binaryLocator, + typeStrategy, + listener, + nativeMethodStrategy, + accessControlContext, + initializationStrategy, + redefinitionStrategy, + BootstrapInjectionStrategy.Disabled.INSTANCE, + lambdaInstrumentationStrategy, + transformation); + } - @Override - public boolean equals(Object other) { - if (this == other) return true; - if (other == null || getClass() != other.getClass()) return false; - BridgeMethodImplementation that = (BridgeMethodImplementation) other; - return lambdaMethodName.equals(that.lambdaMethodName) && lambdaMethod.equals(that.lambdaMethod); - } + @Override + public AgentBuilder with(LambdaInstrumentationStrategy lambdaInstrumentationStrategy) { + return new Default(byteBuddy, + binaryLocator, + typeStrategy, + listener, + nativeMethodStrategy, + accessControlContext, + initializationStrategy, + redefinitionStrategy, + bootstrapInjectionStrategy, + lambdaInstrumentationStrategy, + transformation); + } - @Override - public int hashCode() { - int result = lambdaMethodName.hashCode(); - result = 31 * result + lambdaMethod.hashCode(); - return result; - } + @Override + public ClassFileTransformer makeRaw() { + return new ExecutingTransformer(byteBuddy, + binaryLocator, + typeStrategy, + listener, + nativeMethodStrategy, + accessControlContext, + initializationStrategy, + bootstrapInjectionStrategy, + transformation); + } - @Override - public String toString() { - return "AgentBuilder.Default.LambdaInstrumentationStrategy.LambdaInstanceFactory.BridgeMethodImplementation{" + - "lambdaMethodName='" + lambdaMethodName + '\'' + - ", lambdaMethod=" + lambdaMethod + - '}'; + @Override + public ClassFileTransformer installOn(Instrumentation instrumentation) { + ClassFileTransformer classFileTransformer = makeRaw(); + instrumentation.addTransformer(classFileTransformer, redefinitionStrategy.isRetransforming(instrumentation)); + if (nativeMethodStrategy.isEnabled(instrumentation)) { + instrumentation.setNativeMethodPrefix(classFileTransformer, nativeMethodStrategy.getPrefix()); + } + lambdaInstrumentationStrategy.apply(byteBuddy, instrumentation, classFileTransformer); + if (redefinitionStrategy.isEnabled()) { + RedefinitionStrategy.Collector collector = redefinitionStrategy.makeCollector(transformation); + for (Class type : instrumentation.getAllLoadedClasses()) { + TypeDescription typeDescription = new TypeDescription.ForLoadedType(type); + try { + if (!instrumentation.isModifiableClass(type) || !collector.consider(type)) { + try { + try { + listener.onIgnored(typeDescription); + } finally { + listener.onComplete(typeDescription.getName()); + } + } catch (Throwable ignored) { + // Ignore exceptions that are thrown by listeners to mimic the behavior of a transformation. + } + } + } catch (Throwable throwable) { + try { + try { + listener.onError(typeDescription.getName(), throwable); + } finally { + listener.onComplete(typeDescription.getName()); + } + } catch (Throwable ignored) { + // Ignore exceptions that are thrown by listeners to mimic the behavior of a transformation. + } } + } + try { + collector.apply(instrumentation, + byteBuddy, + binaryLocator, + typeStrategy, + listener, + nativeMethodStrategy, + accessControlContext, + initializationStrategy, + bootstrapInjectionStrategy); + } catch (UnmodifiableClassException exception) { + throw new IllegalStateException("Cannot modify at least one class: " + collector, exception); + } catch (ClassNotFoundException exception) { + throw new IllegalStateException("Cannot find at least one class class: " + collector, exception); + } + } + return classFileTransformer; + } - /** - * An appender for implementing a bridge method for a lambda expression. - */ - protected static class Appender implements ByteCodeAppender { - - /** - * The invocation of the bridge's target method. - */ - private final SpecialMethodInvocation bridgeTargetInvocation; + @Override + public ClassFileTransformer installOnByteBuddyAgent() { + try { + Instrumentation instrumentation = (Instrumentation) ClassLoader.getSystemClassLoader() + .loadClass(INSTALLER_TYPE) + .getDeclaredField(INSTRUMENTATION_FIELD) + .get(STATIC_FIELD); + if (instrumentation == null) { + throw new IllegalStateException("The Byte Buddy agent is not installed"); + } + return installOn(instrumentation); + } catch (RuntimeException exception) { + throw exception; + } catch (Exception exception) { + throw new IllegalStateException("The Byte Buddy agent is not installed or not accessible", exception); + } + } - /** - * Creates a new appender for invoking a lambda expression's bridge method target. - * - * @param bridgeTargetInvocation The invocation of the bridge's target method. - */ - protected Appender(SpecialMethodInvocation bridgeTargetInvocation) { - this.bridgeTargetInvocation = bridgeTargetInvocation; - } + @Override + public boolean equals(Object other) { + if (this == other) return true; + if (other == null || getClass() != other.getClass()) return false; + Default aDefault = (Default) other; + return binaryLocator.equals(aDefault.binaryLocator) + && byteBuddy.equals(aDefault.byteBuddy) + && listener.equals(aDefault.listener) + && nativeMethodStrategy.equals(aDefault.nativeMethodStrategy) + && typeStrategy.equals(aDefault.typeStrategy) + && accessControlContext.equals(aDefault.accessControlContext) + && initializationStrategy == aDefault.initializationStrategy + && redefinitionStrategy == aDefault.redefinitionStrategy + && bootstrapInjectionStrategy.equals(aDefault.bootstrapInjectionStrategy) + && lambdaInstrumentationStrategy.equals(aDefault.lambdaInstrumentationStrategy) + && transformation.equals(aDefault.transformation); + } - @Override - public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) { - return new Compound(new Simple( - MethodVariableAccess.allArgumentsOf(instrumentedMethod) - .asBridgeOf(bridgeTargetInvocation.getMethodDescription()) - .prependThisReference(), - bridgeTargetInvocation, - bridgeTargetInvocation.getMethodDescription().getReturnType().asErasure().isAssignableTo(instrumentedMethod.getReturnType().asErasure()) - ? StackManipulation.Trivial.INSTANCE - : TypeCasting.to(instrumentedMethod.getReceiverType().asErasure()), - MethodReturn.returning(instrumentedMethod.getReturnType().asErasure()) - - )).apply(methodVisitor, implementationContext, instrumentedMethod); - } + @Override + public int hashCode() { + int result = byteBuddy.hashCode(); + result = 31 * result + binaryLocator.hashCode(); + result = 31 * result + listener.hashCode(); + result = 31 * result + typeStrategy.hashCode(); + result = 31 * result + nativeMethodStrategy.hashCode(); + result = 31 * result + accessControlContext.hashCode(); + result = 31 * result + initializationStrategy.hashCode(); + result = 31 * result + redefinitionStrategy.hashCode(); + result = 31 * result + bootstrapInjectionStrategy.hashCode(); + result = 31 * result + lambdaInstrumentationStrategy.hashCode(); + result = 31 * result + transformation.hashCode(); + return result; + } - @Override - public boolean equals(Object other) { - return this == other || !(other == null || getClass() != other.getClass()) - && bridgeTargetInvocation.equals(((Appender) other).bridgeTargetInvocation); - } + @Override + public String toString() { + return "AgentBuilder.Default{" + + "byteBuddy=" + byteBuddy + + ", binaryLocator=" + binaryLocator + + ", typeStrategy=" + typeStrategy + + ", listener=" + listener + + ", nativeMethodStrategy=" + nativeMethodStrategy + + ", accessControlContext=" + accessControlContext + + ", initializationStrategy=" + initializationStrategy + + ", redefinitionStrategy=" + redefinitionStrategy + + ", bootstrapInjectionStrategy=" + bootstrapInjectionStrategy + + ", lambdaInstrumentationStrategy=" + lambdaInstrumentationStrategy + + ", transformation=" + transformation + + '}'; + } - @Override - public int hashCode() { - return bridgeTargetInvocation.hashCode(); - } + /** + * An injection strategy for injecting classes into the bootstrap class loader. + */ + protected interface BootstrapInjectionStrategy { - @Override - public String toString() { - return "AgentBuilder.Default.LambdaInstrumentationStrategy.LambdaInstanceFactory.BridgeMethodImplementation.Appender{" + - "bridgeTargetInvocation=" + bridgeTargetInvocation + - '}'; - } - } - } - } + /** + * Creates an injector for the bootstrap class loader. + * + * @param protectionDomain The protection domain to be used. + * @return A class injector for the bootstrap class loader. + */ + ClassInjector make(ProtectionDomain protectionDomain); /** - * Implements the regular lambda meta factory. The implementation represents the following code: - *
-             * public static CallSite metafactory(MethodHandles.Lookup caller,
-             *     String invokedName,
-             *     MethodType invokedType,
-             *     MethodType samMethodType,
-             *     MethodHandle implMethod,
-             *     MethodType instantiatedMethodType) throws Exception {
-             *   Unsafe unsafe = Unsafe.getUnsafe();
-             *   {@code Class} lambdaClass = unsafe.defineAnonymousClass(caller.lookupClass(),
-             *       (byte[]) ClassLoader.getSystemClassLoader().loadClass("net.bytebuddy.agent.builder.LambdaFactory").getDeclaredMethod("make",
-             *           Object.class,
-             *           String.class,
-             *           Object.class,
-             *           Object.class,
-             *           Object.class,
-             *           Object.class,
-             *           boolean.class,
-             *           List.class,
-             *           List.class).invoke(null,
-             *               caller,
-             *               invokedName,
-             *               invokedType,
-             *               samMethodType,
-             *               implMethod,
-             *               instantiatedMethodType,
-             *               false,
-             *               Collections.emptyList(),
-             *               Collections.emptyList()),
-             *       null);
-             *   unsafe.ensureClassInitialized(lambdaClass);
-             *   return invokedType.parameterCount() == 0
-             *     ? new ConstantCallSite(MethodHandles.constant(invokedType.returnType(), lambdaClass.getDeclaredConstructors()[0].newInstance()))
-             *     : new ConstantCallSite(MethodHandles.Lookup.IMPL_LOOKUP.findStatic(lambdaClass, "get$Lambda", invokedType));
-             * 
+ * A disabled bootstrap injection strategy. */ - protected enum MetaFactoryRedirection implements AsmVisitorWrapper.ForDeclaredMethods.MethodVisitorWrapper { + enum Disabled implements BootstrapInjectionStrategy { /** * The singleton instance. @@ -3368,442 +3723,68 @@ protected enum MetaFactoryRedirection implements AsmVisitorWrapper.ForDeclaredMe INSTANCE; @Override - public MethodVisitor wrap(TypeDescription instrumentedType, MethodDescription.InDefinedShape methodDescription, MethodVisitor methodVisitor) { - methodVisitor.visitCode(); - methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "sun/misc/Unsafe", "getUnsafe", "()Lsun/misc/Unsafe;", false); - methodVisitor.visitVarInsn(Opcodes.ASTORE, 6); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 6); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "lookupClass", "()Ljava/lang/Class;", false); - methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/ClassLoader", "getSystemClassLoader", "()Ljava/lang/ClassLoader;", false); - methodVisitor.visitLdcInsn("net.bytebuddy.agent.builder.LambdaFactory"); - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/ClassLoader", "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;", false); - methodVisitor.visitLdcInsn("make"); - methodVisitor.visitIntInsn(Opcodes.BIPUSH, 9); - methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Class"); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitInsn(Opcodes.ICONST_0); - methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/Object;")); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitInsn(Opcodes.ICONST_1); - methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/String;")); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitInsn(Opcodes.ICONST_2); - methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/Object;")); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitInsn(Opcodes.ICONST_3); - methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/Object;")); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitInsn(Opcodes.ICONST_4); - methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/Object;")); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitInsn(Opcodes.ICONST_5); - methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/Object;")); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitIntInsn(Opcodes.BIPUSH, 6); - methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Boolean", "TYPE", "Ljava/lang/Class;"); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitIntInsn(Opcodes.BIPUSH, 7); - methodVisitor.visitLdcInsn(Type.getType("Ljava/util/List;")); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitIntInsn(Opcodes.BIPUSH, 8); - methodVisitor.visitLdcInsn(Type.getType("Ljava/util/List;")); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "getDeclaredMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", false); - methodVisitor.visitInsn(Opcodes.ACONST_NULL); - methodVisitor.visitIntInsn(Opcodes.BIPUSH, 9); - methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitInsn(Opcodes.ICONST_0); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitInsn(Opcodes.ICONST_1); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 1); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitInsn(Opcodes.ICONST_2); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 2); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitInsn(Opcodes.ICONST_3); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 3); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitInsn(Opcodes.ICONST_4); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 4); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitInsn(Opcodes.ICONST_5); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 5); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitIntInsn(Opcodes.BIPUSH, 6); - methodVisitor.visitInsn(Opcodes.ICONST_0); - methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitIntInsn(Opcodes.BIPUSH, 7); - methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Collections", "emptyList", "()Ljava/util/List;", false); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitIntInsn(Opcodes.BIPUSH, 8); - methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Collections", "emptyList", "()Ljava/util/List;", false); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/reflect/Method", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", false); - methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, "[B"); - methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, "[B"); - methodVisitor.visitInsn(Opcodes.ACONST_NULL); - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "sun/misc/Unsafe", "defineAnonymousClass", "(Ljava/lang/Class;[B[Ljava/lang/Object;)Ljava/lang/Class;", false); - methodVisitor.visitVarInsn(Opcodes.ASTORE, 7); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 6); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 7); - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "sun/misc/Unsafe", "ensureClassInitialized", "(Ljava/lang/Class;)V", false); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 2); - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/invoke/MethodType", "parameterCount", "()I", false); - Label conditionalDefault = new Label(); - methodVisitor.visitJumpInsn(Opcodes.IFNE, conditionalDefault); - methodVisitor.visitTypeInsn(Opcodes.NEW, "java/lang/invoke/ConstantCallSite"); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 2); - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/invoke/MethodType", "returnType", "()Ljava/lang/Class;", false); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 7); - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;", false); - methodVisitor.visitInsn(Opcodes.ICONST_0); - methodVisitor.visitInsn(Opcodes.AALOAD); - methodVisitor.visitInsn(Opcodes.ICONST_0); - methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/reflect/Constructor", "newInstance", "([Ljava/lang/Object;)Ljava/lang/Object;", false); - methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/invoke/MethodHandles", "constant", "(Ljava/lang/Class;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;", false); - methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/invoke/ConstantCallSite", "", "(Ljava/lang/invoke/MethodHandle;)V", false); - Label conditionalAlternative = new Label(); - methodVisitor.visitJumpInsn(Opcodes.GOTO, conditionalAlternative); - methodVisitor.visitLabel(conditionalDefault); - methodVisitor.visitFrame(Opcodes.F_APPEND, 2, new Object[]{"sun/misc/Unsafe", "java/lang/Class"}, 0, null); - methodVisitor.visitTypeInsn(Opcodes.NEW, "java/lang/invoke/ConstantCallSite"); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/invoke/MethodHandles$Lookup", "IMPL_LOOKUP", "Ljava/lang/invoke/MethodHandles$Lookup;"); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 7); - methodVisitor.visitLdcInsn("get$Lambda"); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 2); - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "findStatic", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", false); - methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/invoke/ConstantCallSite", "", "(Ljava/lang/invoke/MethodHandle;)V", false); - methodVisitor.visitLabel(conditionalAlternative); - methodVisitor.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[]{"java/lang/invoke/CallSite"}); - methodVisitor.visitInsn(Opcodes.ARETURN); - methodVisitor.visitMaxs(8, 8); - methodVisitor.visitEnd(); - return IGNORE_ORIGINAL; + public ClassInjector make(ProtectionDomain protectionDomain) { + throw new IllegalStateException("Injecting classes into the bootstrap class loader was not enabled"); } @Override public String toString() { - return "AgentBuilder.Default.LambdaInstrumentationStrategy.MetaFactoryRedirection." + name(); + return "AgentBuilder.Default.BootstrapInjectionStrategy.Disabled." + name(); } } /** - * Implements the alternative lambda meta factory. The implementation represents the following code: - *
-             * public static CallSite altMetafactory(MethodHandles.Lookup caller,
-             *     String invokedName,
-             *     MethodType invokedType,
-             *     Object... args) throws Exception {
-             *   int flags = (Integer) args[3];
-             *   int argIndex = 4;
-             *   {@code Class[]} markerInterface;
-             *   if ((flags & FLAG_MARKERS) != 0) {
-             *     int markerCount = (Integer) args[argIndex++];
-             *     markerInterface = new {@code Class}[markerCount];
-             *     System.arraycopy(args, argIndex, markerInterface, 0, markerCount);
-             *     argIndex += markerCount;
-             *   } else {
-             *     markerInterface = new {@code Class}[0];
-             *   }
-             *   MethodType[] additionalBridge;
-             *   if ((flags & FLAG_BRIDGES) != 0) {
-             *     int bridgeCount = (Integer) args[argIndex++];
-             *     additionalBridge = new MethodType[bridgeCount];
-             *     System.arraycopy(args, argIndex, additionalBridge, 0, bridgeCount);
-             *     // argIndex += bridgeCount;
-             *   } else {
-             *     additionalBridge = new MethodType[0];
-             *   }
-             *   Unsafe unsafe = Unsafe.getUnsafe();
-             *   Class lambdaClass = unsafe.defineAnonymousClass(caller.lookupClass(),
-             *       (byte[]) ClassLoader.getSystemClassLoader().loadClass("net.bytebuddy.agent.builder.LambdaFactory").getDeclaredMethod("make",
-             *           Object.class,
-             *           String.class,
-             *           Object.class,
-             *           Object.class,
-             *           Object.class,
-             *           Object.class,
-             *           boolean.class,
-             *           List.class,
-             *           List.class).invoke(null,
-             *               caller,
-             *               invokedName,
-             *               invokedType,
-             *               args[0],
-             *               args[1],
-             *               args[2],
-             *               (flags & FLAG_SERIALIZABLE) != 0,
-             *               Arrays.asList(markerInterface),
-             *               Arrays.asList(additionalBridge)),
-             *       null);
-             *   unsafe.ensureClassInitialized(lambdaClass);
-             *   return invokedType.parameterCount() == 0
-             *     ? new ConstantCallSite(MethodHandles.constant(invokedType.returnType(), lambdaClass.getDeclaredConstructors()[0].newInstance()))
-             *     : new ConstantCallSite(MethodHandles.Lookup.IMPL_LOOKUP.findStatic(lambdaClass, "get$Lambda", invokedType));
-             * }
-             * 
+ * An enabled bootstrap injection strategy. */ - protected enum AlternativeMetaFactoryRedirection implements AsmVisitorWrapper.ForDeclaredMethods.MethodVisitorWrapper { + class Enabled implements BootstrapInjectionStrategy { /** - * The singleton instance. + * The folder in which jar files are to be saved. */ - INSTANCE; + private final File folder; + + /** + * The instrumentation to use for appending jar files. + */ + private final Instrumentation instrumentation; + + /** + * Creates a new enabled bootstrap class loader injection strategy. + * + * @param folder The folder in which jar files are to be saved. + * @param instrumentation The instrumentation to use for appending jar files. + */ + public Enabled(File folder, Instrumentation instrumentation) { + this.folder = folder; + this.instrumentation = instrumentation; + } + + @Override + public ClassInjector make(ProtectionDomain protectionDomain) { + return ClassInjector.UsingInstrumentation.of(folder, ClassInjector.UsingInstrumentation.Target.BOOTSTRAP, instrumentation); + } @Override - public MethodVisitor wrap(TypeDescription instrumentedType, MethodDescription.InDefinedShape methodDescription, MethodVisitor methodVisitor) { - methodVisitor.visitCode(); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 3); - methodVisitor.visitInsn(Opcodes.ICONST_3); - methodVisitor.visitInsn(Opcodes.AALOAD); - methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Integer"); - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false); - methodVisitor.visitVarInsn(Opcodes.ISTORE, 4); - methodVisitor.visitInsn(Opcodes.ICONST_4); - methodVisitor.visitVarInsn(Opcodes.ISTORE, 5); - methodVisitor.visitVarInsn(Opcodes.ILOAD, 4); - methodVisitor.visitInsn(Opcodes.ICONST_2); - methodVisitor.visitInsn(Opcodes.IAND); - Label markerInterfaceLoop = new Label(); - methodVisitor.visitJumpInsn(Opcodes.IFEQ, markerInterfaceLoop); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 3); - methodVisitor.visitVarInsn(Opcodes.ILOAD, 5); - methodVisitor.visitIincInsn(5, 1); - methodVisitor.visitInsn(Opcodes.AALOAD); - methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Integer"); - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false); - methodVisitor.visitVarInsn(Opcodes.ISTORE, 7); - methodVisitor.visitVarInsn(Opcodes.ILOAD, 7); - methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Class"); - methodVisitor.visitVarInsn(Opcodes.ASTORE, 6); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 3); - methodVisitor.visitVarInsn(Opcodes.ILOAD, 5); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 6); - methodVisitor.visitInsn(Opcodes.ICONST_0); - methodVisitor.visitVarInsn(Opcodes.ILOAD, 7); - methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "arraycopy", "(Ljava/lang/Object;ILjava/lang/Object;II)V", false); - methodVisitor.visitVarInsn(Opcodes.ILOAD, 5); - methodVisitor.visitVarInsn(Opcodes.ILOAD, 7); - methodVisitor.visitInsn(Opcodes.IADD); - methodVisitor.visitVarInsn(Opcodes.ISTORE, 5); - Label markerInterfaceExit = new Label(); - methodVisitor.visitJumpInsn(Opcodes.GOTO, markerInterfaceExit); - methodVisitor.visitLabel(markerInterfaceLoop); - methodVisitor.visitFrame(Opcodes.F_APPEND, 2, new Object[]{Opcodes.INTEGER, Opcodes.INTEGER}, 0, null); - methodVisitor.visitInsn(Opcodes.ICONST_0); - methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Class"); - methodVisitor.visitVarInsn(Opcodes.ASTORE, 6); - methodVisitor.visitLabel(markerInterfaceExit); - methodVisitor.visitFrame(Opcodes.F_APPEND, 1, new Object[]{"[Ljava/lang/Class;"}, 0, null); - methodVisitor.visitVarInsn(Opcodes.ILOAD, 4); - methodVisitor.visitInsn(Opcodes.ICONST_4); - methodVisitor.visitInsn(Opcodes.IAND); - Label additionalBridgesLoop = new Label(); - methodVisitor.visitJumpInsn(Opcodes.IFEQ, additionalBridgesLoop); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 3); - methodVisitor.visitVarInsn(Opcodes.ILOAD, 5); - methodVisitor.visitIincInsn(5, 1); - methodVisitor.visitInsn(Opcodes.AALOAD); - methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Integer"); - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false); - methodVisitor.visitVarInsn(Opcodes.ISTORE, 8); - methodVisitor.visitVarInsn(Opcodes.ILOAD, 8); - methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/invoke/MethodType"); - methodVisitor.visitVarInsn(Opcodes.ASTORE, 7); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 3); - methodVisitor.visitVarInsn(Opcodes.ILOAD, 5); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 7); - methodVisitor.visitInsn(Opcodes.ICONST_0); - methodVisitor.visitVarInsn(Opcodes.ILOAD, 8); - methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "arraycopy", "(Ljava/lang/Object;ILjava/lang/Object;II)V", false); - Label additionalBridgesExit = new Label(); - methodVisitor.visitJumpInsn(Opcodes.GOTO, additionalBridgesExit); - methodVisitor.visitLabel(additionalBridgesLoop); - methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); - methodVisitor.visitInsn(Opcodes.ICONST_0); - methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/invoke/MethodType"); - methodVisitor.visitVarInsn(Opcodes.ASTORE, 7); - methodVisitor.visitLabel(additionalBridgesExit); - methodVisitor.visitFrame(Opcodes.F_APPEND, 1, new Object[]{"[Ljava/lang/invoke/MethodType;"}, 0, null); - methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "sun/misc/Unsafe", "getUnsafe", "()Lsun/misc/Unsafe;", false); - methodVisitor.visitVarInsn(Opcodes.ASTORE, 8); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 8); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "lookupClass", "()Ljava/lang/Class;", false); - methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/ClassLoader", "getSystemClassLoader", "()Ljava/lang/ClassLoader;", false); - methodVisitor.visitLdcInsn("net.bytebuddy.agent.builder.LambdaFactory"); - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/ClassLoader", "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;", false); - methodVisitor.visitLdcInsn("make"); - methodVisitor.visitIntInsn(Opcodes.BIPUSH, 9); - methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Class"); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitInsn(Opcodes.ICONST_0); - methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/Object;")); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitInsn(Opcodes.ICONST_1); - methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/String;")); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitInsn(Opcodes.ICONST_2); - methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/Object;")); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitInsn(Opcodes.ICONST_3); - methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/Object;")); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitInsn(Opcodes.ICONST_4); - methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/Object;")); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitInsn(Opcodes.ICONST_5); - methodVisitor.visitLdcInsn(Type.getType("Ljava/lang/Object;")); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitIntInsn(Opcodes.BIPUSH, 6); - methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Boolean", "TYPE", "Ljava/lang/Class;"); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitIntInsn(Opcodes.BIPUSH, 7); - methodVisitor.visitLdcInsn(Type.getType("Ljava/util/List;")); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitIntInsn(Opcodes.BIPUSH, 8); - methodVisitor.visitLdcInsn(Type.getType("Ljava/util/List;")); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "getDeclaredMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", false); - methodVisitor.visitInsn(Opcodes.ACONST_NULL); - methodVisitor.visitIntInsn(Opcodes.BIPUSH, 9); - methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitInsn(Opcodes.ICONST_0); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitInsn(Opcodes.ICONST_1); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 1); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitInsn(Opcodes.ICONST_2); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 2); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitInsn(Opcodes.ICONST_3); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 3); - methodVisitor.visitInsn(Opcodes.ICONST_0); - methodVisitor.visitInsn(Opcodes.AALOAD); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitInsn(Opcodes.ICONST_4); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 3); - methodVisitor.visitInsn(Opcodes.ICONST_1); - methodVisitor.visitInsn(Opcodes.AALOAD); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitInsn(Opcodes.ICONST_5); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 3); - methodVisitor.visitInsn(Opcodes.ICONST_2); - methodVisitor.visitInsn(Opcodes.AALOAD); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitIntInsn(Opcodes.BIPUSH, 6); - methodVisitor.visitVarInsn(Opcodes.ILOAD, 4); - methodVisitor.visitInsn(Opcodes.ICONST_1); - methodVisitor.visitInsn(Opcodes.IAND); - Label callSiteConditional = new Label(); - methodVisitor.visitJumpInsn(Opcodes.IFEQ, callSiteConditional); - methodVisitor.visitInsn(Opcodes.ICONST_1); - Label callSiteAlternative = new Label(); - methodVisitor.visitJumpInsn(Opcodes.GOTO, callSiteAlternative); - methodVisitor.visitLabel(callSiteConditional); - methodVisitor.visitFrame(Opcodes.F_FULL, 9, new Object[]{"java/lang/invoke/MethodHandles$Lookup", "java/lang/String", "java/lang/invoke/MethodType", "[Ljava/lang/Object;", Opcodes.INTEGER, Opcodes.INTEGER, "[Ljava/lang/Class;", "[Ljava/lang/invoke/MethodType;", "sun/misc/Unsafe"}, 7, new Object[]{"sun/misc/Unsafe", "java/lang/Class", "java/lang/reflect/Method", Opcodes.NULL, "[Ljava/lang/Object;", "[Ljava/lang/Object;", Opcodes.INTEGER}); - methodVisitor.visitInsn(Opcodes.ICONST_0); - methodVisitor.visitLabel(callSiteAlternative); - methodVisitor.visitFrame(Opcodes.F_FULL, 9, new Object[]{"java/lang/invoke/MethodHandles$Lookup", "java/lang/String", "java/lang/invoke/MethodType", "[Ljava/lang/Object;", Opcodes.INTEGER, Opcodes.INTEGER, "[Ljava/lang/Class;", "[Ljava/lang/invoke/MethodType;", "sun/misc/Unsafe"}, 8, new Object[]{"sun/misc/Unsafe", "java/lang/Class", "java/lang/reflect/Method", Opcodes.NULL, "[Ljava/lang/Object;", "[Ljava/lang/Object;", Opcodes.INTEGER, Opcodes.INTEGER}); - methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitIntInsn(Opcodes.BIPUSH, 7); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 6); - methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Arrays", "asList", "([Ljava/lang/Object;)Ljava/util/List;", false); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitIntInsn(Opcodes.BIPUSH, 8); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 7); - methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Arrays", "asList", "([Ljava/lang/Object;)Ljava/util/List;", false); - methodVisitor.visitInsn(Opcodes.AASTORE); - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/reflect/Method", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", false); - methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, "[B"); - methodVisitor.visitInsn(Opcodes.ACONST_NULL); - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "sun/misc/Unsafe", "defineAnonymousClass", "(Ljava/lang/Class;[B[Ljava/lang/Object;)Ljava/lang/Class;", false); - methodVisitor.visitVarInsn(Opcodes.ASTORE, 9); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 8); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 9); - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "sun/misc/Unsafe", "ensureClassInitialized", "(Ljava/lang/Class;)V", false); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 2); - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/invoke/MethodType", "parameterCount", "()I", false); - Label callSiteJump = new Label(); - methodVisitor.visitJumpInsn(Opcodes.IFNE, callSiteJump); - methodVisitor.visitTypeInsn(Opcodes.NEW, "java/lang/invoke/ConstantCallSite"); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 2); - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/invoke/MethodType", "returnType", "()Ljava/lang/Class;", false); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 9); - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;", false); - methodVisitor.visitInsn(Opcodes.ICONST_0); - methodVisitor.visitInsn(Opcodes.AALOAD); - methodVisitor.visitInsn(Opcodes.ICONST_0); - methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/reflect/Constructor", "newInstance", "([Ljava/lang/Object;)Ljava/lang/Object;", false); - methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/invoke/MethodHandles", "constant", "(Ljava/lang/Class;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;", false); - methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/invoke/ConstantCallSite", "", "(Ljava/lang/invoke/MethodHandle;)V", false); - Label callSiteExit = new Label(); - methodVisitor.visitJumpInsn(Opcodes.GOTO, callSiteExit); - methodVisitor.visitLabel(callSiteJump); - methodVisitor.visitFrame(Opcodes.F_APPEND, 1, new Object[]{"java/lang/Class"}, 0, null); - methodVisitor.visitTypeInsn(Opcodes.NEW, "java/lang/invoke/ConstantCallSite"); - methodVisitor.visitInsn(Opcodes.DUP); - methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/invoke/MethodHandles$Lookup", "IMPL_LOOKUP", "Ljava/lang/invoke/MethodHandles$Lookup;"); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 9); - methodVisitor.visitLdcInsn("get$Lambda"); - methodVisitor.visitVarInsn(Opcodes.ALOAD, 2); - methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "findStatic", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", false); - methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/invoke/ConstantCallSite", "", "(Ljava/lang/invoke/MethodHandle;)V", false); - methodVisitor.visitLabel(callSiteExit); - methodVisitor.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[]{"java/lang/invoke/CallSite"}); - methodVisitor.visitInsn(Opcodes.ARETURN); - methodVisitor.visitMaxs(9, 10); - methodVisitor.visitEnd(); - return IGNORE_ORIGINAL; + public boolean equals(Object other) { + if (this == other) return true; + if (other == null || getClass() != other.getClass()) return false; + Enabled enabled = (Enabled) other; + return folder.equals(enabled.folder) && instrumentation.equals(enabled.instrumentation); + } + + @Override + public int hashCode() { + int result = folder.hashCode(); + result = 31 * result + instrumentation.hashCode(); + return result; } @Override public String toString() { - return "AgentBuilder.Default.LambdaInstrumentationStrategy.AlternativeMetaFactoryRedirection." + name(); + return "AgentBuilder.Default.BootstrapInjectionStrategy.Enabled{" + + "folder=" + folder + + ", instrumentation=" + instrumentation + + '}'; } } } @@ -4642,8 +4623,8 @@ public AgentBuilder disableBootstrapInjection() { } @Override - public AgentBuilder enableLambdaInstrumentation(boolean enable) { - return materialize().enableLambdaInstrumentation(enable); + public AgentBuilder with(LambdaInstrumentationStrategy lambdaInstrumentationStrategy) { + return materialize().with(lambdaInstrumentationStrategy); } @Override diff --git a/byte-buddy-dep/src/test/java/net/bytebuddy/agent/builder/AgentBuilderDefaultApplicationTest.java b/byte-buddy-dep/src/test/java/net/bytebuddy/agent/builder/AgentBuilderDefaultApplicationTest.java index 5ef6542224a..ee1467e21b9 100644 --- a/byte-buddy-dep/src/test/java/net/bytebuddy/agent/builder/AgentBuilderDefaultApplicationTest.java +++ b/byte-buddy-dep/src/test/java/net/bytebuddy/agent/builder/AgentBuilderDefaultApplicationTest.java @@ -171,7 +171,7 @@ public void testAgentWithoutSelfInitializationWithNativeMethodPrefix() throws Ex ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(binaryLocator) .with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE) - .withNativeMethodPrefix(QUX) + .enableNativeMethodPrefix(QUX) .type(isAnnotatedWith(ShouldRebase.class), ElementMatchers.is(classLoader)).transform(new FooTransformer()) .installOnByteBuddyAgent(); try { @@ -274,7 +274,7 @@ public void testNonCapturingLambda() throws Exception { ClassLoader classLoader = lambdaSamples(); ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(binaryLocator) - .enableLambdaInstrumentation(true) + .with(AgentBuilder.LambdaInstrumentationStrategy.ENABLED) .type(isSubTypeOf(Callable.class)).transform(new SingleMethodReplacer("call")) .installOn(ByteBuddyAgent.install()); try { @@ -296,7 +296,7 @@ public void testArgumentCapturingLambda() throws Exception { ClassLoader classLoader = lambdaSamples(); ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(binaryLocator) - .enableLambdaInstrumentation(true) + .with(AgentBuilder.LambdaInstrumentationStrategy.ENABLED) .type(isSubTypeOf(Callable.class)).transform(new SingleMethodReplacer("call")) .installOn(ByteBuddyAgent.install()); try { @@ -318,7 +318,7 @@ public void testInstanceCapturingLambda() throws Exception { ClassLoader classLoader = lambdaSamples(); ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(binaryLocator) - .enableLambdaInstrumentation(true) + .with(AgentBuilder.LambdaInstrumentationStrategy.ENABLED) .type(isSubTypeOf(Callable.class)).transform(new SingleMethodReplacer("call")) .installOn(ByteBuddyAgent.install()); try { @@ -340,7 +340,7 @@ public void testNonCapturingLambdaWithArguments() throws Exception { ClassLoader classLoader = lambdaSamples(); ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(binaryLocator) - .enableLambdaInstrumentation(true) + .with(AgentBuilder.LambdaInstrumentationStrategy.ENABLED) .type(isSubTypeOf(Class.forName("java.util.function.Function"))).transform(new SingleMethodReplacer("apply")) .installOn(ByteBuddyAgent.install()); try { @@ -361,7 +361,7 @@ public void testCapturingLambdaWithArguments() throws Exception { ClassLoader classLoader = lambdaSamples(); ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(binaryLocator) - .enableLambdaInstrumentation(true) + .with(AgentBuilder.LambdaInstrumentationStrategy.ENABLED) .type(isSubTypeOf(Class.forName("java.util.function.Function"))).transform(new SingleMethodReplacer("apply")) .installOn(ByteBuddyAgent.install()); try { @@ -382,7 +382,7 @@ public void testSerializableLambda() throws Exception { ClassLoader classLoader = lambdaSamples(); ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(binaryLocator) - .enableLambdaInstrumentation(true) + .with(AgentBuilder.LambdaInstrumentationStrategy.ENABLED) .installOn(ByteBuddyAgent.install()); try { Class sampleFactory = classLoader.loadClass(LAMBDA_SAMPLE_FACTORY); diff --git a/byte-buddy-dep/src/test/java/net/bytebuddy/agent/builder/AgentBuilderDefaultTest.java b/byte-buddy-dep/src/test/java/net/bytebuddy/agent/builder/AgentBuilderDefaultTest.java index 11be95b12d5..2bfb3cc47ed 100644 --- a/byte-buddy-dep/src/test/java/net/bytebuddy/agent/builder/AgentBuilderDefaultTest.java +++ b/byte-buddy-dep/src/test/java/net/bytebuddy/agent/builder/AgentBuilderDefaultTest.java @@ -122,8 +122,7 @@ public void testSuccessfulWithoutExistingClass() throws Exception { .with(binaryLocator) .with(typeStrategy) .with(listener) - .withoutNativeMethodPrefix() - .enableLambdaInstrumentation(false) + .disableNativeMethodPrefix() .with(accessControlContext) .type(rawMatcher).transform(transformer) .installOn(instrumentation); @@ -156,8 +155,7 @@ public void testSuccessfulWithExistingClass() throws Exception { .with(binaryLocator) .with(typeStrategy) .with(listener) - .withoutNativeMethodPrefix() - .enableLambdaInstrumentation(false) + .disableNativeMethodPrefix() .with(accessControlContext) .type(rawMatcher).transform(transformer) .installOn(instrumentation); @@ -193,8 +191,7 @@ public void testSkipRetransformationWithNonRedefinable() throws Exception { .with(binaryLocator) .with(typeStrategy) .with(listener) - .withoutNativeMethodPrefix() - .enableLambdaInstrumentation(false) + .disableNativeMethodPrefix() .with(accessControlContext) .type(rawMatcher).transform(transformer) .installOn(instrumentation); @@ -224,8 +221,7 @@ public void testSkipRetransformationWithNonMatched() throws Exception { .with(binaryLocator) .with(typeStrategy) .with(listener) - .withoutNativeMethodPrefix() - .enableLambdaInstrumentation(false) + .disableNativeMethodPrefix() .with(accessControlContext) .type(rawMatcher).transform(transformer) .installOn(instrumentation); @@ -257,8 +253,7 @@ public void testSkipRetransformationWithNonMatchedListenerException() throws Exc .with(binaryLocator) .with(typeStrategy) .with(listener) - .withoutNativeMethodPrefix() - .enableLambdaInstrumentation(false) + .disableNativeMethodPrefix() .with(accessControlContext) .type(rawMatcher).transform(transformer) .installOn(instrumentation); @@ -290,8 +285,7 @@ public void testSkipRetransformationWithNonMatchedListenerCompleteException() th .with(binaryLocator) .with(typeStrategy) .with(listener) - .withoutNativeMethodPrefix() - .enableLambdaInstrumentation(false) + .disableNativeMethodPrefix() .with(accessControlContext) .type(rawMatcher).transform(transformer) .installOn(instrumentation); @@ -319,8 +313,7 @@ public void testSuccessfulWithRetransformationMatched() throws Exception { .with(binaryLocator) .with(typeStrategy) .with(listener) - .withoutNativeMethodPrefix() - .enableLambdaInstrumentation(false) + .disableNativeMethodPrefix() .with(accessControlContext) .type(rawMatcher).transform(transformer) .installOn(instrumentation); @@ -344,8 +337,7 @@ public void testRetransformationNotSupported() throws Exception { .with(binaryLocator) .with(typeStrategy) .with(listener) - .withoutNativeMethodPrefix() - .enableLambdaInstrumentation(false) + .disableNativeMethodPrefix() .with(accessControlContext) .type(rawMatcher).transform(transformer) .installOn(instrumentation); @@ -365,8 +357,7 @@ public void testSkipRedefinitionWithNonRedefinable() throws Exception { .with(binaryLocator) .with(typeStrategy) .with(listener) - .withoutNativeMethodPrefix() - .enableLambdaInstrumentation(false) + .disableNativeMethodPrefix() .with(accessControlContext) .type(rawMatcher).transform(transformer) .installOn(instrumentation); @@ -396,8 +387,7 @@ public void testSkipRedefinitionWithNonMatched() throws Exception { .with(binaryLocator) .with(typeStrategy) .with(listener) - .withoutNativeMethodPrefix() - .enableLambdaInstrumentation(false) + .disableNativeMethodPrefix() .with(accessControlContext) .type(rawMatcher).transform(transformer) .installOn(instrumentation); @@ -429,8 +419,7 @@ public void testSkipRedefinitionWithNonMatchedListenerException() throws Excepti .with(binaryLocator) .with(typeStrategy) .with(listener) - .withoutNativeMethodPrefix() - .enableLambdaInstrumentation(false) + .disableNativeMethodPrefix() .with(accessControlContext) .type(rawMatcher).transform(transformer) .installOn(instrumentation); @@ -462,8 +451,7 @@ public void testSkipRedefinitionWithNonMatchedListenerFinishedException() throws .with(binaryLocator) .with(typeStrategy) .with(listener) - .withoutNativeMethodPrefix() - .enableLambdaInstrumentation(false) + .disableNativeMethodPrefix() .with(accessControlContext) .type(rawMatcher).transform(transformer) .installOn(instrumentation); @@ -492,8 +480,7 @@ public void testSuccessfulWithRedefinitionMatched() throws Exception { .with(binaryLocator) .with(typeStrategy) .with(listener) - .withoutNativeMethodPrefix() - .enableLambdaInstrumentation(false) + .disableNativeMethodPrefix() .with(accessControlContext) .type(rawMatcher).transform(transformer) .installOn(instrumentation); @@ -530,8 +517,7 @@ public void testRedefinitionNotSupported() throws Exception { .with(typeStrategy) .with(listener) .with(accessControlContext) - .withoutNativeMethodPrefix() - .enableLambdaInstrumentation(false) + .disableNativeMethodPrefix() .type(rawMatcher).transform(transformer) .installOn(instrumentation); } @@ -548,8 +534,7 @@ public void testTransformationWithError() throws Exception { .with(binaryLocator) .with(typeStrategy) .with(listener) - .withoutNativeMethodPrefix() - .enableLambdaInstrumentation(false) + .disableNativeMethodPrefix() .with(accessControlContext) .type(rawMatcher).transform(transformer) .installOn(instrumentation); @@ -574,8 +559,7 @@ public void testIgnored() throws Exception { .with(binaryLocator) .with(typeStrategy) .with(listener) - .withoutNativeMethodPrefix() - .enableLambdaInstrumentation(false) + .disableNativeMethodPrefix() .with(accessControlContext) .type(rawMatcher).transform(transformer) .installOn(instrumentation); @@ -591,7 +575,7 @@ public void testIgnored() throws Exception { @Test(expected = IllegalArgumentException.class) public void testEmptyPrefixThrowsException() throws Exception { - new AgentBuilder.Default(byteBuddy).withNativeMethodPrefix(""); + new AgentBuilder.Default(byteBuddy).enableNativeMethodPrefix(""); } @Test @@ -611,8 +595,7 @@ public void testAuxiliaryTypeInitialization() throws Exception { .with(binaryLocator) .with(typeStrategy) .with(listener) - .withoutNativeMethodPrefix() - .enableLambdaInstrumentation(false) + .disableNativeMethodPrefix() .with(accessControlContext) .type(rawMatcher).transform(transformer) .installOn(instrumentation); @@ -649,8 +632,7 @@ public void testRedefinitionConsiderationException() throws Exception { .with(binaryLocator) .with(typeStrategy) .with(listener) - .withoutNativeMethodPrefix() - .enableLambdaInstrumentation(false) + .disableNativeMethodPrefix() .with(accessControlContext) .type(rawMatcher).transform(transformer) .installOn(instrumentation); @@ -674,8 +656,7 @@ public void testRetransformationConsiderationException() throws Exception { .with(binaryLocator) .with(typeStrategy) .with(listener) - .withoutNativeMethodPrefix() - .enableLambdaInstrumentation(false) + .disableNativeMethodPrefix() .with(accessControlContext) .type(rawMatcher).transform(transformer) .installOn(instrumentation); @@ -700,8 +681,7 @@ public void testRedefinitionConsiderationExceptionListenerException() throws Exc .with(binaryLocator) .with(typeStrategy) .with(listener) - .withoutNativeMethodPrefix() - .enableLambdaInstrumentation(false) + .disableNativeMethodPrefix() .with(accessControlContext) .type(rawMatcher).transform(transformer) .installOn(instrumentation); @@ -726,8 +706,7 @@ public void testRetransformationConsiderationExceptionListenerException() throws .with(binaryLocator) .with(typeStrategy) .with(listener) - .withoutNativeMethodPrefix() - .enableLambdaInstrumentation(false) + .disableNativeMethodPrefix() .with(accessControlContext) .type(rawMatcher).transform(transformer) .installOn(instrumentation); @@ -770,7 +749,7 @@ public void testEnabledBootstrapInjection() throws Exception { } @Test(expected = IllegalStateException.class) - public void testDisableddBootstrapInjection() throws Exception { + public void testDisabledBootstrapInjection() throws Exception { AgentBuilder.Default.BootstrapInjectionStrategy.Disabled.INSTANCE.make(mock(ProtectionDomain.class)); }