3535import org .objectweb .asm .Label ;
3636import org .objectweb .asm .MethodVisitor ;
3737import org .objectweb .asm .Opcodes ;
38+ import org .objectweb .asm .Type ;
3839import org .objectweb .asm .TypePath ;
3940import org .objectweb .asm .commons .JSRInlinerAdapter ;
4041
@@ -49,6 +50,7 @@ public class Parser extends ClassVisitor {
4950 private String clsName ;
5051 private static String [] nativeSources ;
5152 private static List <ByteCodeClass > classes = new ArrayList <ByteCodeClass >();
53+ private int lambdaCounter ;
5254 public static void cleanup () {
5355 nativeSources = null ;
5456 classes .clear ();
@@ -836,6 +838,170 @@ public void visitJumpInsn(int opcode, Label label) {
836838
837839 @ Override
838840 public void visitInvokeDynamicInsn (String name , String desc , Handle bsm , Object ... bsmArgs ) {
841+ if ("java/lang/invoke/LambdaMetafactory" .equals (bsm .getOwner ()) &&
842+ ("metafactory" .equals (bsm .getName ()) || "altMetafactory" .equals (bsm .getName ()))) {
843+
844+ // 1. Generate a unique class name for the lambda
845+ String lambdaClassName = clsName + "_lambda_" + (lambdaCounter ++);
846+
847+ // 2. Create the ByteCodeClass for the lambda
848+ ByteCodeClass lambdaClass = new ByteCodeClass (lambdaClassName , lambdaClassName .replace ('_' , '/' ));
849+ lambdaClass .setBaseClass ("java/lang/Object" );
850+
851+ // The interface implemented is the return type of the invokedynamic descriptor
852+ Type invokedType = Type .getMethodType (desc );
853+ Type interfaceType = invokedType .getReturnType ();
854+ lambdaClass .setBaseInterfaces (new String []{interfaceType .getInternalName ()});
855+
856+ // 3. Add fields for captured arguments
857+ Type [] capturedArgs = invokedType .getArgumentTypes ();
858+ for (int i = 0 ; i < capturedArgs .length ; i ++) {
859+ String fieldName = "arg$" + (i + 1 );
860+ String fieldDesc = capturedArgs [i ].getDescriptor ();
861+ ByteCodeField field = new ByteCodeField (lambdaClassName , Opcodes .ACC_PRIVATE | Opcodes .ACC_FINAL , fieldName , fieldDesc , null , null );
862+ lambdaClass .addField (field );
863+ }
864+
865+ // 4. Add Constructor
866+ StringBuilder ctorDesc = new StringBuilder ("(" );
867+ for (Type t : capturedArgs ) {
868+ ctorDesc .append (t .getDescriptor ());
869+ }
870+ ctorDesc .append (")V" );
871+
872+ BytecodeMethod ctor = new BytecodeMethod (lambdaClassName , Opcodes .ACC_PUBLIC , "<init>" , ctorDesc .toString (), null , null );
873+ lambdaClass .addMethod (ctor );
874+
875+ // Constructor body (we need to generate instructions manually)
876+ // ALOAD 0
877+ // INVOKESPECIAL java/lang/Object.<init>
878+ // ... assign fields ...
879+ // RETURN
880+
881+ ctor .addInstruction (Opcodes .ALOAD ); // 25
882+ ctor .addVariableOperation (Opcodes .ALOAD , 0 );
883+ ctor .addInvoke (Opcodes .INVOKESPECIAL , "java/lang/Object" , "<init>" , "()V" , false );
884+
885+ int varIndex = 1 ;
886+ for (int i = 0 ; i < capturedArgs .length ; i ++) {
887+ ctor .addInstruction (Opcodes .ALOAD );
888+ ctor .addVariableOperation (Opcodes .ALOAD , 0 ); // this
889+
890+ Type t = capturedArgs [i ];
891+ int opcode = t .getOpcode (Opcodes .ILOAD ); // correct load opcode for type
892+ ctor .addVariableOperation (opcode , varIndex );
893+ varIndex += t .getSize ();
894+
895+ String fieldName = "arg$" + (i + 1 );
896+ ctor .addField (lambdaClass , Opcodes .PUTFIELD , lambdaClassName , fieldName , t .getDescriptor ());
897+ }
898+ ctor .addInstruction (Opcodes .RETURN );
899+ ctor .setMaxes (varIndex + 1 , varIndex ); // Approximate maxes
900+
901+
902+ // 5. Implement the interface method
903+ Type samMethodType = (Type ) bsmArgs [0 ];
904+ Handle implMethod = (Handle ) bsmArgs [1 ];
905+ Type instantiatedMethodType = (Type ) bsmArgs [2 ];
906+
907+ String samMethodName = name ; // Name from invokedynamic
908+ String samMethodDesc = samMethodType .getDescriptor (); // Signature from BSM arg 0
909+
910+ BytecodeMethod interfaceMethod = new BytecodeMethod (lambdaClassName , Opcodes .ACC_PUBLIC , samMethodName , samMethodDesc , null , null );
911+ lambdaClass .addMethod (interfaceMethod );
912+
913+ // Method Body:
914+ // Load captured arguments from fields
915+ // Load method arguments
916+ // Invoke implMethod
917+ // Return result
918+
919+ // Handle Constructor Reference (special case)
920+ boolean isCtorRef = (implMethod .getTag () == Opcodes .H_NEWINVOKESPECIAL );
921+ if (isCtorRef ) {
922+ interfaceMethod .addTypeInstruction (Opcodes .NEW , implMethod .getOwner ());
923+ interfaceMethod .addInstruction (Opcodes .DUP );
924+ }
925+
926+ // Load captured args
927+ for (int i = 0 ; i < capturedArgs .length ; i ++) {
928+ interfaceMethod .addInstruction (Opcodes .ALOAD );
929+ interfaceMethod .addVariableOperation (Opcodes .ALOAD , 0 );
930+ String fieldName = "arg$" + (i + 1 );
931+ interfaceMethod .addField (lambdaClass , Opcodes .GETFIELD , lambdaClassName , fieldName , capturedArgs [i ].getDescriptor ());
932+ }
933+
934+ // Load method args
935+ Type [] samArgs = samMethodType .getArgumentTypes ();
936+ int localIndex = 1 ;
937+ for (Type t : samArgs ) {
938+ interfaceMethod .addVariableOperation (t .getOpcode (Opcodes .ILOAD ), localIndex );
939+ localIndex += t .getSize ();
940+ }
941+
942+ // Invoke implMethod
943+ int invokeOpcode ;
944+ switch (implMethod .getTag ()) {
945+ case Opcodes .H_INVOKESTATIC : invokeOpcode = Opcodes .INVOKESTATIC ; break ;
946+ case Opcodes .H_INVOKEVIRTUAL : invokeOpcode = Opcodes .INVOKEVIRTUAL ; break ;
947+ case Opcodes .H_INVOKEINTERFACE : invokeOpcode = Opcodes .INVOKEINTERFACE ; break ;
948+ case Opcodes .H_INVOKESPECIAL : invokeOpcode = Opcodes .INVOKESPECIAL ; break ;
949+ case Opcodes .H_NEWINVOKESPECIAL : invokeOpcode = Opcodes .INVOKESPECIAL ; break ;
950+ default : invokeOpcode = Opcodes .INVOKESTATIC ; // Fallback
951+ }
952+
953+ if (isCtorRef ) {
954+ interfaceMethod .addInvoke (Opcodes .INVOKESPECIAL , implMethod .getOwner (), implMethod .getName (), implMethod .getDesc (), false );
955+ } else {
956+ interfaceMethod .addInvoke (invokeOpcode , implMethod .getOwner (), implMethod .getName (), implMethod .getDesc (), implMethod .isInterface ());
957+ }
958+
959+ // Return
960+ Type returnType = samMethodType .getReturnType ();
961+ interfaceMethod .addInstruction (returnType .getOpcode (Opcodes .IRETURN ));
962+ interfaceMethod .setMaxes (20 , 20 ); // Approximation
963+
964+
965+ // 6. Add static factory method
966+ String factoryMethodName = "lambda$factory" ;
967+ String factoryDesc = desc ; // The desc of invokedynamic is (CapturedArgs)Interface.
968+
969+ // We want factory to be (CapturedArgs)LambdaClass (to match NEW output but wrapped)
970+ // Actually, replacing invokedynamic with INVOKESTATIC means the return type on stack should match
971+ // what invokedynamic promised, which is the Interface.
972+ // Our factory returns LambdaClass, which implements Interface. So it's assignment compatible.
973+ // However, the method signature in C needs to return an object pointer anyway.
974+
975+ // Let's make the factory return the class type explicitly in signature
976+ String factoryRetType = "L" + lambdaClassName + ";" ;
977+ String actualFactoryDesc = desc .substring (0 , desc .lastIndexOf (')' ) + 1 ) + factoryRetType ;
978+
979+ BytecodeMethod factory = new BytecodeMethod (lambdaClassName , Opcodes .ACC_PUBLIC | Opcodes .ACC_STATIC , factoryMethodName , actualFactoryDesc , null , null );
980+ lambdaClass .addMethod (factory );
981+
982+ factory .addTypeInstruction (Opcodes .NEW , lambdaClassName );
983+ factory .addInstruction (Opcodes .DUP );
984+
985+ // Load factory arguments (captured args)
986+ localIndex = 0 ; // Static method
987+ for (Type t : capturedArgs ) {
988+ factory .addVariableOperation (t .getOpcode (Opcodes .ILOAD ), localIndex );
989+ localIndex += t .getSize ();
990+ }
991+
992+ factory .addInvoke (Opcodes .INVOKESPECIAL , lambdaClassName , "<init>" , ctorDesc .toString (), false );
993+ factory .addInstruction (Opcodes .ARETURN );
994+ factory .setMaxes (localIndex + 2 , localIndex );
995+
996+ // 7. Register the new class
997+ classes .add (lambdaClass );
998+
999+ // 8. Replace invokedynamic with INVOKESTATIC to factory
1000+ mtd .addInvoke (Opcodes .INVOKESTATIC , lambdaClassName , factoryMethodName , actualFactoryDesc , false );
1001+
1002+ return ;
1003+ }
1004+
8391005 super .visitInvokeDynamicInsn (name , desc , bsm , bsmArgs );
8401006 }
8411007
0 commit comments