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.DynamicType;
import net.bytebuddy.dynamic.loading.ClassInjector;
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
import net.bytebuddy.dynamic.scaffold.inline.MethodNameTransformer;
import net.bytebuddy.implementation.Implementation;
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);
}
ClassInjector classInjector = ClassInjector.UsingReflection.ofSystemClassLoader();
TypeDescription dispatcherType = new TypeDescription.ForLoadedType(LambdaCreationDispatcher.class);
TypeDescription dispatcherType = new TypeDescription.ForLoadedType(LambdaFactory.class);
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);
try {
@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 {
if (!classFileTransformers.isEmpty()) {
return;
}
} finally {
classFileTransformers.add(classFileTransformer);
classFileTransformers.put(classFileTransformer, LambdaCreator.class);
}
} catch (Exception exception) {
throw new IllegalStateException("Could not access class file transformers", exception);
}
byteBuddy.with(Implementation.Context.Disabled.Factory.INSTANCE)
.redefine(lambdaMetaFactory)
.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 {
Unsafe unsafe = Unsafe.getUnsafe();
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,
String.class,
Object.class,
Object.class,
Object.class).invoke(null, caller, invokedName, invokedType, samMethodType, implMethod),
null);
unsafe.ensureClassInitialized(lambdaClass);
//IMPL_LOOKUP not lookup()
return invokedType.parameterCount() == 0
? new ConstantCallSite(MethodHandles.constant(lambdaClass, lambdaClass.getDeclaredConstructors()[0].newInstance()))
: new ConstantCallSite(MethodHandles.lookup().findStatic(lambdaClass, "get$Lambda", invokedType));
? new ConstantCallSite(MethodHandles.constant(invokedType.returnType(), lambdaClass.getDeclaredConstructors()[0].newInstance()))
: new ConstantCallSite(MethodHandles.IMPL_LOOKUP.findStatic(lambdaClass, "get$Lambda", invokedType));
}
*/
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.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.LambdaCreationDispatcher");
methodVisitor.visitLdcInsn(LambdaFactory.class.getName());
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/ClassLoader", "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;", false);
methodVisitor.visitLdcInsn("make");
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.visitTypeInsn(Opcodes.NEW, "java/lang/invoke/ConstantCallSite");
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.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;", false);
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.visitTypeInsn(Opcodes.NEW, "java/lang/invoke/ConstantCallSite");
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.visitLdcInsn("get$Lambda");
methodVisitor.visitVarInsn(Opcodes.ALOAD, 2);
Expand All @@ -2620,6 +2624,11 @@ public MethodVisitor wrap(TypeDescription instrumentedType, MethodDescription.In
methodVisitor.visitEnd();
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.dynamic.DynamicType;
import net.bytebuddy.dynamic.scaffold.InstrumentedType;
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.implementation.SuperMethodCall;
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
Expand All @@ -22,14 +23,14 @@
import org.objectweb.asm.MethodVisitor;

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 static net.bytebuddy.matcher.ElementMatchers.*;

public class LambdaCreationDispatcher {

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

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

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

private static final AtomicInteger lambdaNameCounter = new AtomicInteger();

public static byte[] make(Object callerClassLookup,
public static byte[] make(Object callerTypeLookup,
String functionalMethodName,
Object functionalMethodType,
Object expectedMethodType,
Object targetMethodHandle,
boolean serializable) throws Exception {
JavaInstance.MethodType factoryMethodType = JavaInstance.MethodType.of(expectedMethodType);
JavaInstance.MethodType lambdaMethodType = JavaInstance.MethodType.of(functionalMethodType);
JavaInstance.MethodHandle lambdaImplementationHandle = JavaInstance.MethodHandle.of(targetMethodHandle, callerClassLookup);
Class<?> lookupClass = null; // From callerClassLookup!
String lambdaClassName = lookupClass.getName() + LAMBDA_TYPE_INFIX + lambdaNameCounter.incrementAndGet();
Object factoryMethod,
Object implementedMethod,
Object targetMethod,
Collection<? extends ClassFileTransformer> classFileTransformers) throws Exception {
JavaInstance.MethodType factoryMethodType = JavaInstance.MethodType.of(factoryMethod);
JavaInstance.MethodType implementedMethodType = JavaInstance.MethodType.of(implementedMethod);
JavaInstance.MethodHandle targetMethodHandle = JavaInstance.MethodHandle.of(targetMethod, callerTypeLookup);
Class<?> lookupType = JavaInstance.MethodHandle.lookupType(callerTypeLookup);
String lambdaClassName = lookupType.getName() + LAMBDA_TYPE_INFIX + lambdaNameCounter.incrementAndGet();
DynamicType.Builder<?> builder = new ByteBuddy()
.subclass(lambdaMethodType.getReturnType())
.modifiers(SyntheticState.SYNTHETIC, TypeManifestation.FINAL)
.implement(factoryMethodType.getReturnType())
.subclass(factoryMethodType.getReturnType(), ConstructorStrategy.Default.NO_CONSTRUCTORS)
.modifiers(SyntheticState.SYNTHETIC, TypeManifestation.FINAL, implementedMethodType.getParameterTypes().isEmpty()
? Visibility.PUBLIC
: Visibility.PACKAGE_PRIVATE)
.name(lambdaClassName);
int index = 0;
for (TypeDescription parameterTypes : factoryMethodType.getParameterTypes()) {
for (TypeDescription parameterTypes : implementedMethodType.getParameterTypes()) {
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)
.withParameters(factoryMethodType.getParameterTypes())
.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()))
.method(named(functionalMethodName).and(takesArguments(factoryMethodType.getParameterTypes())).and(returns(factoryMethodType.getReturnType())))
.intercept(new LambdaMethodImplementation(lambdaImplementationHandle))
.method(named(functionalMethodName)
.and(takesArguments(implementedMethodType.getParameterTypes()))
.and(returns(implementedMethodType.getReturnType())))
.intercept(new LambdaMethodImplementation(targetMethodHandle))
// TODO: Serialization
.make()
.getBytes();
for (ClassFileTransformer classFileTransformer : CLASS_FILE_TRANSFORMERS) {
byte[] transformedClassFile = classFileTransformer.transform(lookupClass.getClassLoader(),
for (ClassFileTransformer classFileTransformer : classFileTransformers) {
byte[] transformedClassFile = classFileTransformer.transform(lookupType.getClassLoader(),
lambdaClassName.replace('.', '/'),
NOT_PREVIOUSLY_DEFINED,
lookupClass.getProtectionDomain(),
lookupType.getProtectionDomain(),
classFile);
classFile = transformedClassFile == null
? 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 {
engine.apply(instrumentation, classDefinitions);
ClassInjector classInjector = classLoader == null
? bootstrapInjection.make(instrumentation)
: new ClassInjector.UsingReflection(classLoader);
loadedClasses.putAll(classInjector.inject(unloadedClasses));
if (!unloadedClasses.isEmpty()) {
loadedClasses.putAll((classLoader == null
? bootstrapInjection.make(instrumentation)
: new ClassInjector.UsingReflection(classLoader)).inject(unloadedClasses));
}
} catch (ClassNotFoundException exception) {
throw new IllegalArgumentException("Could not locate classes for redefinition", exception);
} catch (UnmodifiableClassException exception) {
Expand Down

0 comments on commit e9a3d4b

Please sign in to comment.