Skip to content

Illegal arguments for bootstrap method for invokedynamic #15736

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ddtthh opened this issue Jul 22, 2022 · 3 comments · Fixed by #22632
Closed

Illegal arguments for bootstrap method for invokedynamic #15736

ddtthh opened this issue Jul 22, 2022 · 3 comments · Fixed by #22632

Comments

@ddtthh
Copy link
Contributor

ddtthh commented Jul 22, 2022

Compiler version

3.1.3

Description

When compiling a lambda, the arguments to the generated bootstrap method in the generated class file violate the rules given in the JDK documentation. While this does not seem to cause any issues on OpenJDK, the Android Dexer d8 cannot handle this.

According to https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/invoke/LambdaMetafactory.html interfaceMethodType and dynamicMethodType have to fullfill the following constraints:

  • Same arity
  • Argument types and return types are either the same as in the interface, or in case of reference may be subtypes.
    There is no type conversion between primitive types and reference types

In the following example, the scala compiler generates a bootstrap method call to LambdaMetafactory.altMetafactory with
interfaceMethodType: (Ljava/lang/Object;)Ljava/lang/Object;
dynamicMethodType: (Ljava/lang/Object;)Z

This clearly violates the specifiaction of LambdaMetafactory.
The dynamicMethodeType should be (Ljava/lang/Object;)Ljava/lang/Boolean;

It is ok for the MethodHandle supplied as implementation to have a MethodType of (Ljava/lang/Object;)Z, LambdaMetafactory will handle boxing, but the dynamicMethodType has to specify the boxed types.

Minimized code

class Foo[T]():
  val f = (x: T) => true

Output

Taken from javap -private -c -v

BootstrapMethods:
  0: #29 REF_invokeStatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #14 (Ljava/lang/Object;)Ljava/lang/Object;
      #19 REF_invokeStatic Foo.$init$$$anonfun$1:(Ljava/lang/Object;)Z
      #20 (Ljava/lang/Object;)Z
      #21 5
      #22 1
      #20 (Ljava/lang/Object;)Z

Expectation

Taken from javap -private -c -v

BootstrapMethods:
  0: #29 REF_invokeStatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #14 (Ljava/lang/Object;)Ljava/lang/Object;
      #19 REF_invokeStatic Foo.$init$$$anonfun$1:(Ljava/lang/Object;)Z
      #20 (Ljava/lang/Object;)Ljava/lang/Boolean;
      #21 5
      #22 1
      #20 (Ljava/lang/Object;)Z
@ddtthh ddtthh added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels Jul 22, 2022
@szymon-rd szymon-rd added area:backend and removed stat:needs triage Every issue needs to have an "area" and "itype" label labels Jul 25, 2022
ddtthh added a commit to ddtthh/scala3 that referenced this issue Feb 20, 2025
Box native instantiated method return type if sam method return type
is not a primitive type to satisfy conditions specified in
  https://docs.oracle.com/javase/8/docs/api/java/lang/invoke/LambdaMetafactory.html
Condition is not enforced by JVM but by Android ART.
@lrytz
Copy link
Member

lrytz commented Feb 21, 2025

The anonfun method in Scala 2 is also (Object)Z, but then Scala 2 emits an anonfun$adapted bridge with signature (Object)Object which is used in the indy-lambda.

Corresponding code in the compiler: https://github.com/scala/scala/blob/v2.13.16/src/compiler/scala/tools/nsc/transform/Delambdafy.scala#L162-L175

ASM of the Scala 2 compiled class:

public class Foo {
  public final static synthetic $anonfun$f$1(Ljava/lang/Object;)Z
    ...

  public final static synthetic $anonfun$f$1$adapted(Ljava/lang/Object;)Ljava/lang/Object;
    // parameter final  x
   L0
    LINENUMBER 2 L0
    ALOAD 0
    INVOKESTATIC Foo.$anonfun$f$1 (Ljava/lang/Object;)Z
    INVOKESTATIC scala/runtime/BoxesRunTime.boxToBoolean (Z)Ljava/lang/Boolean;
    ARETURN

  public <init>()V
    ...
    INVOKEDYNAMIC apply()Lscala/Function1; [
      // handle kind 0x6 : INVOKESTATIC
      java/lang/invoke/LambdaMetafactory.altMetafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
      // arguments:
      (Ljava/lang/Object;)Ljava/lang/Object;, 
      // handle kind 0x6 : INVOKESTATIC
      Foo.$anonfun$f$1$adapted(Ljava/lang/Object;)Ljava/lang/Object;, 
      (Ljava/lang/Object;)Ljava/lang/Object;, 
      1
    ]
}

@lrytz
Copy link
Member

lrytz commented Feb 21, 2025

Oh, I only saw the PR now. Will take a look, probably next week though..

@lrytz
Copy link
Member

lrytz commented Feb 25, 2025

Scala 3 also creates an $adapted bridge when it needs to unbox a parameter (https://github.com/scala/scala3/blob/3.6.3/compiler/src/dotty/tools/dotc/transform/Erasure.scala#L464-L477). For example in val f = (x: Boolean) => "".

Turns out Scala 2 is a bit more eager than needed in creating such a bridge: boxing the result value doesn't need any special casing for Scala, we can rely on the boxing adaptation implemented in LambdaMetaFactory.

lrytz pushed a commit to lrytz/scala3 that referenced this issue Mar 11, 2025
Box native instantiated method return type if sam method return type
is not a primitive type to satisfy conditions specified in
  https://docs.oracle.com/javase/8/docs/api/java/lang/invoke/LambdaMetafactory.html
Condition is not enforced by JVM but by Android ART.

Fixes scala#15736
@lrytz lrytz closed this as completed in 24d8ecb Mar 11, 2025
lrytz added a commit that referenced this issue Mar 11, 2025
Fixes #15736

Box native instantiated method return type if sam method return type is
not a primitive type to satisfy conditions specified in

https://docs.oracle.com/javase/8/docs/api/java/lang/invoke/LambdaMetafactory.html
Condition is not enforced by JVM but by Android ART.
@WojciechMazur WojciechMazur added this to the 3.7.0 milestone Mar 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants