diff options
author | Antoine Gourlay <antoine@gourlay.fr> | 2014-10-27 16:11:23 +0100 |
---|---|---|
committer | Antoine Gourlay <antoine@gourlay.fr> | 2014-11-05 11:54:07 +0100 |
commit | ced3ca8ae1aedd37591d85474d9ee85be5c9b56a (patch) | |
tree | ba5993b69c2314dda9c7416f3bf0c63727de9f3d /src/compiler | |
parent | bdae51d6a8f18a5456a32c350cb551d42a3cb6c6 (diff) | |
download | scala-ced3ca8ae1aedd37591d85474d9ee85be5c9b56a.tar.gz scala-ced3ca8ae1aedd37591d85474d9ee85be5c9b56a.tar.bz2 scala-ced3ca8ae1aedd37591d85474d9ee85be5c9b56a.zip |
SI-8931 make generic signature consistent with interface list in classfiles
An optimization was introduced in 7a99c03 (SI-5278) to remove redundant
interfaces from the list of implemented interfaces in the bytecode.
However the same change was not propagated to the generic signature
of a class, which also contains a list of direct parent classes and
interfaces.
The JVM does not check the well-formedness of signatures at class
loading or linking (see ยง4.3.4 of jdk7 jvms), but other tools might
assume the number of implemented interfaces is the same whether one
asked for generic or erased interfaces.
It doesn't break reflection so nobody complained, but it does show:
scala> val c = classOf[Tuple1[String]]
c: Class[(String,)] = class scala.Tuple1
scala> c.getInterfaces // Product is gone
res0: Array[Class[_]] = Array(interface scala.Product1, interface
scala.Serializable)
scala> c.getGenericInterfaces // Product is back!
res1: Array[java.lang.reflect.Type] = Array(scala.Product1<T1>,
interface scala.Product, interface scala.Serializable)
This moves the optimization to erasure, for use in emitting the generic
signature, and the backend calls into it later for the list of
interfaces.
Diffstat (limited to 'src/compiler')
3 files changed, 38 insertions, 48 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala index 0e2f938602..3b7cbd6392 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala @@ -195,32 +195,13 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { case _ => NoSymbol } - /** - * Drop redundant interfaces (which are implemented by some other parent) from the immediate - * parents. In other words, no two interfaces in the result are related by subtyping. - */ - def dropRedundantInterfaces(lstIfaces: List[Symbol]): List[Symbol] = { - var rest = lstIfaces - var leaves = List.empty[Symbol] - while (!rest.isEmpty) { - val candidate = rest.head - val nonLeaf = leaves exists { lsym => lsym isSubClass candidate } - if (!nonLeaf) { - leaves = candidate :: (leaves filterNot { lsym => candidate isSubClass lsym }) - } - rest = rest.tail - } - - leaves - } - val superInterfaces0: List[Symbol] = classSym.mixinClasses val superInterfaces = existingSymbols(superInterfaces0 ++ classSym.annotations.map(newParentForAnnotation)).distinct assert(!superInterfaces.contains(NoSymbol), s"found NoSymbol among: ${superInterfaces.mkString(", ")}") assert(superInterfaces.forall(s => s.isInterface || s.isTrait), s"found non-interface among: ${superInterfaces.mkString(", ")}") - dropRedundantInterfaces(superInterfaces) + erasure.minimizeInterfaces(superInterfaces.map(_.info)).map(_.typeSymbol) } private def buildNestedInfo(innerClassSym: Symbol): Option[NestedInfo] = { diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index d0a12c32e5..e56a20c2e7 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -1214,30 +1214,12 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self => case _ => NoSymbol } - /* Drop redundant interfaces (ones which are implemented by some other parent) from the immediate parents. - * This is important on Android because there is otherwise an interface explosion. - */ - def minimizeInterfaces(lstIfaces: List[Symbol]): List[Symbol] = { - var rest = lstIfaces - var leaves = List.empty[Symbol] - while(!rest.isEmpty) { - val candidate = rest.head - val nonLeaf = leaves exists { lsym => lsym isSubClass candidate } - if(!nonLeaf) { - leaves = candidate :: (leaves filterNot { lsym => candidate isSubClass lsym }) - } - rest = rest.tail - } - - leaves - } - val ps = c.symbol.info.parents val superInterfaces0: List[Symbol] = if(ps.isEmpty) Nil else c.symbol.mixinClasses val superInterfaces = existingSymbols(superInterfaces0 ++ c.symbol.annotations.map(newParentForAttr)).distinct if(superInterfaces.isEmpty) EMPTY_STRING_ARRAY - else mkArray(minimizeInterfaces(superInterfaces) map javaName) + else mkArray(erasure.minimizeInterfaces(superInterfaces.map(_.info)).map(t => javaName(t.typeSymbol))) } var clasz: IClass = _ // this var must be assigned only by genClass() diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 3d8b2f02f3..dc1369bf58 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -185,6 +185,25 @@ abstract class Erasure extends AddInterfaces private def isErasedValueType(tpe: Type) = tpe.isInstanceOf[ErasedValueType] + /* Drop redundant interfaces (ones which are implemented by some other parent) from the immediate parents. + * This is important on Android because there is otherwise an interface explosion. + */ + def minimizeInterfaces(lstIfaces: List[Type]): List[Type] = { + var rest = lstIfaces + var leaves = List.empty[Type] + while(!rest.isEmpty) { + val candidate = rest.head + val nonLeaf = leaves exists { t => t.typeSymbol isSubClass candidate.typeSymbol } + if(!nonLeaf) { + leaves = candidate :: (leaves filterNot { t => candidate.typeSymbol isSubClass t.typeSymbol }) + } + rest = rest.tail + } + + leaves.reverse + } + + /** The Java signature of type 'info', for symbol sym. The symbol is used to give the right return * type for constructors. */ @@ -192,16 +211,24 @@ abstract class Erasure extends AddInterfaces val isTraitSignature = sym0.enclClass.isTrait def superSig(parents: List[Type]) = { - val ps = ( - if (isTraitSignature) { + def isInterfaceOrTrait(sym: Symbol) = sym.isInterface || sym.isTrait + + // a signature should always start with a class + def ensureClassAsFirstParent(tps: List[Type]) = tps match { + case Nil => ObjectTpe :: Nil + case head :: tail if isInterfaceOrTrait(head.typeSymbol) => ObjectTpe :: tps + case _ => tps + } + + val minParents = minimizeInterfaces(parents) + val validParents = + if (isTraitSignature) // java is unthrilled about seeing interfaces inherit from classes - val ok = parents filter (p => p.typeSymbol.isTrait || p.typeSymbol.isInterface) - // traits should always list Object. - if (ok.isEmpty || ok.head.typeSymbol != ObjectClass) ObjectTpe :: ok - else ok - } - else parents - ) + minParents filter (p => isInterfaceOrTrait(p.typeSymbol)) + else minParents + + val ps = ensureClassAsFirstParent(validParents) + (ps map boxedSig).mkString } def boxedSig(tp: Type) = jsig(tp, primitiveOK = false) |