Skip to content

Commit

Permalink
Added dispatcher for creation of instrumented lambda expressions.
Browse files Browse the repository at this point in the history
  • Loading branch information
raphw committed Jan 24, 2016
1 parent a55b34e commit e9a3d4b
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 47 deletions.
Expand Up @@ -8,6 +8,7 @@
import net.bytebuddy.dynamic.ClassFileLocator; import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassInjector; import net.bytebuddy.dynamic.loading.ClassInjector;
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
import net.bytebuddy.dynamic.scaffold.inline.MethodNameTransformer; import net.bytebuddy.dynamic.scaffold.inline.MethodNameTransformer;
import net.bytebuddy.implementation.Implementation; import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.implementation.LoadedTypeInitializer; import net.bytebuddy.implementation.LoadedTypeInitializer;
Expand Down Expand Up @@ -2458,27 +2459,30 @@ protected void apply(ByteBuddy byteBuddy, Instrumentation instrumentation, Class
throw new IllegalStateException("Cannot find meta factory", exception); throw new IllegalStateException("Cannot find meta factory", exception);
} }
ClassInjector classInjector = ClassInjector.UsingReflection.ofSystemClassLoader(); ClassInjector classInjector = ClassInjector.UsingReflection.ofSystemClassLoader();
TypeDescription dispatcherType = new TypeDescription.ForLoadedType(LambdaCreationDispatcher.class); TypeDescription dispatcherType = new TypeDescription.ForLoadedType(LambdaFactory.class);
Class<?> loadedDispatcher = classInjector Class<?> loadedDispatcher = classInjector
.inject(Collections.singletonMap(dispatcherType, ClassFileLocator.ForClassLoader.read(LambdaInstrumentationStrategy.class).resolve())) .inject(Collections.singletonMap(dispatcherType, ClassFileLocator.ForClassLoader.read(LambdaFactory.class).resolve()))
.get(dispatcherType); .get(dispatcherType);
try { try {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Set<ClassFileTransformer> classFileTransformers = (Set<ClassFileTransformer>) loadedDispatcher.getDeclaredField("CLASS_FILE_TRANSFORMERS").get(null); Map<ClassFileTransformer, Class<?>> classFileTransformers = (Map<ClassFileTransformer, Class<?>>) loadedDispatcher
.getDeclaredField("CLASS_FILE_TRANSFORMERS")
.get(null);
try { try {
if (!classFileTransformers.isEmpty()) { if (!classFileTransformers.isEmpty()) {
return; return;
} }
} finally { } finally {
classFileTransformers.add(classFileTransformer); classFileTransformers.put(classFileTransformer, LambdaCreator.class);
} }
} catch (Exception exception) { } catch (Exception exception) {
throw new IllegalStateException("Could not access class file transformers", exception); throw new IllegalStateException("Could not access class file transformers", exception);
} }
byteBuddy.with(Implementation.Context.Disabled.Factory.INSTANCE) byteBuddy.with(Implementation.Context.Disabled.Factory.INSTANCE)
.redefine(lambdaMetaFactory) .redefine(lambdaMetaFactory)
.visit(new AsmVisitorWrapper.ForDeclaredMethods().method(named("metafactory"), new MetaFactoryRedirection())) .visit(new AsmVisitorWrapper.ForDeclaredMethods().method(named("metafactory"), new MetaFactoryRedirection()))
.make(); .make()
.load(lambdaMetaFactory.getClassLoader(), ClassReloadingStrategy.of(instrumentation));
} }
}, },


Expand All @@ -2501,18 +2505,17 @@ public static CallSite metafactory(MethodHandles.Lookup caller,
throws Exception { throws Exception {
Unsafe unsafe = Unsafe.getUnsafe(); Unsafe unsafe = Unsafe.getUnsafe();
final Class<?> lambdaClass = unsafe.defineAnonymousClass(caller.lookupClass(), final Class<?> lambdaClass = unsafe.defineAnonymousClass(caller.lookupClass(),
(byte[]) ClassLoader.getSystemClassLoader().loadClass("net.bytebuddy.agent.builder.LambdaCreationDispatcher").getDeclaredMethod("make", (byte[]) ClassLoader.getSystemClassLoader().loadClass("net.bytebuddy.agent.builder.LambdaFactory").getDeclaredMethod("make",
Object.class, Object.class,
String.class, String.class,
Object.class, Object.class,
Object.class, Object.class,
Object.class).invoke(null, caller, invokedName, invokedType, samMethodType, implMethod), Object.class).invoke(null, caller, invokedName, invokedType, samMethodType, implMethod),
null); null);
unsafe.ensureClassInitialized(lambdaClass); unsafe.ensureClassInitialized(lambdaClass);
//IMPL_LOOKUP not lookup()
return invokedType.parameterCount() == 0 return invokedType.parameterCount() == 0
? new ConstantCallSite(MethodHandles.constant(lambdaClass, lambdaClass.getDeclaredConstructors()[0].newInstance())) ? new ConstantCallSite(MethodHandles.constant(invokedType.returnType(), lambdaClass.getDeclaredConstructors()[0].newInstance()))
: new ConstantCallSite(MethodHandles.lookup().findStatic(lambdaClass, "get$Lambda", invokedType)); : new ConstantCallSite(MethodHandles.IMPL_LOOKUP.findStatic(lambdaClass, "get$Lambda", invokedType));
} }
*/ */
protected static class MetaFactoryRedirection implements AsmVisitorWrapper.ForDeclaredMethods.MethodVisitorWrapper { protected static class MetaFactoryRedirection implements AsmVisitorWrapper.ForDeclaredMethods.MethodVisitorWrapper {
Expand All @@ -2527,7 +2530,7 @@ public MethodVisitor wrap(TypeDescription instrumentedType, MethodDescription.In
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "lookupClass", "()Ljava/lang/Class;", false); 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.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/ClassLoader", "getSystemClassLoader", "()Ljava/lang/ClassLoader;", false);
methodVisitor.visitLdcInsn("net.bytebuddy.agent.builder.LambdaCreationDispatcher"); methodVisitor.visitLdcInsn(LambdaFactory.class.getName());
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/ClassLoader", "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;", false); methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/ClassLoader", "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;", false);
methodVisitor.visitLdcInsn("make"); methodVisitor.visitLdcInsn("make");
methodVisitor.visitInsn(Opcodes.ICONST_5); methodVisitor.visitInsn(Opcodes.ICONST_5);
Expand Down Expand Up @@ -2591,7 +2594,8 @@ public MethodVisitor wrap(TypeDescription instrumentedType, MethodDescription.In
methodVisitor.visitJumpInsn(Opcodes.IFNE, firstJump); methodVisitor.visitJumpInsn(Opcodes.IFNE, firstJump);
methodVisitor.visitTypeInsn(Opcodes.NEW, "java/lang/invoke/ConstantCallSite"); methodVisitor.visitTypeInsn(Opcodes.NEW, "java/lang/invoke/ConstantCallSite");
methodVisitor.visitInsn(Opcodes.DUP); methodVisitor.visitInsn(Opcodes.DUP);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 7); methodVisitor.visitVarInsn(Opcodes.ALOAD, 2);
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/invoke/MethodType", "returnType", "()Ljava/lang/Class;", false);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 7); methodVisitor.visitVarInsn(Opcodes.ALOAD, 7);
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;", false); methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;", false);
methodVisitor.visitInsn(Opcodes.ICONST_0); methodVisitor.visitInsn(Opcodes.ICONST_0);
Expand All @@ -2607,7 +2611,7 @@ public MethodVisitor wrap(TypeDescription instrumentedType, MethodDescription.In
methodVisitor.visitFrame(Opcodes.F_APPEND, 2, new Object[]{"sun/misc/Unsafe", "java/lang/Class"}, 0, null); 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.visitTypeInsn(Opcodes.NEW, "java/lang/invoke/ConstantCallSite");
methodVisitor.visitInsn(Opcodes.DUP); methodVisitor.visitInsn(Opcodes.DUP);
methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/invoke/MethodHandles", "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;", false); methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/invoke/MethodHandles", "IMPL_LOOKUP", "Ljava/lang/invoke/MethodHandles$Lookup;");
methodVisitor.visitVarInsn(Opcodes.ALOAD, 7); methodVisitor.visitVarInsn(Opcodes.ALOAD, 7);
methodVisitor.visitLdcInsn("get$Lambda"); methodVisitor.visitLdcInsn("get$Lambda");
methodVisitor.visitVarInsn(Opcodes.ALOAD, 2); methodVisitor.visitVarInsn(Opcodes.ALOAD, 2);
Expand All @@ -2620,6 +2624,11 @@ public MethodVisitor wrap(TypeDescription instrumentedType, MethodDescription.In
methodVisitor.visitEnd(); methodVisitor.visitEnd();
return IGNORE_ORIGINAL; return IGNORE_ORIGINAL;
} }

void foo () {
Object o = null;
System.out.println(o);
}
} }
} }


Expand Down
Expand Up @@ -8,6 +8,7 @@
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.scaffold.InstrumentedType; import net.bytebuddy.dynamic.scaffold.InstrumentedType;
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
import net.bytebuddy.implementation.Implementation; import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.implementation.SuperMethodCall; import net.bytebuddy.implementation.SuperMethodCall;
import net.bytebuddy.implementation.bytecode.ByteCodeAppender; import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
Expand All @@ -22,14 +23,14 @@
import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.MethodVisitor;


import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.ClassFileTransformer;
import java.util.*; import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;


import static net.bytebuddy.matcher.ElementMatchers.*; import static net.bytebuddy.matcher.ElementMatchers.*;


public class LambdaCreationDispatcher { public class LambdaCreator {

private static final Set<ClassFileTransformer> CLASS_FILE_TRANSFORMERS = Collections.synchronizedSet(new LinkedHashSet<ClassFileTransformer>());


private static final String LAMBDA_FACTORY = "get$Lambda"; private static final String LAMBDA_FACTORY = "get$Lambda";


Expand All @@ -41,42 +42,46 @@ public class LambdaCreationDispatcher {


private static final AtomicInteger lambdaNameCounter = new AtomicInteger(); private static final AtomicInteger lambdaNameCounter = new AtomicInteger();


public static byte[] make(Object callerClassLookup, public static byte[] make(Object callerTypeLookup,
String functionalMethodName, String functionalMethodName,
Object functionalMethodType, Object factoryMethod,
Object expectedMethodType, Object implementedMethod,
Object targetMethodHandle, Object targetMethod,
boolean serializable) throws Exception { Collection<? extends ClassFileTransformer> classFileTransformers) throws Exception {
JavaInstance.MethodType factoryMethodType = JavaInstance.MethodType.of(expectedMethodType); JavaInstance.MethodType factoryMethodType = JavaInstance.MethodType.of(factoryMethod);
JavaInstance.MethodType lambdaMethodType = JavaInstance.MethodType.of(functionalMethodType); JavaInstance.MethodType implementedMethodType = JavaInstance.MethodType.of(implementedMethod);
JavaInstance.MethodHandle lambdaImplementationHandle = JavaInstance.MethodHandle.of(targetMethodHandle, callerClassLookup); JavaInstance.MethodHandle targetMethodHandle = JavaInstance.MethodHandle.of(targetMethod, callerTypeLookup);
Class<?> lookupClass = null; // From callerClassLookup! Class<?> lookupType = JavaInstance.MethodHandle.lookupType(callerTypeLookup);
String lambdaClassName = lookupClass.getName() + LAMBDA_TYPE_INFIX + lambdaNameCounter.incrementAndGet(); String lambdaClassName = lookupType.getName() + LAMBDA_TYPE_INFIX + lambdaNameCounter.incrementAndGet();
DynamicType.Builder<?> builder = new ByteBuddy() DynamicType.Builder<?> builder = new ByteBuddy()
.subclass(lambdaMethodType.getReturnType()) .subclass(factoryMethodType.getReturnType(), ConstructorStrategy.Default.NO_CONSTRUCTORS)
.modifiers(SyntheticState.SYNTHETIC, TypeManifestation.FINAL) .modifiers(SyntheticState.SYNTHETIC, TypeManifestation.FINAL, implementedMethodType.getParameterTypes().isEmpty()
.implement(factoryMethodType.getReturnType()) ? Visibility.PUBLIC
: Visibility.PACKAGE_PRIVATE)
.name(lambdaClassName); .name(lambdaClassName);
int index = 0; int index = 0;
for (TypeDescription parameterTypes : factoryMethodType.getParameterTypes()) { for (TypeDescription parameterTypes : implementedMethodType.getParameterTypes()) {
builder = builder.defineField(FIELD_PREFIX + index++, parameterTypes, Visibility.PUBLIC, FieldManifestation.FINAL); builder = builder.defineField(FIELD_PREFIX + index++, parameterTypes, Visibility.PUBLIC, FieldManifestation.FINAL);
} }
if (!factoryMethodType.getParameterTypes().isEmpty()) { if (!implementedMethodType.getParameterTypes().isEmpty()) {
builder = builder.defineMethod(LAMBDA_FACTORY, factoryMethodType.getReturnType(), Visibility.PRIVATE, Ownership.STATIC) builder = builder.defineMethod(LAMBDA_FACTORY, factoryMethodType.getReturnType(), Visibility.PRIVATE, Ownership.STATIC)
.withParameters(factoryMethodType.getParameterTypes())
.intercept(new FactoryImplementation()); .intercept(new FactoryImplementation());
} }
byte[] classFile = builder.defineConstructor(Visibility.PRIVATE) byte[] classFile = builder.defineConstructor(factoryMethodType.getParameterTypes().isEmpty() ? Visibility.PUBLIC : Visibility.PRIVATE)
.intercept(SuperMethodCall.INSTANCE.andThen(new ConstructorImplementation())) .intercept(SuperMethodCall.INSTANCE.andThen(new ConstructorImplementation()))
.method(named(functionalMethodName).and(takesArguments(factoryMethodType.getParameterTypes())).and(returns(factoryMethodType.getReturnType()))) .method(named(functionalMethodName)
.intercept(new LambdaMethodImplementation(lambdaImplementationHandle)) .and(takesArguments(implementedMethodType.getParameterTypes()))
.and(returns(implementedMethodType.getReturnType())))
.intercept(new LambdaMethodImplementation(targetMethodHandle))
// TODO: Serialization // TODO: Serialization
.make() .make()
.getBytes(); .getBytes();
for (ClassFileTransformer classFileTransformer : CLASS_FILE_TRANSFORMERS) { for (ClassFileTransformer classFileTransformer : classFileTransformers) {
byte[] transformedClassFile = classFileTransformer.transform(lookupClass.getClassLoader(), byte[] transformedClassFile = classFileTransformer.transform(lookupType.getClassLoader(),
lambdaClassName.replace('.', '/'), lambdaClassName.replace('.', '/'),
NOT_PREVIOUSLY_DEFINED, NOT_PREVIOUSLY_DEFINED,
lookupClass.getProtectionDomain(), lookupType.getProtectionDomain(),
classFile); classFile);
classFile = transformedClassFile == null classFile = transformedClassFile == null
? classFile ? classFile
Expand Down Expand Up @@ -200,4 +205,5 @@ public Size apply(MethodVisitor methodVisitor, Context implementationContext, Me
} }
} }
} }

} }
@@ -0,0 +1,27 @@
package net.bytebuddy.agent.builder;

import java.lang.instrument.ClassFileTransformer;
import java.lang.invoke.LambdaConversionException;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;

public class LambdaFactory {

public static final Map<ClassFileTransformer, Class<?>> CLASS_FILE_TRANSFORMERS = Collections.synchronizedMap(new LinkedHashMap<ClassFileTransformer, Class<?>>());

public static byte[] make(Object caller,
String invokedName,
Object invokedType,
Object samMethodType,
Object implMethod) throws LambdaConversionException {
try {
return (byte[]) CLASS_FILE_TRANSFORMERS.values().iterator().next()
.getDeclaredMethod("make", Object.class, String.class, Object.class, Object.class, Object.class, Collection.class)
.invoke(null, caller, invokedName, invokedType, samMethodType, implMethod, CLASS_FILE_TRANSFORMERS.keySet());
} catch (Exception exception) {
throw new IllegalStateException(exception);
}
}
}
Expand Up @@ -168,10 +168,11 @@ public Map<TypeDescription, Class<?>> load(ClassLoader classLoader, Map<TypeDesc
} }
try { try {
engine.apply(instrumentation, classDefinitions); engine.apply(instrumentation, classDefinitions);
ClassInjector classInjector = classLoader == null if (!unloadedClasses.isEmpty()) {
? bootstrapInjection.make(instrumentation) loadedClasses.putAll((classLoader == null
: new ClassInjector.UsingReflection(classLoader); ? bootstrapInjection.make(instrumentation)
loadedClasses.putAll(classInjector.inject(unloadedClasses)); : new ClassInjector.UsingReflection(classLoader)).inject(unloadedClasses));
}
} catch (ClassNotFoundException exception) { } catch (ClassNotFoundException exception) {
throw new IllegalArgumentException("Could not locate classes for redefinition", exception); throw new IllegalArgumentException("Could not locate classes for redefinition", exception);
} catch (UnmodifiableClassException exception) { } catch (UnmodifiableClassException exception) {
Expand Down

0 comments on commit e9a3d4b

Please sign in to comment.