// Copyright © 2013-2014 Esko Luontola <www.orfjackal.net> // This software is released under the Apache License 2.0. // The license text is at http://www.apache.org/licenses/LICENSE-2.0 package net.orfjackal.retrolambda.lambdas; import org.objectweb.asm.*; import java.lang.invoke.*; import java.lang.reflect.Constructor; import java.util.*; import java.util.concurrent.*; public class LambdaReifier { // These globals are used for communicating with the Java agent which // is spying on the LambdaMetafactory's dynamically generated bytecode. // We expect only one class being processed at a time, so it should // be an error if these collections contain more than one element. private static final BlockingDeque<Handle> currentLambdaImplMethod = new LinkedBlockingDeque<>(1); private static final BlockingDeque<Handle> currentLambdaAccessMethod = new LinkedBlockingDeque<>(1); private static final BlockingDeque<Class<?>> currentInvoker = new LinkedBlockingDeque<>(1); private static final BlockingDeque<Type> currentInvokedType = new LinkedBlockingDeque<>(1); private static final BlockingDeque<String> currentLambdaClass = new LinkedBlockingDeque<>(1); public static LambdaFactoryMethod reifyLambdaClass(Handle lambdaImplMethod, Handle lambdaAccessMethod, Class<?> invoker, String invokedName, Type invokedType, Handle bsm, Object[] bsmArgs) { try { setLambdaImplMethod(lambdaImplMethod); setLambdaAccessMethod(lambdaAccessMethod); setInvoker(invoker); setInvokedType(invokedType); // Causes the lambda class to be loaded. Retrolambda's Java agent // will detect it, save it to a file and tell us (via the globals // in this class) that what the name of the lambda class was. callBootstrapMethod(invoker, invokedName, invokedType, bsm, bsmArgs); return getLambdaFactoryMethod(); } catch (Throwable t) { throw new RuntimeException("Failed to backport lambda or method reference: " + lambdaImplMethod, t); } finally { resetGlobals(); } } private static void setLambdaImplMethod(Handle lambdaImplMethod) { currentLambdaImplMethod.push(lambdaImplMethod); } private static void setLambdaAccessMethod(Handle lambdaAccessMethod) { currentLambdaAccessMethod.push(lambdaAccessMethod); } private static void setInvoker(Class<?> lambdaInvoker) { currentInvoker.push(lambdaInvoker); } private static void setInvokedType(Type invokedType) { currentInvokedType.push(invokedType); } public static void setLambdaClass(String lambdaClass) { currentLambdaClass.push(lambdaClass); } public static boolean isLambdaClassToReify(String className) { Class<?> invoker = currentInvoker.peekFirst(); return invoker != null && className.startsWith(Type.getInternalName(invoker)) && LambdaNaming.LAMBDA_CLASS.matcher(className).matches(); } public static Handle getLambdaImplMethod() { return currentLambdaImplMethod.getFirst(); } public static Handle getLambdaAccessMethod() { return currentLambdaAccessMethod.getFirst(); } public static LambdaFactoryMethod getLambdaFactoryMethod() { String lambdaClass = currentLambdaClass.getFirst(); Type invokedType = currentInvokedType.getFirst(); return new LambdaFactoryMethod(lambdaClass, invokedType); } private static void resetGlobals() { currentLambdaImplMethod.clear(); currentLambdaAccessMethod.clear(); currentInvoker.clear(); currentInvokedType.clear(); currentLambdaClass.clear(); } private static CallSite callBootstrapMethod(Class<?> invoker, String invokedName, Type invokedType, Handle bsm, Object[] bsmArgs) throws Throwable { ClassLoader cl = invoker.getClassLoader(); MethodHandles.Lookup caller = getLookup(invoker); List<Object> args = new ArrayList<>(); args.add(caller); args.add(invokedName); args.add(Types.toMethodType(invokedType, cl)); for (Object arg : bsmArgs) { args.add(Types.asmToJdkType(arg, cl, caller)); } MethodHandle bootstrapMethod = Types.toMethodHandle(bsm, cl, caller); return (CallSite) bootstrapMethod.invokeWithArguments(args); } private static MethodHandles.Lookup getLookup(Class<?> targetClass) throws Exception { Constructor<MethodHandles.Lookup> ctor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class); ctor.setAccessible(true); return ctor.newInstance(targetClass); } }