summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Rytz <lukas.rytz@gmail.com>2015-01-21 19:57:45 +0100
committerLukas Rytz <lukas.rytz@gmail.com>2015-02-07 07:45:47 +0100
commit0bcc871e3f4c4331fb53ec0e7087669589a607d6 (patch)
tree86af55ac347552c35a8d5b9bf1a6085e9177b3fb
parentd7b11520222c9e995d27de46fecc19c5a1fa5b74 (diff)
downloadscala-0bcc871e3f4c4331fb53ec0e7087669589a607d6.tar.gz
scala-0bcc871e3f4c4331fb53ec0e7087669589a607d6.tar.bz2
scala-0bcc871e3f4c4331fb53ec0e7087669589a607d6.zip
SI-9105 Fix EnclosingMethod for classes defined in lambdas
This change fixes both GenASM and GenBCode, except for the change to renaming in LamdaLift mentioned below. The reason for an inconsistent EnclosingMethod attribute was the symbol owner chain. Initially, closure class symbols don't exist, they are only created in UnCurry (delambdafy:inline). So walking the originalOwner of a definition does not yield closure classes. The commit also fixes uses of isAnonymousClass, isAnonymousFunction and isDelambdafyFunction in two ways: 1. by phase-travelling to an early phase. after flatten, the name includes the name of outer classes, so the properties may become accidentally true (they check for a substring in the name) 2. by ensuring that the (destructive) renames during LambdaLift don't make the above properties accidentally true. This was in fact the cause for SI-8900.
-rw-r--r--src/compiler/scala/tools/nsc/CompilationUnits.scala6
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala82
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala4
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala11
-rw-r--r--src/compiler/scala/tools/nsc/transform/LambdaLift.scala21
-rw-r--r--src/reflect/scala/reflect/internal/StdNames.scala17
-rw-r--r--test/files/jvm/innerClassAttribute.check30
-rw-r--r--test/files/jvm/innerClassAttribute/Classes_1.scala38
-rw-r--r--test/files/jvm/innerClassAttribute/Test.scala78
-rw-r--r--test/files/jvm/t9105.check18
-rw-r--r--test/files/jvm/t9105.scala22
12 files changed, 286 insertions, 43 deletions
diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala
index 1a6843a249..6be1fda1b5 100644
--- a/src/compiler/scala/tools/nsc/CompilationUnits.scala
+++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala
@@ -25,9 +25,9 @@ trait CompilationUnits { global: Global =>
class CompilationUnit(val source: SourceFile) extends CompilationUnitContextApi { self =>
/** the fresh name creator */
- implicit val fresh: FreshNameCreator = new FreshNameCreator
- def freshTermName(prefix: String = "x$") = global.freshTermName(prefix)
- def freshTypeName(prefix: String) = global.freshTypeName(prefix)
+ implicit val fresh: FreshNameCreator = new FreshNameCreator
+ def freshTermName(prefix: String = nme.FRESH_TERM_NAME_PREFIX) = global.freshTermName(prefix)
+ def freshTypeName(prefix: String) = global.freshTypeName(prefix)
/** the content of the compilation unit in tree form */
var body: Tree = EmptyTree
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
index a5f33aa786..90d2fcf4dc 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
@@ -22,6 +22,23 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
}
/**
+ * Cache the value of delambdafy == "inline" for each run. We need to query this value many
+ * times, so caching makes sense.
+ */
+ object delambdafyInline {
+ private var runId = -1
+ private var value = false
+
+ def apply(): Boolean = {
+ if (runId != global.currentRunId) {
+ runId = global.currentRunId
+ value = settings.Ydelambdafy.value == "inline"
+ }
+ value
+ }
+ }
+
+ /**
* True if `classSym` is an anonymous class or a local class. I.e., false if `classSym` is a
* member class. This method is used to decide if we should emit an EnclosingMethod attribute.
* It is also used to decide whether the "owner" field in the InnerClass attribute should be
@@ -29,10 +46,59 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
*/
def isAnonymousOrLocalClass(classSym: Symbol): Boolean = {
assert(classSym.isClass, s"not a class: $classSym")
- // Here used to be an `assert(!classSym.isDelambdafyFunction)`: delambdafy lambda classes are
- // always top-level. However, SI-8900 shows an example where the weak name-based implementation
- // of isDelambdafyFunction failed (for a function declared in a package named "lambda").
- classSym.isAnonymousClass || !classSym.originalOwner.isClass
+ val r = exitingPickler(classSym.isAnonymousClass) || !classSym.originalOwner.isClass
+ if (r && settings.Ybackend.value == "GenBCode") {
+ // this assertion only holds in GenBCode. lambda lift renames symbols and may accidentally
+ // introduce `$lambda` into a class name, making `isDelambdafyFunction` true. under GenBCode
+ // we prevent this, see `nonAnon` in LambdaLift.
+ // phase travel necessary: after flatten, the name includes the name of outer classes.
+ // if some outer name contains $lambda, a non-lambda class is considered lambda.
+ assert(exitingPickler(!classSym.isDelambdafyFunction), classSym.name)
+ }
+ r
+ }
+
+ /**
+ * The next enclosing definition in the source structure. Includes anonymous function classes
+ * under delambdafy:inline, even though they are only generated during UnCurry.
+ */
+ def nextEnclosing(sym: Symbol): Symbol = {
+ val origOwner = sym.originalOwner
+ // phase travel necessary: after flatten, the name includes the name of outer classes.
+ // if some outer name contains $anon, a non-anon class is considered anon.
+ if (delambdafyInline() && sym.rawowner.isAnonymousFunction) {
+ // SI-9105: special handling for anonymous functions under delambdafy:inline.
+ //
+ // class C { def t = () => { def f { class Z } } }
+ //
+ // class C { def t = byNameMethod { def f { class Z } } }
+ //
+ // In both examples, the method f lambda-lifted into the anonfun class.
+ //
+ // In both examples, the enclosing method of Z is f, the enclosing class is the anonfun.
+ // So nextEnclosing needs to return the following chain: Z - f - anonFunClassSym - ...
+ //
+ // In the first example, the initial owner of f is a TermSymbol named "$anonfun" (note: not the anonFunClassSym!)
+ // In the second, the initial owner of f is t (no anon fun term symbol for by-name args!).
+ //
+ // In both cases, the rawowner of class Z is the anonFunClassSym. So the check in the `if`
+ // above makes sure we don't jump over the anonymous function in the by-name argument case.
+ //
+ // However, we cannot directly return the rawowner: if `sym` is Z, we need to include method f
+ // in the result. This is done by comparing the rawowners (read: lambdalift-targets) of `sym`
+ // and `sym.originalOwner`: if they are the same, then the originalOwner is "in between", and
+ // we need to return it.
+ // If the rawowners are different, the symbol was not in between. In the first example, the
+ // originalOwner of `f` is the anonfun-term-symbol, whose rawowner is C. So the nextEnclosing
+ // of `f` is its rawowner, the anonFunClassSym.
+ //
+ // In delambdafy:method we don't have that problem. The f method is lambda-lifted into C,
+ // not into the anonymous function class. The originalOwner chain is Z - f - C.
+ if (sym.originalOwner.rawowner == sym.rawowner) sym.originalOwner
+ else sym.rawowner
+ } else {
+ origOwner
+ }
}
/**
@@ -63,9 +129,9 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
def enclosingMethod(sym: Symbol): Option[Symbol] = {
if (sym.isClass || sym == NoSymbol) None
else if (sym.isMethod) Some(sym)
- else enclosingMethod(sym.originalOwner)
+ else enclosingMethod(nextEnclosing(sym))
}
- enclosingMethod(classSym.originalOwner)
+ enclosingMethod(nextEnclosing(classSym))
}
/**
@@ -76,9 +142,9 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
assert(classSym.isClass, classSym)
def enclosingClass(sym: Symbol): Symbol = {
if (sym.isClass) sym
- else enclosingClass(sym.originalOwner)
+ else enclosingClass(nextEnclosing(sym))
}
- enclosingClass(classSym.originalOwner)
+ enclosingClass(nextEnclosing(classSym))
}
final case class EnclosingMethodEntry(owner: String, name: String, methodDescriptor: String)
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
index a9bce82acd..7814ed858b 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
@@ -527,7 +527,7 @@ abstract class BTypes {
* local and anonymous classes, no matter if there is an enclosing method or not. Accordingly, the
* "class" field (see below) must be always defined, while the "method" field may be null.
*
- * NOTE: When a EnclosingMethod attribute is requried (local and anonymous classes), the "outer"
+ * NOTE: When an EnclosingMethod attribute is requried (local and anonymous classes), the "outer"
* field in the InnerClass table must be null.
*
* Fields:
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
index 94f9b585d9..bc880e002e 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
@@ -227,7 +227,9 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
}
val innerName: Option[String] = {
- if (innerClassSym.isAnonymousClass || innerClassSym.isAnonymousFunction) None
+ // phase travel necessary: after flatten, the name includes the name of outer classes.
+ // if some outer name contains $anon, a non-anon class is considered anon.
+ if (exitingPickler(innerClassSym.isAnonymousClass || innerClassSym.isAnonymousFunction)) None
else Some(innerClassSym.rawname + innerClassSym.moduleSuffix) // moduleSuffix for module classes
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
index abe3bc512c..c36afd018b 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
@@ -692,11 +692,12 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self =>
}
}
- def innerName(innerSym: Symbol): String =
- if (innerSym.isAnonymousClass || innerSym.isAnonymousFunction)
- null
- else
- innerSym.rawname + innerSym.moduleSuffix
+ def innerName(innerSym: Symbol): String = {
+ // phase travel necessary: after flatten, the name includes the name of outer classes.
+ // if some outer name contains $anon, a non-anon class is considered anon.
+ if (exitingPickler(innerSym.isAnonymousClass || innerSym.isAnonymousFunction)) null
+ else innerSym.rawname + innerSym.moduleSuffix
+ }
innerClassBuffer ++= {
val members = exitingPickler(memberClassesOf(csym))
diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala
index fa0c1f797b..e9fd6e14a9 100644
--- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala
+++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala
@@ -250,21 +250,30 @@ abstract class LambdaLift extends InfoTransform {
debuglog("renaming in %s: %s => %s".format(sym.owner.fullLocationString, originalName, sym.name))
}
+ // make sure that the name doesn't make the symbol accidentally `isAnonymousClass` (et.al) by
+ // introducing `$anon` in its name. to be cautious, we don't make this change in the default
+ // backend under 2.11.x, so only in GenBCode.
+ def nonAnon(s: String) = if (settings.Ybackend.value == "GenBCode") nme.ensureNonAnon(s) else s
+
def newName(sym: Symbol): Name = {
val originalName = sym.name
def freshen(prefix: String): Name =
if (originalName.isTypeName) unit.freshTypeName(prefix)
else unit.freshTermName(prefix)
+ val join = nme.NAME_JOIN_STRING
if (sym.isAnonymousFunction && sym.owner.isMethod) {
- freshen(sym.name + nme.NAME_JOIN_STRING + sym.owner.name + nme.NAME_JOIN_STRING)
+ freshen(sym.name + join + nonAnon(sym.owner.name.toString) + join)
} else {
+ val name = freshen(sym.name + join)
// SI-5652 If the lifted symbol is accessed from an inner class, it will be made public. (where?)
- // Generating a unique name, mangled with the enclosing class name, avoids a VerifyError
- // in the case that a sub-class happens to lifts out a method with the *same* name.
- val name = freshen("" + sym.name + nme.NAME_JOIN_STRING)
- if (originalName.isTermName && !sym.enclClass.isImplClass && calledFromInner(sym)) nme.expandedName(name.toTermName, sym.enclClass)
- else name
+ // Generating a unique name, mangled with the enclosing full class name (including
+ // package - subclass might have the same name), avoids a VerifyError in the case
+ // that a sub-class happens to lifts out a method with the *same* name.
+ if (originalName.isTermName && !sym.enclClass.isImplClass && calledFromInner(sym))
+ newTermNameCached(nonAnon(sym.enclClass.fullName('$')) + nme.EXPAND_SEPARATOR_STRING + name)
+ else
+ name
}
}
diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala
index c94ee996e4..f32b7326fe 100644
--- a/src/reflect/scala/reflect/internal/StdNames.scala
+++ b/src/reflect/scala/reflect/internal/StdNames.scala
@@ -112,6 +112,23 @@ trait StdNames {
val ROOT: NameType = "<root>"
val SPECIALIZED_SUFFIX: NameType = "$sp"
+ val NESTED_IN: String = "$nestedIn"
+ val NESTED_IN_ANON_CLASS: String = NESTED_IN + ANON_CLASS_NAME.toString.replace("$", "")
+ val NESTED_IN_ANON_FUN: String = NESTED_IN + ANON_FUN_NAME.toString.replace("$", "")
+ val NESTED_IN_LAMBDA: String = NESTED_IN + DELAMBDAFY_LAMBDA_CLASS_NAME.toString.replace("$", "")
+
+ /**
+ * Ensures that name mangling does not accidentally make a class respond `true` to any of
+ * isAnonymousClass, isAnonymousFunction, isDelambdafyFunction, e.g. by introducing "$anon".
+ */
+ def ensureNonAnon(name: String) = {
+ name
+ .replace(nme.ANON_CLASS_NAME.toString, NESTED_IN_ANON_CLASS)
+ .replace(nme.ANON_FUN_NAME.toString, NESTED_IN_ANON_FUN)
+ .replace(nme.DELAMBDAFY_LAMBDA_CLASS_NAME.toString, NESTED_IN_LAMBDA)
+ }
+
+
// value types (and AnyRef) are all used as terms as well
// as (at least) arguments to the @specialize annotation.
final val Boolean: NameType = "Boolean"
diff --git a/test/files/jvm/innerClassAttribute.check b/test/files/jvm/innerClassAttribute.check
index 20518aa49e..8395ff0b09 100644
--- a/test/files/jvm/innerClassAttribute.check
+++ b/test/files/jvm/innerClassAttribute.check
@@ -14,27 +14,27 @@ A19 / null / null
A19 / null / null
A19 / null / null
-- A20 --
-A20$$anonfun$4 / null / null / 17
+A20$$anonfun$6 / null / null / 17
fun1: attribute for itself and the two child closures `() => ()` and `() => () => 1`
-A20$$anonfun$4 / null / null / 17
-A20$$anonfun$4$$anonfun$apply$1 / null / null / 17
-A20$$anonfun$4$$anonfun$apply$3 / null / null / 17
+A20$$anonfun$6 / null / null / 17
+A20$$anonfun$6$$anonfun$apply$1 / null / null / 17
+A20$$anonfun$6$$anonfun$apply$3 / null / null / 17
fun2 () => (): itself and the outer closure
-A20$$anonfun$4 / null / null / 17
-A20$$anonfun$4$$anonfun$apply$1 / null / null / 17
+A20$$anonfun$6 / null / null / 17
+A20$$anonfun$6$$anonfun$apply$1 / null / null / 17
fun3 () => () => (): itself, the outer closure and its child closure
-A20$$anonfun$4 / null / null / 17
-A20$$anonfun$4$$anonfun$apply$3 / null / null / 17
-A20$$anonfun$4$$anonfun$apply$3$$anonfun$apply$2 / null / null / 17
+A20$$anonfun$6 / null / null / 17
+A20$$anonfun$6$$anonfun$apply$3 / null / null / 17
+A20$$anonfun$6$$anonfun$apply$3$$anonfun$apply$2 / null / null / 17
fun4: () => 1: itself and the two outer closures
-A20$$anonfun$4 / null / null / 17
-A20$$anonfun$4$$anonfun$apply$3 / null / null / 17
-A20$$anonfun$4$$anonfun$apply$3$$anonfun$apply$2 / null / null / 17
+A20$$anonfun$6 / null / null / 17
+A20$$anonfun$6$$anonfun$apply$3 / null / null / 17
+A20$$anonfun$6$$anonfun$apply$3$$anonfun$apply$2 / null / null / 17
enclosing: nested closures have the apply method of the outer closure
A20 / null / null
-A20$$anonfun$4 / apply / ()Lscala/Function0;
-A20$$anonfun$4 / apply / ()Lscala/Function0;
-A20$$anonfun$4$$anonfun$apply$3 / apply / ()Lscala/Function0;
+A20$$anonfun$6 / apply / ()Lscala/Function0;
+A20$$anonfun$6 / apply / ()Lscala/Function0;
+A20$$anonfun$6$$anonfun$apply$3 / apply / ()Lscala/Function0;
#partest -Ydelambdafy:method
-- A4 --
null / null / null
diff --git a/test/files/jvm/innerClassAttribute/Classes_1.scala b/test/files/jvm/innerClassAttribute/Classes_1.scala
index 9c3ea7f013..a2ade70c18 100644
--- a/test/files/jvm/innerClassAttribute/Classes_1.scala
+++ b/test/files/jvm/innerClassAttribute/Classes_1.scala
@@ -185,3 +185,41 @@ trait A24 extends A24Base {
override object Conc extends A24Sym
}
}
+
+class SI_9105 {
+ // the EnclosingMethod attributes depend on the delambdafy strategy (inline vs method)
+
+ // outerClass-inline enclMeth-inline outerClass-method enclMeth-method
+ val fun = () => {
+ class A // closure null (*) SI_9105 null
+ def m: Object = { class B; new B } // closure m$1 SI_9105 m$1
+ val f: Object = { class C; new C } // closure null (*) SI_9105 null
+ }
+ def met = () => {
+ class D // closure null (*) SI_9105 met
+ def m: Object = { class E; new E } // closure m$1 SI_9105 m$1
+ val f: Object = { class F; new F } // closure null (*) SI_9105 met
+ }
+
+ // (*) the originalOwner chain of A (similar for D) is: SI_9105.fun.$anonfun-value.A
+ // we can get to the anonfun-class (created by uncurry), but not to the apply method.
+ //
+ // for C and F, the originalOwner chain is fun.$anonfun-value.f.C. at later phases, the rawowner of f is
+ // an apply$sp method of the closure class. we could use that as enclosing method, but it would be unsystematic
+ // (A / D don't have an encl meth either), and also strange to use the $sp, which is a compilation artifact.
+ // So using `null` looks more like the situation in the source code: C / F are nested classes of the anon-fun, and
+ // there's no method in between.
+
+ def byName[T](op: => T) = 0
+
+ val bnV = byName {
+ class G // closure null (*) SI_9105 null
+ def m: Object = { class H; new H } // closure m$1 SI_9105 m$1
+ val f: Object = { class I; new I } // closure null (*) SI_9105 null
+ }
+ def bnM = byName {
+ class J // closure null (*) SI_9105 bnM
+ def m: Object = { class K; new K } // closure m$1 SI_9105 m$1
+ val f: Object = { class L; new L } // closure null (*) SI_9105 bnM
+ }
+}
diff --git a/test/files/jvm/innerClassAttribute/Test.scala b/test/files/jvm/innerClassAttribute/Test.scala
index 3820048cb4..30dc412ff7 100644
--- a/test/files/jvm/innerClassAttribute/Test.scala
+++ b/test/files/jvm/innerClassAttribute/Test.scala
@@ -16,6 +16,8 @@ object Test extends BytecodeTest {
loadClassNode(className).innerClasses.asScala.toList.sortBy(_.name)
}
+ def ownInnerClassNode(n: String) = innerClassNodes(n).filter(_.name == n).head
+
final case class EnclosingMethod(name: String, descriptor: String, outerClass: String)
def enclosingMethod(className: String) = {
val n = loadClassNode(className)
@@ -256,10 +258,10 @@ object Test extends BytecodeTest {
printInnerClassNodes("A20")
- val fun1 = lambdaClass("A20$$anonfun$4", "A20$lambda$1")
- val fun2 = lambdaClass("A20$$anonfun$4$$anonfun$apply$1", "A20$lambda$$$nestedInAnonfun$5$1")
- val fun3 = lambdaClass("A20$$anonfun$4$$anonfun$apply$3", "A20$lambda$$$nestedInAnonfun$5$2")
- val fun4 = lambdaClass("A20$$anonfun$4$$anonfun$apply$3$$anonfun$apply$2", "A20$lambda$$$nestedInAnonfun$7$1")
+ val fun1 = lambdaClass("A20$$anonfun$6", "A20$lambda$1")
+ val fun2 = lambdaClass("A20$$anonfun$6$$anonfun$apply$1", "A20$lambda$$$nestedInAnonfun$5$1")
+ val fun3 = lambdaClass("A20$$anonfun$6$$anonfun$apply$3", "A20$lambda$$$nestedInAnonfun$5$2")
+ val fun4 = lambdaClass("A20$$anonfun$6$$anonfun$apply$3$$anonfun$apply$2", "A20$lambda$$$nestedInAnonfun$7$1")
println("fun1: attribute for itself and the two child closures `() => ()` and `() => () => 1`")
printInnerClassNodes(fun1)
@@ -324,6 +326,73 @@ object Test extends BytecodeTest {
assertMember(defsApiImpl, "A24Base", "DefinitionsApi$class", flags = Flags.ACC_PUBLIC | Flags.ACC_ABSTRACT)
}
+ def testSI_9105() {
+ val isDelambdafyMethod = classpath.findClass("SI_9105$lambda$1").isDefined
+ if (isDelambdafyMethod) {
+ assertEnclosingMethod ("SI_9105$A$2" , "SI_9105", null , null)
+ assertEnclosingMethod ("SI_9105$B$5" , "SI_9105", "m$1", "()Ljava/lang/Object;")
+ assertEnclosingMethod ("SI_9105$C$1" , "SI_9105", null , null)
+ assertEnclosingMethod ("SI_9105$D$1" , "SI_9105", "met", "()Lscala/Function0;")
+ assertEnclosingMethod ("SI_9105$E$1" , "SI_9105", "m$3", "()Ljava/lang/Object;")
+ assertEnclosingMethod ("SI_9105$F$1" , "SI_9105", "met", "()Lscala/Function0;")
+ assertNoEnclosingMethod("SI_9105$lambda$$met$1")
+ assertNoEnclosingMethod("SI_9105$lambda$1")
+ assertNoEnclosingMethod("SI_9105")
+
+ assertLocal(innerClassNodes("SI_9105$A$2").head, "SI_9105$A$2", "A$2")
+ assertLocal(innerClassNodes("SI_9105$B$5").head, "SI_9105$B$5", "B$5")
+ assertLocal(innerClassNodes("SI_9105$C$1").head, "SI_9105$C$1", "C$1")
+ assertLocal(innerClassNodes("SI_9105$D$1").head, "SI_9105$D$1", "D$1")
+ assertLocal(innerClassNodes("SI_9105$E$1").head, "SI_9105$E$1", "E$1")
+ assertLocal(innerClassNodes("SI_9105$F$1").head, "SI_9105$F$1", "F$1")
+
+ // by-name
+ assertEnclosingMethod("SI_9105$G$1", "SI_9105", null , null)
+ assertEnclosingMethod("SI_9105$H$1", "SI_9105", "m$2", "()Ljava/lang/Object;")
+ assertEnclosingMethod("SI_9105$I$1", "SI_9105", null , null)
+ assertEnclosingMethod("SI_9105$J$1", "SI_9105", "bnM", "()I")
+ assertEnclosingMethod("SI_9105$K$2", "SI_9105", "m$4", "()Ljava/lang/Object;")
+ assertEnclosingMethod("SI_9105$L$1", "SI_9105", "bnM", "()I")
+
+ assert(innerClassNodes("SI_9105$lambda$$met$1").isEmpty)
+ assert(innerClassNodes("SI_9105$lambda$1").isEmpty)
+ assert(innerClassNodes("SI_9105").length == 12) // the 12 local classes
+ } else {
+ // comment in innerClassAttribute/Classes_1.scala explains the difference between A / C and D / F.
+ assertEnclosingMethod ("SI_9105$$anonfun$4$A$2" , "SI_9105$$anonfun$4" , null , null)
+ assertEnclosingMethod ("SI_9105$$anonfun$4$B$5" , "SI_9105$$anonfun$4" , "m$1" , "()Ljava/lang/Object;")
+ assertEnclosingMethod ("SI_9105$$anonfun$4$C$1" , "SI_9105$$anonfun$4" , null , null)
+ assertEnclosingMethod ("SI_9105$$anonfun$met$1$D$1", "SI_9105$$anonfun$met$1", null , null)
+ assertEnclosingMethod ("SI_9105$$anonfun$met$1$E$1", "SI_9105$$anonfun$met$1", "m$3" , "()Ljava/lang/Object;")
+ assertEnclosingMethod ("SI_9105$$anonfun$met$1$F$1", "SI_9105$$anonfun$met$1", null , null)
+ assertEnclosingMethod ("SI_9105$$anonfun$4" , "SI_9105" , null , null)
+ assertEnclosingMethod ("SI_9105$$anonfun$met$1" , "SI_9105" , "met" , "()Lscala/Function0;")
+ assertNoEnclosingMethod("SI_9105")
+
+ assertLocal(ownInnerClassNode("SI_9105$$anonfun$4$A$2"), "SI_9105$$anonfun$4$A$2" , "A$2")
+ assertLocal(ownInnerClassNode("SI_9105$$anonfun$4$B$5"), "SI_9105$$anonfun$4$B$5" , "B$5")
+ assertLocal(ownInnerClassNode("SI_9105$$anonfun$4$C$1"), "SI_9105$$anonfun$4$C$1" , "C$1")
+ assertLocal(ownInnerClassNode("SI_9105$$anonfun$met$1$D$1"), "SI_9105$$anonfun$met$1$D$1", "D$1")
+ assertLocal(ownInnerClassNode("SI_9105$$anonfun$met$1$E$1"), "SI_9105$$anonfun$met$1$E$1", "E$1")
+ assertLocal(ownInnerClassNode("SI_9105$$anonfun$met$1$F$1"), "SI_9105$$anonfun$met$1$F$1", "F$1")
+
+ // by-name
+ assertEnclosingMethod("SI_9105$$anonfun$5$G$1", "SI_9105$$anonfun$5", null, null)
+ assertEnclosingMethod("SI_9105$$anonfun$5$H$1", "SI_9105$$anonfun$5", "m$2", "()Ljava/lang/Object;")
+ assertEnclosingMethod("SI_9105$$anonfun$5$I$1", "SI_9105$$anonfun$5", null, null)
+ assertEnclosingMethod("SI_9105$$anonfun$bnM$1$J$1", "SI_9105$$anonfun$bnM$1", null, null)
+ assertEnclosingMethod("SI_9105$$anonfun$bnM$1$K$2", "SI_9105$$anonfun$bnM$1", "m$4", "()Ljava/lang/Object;")
+ assertEnclosingMethod("SI_9105$$anonfun$bnM$1$L$1", "SI_9105$$anonfun$bnM$1", null, null)
+
+ assertAnonymous(ownInnerClassNode("SI_9105$$anonfun$4"), "SI_9105$$anonfun$4")
+ assertAnonymous(ownInnerClassNode("SI_9105$$anonfun$met$1"), "SI_9105$$anonfun$met$1")
+
+ assert(innerClassNodes("SI_9105$$anonfun$4").length == 4) // itself and three of the local classes
+ assert(innerClassNodes("SI_9105$$anonfun$met$1").length == 4) // itself and three of the local classes
+ assert(innerClassNodes("SI_9105").length == 4) // the four anon funs
+ }
+ }
+
def show(): Unit = {
testA1()
testA2()
@@ -347,5 +416,6 @@ object Test extends BytecodeTest {
testA22()
testA23()
testA24()
+ testSI_9105()
}
}
diff --git a/test/files/jvm/t9105.check b/test/files/jvm/t9105.check
new file mode 100644
index 0000000000..34750833f1
--- /dev/null
+++ b/test/files/jvm/t9105.check
@@ -0,0 +1,18 @@
+#partest !-Ydelambdafy:method
+(class C$$anonfun$1$A$1,class C$$anonfun$1,null)
+(class C$$anonfun$1$B$1,class C$$anonfun$1,private final java.lang.Object C$$anonfun$1.m$1())
+(class C$$anonfun$1$C$1,class C$$anonfun$1,null)
+(class C$$anonfun$1$$anonfun$2$D$1,class C$$anonfun$1$$anonfun$2,null)
+(class C$$anonfun$met$1$E$1,class C$$anonfun$met$1,null)
+(class C$$anonfun$met$1$F$1,class C$$anonfun$met$1,private final java.lang.Object C$$anonfun$met$1.m$2())
+(class C$$anonfun$met$1$G$1,class C$$anonfun$met$1,null)
+(class C$$anonfun$met$1$$anonfun$3$H$1,class C$$anonfun$met$1$$anonfun$3,null)
+#partest -Ydelambdafy:method
+(class C$A$1,class C,null)
+(class C$B$1,class C,private final java.lang.Object C.m$1())
+(class C$C$1,class C,null)
+(class C$D$1,class C,null)
+(class C$E$1,class C,public scala.Function0 C.met())
+(class C$F$1,class C,private final java.lang.Object C.m$2())
+(class C$G$1,class C,public scala.Function0 C.met())
+(class C$H$1,class C,public scala.Function0 C.met())
diff --git a/test/files/jvm/t9105.scala b/test/files/jvm/t9105.scala
new file mode 100644
index 0000000000..636ee8a768
--- /dev/null
+++ b/test/files/jvm/t9105.scala
@@ -0,0 +1,22 @@
+class C {
+ val fun = () => {
+ class A
+ def m: Object = { class B; new B }
+ val f: Object = { class C; new C }
+ val g = () => { class D; new D }
+ List[Object](new A, m, f, g())
+ }
+ def met = () => {
+ class E
+ def m: Object = { class F; new F }
+ val f: Object = { class G; new G }
+ val g = () => { class H; new H }
+ List[Object](new E, m, f, g())
+ }
+}
+
+object Test extends App {
+ val x = new C().fun.apply() ::: new C().met.apply()
+ val results = x.map(_.getClass).map(cls => (cls, cls.getEnclosingClass, cls.getEnclosingMethod))
+ println(results.mkString("\n"))
+}