Description
(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.).