Andrey Loskutov created BCEL-378: ------------------------------------ Summary: BCEL doesn't properly write lambdas Key: BCEL-378 URL: https://issues.apache.org/jira/browse/BCEL-378 Project: Commons BCEL Issue Type: Bug Components: Main Affects Versions: 6.10.0 Reporter: Andrey Loskutov Attachments: javap_bcel.txt, javap_javac.txt
Follow up on BCEL-377. It looks like BCEl is able to *read* but unable to *write* bytecode related to lambdas. This is visible by disassembling class files generated by BCEL and javac for https://github.com/apache/commons-bcel/blob/master/src/test/resources/Java8Example.java as it is done by https://github.com/apache/commons-bcel/blob/master/src/test/java/org/apache/bcel/util/BCELifierTest.java After Java 22, also javap reports errors on such classes. ASM would generate this code for the lambda in the Java8Example: {code} .filter((String s) -> s.length() > 2) {code} {code:title=ASMifier version} methodVisitor.visitInvokeDynamicInsn("test", "()Ljava/util/function/Predicate;", new Handle(Opcodes.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;", false), new Object[]{Type.getType("(Ljava/lang/Object;)Z"), new Handle(Opcodes.H_INVOKESTATIC, "Java8Example", "lambda$0", "(Ljava/lang/String;)Z", true), Type.getType("(Ljava/lang/String;)Z")}); {code} BCEL however seem to do way less for same instruction: {code:title=BCELifier version} il.append(_factory.createInvoke("test", "test", new ObjectType("java.util.function.Predicate"), Type.NO_ARGS, Const.INVOKEDYNAMIC)); {code} I believe org.apache.bcel.generic.INVOKEDYNAMIC is not properly implemented. I don't see anywhere references to "BootstrapMethods" https://stackoverflow.com/questions/30733557/what-is-a-bootstrap-method ASM does this: https://gitlab.ow2.org/asm/asm/-/blob/master/asm/src/main/java/org/objectweb/asm/MethodWriter.java?ref_type=heads#L1066-1092 Note symbolTable.addConstantInvokeDynamic(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); This calls addBootstrapMethod https://gitlab.ow2.org/asm/asm/-/blob/master/asm/src/main/java/org/objectweb/asm/SymbolTable.java?ref_type=heads#L946-L954 I don't see anything comparable coming in BCEL. I would expect code below doing something like adding bootstrap method handles but I don't see it: * org.apache.bcel.util.BCELifier.visitJavaClass(JavaClass) * or org.apache.bcel.generic.ClassGen * or org.apache.bcel.generic.InstructionFactory.createInvoke(String, String, Type, Type[], short, boolean) Here the javap output files produced by running *verbose* javap command on javac generated class file and on BCEL generated class file. {code} /usr/lib/jvm/java-25/bin/javap -v -p -c -s -verify /data/javac/Java8Example.class > /data/javac/javap_javac.txt /usr/lib/jvm/java-25/bin/javap -v -p -c -s -verify /data/bcel/Java8Example.class > /data/bcel/javap_bcel.txt {code} Note "BootstrapMethods" entry added by javac and missing in BCEL version. Note also "InvokeDynamic #0" reference to the first bootstrap method in the class: {code} #70 = Utf8 BootstrapMethods #71 = MethodType #72 // (Ljava/lang/Object;)Z #72 = Utf8 (Ljava/lang/Object;)Z #73 = MethodHandle 6:#74 // REF_invokeStatic Java8Example.lambda$hello$0:(Ljava/lang/String;)Z #74 = InterfaceMethodref #57.#75 // Java8Example.lambda$hello$0:(Ljava/lang/String;)Z #75 = NameAndType #65:#66 // lambda$hello$0:(Ljava/lang/String;)Z #76 = MethodType #66 // (Ljava/lang/String;)Z #77 = MethodHandle 6:#78 // REF_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; #78 = Methodref #79.#80 // 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; #79 = Class #81 // java/lang/invoke/LambdaMetafactory #80 = NameAndType #82:#83 // 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; #81 = Utf8 java/lang/invoke/LambdaMetafactory #82 = Utf8 metafactory #83 = Utf8 (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; #84 = Utf8 InnerClasses #85 = Class #86 // java/lang/invoke/MethodHandles$Lookup #86 = Utf8 java/lang/invoke/MethodHandles$Lookup #87 = Class #88 // java/lang/invoke/MethodHandles #88 = Utf8 java/lang/invoke/MethodHandles #89 = Utf8 Lookup ... 36: invokedynamic #33, 0 // InvokeDynamic #0:test:()Ljava/util/function/Predicate; ... BootstrapMethods: 0: #77 REF_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; Method arguments: #71 (Ljava/lang/Object;)Z #73 REF_invokeStatic Java8Example.lambda$hello$0:(Ljava/lang/String;)Z #76 (Ljava/lang/String;)Z {code} These are abscent in BCEL version. [^javap_bcel.txt] [^javap_javac.txt] I assume the problem was *always* there since introduction of INVOKEDYNAMIC and just uncovered by the new javap implementation in Java 22+. -- This message was sent by Atlassian Jira (v8.20.10#820010)