Skip to content

Commit

Permalink
SI-9937 find nested java classes if InnnerClass entry is missing
Browse files Browse the repository at this point in the history
When a classfile has a reference to an inner class C$D but no
InnerClass entry for it, the classfile parser would use the
top-level symbol C$D. In a different classfile, if there's also
a reference to C$D, but the InnerClass entry exists, the
symbol D owned by C (C.D) would be used. Therefore the two
signatures would be incompatible, which can lead to a spurious
type error.

Also, when an inner symbol C.D is resolved, the top-level
symbol C$D is invalidated and removed from the scope. A
subsequent lookup of the top-level symbol C$D (from a classfile
with a missing InnerClass entry) would fail.

This patch identifies the case when a class name containing
a $ is being looked up in a package. It splits the name, resolves
the outer class, and then searches for a member class.
  • Loading branch information
lrytz committed Apr 4, 2017
1 parent 232d95a commit 811faf4
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -380,11 +380,35 @@ abstract class ClassfileParser {
}

private def lookupClass(name: Name) = try {
if (name containsChar '.')
rootMirror getClassByName name
else
def lookupTopLevel = {
if (name containsChar '.')
rootMirror getClassByName name
else
// FIXME - we shouldn't be doing ad hoc lookups in the empty package, getClassByName should return the class
definitions.getMember(rootMirror.EmptyPackageClass, name.toTypeName)
definitions.getMember(rootMirror.EmptyPackageClass, name.toTypeName)
}

val split = if (isScalaRaw) -1 else name.lastIndexOf('$')
if (split > 0 && split < name.length) {
val outerName = name.subName(0, split)
val innerName = name.subName(split + 1, name.length).toTypeName
val outerSym = classNameToSymbol(outerName)

// If the outer class C cannot be found, look for a top-level class C$D
if (outerSym.isInstanceOf[StubSymbol]) lookupTopLevel
else {
// We have a java-defined class name C$D and look for a member D of C. But we don't know if
// D is declared static or not, so we have to search both in class C and its companion.
val r = if (outerSym == clazz)
staticScope.lookup(innerName) orElse
instanceScope.lookup(innerName)
else
lookupMemberAtTyperPhaseIfPossible(outerSym, innerName) orElse
lookupMemberAtTyperPhaseIfPossible(outerSym.companionModule, innerName)
r orElse lookupTopLevel
}
} else
lookupTopLevel
} catch {
// The handler
// - prevents crashes with deficient InnerClassAttributes (SI-2464, 0ce0ad5)
Expand Down
9 changes: 9 additions & 0 deletions test/files/run/t9937/Test_1.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class C$D { public int i() { return 1; } }
class C$E { public int i() { return 1; } }

// Test1 has a reference to C$D, which is a top-level class in this case,
// so there's no INNERCLASS attribute in Test1
class Test_1 {
static C$D mD(C$D cd) { return cd; }
static C$E mE(C$E ce) { return ce; }
}
10 changes: 10 additions & 0 deletions test/files/run/t9937/Test_2.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class C {
class D { public int i() { return 2; } }
static class E { public int i() { return 2; } }
}

// Test2 has an INNERCLASS attribute for C$D
class Test_2 {
public static int acceptD(C.D cd) { return cd.i(); }
public static int acceptE(C.E ce) { return ce.i(); }
}
7 changes: 7 additions & 0 deletions test/files/run/t9937/Test_3.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
object Test {
def main(args: Array[String]): Unit = {
val c = new C
assert(Test_2.acceptD(Test_1.mD(new c.D)) == 2)
assert(Test_2.acceptE(Test_1.mE(new C.E)) == 2)
}
}

0 comments on commit 811faf4

Please sign in to comment.