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)

Reply via email to