8000 Make all lambda impl methods static · retronym/scala@6b466ae · GitHub
[go: up one dir, main page]

Skip to content

Commit 6b466ae

Browse files
committed
Make all lambda impl methods static
Motivation: - Avoid introducing public virtual methods. (javac uses private methods, but we prefer to make the public to support important AOT inlining use cases) - No more need for unsightly expanded names in lambda stack traces! - CHA in on HotSpot is great at devirtualizing, but that doesn't mean we *should* emit non-virtual methods as virtual so pervasively. ``` // Entering paste mode (ctrl-D to finish) package com.acme.wizzle.wozzle; class C { def foo = () => ??? } // Exiting paste mode, now interpreting. scala> new com.acme.wizzle.wozzle.C().foo res0: () => Nothing = com.acme.wizzle.wozzle.C$$Lambda$1986/43856716@100f9bbe scala> new com.acme.wizzle.wozzle.C().foo.apply() scala.NotImplementedError: an implementation is missing at scala.Predef$.$qmark$qmark$qmark(Predef.scala:284) at com.acme.wizzle.wozzle.C.$anonfun$1(<pastie>:1) ... 30 elided scala> :paste -raw // Entering paste mode (ctrl-D to finish) package p1; class StaticAllTheThings { def foo = () => ""; def bar = () => foo; def baz = () => this } // Exiting paste mode, now interpreting. scala> :javap -private -c p1.StaticAllTheThings Compiled from "<pastie>" public class p1.StaticAllTheThings { public scala.Function0<java.lang.String> foo(); Code: 0: invokedynamic #38, 0 // InvokeDynamic #0:apply:()Lscala/Function0; 5: areturn public scala.Function0<scala.Function0<java.lang.String>> bar(); Code: 0: aload_0 1: invokedynamic #49, 0 // InvokeDynamic #1:apply:(Lp1/StaticAllTheThings;)Lscala/Function0; 6: areturn public scala.Function0<p1.StaticAllTheThings> baz(); Code: 0: aload_0 1: invokedynamic #58, 0 // InvokeDynamic #2:apply:(Lp1/StaticAllTheThings;)Lscala/Function0; 6: areturn public static final java.lang.String $anonfun$1(); Code: 0: ldc #60 // String 2: areturn public static final scala.Function0 $anonfun$2(p1.StaticAllTheThings); Code: 0: aload_0 1: invokevirtual #63 // Method foo:()Lscala/Function0; 4: areturn public static final p1.StaticAllTheThings $anonfun$3(p1.StaticAllTheThings); Code: 0: aload_0 1: areturn public p1.StaticAllTheThings(); Code: 0: aload_0 1: invokespecial #67 // Method java/lang/Object."<init>":()V 4: return private static java.lang.Object $deserializeLambda$(java.lang.invoke.SerializedLambda); Code: 0: aload_0 1: invokedynamic #79, 0 // InvokeDynamic #3:lambdaDeserialize:(Ljava/lang/invoke/SerializedLambda;)Ljava/lang/Object; 6: areturn } ```
1 parent 5318a6b commit 6b466ae

File tree

1 file changed

+31
-11
lines changed

1 file changed

+31
-11
lines changed

src/compiler/scala/tools/nsc/transform/Delambdafy.scala

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,16 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
6161

6262
private def mkLambdaMetaFactoryCall(fun: Function, target: Symbol, functionalInterface: Symbol, samUserDefined: Symbol, isSpecialized: Boolean): Tree = {
6363
val pos = fun.pos
64+
val hasSelfParam = target.firstParam.isSynthetic && target.firstParam.name == nme.SELF
65+
6466
val allCapturedArgRefs = {
6567
// find which variables are free in the lambda because those are captures that need to be
6668
// passed into the constructor of the anonymous function class
6769
val captureArgs = FreeVarTraverser.freeVarsOf(fun).iterator.map(capture =>
6870
gen.mkAttributedRef(capture) setPos pos
6971
).toList
7072

71-
if (target hasFlag STATIC) captureArgs // no `this` reference needed
73+
if (!hasSelfParam) captureArgs // no `this` reference needed
7274
else (gen.mkAttributedThis(fun.symbol.enclClass) setPos pos) :: captureArgs
7375
}
7476

@@ -90,7 +92,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
9092
}
9193

9294
// no need for adaptation when the implemented sam is of a specialized built-in function type
93-
val lambdaTarget = if (isSpecialized) target else createBoxingBridgeMethodIfNeeded(fun, target, functionalInterface, sam)
95+
val lambdaTarget = if (isSpecialized) target else createBoxingBridgeMethodIfNeeded(fun, target, hasSelfParam, functionalInterface, sam)
9496

9597
// The backend needs to know the target of the lambda and the functional interface in order
9698
// to emit the invokedynamic instruction. We pass this information as tree attachment.
@@ -118,12 +120,12 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
118120
}
119121

120122
// determine which lambda target to use with java's LMF -- create a new one if scala-specific boxing is required
121-
def createBoxingBridgeMethodIfNeeded(fun: Function, target: Symbol, functionalInterface: Symbol, sam: Symbol): Symbol = {
123+
def createBoxingBridgeMethodIfNeeded(fun: Function, target: Symbol, hasSelfParam: Boolean, functionalInterface: Symbol, sam: Symbol): Symbol = {
122124
val oldClass = fun.symbol.enclClass
123125
val pos = fun.pos
124126

125127
// At erasure, there won't be any captured arguments (they are added in constructors)
126-
val functionParamTypes = exitingErasure(target.info.paramTypes)
128+
val functionParamTypes = exitingErasure(target.info.paramTypes).drop(if (hasSelfParam) 1 else 0)
127129
val functionResultType = exitingErasure(target.info.resultType)
128130

129131
val samParamTypes = exitingErasure(sam.info.paramTypes)
@@ -210,7 +212,8 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
210212
adaptToType(unboxed, targetParamTp)
211213
}
212214

213-
gen.mkMethodCall(Select(gen.mkAttributedThis(oldClass), target), capturedArgRefs ::: functionArgRefs)
215+
val selfArg = if (hasSelfParam) gen.mkAttributedThis(oldClass) :: Nil else Nil
216+
gen.mkMethodCall(Select(gen.mkAttributedThis(oldClass), target), selfArg ::: capturedArgRefs ::: functionArgRefs)
214217
}
215218

216219
val bridge = postErasure.newTransformer(unit).transform(DefDef(methSym, List(bridgeParams.map(ValDef(_))),
@@ -223,10 +226,8 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
223226

224227
private def transformFunction(originalFunction: Function): Tree = {
225228
val target = targetMethod(originalFunction)
226-
target.makeNotPrivate(target.owner)
227-
228-
// must be done before calling createBoxingBridgeMethod and mkLambdaMetaFactoryCall
229-
if (!(target hasFlag STATIC) && !methodReferencesThis(target)) target setFlag STATIC
229+
assert(target.hasFlag(Flags.STATIC))
230+
target.setFlag(notPRIVATE)
230231

231232
val funSym = originalFunction.tpe.typeSymbolDirect
232233
// The functional interface that can be used to adapt the lambda target method `target` to the given function type.
@@ -252,11 +253,30 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
252253
// here's the main entry point of the transform
253254
override def transform(tree: Tree): Tree = tree match {
254255
// the main thing we care about is lambdas
255-
case fun: Function => super.transform(transformFunction(fun))
256+
case fun: Function =>
257+
super.transform(transformFunction(fun))
256258
case Template(_, _, _) =>
259+
def pretransform(tree: Tree): Tree = tree match {
260+
case dd: DefDef if dd.symbol.isDelambdafyTarget =>
261+
if (!dd.symbol.hasFlag(Flags.STATIC) && methodReferencesThis(dd.symbol)) {
262+
dd.symbol.setFlag(Flags.STATIC)
263+
val selfParamSym = dd.symbol.newSyntheticValueParam(dd.symbol.owner.typeConstructor, nme.SELF)
264+
dd.symbol.modifyInfo {
265+
case mt @ MethodType(params, res) => copyMethodType(mt, selfParamSym :: params, res)
266+
}
267+
val selfParam = ValDef(selfParamSym)
268+
val rhs = dd.rhs.substituteThis(dd.symbol.owner, atPos(dd.symbol.pos)(gen.mkAttributedIdent(selfParamSym)))
269+
val result = treeCopy.DefDef(dd, dd.mods, dd.name, dd.tparams, (selfParam :: dd.vparamss.head) :: Nil, dd.tpt, rhs)
270+
result
271+
} else {
272+
dd.symbol setFlag STATIC
273+
dd
274+
}
275+
case t => t
276+
}
257277
try {
258278
// during this call boxingBridgeMethods will be populated from the Function case
259-
val Template(parents, self, body) = super.transform(tree)
279+
val Template(parents, self, body) = super.transform(deriveTemplate(tree)(_.mapConserve(pretransform)))
260280
Template(parents, self, body ++ boxingBridgeMethods)
261281
} finally boxingBridgeMethods.clear()
262282
case _ => super.transform(tree)

0 commit comments

Comments
 (0)
0