Skip to content

Commit d81567b

Browse files
committed
Make ClassfileParser robust for directly reading Java inner classes
Usually when the ClassfileParser reads a classfile for a Java inner class, the entry point is the outer class, the inner symbol is created in `enterOwnInnerClasses`. But when scanning the classpath, a Symbol is also created because of the presence of the `C$I.class` classfile. (This symbol is later unlinked and marked invalid.) When invoking the classfile parser on this symbol directly, reading generic signatures can fail because they may refer to type variables defined in the outer class: ``` class C1<X> { // signature: Lp/C1<TX;>; -- `TX;` references outer `X` class I extends C1<X> { } } ```
1 parent 24014e9 commit d81567b

File tree

4 files changed

+96
-20
lines changed

4 files changed

+96
-20
lines changed

src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,15 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) {
627627
}
628628
}
629629

630+
private class SigToTypeFail(msg: String) extends Throwable(msg)
631+
632+
// scala/bug#9152: sigToType can fail when directly accessing the symbol of a Java nested class
633+
// `None` if `sig == null` or when `sigToType` fails
634+
private def sigToTypeOpt(sym: Symbol, sig: String): Option[Type] = {
635+
try Option(sig).map(sigToType(sym, _))
636+
catch { case _: SigToTypeFail => None}
637+
}
638+
630639
private def sigToType(sym: Symbol, sig: String): Type = {
631640
val sigChars = sig.toCharArray
632641
var index = 0
@@ -749,7 +758,7 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) {
749758
val n = newTypeName(subName(';'.==))
750759
index += 1
751760
if (skiptvs) AnyTpe
752-
else tparams(n).typeConstructor
761+
else tparams.getOrElse(n, throw new SigToTypeFail(s"unknown type variable: $n")).typeConstructor
753762
}
754763
} // sig2type(tparams, skiptvs)
755764

@@ -1295,7 +1304,7 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) {
12951304
private final class ClassTypeCompleter(@unused name: Name, @unused jflags: JavaAccFlags, parent: NameOrString, ifaces: List[NameOrString]) extends JavaTypeCompleter {
12961305
var permittedSubclasses: List[symbolTable.Symbol] = Nil
12971306
override def complete(sym: symbolTable.Symbol): Unit = {
1298-
val info = if (sig != null) sigToType(sym, sig) else {
1307+
val info = sigToTypeOpt(sym, sig).getOrElse {
12991308
val superTpe = if (parent == null) definitions.AnyClass.tpe_* else getClassSymbol(parent.value).tpe_*
13001309
val superTpe1 = if (superTpe == ObjectTpeJava) ObjectTpe else superTpe
13011310
val ifacesTypes = ifaces.filterNot(_ eq null).map(x => getClassSymbol(x.value).tpe_*)
@@ -1335,25 +1344,25 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) {
13351344
case _ => false
13361345
})
13371346

1338-
val info = if (sig != null) {
1339-
sigToType(sym, sig)
1340-
} else if (name == nme.CONSTRUCTOR) {
1341-
descriptorInfo match {
1342-
case MethodType(params, _) =>
1343-
val paramsNoOuter = if (hasOuterParam) params.tail else params
1344-
val newParams = paramsNoOuter match {
1345-
case init :+ _ if jflags.isSynthetic =>
1346-
// scala/bug#7455 strip trailing dummy argument ("access constructor tag") from synthetic constructors which
1347-
// are added when an inner class needs to access a private constructor.
1348-
init
1349-
case _ =>
1350-
paramsNoOuter
1351-
}
1352-
MethodType(newParams, clazz.tpe)
1353-
case info => info
1347+
val info = sigToTypeOpt(sym, sig).getOrElse {
1348+
if (name == nme.CONSTRUCTOR) {
1349+
descriptorInfo match {
1350+
case MethodType(params, _) =>
1351+
val paramsNoOuter = if (hasOuterParam) params.tail else params
1352+
val newParams = paramsNoOuter match {
1353+
case init :+ _ if jflags.isSynthetic =>
1354+
// scala/bug#7455 strip trailing dummy argument ("access constructor tag") from synthetic constructors which
1355+
// are added when an inner class needs to access a private constructor.
1356+
init
1357+
case _ =>
1358+
paramsNoOuter
1359+
}
1360+
MethodType(newParams, clazz.tpe)
1361+
case info => info
1362+
}
1363+
} else {
1364+
descriptorInfo
13541365
}
1355-
} else {
1356-
descriptorInfo
13571366
}
13581367
if (constant != null) {
13591368
val c1 = convertTo(constant, info.resultType)

test/files/run/t9152.check

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
2+
scala> :power
3+
Power mode enabled. :phase is at typer.
4+
import scala.tools.nsc._, intp.global._, definitions._
5+
Try :help or completions for vals._ and power._
6+
7+
scala> import rootMirror._
8+
import rootMirror._
9+
10+
scala> getClassIfDefined("p.C1$I").info
11+
val res0: $r.intp.global.Type =
12+
p.C1 {
13+
private[package p] def <init>(): p.C1$I
14+
}
15+
16+
scala> getClassIfDefined("p.C2$I").info
17+
val res1: $r.intp.global.Type =
18+
Object {
19+
private[package p] def <init>(): p.C2$I
20+
private[package p] def foo(): p.C2[_]
21+
}
22+
23+
scala> getClassIfDefined("p.C1$I") // symbol unlinked after reading C1
24+
val res2: $r.intp.global.Symbol = <none>
25+
26+
scala> getClassIfDefined("p.C2$I") // symbol unlinked after reading C2
27+
val res3: $r.intp.global.Symbol = <none>
28+
29+
scala> getClassIfDefined("p.C1").info.member(TypeName("I")).info
30+
val res4: $r.intp.global.Type =
31+
p.C1[X] {
32+
private[package p] def <init>(): C1.this.I
33+
}
34+
35+
scala> getClassIfDefined("p.C2").info.member(TypeName("I")).info
36+
val res5: $r.intp.global.Type =
37+
Object {
38+
private[package p] def <init>(): C2.this.I
39+
private[package p] def foo(): p.C2[X]
40+
}
41+
42+
scala> :quit

test/files/run/t9152/C_1.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package p;
2+
3+
class C1<X> {
4+
class I extends C1<X> { }
5+
}
6+
7+
class C2<X> {
8+
class I {
9+
C2<X> foo() { return null; }
10+
}
11+
}

test/files/run/t9152/Test.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import scala.tools.partest.ReplTest
2+
3+
object Test extends ReplTest {
4+
def code = """
5+
|:power
6+
|import rootMirror._
7+
|getClassIfDefined("p.C1$I").info
8+
|getClassIfDefined("p.C2$I").info
9+
|getClassIfDefined("p.C1$I") // symbol unlinked after reading C1
10+
|getClassIfDefined("p.C2$I") // symbol unlinked after reading C2
11+
|getClassIfDefined("p.C1").info.member(TypeName("I")).info
12+
|getClassIfDefined("p.C2").info.member(TypeName("I")).info
13+
""".stripMargin
14+
}

0 commit comments

Comments
 (0)