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)