From 3fca034c51dd159ff077fdde7e3146b1e41cc925 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 12 Apr 2016 10:59:58 +0200 Subject: 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. --- .../tools/nsc/backend/jvm/BCodeBodyBuilder.scala | 4 +- .../tools/nsc/backend/jvm/BCodeSkelBuilder.scala | 1 - .../scala/tools/nsc/backend/jvm/BTypes.scala | 30 +++++++++++++-- .../nsc/backend/jvm/opt/InlineInfoAttribute.scala | 44 ++++++++++++++-------- 4 files changed, 58 insertions(+), 21 deletions(-) (limited to 'src') 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 -- cgit v1.2.3