package me.qmx.jitescript; import static java.util.Arrays.asList; import static me.qmx.jitescript.util.CodegenUtils.ci; import static org.objectweb.asm.Opcodes.ACC_STATIC; import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC; import static org.objectweb.asm.Opcodes.H_INVOKESTATIC; import static org.objectweb.asm.Opcodes.H_INVOKEVIRTUAL; import static org.objectweb.asm.Type.getMethodType; import java.util.ArrayList; import java.util.List; import org.objectweb.asm.Handle; public class LambdaBlock { public static final Handle METAFACTORY = new Handle( H_INVOKESTATIC, "java/lang/invoke/LambdaMetafactory", "metafactory", "(Ljava/lang/invoke/MethodHandles$Lookup;" + "Ljava/lang/String;" + "Ljava/lang/invoke/MethodType;" + "Ljava/lang/invoke/MethodType;" + "Ljava/lang/invoke/MethodHandle;" + "Ljava/lang/invoke/MethodType;" + ")Ljava/lang/invoke/CallSite;" ); /** The argument types to capture off the stack. */ private List<String> captureArguments = new ArrayList<String>(); /** Access level of the implementation method. */ private int implementationAccess; /** Signature of the implementation method. */ private String implementationSignature; /** Code for the implementation method. */ private CodeBlock implementationCode; /** The functional interface type. */ private String interfaceType; /** The functional interface method name. */ private String interfaceMethod; /** The functional interface signature. */ private String interfaceSignature; /** The specialized functional interface method signature. */ private String specializedSignature; private String lambdaName; public LambdaBlock() { // intentionally empty } public LambdaBlock(String lambdaName) { this.lambdaName = lambdaName; } /** * Applies this lambda block to the given class and code block, inserting the associated invokedynamic instruction. * * @param jiteClass The JiteClass to define the lambda within. * @param block The code block referencing the lambda. */ public void apply(JiteClass jiteClass, CodeBlock block) { int handleType = ((implementationAccess & ACC_STATIC) == ACC_STATIC) ? H_INVOKESTATIC : H_INVOKEVIRTUAL; if (lambdaName == null) { lambdaName = jiteClass.reserveLambda(); } jiteClass.defineMethod(lambdaName, implementationAccess, implementationSignature, implementationCode); block.invokedynamic(interfaceMethod, getCallSiteSignature(), METAFACTORY, getMethodType(interfaceSignature), new Handle(handleType, jiteClass.getClassName(), lambdaName, implementationSignature), getMethodType(specializedSignature == null ? interfaceSignature : specializedSignature) ); } /** * Specifies the argument types to capture off the stack. * * @param captureArguments The argument types. * @return The lambda block. */ public LambdaBlock capture(Class<?>... captureArguments) { this.captureArguments = new ArrayList<String>(); for (Class<?> argType : captureArguments) { this.captureArguments.add(ci(argType)); } return this; } /** * Specifies the argument types to capture off the stack. * * @param captureArguments The array of argument types. * @return The lambda block. */ public LambdaBlock capture(String... captureArguments) { this.captureArguments = new ArrayList<String>(asList(captureArguments)); return this; } /** * Defines the method to delegate the lambda to. * * @param implementationAccess The access level of the method. * @param implementationSignature The signature of the method. * @param implementationCode The code block for the method. * @return The lambda block. */ public LambdaBlock delegateTo(int implementationAccess, String implementationSignature, CodeBlock implementationCode) { this.implementationSignature = implementationSignature; this.implementationAccess = implementationAccess | ACC_SYNTHETIC; this.implementationCode = implementationCode; return this; } /** * Declares the functional interface type, method name, and method signature for the lambda. * * @param interfaceType The interface type. * @param interfaceMethod The method to implement. * @param interfaceSignature The signature of the method. * @return The lambda block. */ public LambdaBlock function(String interfaceType, String interfaceMethod, String interfaceSignature) { this.interfaceType = interfaceType; this.interfaceMethod = interfaceMethod; this.interfaceSignature = interfaceSignature; return this; } /** * Specializes the interface method type. * * @param specializedSignature The specialized method signature. * @return The lambda block. */ public LambdaBlock specialize(String specializedSignature) { this.specializedSignature = specializedSignature; return this; } /** * Generates the call site signature. * * @return signature */ private String getCallSiteSignature() { StringBuilder builder = new StringBuilder(); for (String arg : captureArguments) { builder.append(arg); } return "(" + builder.toString() + ")L" + interfaceType + ";"; } }