Skip to content

consider making lambda implementation methods non public in bytecode #111

Closed
@retronym

Description

@retronym

(I thought I already opened a ticket for this, but I can't find it.)

We chose to make lambda implementation methods bytecode public (they are initialily Scala-private, but we call makeNotPrivate in Delambdafy to name mangle and make them bytecode public).

This was done for two reasons:

  • to make it possible to generically deserialize them in LambdaDeserializer, without having to generate deserialization code per-lambda like Javac does.
  • to enable closure inlining across class boundaries.

The first reason turned out to be unnecessary; the current design can deserialize lambdas that target private methods.

The second reason is still valid. I removed the call to makeNotPrivate in retronym:ticket/SD-111, and noted test failures in:

[error] Failed: Total 727, Failed 8, Errors 0, Passed 716, Skipped 3
[error] Failed tests:
[error]     scala.tools.nsc.backend.jvm.opt.ClosureOptimizerTest
[error]     scala.tools.nsc.backend.jvm.opt.InlinerTest
[error]     scala.tools.nsc.backend.jvm.opt.InlineWarningTest
[error]     scala.issues.OptimizedBytecodeTest
[error] (junit/test:test) sbt.TestsFailedException: Tests unsuccessful

for instance:

[error] Test scala.tools.nsc.backend.jvm.opt.InlinerTest.inlineIndyLambda failed: java.lang.AssertionError: assertion failed: The compiler issued non-allowed warnings or errors:
[error] pos: source-unitTestSource.scala,line-13,offset=219 M$::m(Ljava/lang/String;)Ljava/lang/String; is annotated @inline but could not be inlined:
[error] The callee M$::m(Ljava/lang/String;)Ljava/lang/String; contains the instruction INVOKEDYNAMIC apply()Lscala/runtime/java8/JFunction1; [
[error]       // handle kind 0x6 : INVOKESTATIC
[error]       java/lang/invoke/LambdaMetafactory.altMetafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
[error]       // arguments:
[error]       (Ljava/lang/Object;)Ljava/lang/Object;,
[error]       // handle kind 0x6 : INVOKESTATIC
[error]       M$.$anonfun$1(Ljava/lang/String;)Ljava/lang/String;,
[error]       (Ljava/lang/String;)Ljava/lang/String;,
[error]       3,
[error]       1,
[error]       Lscala/Serializable;.class,
[error]       0
[error]     ]
[error] that would cause an IllegalAccessError when inlined into class C. WARNING, took 0.136 sec

The motivation to make the methods private:

  • The "Do as javac does" principle
  • Avoiding leaking implementation details in the the Java view of Scala APIs.
  • Avoid needing to name mangle anonfun method names, which pollute stack traces that involve function invocations.

Not sure if we can find a way to make them really private if we want to support cross class inlining. Another possibility would be to make them public static (with an explicit this parameter.).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions