Skip to content

Commit a0b11b3

Browse files
committed
Merge pull request scala#4208 from lrytz/t9044
SI-9044 Fix order of interfaces in classfiles
2 parents c77b7a7 + 7552739 commit a0b11b3

File tree

5 files changed

+45
-49
lines changed

5 files changed

+45
-49
lines changed

src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,4 +162,32 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
162162
assoc.collectFirst {
163163
case (`nme`.value, LiteralAnnotArg(Constant(value: Symbol))) => value
164164
}).flatten.getOrElse(AnnotationRetentionPolicyClassValue)
165+
166+
def implementedInterfaces(classSym: Symbol): List[Symbol] = {
167+
// Additional interface parents based on annotations and other cues
168+
def newParentForAnnotation(ann: AnnotationInfo): Option[Type] = ann.symbol match {
169+
case RemoteAttr => Some(RemoteInterfaceClass.tpe)
170+
case _ => None
171+
}
172+
173+
def isInterfaceOrTrait(sym: Symbol) = sym.isInterface || sym.isTrait
174+
175+
val allParents = classSym.info.parents ++ classSym.annotations.flatMap(newParentForAnnotation)
176+
177+
// We keep the superClass when computing minimizeParents to eliminate more interfaces.
178+
// Example: T can be eliminated from D
179+
// trait T
180+
// class C extends T
181+
// class D extends C with T
182+
val interfaces = erasure.minimizeParents(allParents) match {
183+
case superClass :: ifs if !isInterfaceOrTrait(superClass.typeSymbol) =>
184+
ifs
185+
case ifs =>
186+
// minimizeParents removes the superclass if it's redundant, for example:
187+
// trait A
188+
// class C extends Object with A // minimizeParents removes Object
189+
ifs
190+
}
191+
interfaces.map(_.typeSymbol)
192+
}
165193
}

src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
114114
val superClass = if (superClassSym == NoSymbol) None
115115
else Some(classBTypeFromSymbol(superClassSym))
116116

117-
val interfaces = getSuperInterfaces(classSym).map(classBTypeFromSymbol)
117+
val interfaces = implementedInterfaces(classSym).map(classBTypeFromSymbol)
118118

119119
val flags = javaFlags(classSym)
120120

@@ -182,28 +182,6 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
182182
classBType
183183
}
184184

185-
/**
186-
* All interfaces implemented by a class, except for those inherited through the superclass.
187-
*
188-
* TODO @lry share code with GenASM
189-
*/
190-
private def getSuperInterfaces(classSym: Symbol): List[Symbol] = {
191-
192-
// Additional interface parents based on annotations and other cues
193-
def newParentForAnnotation(ann: AnnotationInfo): Symbol = ann.symbol match {
194-
case RemoteAttr => RemoteInterfaceClass
195-
case _ => NoSymbol
196-
}
197-
198-
val superInterfaces0: List[Symbol] = classSym.mixinClasses
199-
val superInterfaces = existingSymbols(superInterfaces0 ++ classSym.annotations.map(newParentForAnnotation)).distinct
200-
201-
assert(!superInterfaces.contains(NoSymbol), s"found NoSymbol among: ${superInterfaces.mkString(", ")}")
202-
assert(superInterfaces.forall(s => s.isInterface || s.isTrait), s"found non-interface among: ${superInterfaces.mkString(", ")}")
203-
204-
erasure.minimizeInterfaces(superInterfaces.map(_.info)).map(_.typeSymbol)
205-
}
206-
207185
private def buildNestedInfo(innerClassSym: Symbol): Option[NestedInfo] = {
208186
assert(innerClassSym.isClass, s"Cannot build NestedInfo for non-class symbol $innerClassSym")
209187

src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1206,22 +1206,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self =>
12061206

12071207
def serialVUID: Option[Long] = genBCode.serialVUID(clasz.symbol)
12081208

1209-
private def getSuperInterfaces(c: IClass): Array[String] = {
1210-
1211-
// Additional interface parents based on annotations and other cues
1212-
def newParentForAttr(ann: AnnotationInfo): Symbol = ann.symbol match {
1213-
case RemoteAttr => RemoteInterfaceClass
1214-
case _ => NoSymbol
1215-
}
1216-
1217-
val ps = c.symbol.info.parents
1218-
val superInterfaces0: List[Symbol] = if(ps.isEmpty) Nil else c.symbol.mixinClasses
1219-
val superInterfaces = existingSymbols(superInterfaces0 ++ c.symbol.annotations.map(newParentForAttr)).distinct
1220-
1221-
if(superInterfaces.isEmpty) EMPTY_STRING_ARRAY
1222-
else mkArray(erasure.minimizeInterfaces(superInterfaces.map(_.info)).map(t => javaName(t.typeSymbol)))
1223-
}
1224-
12251209
var clasz: IClass = _ // this var must be assigned only by genClass()
12261210
var jclass: asm.ClassWriter = _ // the classfile being emitted
12271211
var thisName: String = _ // the internal name of jclass
@@ -1242,7 +1226,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self =>
12421226
val ps = c.symbol.info.parents
12431227
val superClass: String = if(ps.isEmpty) JAVA_LANG_OBJECT.getInternalName else javaName(ps.head.typeSymbol)
12441228

1245-
val ifaces = getSuperInterfaces(c)
1229+
val ifaces: Array[String] = implementedInterfaces(c.symbol).map(javaName)(collection.breakOut)
12461230

12471231
val thisSignature = getGenericSignature(c.symbol, c.symbol.owner)
12481232
val flags = mkFlags(

src/compiler/scala/tools/nsc/transform/Erasure.scala

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -185,22 +185,22 @@ abstract class Erasure extends AddInterfaces
185185

186186
private def isErasedValueType(tpe: Type) = tpe.isInstanceOf[ErasedValueType]
187187

188-
/* Drop redundant interfaces (ones which are implemented by some other parent) from the immediate parents.
188+
/* Drop redundant types (ones which are implemented by some other parent) from the immediate parents.
189189
* This is important on Android because there is otherwise an interface explosion.
190190
*/
191-
def minimizeInterfaces(lstIfaces: List[Type]): List[Type] = {
192-
var rest = lstIfaces
193-
var leaves = List.empty[Type]
194-
while(!rest.isEmpty) {
191+
def minimizeParents(parents: List[Type]): List[Type] = {
192+
var rest = parents
193+
var leaves = collection.mutable.ListBuffer.empty[Type]
194+
while(rest.nonEmpty) {
195195
val candidate = rest.head
196196
val nonLeaf = leaves exists { t => t.typeSymbol isSubClass candidate.typeSymbol }
197197
if(!nonLeaf) {
198-
leaves = candidate :: (leaves filterNot { t => candidate.typeSymbol isSubClass t.typeSymbol })
198+
leaves = leaves filterNot { t => candidate.typeSymbol isSubClass t.typeSymbol }
199+
leaves += candidate
199200
}
200201
rest = rest.tail
201202
}
202-
203-
leaves.reverse
203+
leaves.toList
204204
}
205205

206206

@@ -220,7 +220,7 @@ abstract class Erasure extends AddInterfaces
220220
case _ => tps
221221
}
222222

223-
val minParents = minimizeInterfaces(parents)
223+
val minParents = minimizeParents(parents)
224224
val validParents =
225225
if (isTraitSignature)
226226
// java is unthrilled about seeing interfaces inherit from classes

test/files/jvm/t9044.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
trait A
2+
trait B
3+
object Test extends A with B with App {
4+
val is = Test.getClass.getInterfaces.mkString(", ")
5+
assert(is == "interface A, interface B, interface scala.App", is)
6+
}

0 commit comments

Comments
 (0)