Skip to content

Commit

Permalink
isAnonymousClass/Function for delambdafy classes is not true
Browse files Browse the repository at this point in the history
Ydelambdafy:method lambda classes are not anonymous classes, and not
anonymous function classes either. They are somethig new, so there's
a new predicate isDelambdafyFunction.

They are not anonymous classes (or functions) because anonymous
classes in Java speak are nested. Delambdafy classes are always
top-level, they are just synthetic.

Before this patch, isAnonymous was sometimes accidentailly true: if
the lambda is nested in an anonymous class. Now it's always false.
  • Loading branch information
lrytz committed Sep 12, 2014
1 parent 2ac6dc0 commit 63207e1
Show file tree
Hide file tree
Showing 9 changed files with 52 additions and 33 deletions.
9 changes: 7 additions & 2 deletions build.xml
Expand Up @@ -1512,7 +1512,12 @@ TODO:
<!-- ===========================================================================
BINARY COMPATIBILITY TESTING
============================================================================ -->
<target name="bc.init" depends="init" unless="maven-deps-done-mima">
<target name="bc.init" depends="init" if="test.bc.skip">
<!-- if test.bc.skip is set, make sure that pc.prepare is not executed either -->
<property name="maven-deps-done-mima" value="true"/>
</target>

<target name="bc.prepare" depends="bc.init" unless="maven-deps-done-mima">
<property name="bc-reference-version" value="2.11.0"/>

<property name="bc-build.dir" value="${build.dir}/bc"/>
Expand All @@ -1530,7 +1535,7 @@ TODO:
</target>

<target name="test.bc-opt" description="Optimized version of test.bc."> <optimized name="test.bc"/></target>
<target name="test.bc" depends="bc.init, pack.lib, pack.reflect">
<target name="test.bc" depends="bc.prepare, pack.lib, pack.reflect" unless="test.bc.skip">
<bc.check project="library"/>
<bc.check project="reflect"/>
</target>
Expand Down
13 changes: 5 additions & 8 deletions src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
Expand Up @@ -22,14 +22,11 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
* null.
*/
def isAnonymousOrLocalClass(classSym: Symbol): Boolean = {
def isDelambdafyLambdaClass(classSym: Symbol): Boolean = {
classSym.isAnonymousFunction && (settings.Ydelambdafy.value == "method")
}

assert(classSym.isClass, s"not a class: $classSym")

!isDelambdafyLambdaClass(classSym) &&
(classSym.isAnonymousClass || !classSym.originalOwner.isClass)
val res = (classSym.isAnonymousClass || !classSym.originalOwner.isClass)
// lambda classes are always top-level classes.
if (res) assert(!classSym.isDelambdafyFunction)
res
}

/**
Expand Down Expand Up @@ -81,7 +78,7 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
final case class EnclosingMethodEntry(owner: String, name: String, methodDescriptor: String)

/**
* If data for emitting an EnclosingMethod attribute. None if `classSym` is a member class (not
* Data for emitting an EnclosingMethod attribute. None if `classSym` is a member class (not
* an anonymous or local class). See doc in BTypes.
*
* The class is parametrized by two functions to obtain a bytecode class descriptor for a class
Expand Down
28 changes: 16 additions & 12 deletions src/compiler/scala/tools/nsc/transform/Delambdafy.scala
Expand Up @@ -245,18 +245,22 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
// - make `anonClass.isAnonymousClass` true.
// - use `newAnonymousClassSymbol` or push the required variations into a similar factory method
// - reinstate the assertion in `Erasure.resolveAnonymousBridgeClash`
val suffix = "$lambda$" + (
val suffix = nme.DELAMBDAFY_LAMBDA_CLASS_NAME + "$" + (
if (funOwner.isPrimaryConstructor) ""
else "$" + funOwner.name + "$"
)
val name = unit.freshTypeName(s"${oldClass.name.decode}$suffix")
val oldClassPart = oldClass.name.decode
// make sure the class name doesn't contain $anon, otherwsie isAnonymousClass/Function may be true
val name = unit.freshTypeName(s"$oldClassPart$suffix".replace("$anon", "$nestedInAnon"))

val anonClass = pkg newClassSymbol(name, originalFunction.pos, FINAL | SYNTHETIC) addAnnotation SerialVersionUIDAnnotation
anonClass setInfo ClassInfoType(parents, newScope, anonClass)
val lambdaClass = pkg newClassSymbol(name, originalFunction.pos, FINAL | SYNTHETIC) addAnnotation SerialVersionUIDAnnotation
lambdaClass setInfo ClassInfoType(parents, newScope, lambdaClass)
assert(!lambdaClass.isAnonymousClass && !lambdaClass.isAnonymousFunction, "anonymous class name: "+ lambdaClass.name)
assert(lambdaClass.isDelambdafyFunction, "not lambda class name: " + lambdaClass.name)

val captureProxies2 = new LinkedHashMap[Symbol, TermSymbol]
captures foreach {capture =>
val sym = anonClass.newVariable(capture.name.toTermName, capture.pos, SYNTHETIC)
val sym = lambdaClass.newVariable(capture.name.toTermName, capture.pos, SYNTHETIC)
sym setInfo capture.info
captureProxies2 += ((capture, sym))
}
Expand All @@ -266,30 +270,30 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
val thisProxy = {
val target = targetMethod(originalFunction)
if (thisReferringMethods contains target) {
val sym = anonClass.newVariable(nme.FAKE_LOCAL_THIS, originalFunction.pos, SYNTHETIC)
val sym = lambdaClass.newVariable(nme.FAKE_LOCAL_THIS, originalFunction.pos, SYNTHETIC)
sym.info = oldClass.tpe
sym
} else NoSymbol
}

val decapturify = new DeCapturifyTransformer(captureProxies2, unit, oldClass, anonClass, originalFunction.symbol.pos, thisProxy)
val decapturify = new DeCapturifyTransformer(captureProxies2, unit, oldClass, lambdaClass, originalFunction.symbol.pos, thisProxy)

val accessorMethod = createAccessorMethod(thisProxy, originalFunction)

val decapturedFunction = decapturify.transform(originalFunction).asInstanceOf[Function]

val members = (optionSymbol(thisProxy).toList ++ (captureProxies2 map (_._2))) map {member =>
anonClass.info.decls enter member
lambdaClass.info.decls enter member
ValDef(member, gen.mkZero(member.tpe)) setPos decapturedFunction.pos
}

// constructor
val constr = createConstructor(anonClass, members)
val constr = createConstructor(lambdaClass, members)

// apply method with same arguments and return type as original lambda.
val applyMethodDef = createApplyMethod(anonClass, decapturedFunction, accessorMethod, thisProxy)
val applyMethodDef = createApplyMethod(lambdaClass, decapturedFunction, accessorMethod, thisProxy)

val bridgeMethod = createBridgeMethod(anonClass, originalFunction, applyMethodDef)
val bridgeMethod = createBridgeMethod(lambdaClass, originalFunction, applyMethodDef)

def fulldef(sym: Symbol) =
if (sym == NoSymbol) sym.toString
Expand All @@ -305,7 +309,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
val body = members ++ List(constr, applyMethodDef) ++ bridgeMethod

// TODO if member fields are private this complains that they're not accessible
(localTyper.typedPos(decapturedFunction.pos)(ClassDef(anonClass, body)).asInstanceOf[ClassDef], thisProxy, accessorMethod)
(localTyper.typedPos(decapturedFunction.pos)(ClassDef(lambdaClass, body)).asInstanceOf[ClassDef], thisProxy, accessorMethod)
}

val (anonymousClassDef, thisProxy, accessorMethod) = makeAnonymousClass
Expand Down
23 changes: 12 additions & 11 deletions src/reflect/scala/reflect/internal/StdNames.scala
Expand Up @@ -99,17 +99,18 @@ trait StdNames {

val SINGLETON_SUFFIX: String = ".type"

val ANON_CLASS_NAME: NameType = "$anon"
val ANON_FUN_NAME: NameType = "$anonfun"
val EMPTY: NameType = ""
val EMPTY_PACKAGE_NAME: NameType = "<empty>"
val IMPL_CLASS_SUFFIX = "$class"
val IMPORT: NameType = "<import>"
val MODULE_SUFFIX_NAME: NameType = MODULE_SUFFIX_STRING
val MODULE_VAR_SUFFIX: NameType = "$module"
val PACKAGE: NameType = "package"
val ROOT: NameType = "<root>"
val SPECIALIZED_SUFFIX: NameType = "$sp"
val ANON_CLASS_NAME: NameType = "$anon"
val DELAMBDAFY_LAMBDA_CLASS_NAME: NameType = "$lambda"
val ANON_FUN_NAME: NameType = "$anonfun"
val EMPTY: NameType = ""
val EMPTY_PACKAGE_NAME: NameType = "<empty>"
val IMPL_CLASS_SUFFIX = "$class"
val IMPORT: NameType = "<import>"
val MODULE_SUFFIX_NAME: NameType = MODULE_SUFFIX_STRING
val MODULE_VAR_SUFFIX: NameType = "$module"
val PACKAGE: NameType = "package"
val ROOT: NameType = "<root>"
val SPECIALIZED_SUFFIX: NameType = "$sp"

// value types (and AnyRef) are all used as terms as well
// as (at least) arguments to the @specialize annotation.
Expand Down
1 change: 1 addition & 0 deletions src/reflect/scala/reflect/internal/Symbols.scala
Expand Up @@ -789,6 +789,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
isMethod && owner.isDerivedValueClass && !isParamAccessor && !isConstructor && !hasFlag(SUPERACCESSOR) && !isMacro && !isSpecialized

final def isAnonymousFunction = isSynthetic && (name containsName tpnme.ANON_FUN_NAME)
final def isDelambdafyFunction = isSynthetic && (name containsName tpnme.DELAMBDAFY_LAMBDA_CLASS_NAME)
final def isDefinedInPackage = effectiveOwner.isPackageClass
final def needsFlatClasses = phase.flatClasses && rawowner != NoSymbol && !rawowner.isPackageClass

Expand Down
1 change: 1 addition & 0 deletions test/files/run/delambdafyLambdaClassNames.check
@@ -0,0 +1 @@
A$$nestedInAnon$1$lambda$$run$1
1 change: 1 addition & 0 deletions test/files/run/delambdafyLambdaClassNames.flags
@@ -0,0 +1 @@
-Ybackend:GenBCode -Ydelambdafy:method
5 changes: 5 additions & 0 deletions test/files/run/delambdafyLambdaClassNames/A_1.scala
@@ -0,0 +1,5 @@
class A {
def f = new Runnable {
def run(): Unit = List(1,2).foreach(println)
}
}
4 changes: 4 additions & 0 deletions test/files/run/delambdafyLambdaClassNames/Test.scala
@@ -0,0 +1,4 @@
object Test extends App {
val c = Class.forName("A$$nestedInAnon$1$lambda$$run$1")
println(c.getName)
}

0 comments on commit 63207e1

Please sign in to comment.