diff options
author | Jason Zaugg <jzaugg@gmail.com> | 2016-05-04 21:16:41 +1000 |
---|---|---|
committer | Jason Zaugg <jzaugg@gmail.com> | 2016-06-01 11:15:48 +1000 |
commit | 0533a3df71e9c855ac68e10d060c2c87d16994e0 (patch) | |
tree | 4aa840fb0367b00a0a2dc0c1108b064e7166669c /src/compiler/scala/tools/nsc/transform/Delambdafy.scala | |
parent | 7b132f39b82e4fc47cd95eadce9e3f22da8c8d82 (diff) | |
download | scala-0533a3df71e9c855ac68e10d060c2c87d16994e0.tar.gz scala-0533a3df71e9c855ac68e10d060c2c87d16994e0.tar.bz2 scala-0533a3df71e9c855ac68e10d060c2c87d16994e0.zip |
Lambda impl methods static and more stably named
The body of lambdas is compiled into a synthetic method
in the enclosing class. Previously, this method was a public
virtual method named `fully$qualified$Class$$anonfun$n`.
For lambdas that didn't capture a `this` reference, a static
method was used.
This commit changes two aspects.
Firstly, all lambda impl methods are now emitted static.
An extra parameter is added to those that require a this
reference.
This is an improvement as it:
- allows, shorter, more readable names for the lambda impl method
- avoids pollution of the vtable of the class. Note that javac uses
private instance methods, rather than public static methods. If
we followed its lead, we would be unable to support important use
cases in our inliner
Secondly, the name of the enclosing method has been included in
the name of the lambda impl method to improve debuggability and
to improve serialization compatibility. The serialization improvement
comes from the way that fresh names for the impl methods are
allocated: adding or removing lambdas in methods not named "foo" won't
change the numbering of the `anonfun$foo$n` impl methods from methods
named "foo". This is in line with user expectations about anonymous
class and lambda serialization stability. Brian Goetz has described
this tricky area well in:
http://cr.openjdk.java.net/~briangoetz/eg-attachments/lambda-serialization.html
This commit doesn't go as far a Javac, we don't use the hash of the
lambda type info, param names, etc to map to a lambda impl method name.
As such, we are more prone to the type-1 and -2 failures described there.
However, our Scala 2.11.8 has similar characteristics, so we aren't going
backwards.
Special case in the naming: Use "new" rather than "<init>" for constructor enclosed
lambdas, as javac does.
I have also changed the way that "delambdafy target" methods are identifed.
Rather than relying on the naming convention, I have switched to using a
symbol attachment. The assumption is that we only need to identify them
from within the same compilation unit.
This means we can distinguish impl metbods for expanded functions
(ones called from an `apply` method of an ahead-of-time expanded
anonfun class), from those that truly end up as targets for lambda
metafactory. Only the latter are translated to static methods in
this patch.
Diffstat (limited to 'src/compiler/scala/tools/nsc/transform/Delambdafy.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/Delambdafy.scala | 29 |
1 files changed, 21 insertions, 8 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala index d350ca8e17..1dfc1330c6 100644 --- a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala +++ b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala @@ -61,6 +61,9 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre private def mkLambdaMetaFactoryCall(fun: Function, target: Symbol, functionalInterface: Symbol, samUserDefined: Symbol, isSpecialized: Boolean): Tree = { val pos = fun.pos + def isSelfParam(p: Symbol) = p.isSynthetic && p.name == nme.SELF + val hasSelfParam = isSelfParam(target.firstParam) + val allCapturedArgRefs = { // find which variables are free in the lambda because those are captures that need to be // passed into the constructor of the anonymous function class @@ -68,7 +71,8 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre gen.mkAttributedRef(capture) setPos pos ).toList - if (target hasFlag STATIC) captureArgs // no `this` reference needed + if (!hasSelfParam) captureArgs.filterNot(arg => isSelfParam(arg.symbol)) + else if (currentMethod.hasFlag(Flags.STATIC)) captureArgs else (gen.mkAttributedThis(fun.symbol.enclClass) setPos pos) :: captureArgs } @@ -179,7 +183,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre val numCaptures = targetParams.length - functionParamTypes.length val (targetCapturedParams, targetFunctionParams) = targetParams.splitAt(numCaptures) - val methSym = oldClass.newMethod(target.name.append("$adapted").toTermName, target.pos, target.flags | FINAL | ARTIFACT) + val methSym = oldClass.newMethod(target.name.append("$adapted").toTermName, target.pos, target.flags | FINAL | ARTIFACT | STATIC) val bridgeCapturedParams = targetCapturedParams.map(param => methSym.newSyntheticValueParam(param.tpe, param.name.toTermName)) val bridgeFunctionParams = map2(targetFunctionParams, bridgeParamTypes)((param, tp) => methSym.newSyntheticValueParam(tp, param.name.toTermName)) @@ -223,10 +227,8 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre private def transformFunction(originalFunction: Function): Tree = { val target = targetMethod(originalFunction) - target.makeNotPrivate(target.owner) - - // must be done before calling createBoxingBridgeMethod and mkLambdaMetaFactoryCall - if (!(target hasFlag STATIC) && !methodReferencesThis(target)) target setFlag STATIC + assert(target.hasFlag(Flags.STATIC)) + target.setFlag(notPRIVATE) val funSym = originalFunction.tpe.typeSymbolDirect // The functional interface that can be used to adapt the lambda target method `target` to the given function type. @@ -252,11 +254,22 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre // here's the main entry point of the transform override def transform(tree: Tree): Tree = tree match { // the main thing we care about is lambdas - case fun: Function => super.transform(transformFunction(fun)) + case fun: Function => + super.transform(transformFunction(fun)) case Template(_, _, _) => + def pretransform(tree: Tree): Tree = tree match { + case dd: DefDef if dd.symbol.isDelambdafyTarget => + if (!dd.symbol.hasFlag(STATIC) && methodReferencesThis(dd.symbol)) { + gen.mkStatic(dd, sym => sym) + } else { + dd.symbol.setFlag(STATIC) + dd + } + case t => t + } try { // during this call boxingBridgeMethods will be populated from the Function case - val Template(parents, self, body) = super.transform(tree) + val Template(parents, self, body) = super.transform(deriveTemplate(tree)(_.mapConserve(pretransform))) Template(parents, self, body ++ boxingBridgeMethods) } finally boxingBridgeMethods.clear() case _ => super.transform(tree) |