summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Rytz <lukas.rytz@gmail.com>2016-04-12 10:59:58 +0200
committerLukas Rytz <lukas.rytz@gmail.com>2016-04-20 08:53:52 +0200
commit3fca034c51dd159ff077fdde7e3146b1e41cc925 (patch)
treeb68c166936621fe13a3ded46b36408ecb4db52de
parent765eb29a769488d56f9ff2e4dde105961dbd55db (diff)
downloadscala-3fca034c51dd159ff077fdde7e3146b1e41cc925.tar.gz
scala-3fca034c51dd159ff077fdde7e3146b1e41cc925.tar.bz2
scala-3fca034c51dd159ff077fdde7e3146b1e41cc925.zip
Ensure ClassBTypes constructed from symbol and classfile are identical
A super call (invokespecial) to a default method T.m is only allowed if the interface T is a direct parent of the class. Super calls are introduced for example in Mixin when generating forwarder methods: trait T { override def clone(): Object = "hi" } trait U extends T class C extends U The class C gets a forwarder that invokes T.clone(). During code generation the interface T is added as direct parent to class C. Note that T is not a (direct) parent in the frontend type of class C. This commit stores interfaces that are added to a class during code generation in the InlineInfo classfile attribute. This allows filtering the interface list when constructing a ClassBType from a classfile.
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala4
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala1
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala30
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/InlineInfoAttribute.scala44
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/BTypesFromClassfileTest.scala14
5 files changed, 59 insertions, 34 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
index a429cfa0f2..a2c521eb91 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
@@ -1060,8 +1060,10 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
val receiverName = internalName(receiverClass)
// super calls are only allowed to direct parents
- if (style.isSuper && receiverClass.isTraitOrInterface && !cnode.interfaces.contains(receiverName))
+ if (style.isSuper && receiverClass.isTraitOrInterface && !cnode.interfaces.contains(receiverName)) {
+ thisBType.info.get.inlineInfo.lateInterfaces += receiverName
cnode.interfaces.add(receiverName)
+ }
def needsInterfaceCall(sym: Symbol) = {
sym.isTraitOrInterface ||
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
index 47884da560..f190c1f2de 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
@@ -112,7 +112,6 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
gen(cd.impl)
-
val shouldAddLambdaDeserialize = (
settings.target.value == "jvm-1.8"
&& settings.Ydelambdafy.value == "method"
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
index f6bccca050..49353afef4 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
@@ -7,7 +7,7 @@ package scala.tools.nsc
package backend.jvm
import scala.annotation.switch
-import scala.collection.{mutable, concurrent}
+import scala.collection.{concurrent, mutable}
import scala.collection.concurrent.TrieMap
import scala.reflect.internal.util.Position
import scala.tools.asm
@@ -18,6 +18,7 @@ import scala.tools.nsc.backend.jvm.BackendReporting._
import scala.tools.nsc.backend.jvm.analysis.BackendUtils
import scala.tools.nsc.backend.jvm.opt._
import scala.collection.convert.decorateAsScala._
+import scala.collection.mutable.ListBuffer
import scala.tools.nsc.settings.ScalaSettings
/**
@@ -180,8 +181,6 @@ abstract class BTypes {
Some(classBTypeFromParsedClassfile(superName))
}
- val interfaces: List[ClassBType] = classNode.interfaces.asScala.map(classBTypeFromParsedClassfile)(collection.breakOut)
-
val flags = classNode.access
/**
@@ -226,6 +225,9 @@ abstract class BTypes {
val inlineInfo = inlineInfoFromClassfile(classNode)
+ val classfileInterfaces: List[ClassBType] = classNode.interfaces.asScala.map(classBTypeFromParsedClassfile)(collection.breakOut)
+ val interfaces = classfileInterfaces.filterNot(i => inlineInfo.lateInterfaces.contains(i.internalName))
+
classBType.info = Right(ClassInfo(superClass, interfaces, flags, nestedClasses, nestedInfo, inlineInfo))
classBType
}
@@ -1144,7 +1146,27 @@ object BTypes {
final case class InlineInfo(isEffectivelyFinal: Boolean,
sam: Option[String],
methodInfos: Map[String, MethodInlineInfo],
- warning: Option[ClassInlineInfoWarning])
+ warning: Option[ClassInlineInfoWarning]) {
+ /**
+ * A super call (invokespecial) to a default method T.m is only allowed if the interface T is
+ * a direct parent of the class. Super calls are introduced for example in Mixin when generating
+ * forwarder methods:
+ *
+ * trait T { override def clone(): Object = "hi" }
+ * trait U extends T
+ * class C extends U
+ *
+ * The class C gets a forwarder that invokes T.clone(). During code generation the interface T
+ * is added as direct parent to class C. Note that T is not a (direct) parent in the frontend
+ * type of class C.
+ *
+ * All interfaces that are added to a class during code generation are added to this buffer and
+ * stored in the InlineInfo classfile attribute. This ensures that the ClassBTypes for a
+ * specific class is the same no matter if it's constructed from a Symbol or from a classfile.
+ * This is tested in BTypesFromClassfileTest.
+ */
+ val lateInterfaces: ListBuffer[InternalName] = ListBuffer.empty
+ }
val EmptyInlineInfo = InlineInfo(false, None, Map.empty, None)
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/InlineInfoAttribute.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/InlineInfoAttribute.scala
index 079a9eec9b..79d26b0b4e 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/InlineInfoAttribute.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/InlineInfoAttribute.scala
@@ -47,11 +47,12 @@ case class InlineInfoAttribute(inlineInfo: InlineInfo) extends Attribute(InlineI
result.putByte(InlineInfoAttribute.VERSION)
- var finalSelfSam = 0
- if (inlineInfo.isEffectivelyFinal) finalSelfSam |= 1
- // finalSelfSam |= 2 // no longer written
- if (inlineInfo.sam.isDefined) finalSelfSam |= 4
- result.putByte(finalSelfSam)
+ var flags = 0
+ if (inlineInfo.isEffectivelyFinal) flags |= 1
+ // flags |= 2 // no longer written
+ if (inlineInfo.sam.isDefined) flags |= 4
+ if (inlineInfo.lateInterfaces.nonEmpty) flags |= 8
+ result.putByte(flags)
for (samNameDesc <- inlineInfo.sam) {
val (name, desc) = samNameDesc.span(_ != '(')
@@ -78,6 +79,9 @@ case class InlineInfoAttribute(inlineInfo: InlineInfo) extends Attribute(InlineI
result.putByte(inlineInfo)
}
+ result.putShort(inlineInfo.lateInterfaces.length)
+ for (i <- inlineInfo.lateInterfaces) result.putShort(cw.newUTF8(i))
+
result
}
@@ -97,10 +101,11 @@ case class InlineInfoAttribute(inlineInfo: InlineInfo) extends Attribute(InlineI
val version = nextByte()
if (version == 1) {
- val finalSelfSam = nextByte()
- val isFinal = (finalSelfSam & 1) != 0
- val hasSelf = (finalSelfSam & 2) != 0
- val hasSam = (finalSelfSam & 4) != 0
+ val flags = nextByte()
+ val isFinal = (flags & 1) != 0
+ val hasSelf = (flags & 2) != 0
+ val hasSam = (flags & 4) != 0
+ val hasLateInterfaces = (flags & 8) != 0
if (hasSelf) nextUTF8() // no longer used
@@ -116,14 +121,21 @@ case class InlineInfoAttribute(inlineInfo: InlineInfo) extends Attribute(InlineI
val desc = nextUTF8()
val inlineInfo = nextByte()
- val isFinal = (inlineInfo & 1) != 0
- // val traitMethodWithStaticImplementation = (inlineInfo & 2) != 0 // no longer used
- val isInline = (inlineInfo & 4) != 0
- val isNoInline = (inlineInfo & 8) != 0
+ val isFinal = (inlineInfo & 1) != 0
+ // = (inlineInfo & 2) != 0 // no longer used
+ val isInline = (inlineInfo & 4) != 0
+ val isNoInline = (inlineInfo & 8) != 0
(name + desc, MethodInlineInfo(isFinal, isInline, isNoInline))
}).toMap
- InlineInfoAttribute(InlineInfo(isFinal, sam, infos, None))
+ val lateInterfaces = if (!hasLateInterfaces) Nil else {
+ val numLateInterfaces = nextShort()
+ (0 until numLateInterfaces).map(_ => nextUTF8())
+ }
+
+ val info = InlineInfo(isFinal, sam, infos, None)
+ info.lateInterfaces ++= lateInterfaces
+ InlineInfoAttribute(info)
} else {
val msg = UnknownScalaInlineInfoVersion(cr.getClassName, version)
InlineInfoAttribute(BTypes.EmptyInlineInfo.copy(warning = Some(msg)))
@@ -141,7 +153,7 @@ object InlineInfoAttribute {
* is ignored.
*
* [u1] version
- * [u1] isEffectivelyFinal (<< 0), hasTraitImplClassSelfType (<< 1), hasSam (<< 2)
+ * [u1] isEffectivelyFinal (<< 0), hasTraitImplClassSelfType (<< 1), hasSam (<< 2), hasLateInterfaces (<< 3)
* [u2]? traitImplClassSelfType (reference)
* [u2]? samName (reference)
* [u2]? samDescriptor (reference)
@@ -149,6 +161,8 @@ object InlineInfoAttribute {
* [u2] name (reference)
* [u2] descriptor (reference)
* [u1] isFinal (<< 0), traitMethodWithStaticImplementation (<< 1), hasInlineAnnotation (<< 2), hasNoInlineAnnotation (<< 3)
+ * [u2]? numLateInterfaces
+ * [u2] lateInterface (reference)
*/
final val VERSION: Byte = 1
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/BTypesFromClassfileTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/BTypesFromClassfileTest.scala
index 1ce913006d..9e6a148dba 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/BTypesFromClassfileTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/BTypesFromClassfileTest.scala
@@ -67,19 +67,7 @@ class BTypesFromClassfileTest {
// there's a separate InlineInfoTest.
val chk1 = sameBTypes(fromSym.superClass, fromClassfile.superClass, checked)
-
- // was:
- // val chk2 = sameBTypes(fromSym.interfaces, fromClassfile.interfaces, chk1)
-
- // TODO: The new trait encoding emits redundant parents in the backend to avoid linkage errors in invokespecial
- // Need to give this some more thought, maybe do it earlier so it is reflected in the Symbol's info, too.
- val fromSymInterfaces = fromSym.interfaces
- val fromClassFileInterfaces = fromClassfile.interfaces
- val (matching, other) = fromClassFileInterfaces.partition(x => fromSymInterfaces.exists(_.internalName == x.internalName))
- val chk2 = sameBTypes(fromSym.interfaces, matching, chk1)
- for (redundant <- other) {
- assert(matching.exists(x => x.isSubtypeOf(redundant).orThrow), redundant)
- }
+ val chk2 = sameBTypes(fromSym.interfaces, fromClassfile.interfaces, chk1)
// The fromSym info has only member classes, no local or anonymous. The symbol is read from the
// Scala pickle data and only member classes are created / entered.