diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala b/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala index 7bcc8fcf6b09..e2a18386224e 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala @@ -39,7 +39,6 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { import DottyBackendInterface.symExtensions import bTypes._ import coreBTypes._ - import BCodeBodyBuilder._ protected val primitives: DottyPrimitives @@ -431,7 +430,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { if (value.tag != UnitTag) (value.tag, expectedType) match { case (IntTag, LONG ) => bc.lconst(value.longValue); generatedType = LONG case (FloatTag, DOUBLE) => bc.dconst(value.doubleValue); generatedType = DOUBLE - case (NullTag, _ ) => bc.emit(asm.Opcodes.ACONST_NULL); generatedType = RT_NULL + case (NullTag, _ ) => bc.emit(asm.Opcodes.ACONST_NULL); generatedType = srNullRef case _ => genConstant(value); generatedType = tpeTK(tree) } @@ -490,7 +489,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { val thrownType = expectedType // `throw null` is valid although scala.Null (as defined in src/libray-aux) isn't a subtype of Throwable. // Similarly for scala.Nothing (again, as defined in src/libray-aux). - assert(thrownType.isNullType || thrownType.isNothingType || thrownType.asClassBType.isSubtypeOf(ThrowableReference)) + assert(thrownType.isNullType || thrownType.isNothingType || thrownType.asClassBType.isSubtypeOf(jlThrowableRef)) emit(asm.Opcodes.ATHROW) end genAdaptAndSendToDest @@ -674,8 +673,8 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { else if (l.isPrimitive) { bc drop l if (cast) { - mnode.visitTypeInsn(asm.Opcodes.NEW, classCastExceptionReference.internalName) - bc dup ObjectReference + mnode.visitTypeInsn(asm.Opcodes.NEW, jlClassCastExceptionRef.internalName) + bc dup ObjectRef emit(asm.Opcodes.ATHROW) } else { bc boolconst false @@ -777,7 +776,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { val nativeKind = tpeTK(expr) genLoad(expr, nativeKind) val MethodNameAndType(mname, methodType) = asmBoxTo(nativeKind) - bc.invokestatic(BoxesRunTime.internalName, mname, methodType.descriptor, itf = false) + bc.invokestatic(srBoxesRuntimeRef.internalName, mname, methodType.descriptor, itf = false) generatedType = boxResultType(fun.symbol) // was toTypeKind(fun.symbol.tpe.resultType) case Apply(fun, List(expr)) if Erasure.Boxing.isUnbox(fun.symbol) && fun.symbol.denot.owner != defn.UnitModuleClass => @@ -785,7 +784,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { val boxType = unboxResultType(fun.symbol) // was toTypeKind(fun.symbol.owner.linkedClassOfClass.tpe) generatedType = boxType val MethodNameAndType(mname, methodType) = asmUnboxTo(boxType) - bc.invokestatic(BoxesRunTime.internalName, mname, methodType.descriptor, itf = false) + bc.invokestatic(srBoxesRuntimeRef.internalName, mname, methodType.descriptor, itf = false) case app @ Apply(fun, args) => val sym = fun.symbol @@ -1237,7 +1236,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { liftStringConcat(tree) match { // Optimization for expressions of the form "" + x case List(Literal(Constant("")), arg) => - genLoad(arg, ObjectReference) + genLoad(arg, ObjectRef) genCallMethod(defn.String_valueOf_Object, InvokeStyle.Static) case concatenations => @@ -1409,7 +1408,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { /* Generate the scala ## method. */ def genScalaHash(tree: Tree): BType = { - genLoad(tree, ObjectReference) + genLoad(tree, ObjectRef) genCallMethod(NoSymbol, InvokeStyle.Static) // used to dispatch ## on primitives to ScalaRuntime.hash. Should be implemented by a miniphase } @@ -1508,8 +1507,8 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { val nonNullSide = if (ScalaPrimitivesOps.isReferenceEqualityOp(code)) ifOneIsNull(l, r) else null if (nonNullSide != null) { // special-case reference (in)equality test for null (null eq x, x eq null) - genLoad(nonNullSide, ObjectReference) - genCZJUMP(success, failure, op, ObjectReference, targetIfNoJump) + genLoad(nonNullSide, ObjectRef) + genCZJUMP(success, failure, op, ObjectRef, targetIfNoJump) } else { val tk = tpeTK(l).maxType(tpeTK(r)) genLoad(l, tk) @@ -1627,42 +1626,42 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { } else defn.BoxesRunTimeModule_externalEquals } - genLoad(l, ObjectReference) - genLoad(r, ObjectReference) + genLoad(l, ObjectRef) + genLoad(r, ObjectRef) genCallMethod(equalsMethod, InvokeStyle.Static) genCZJUMP(success, failure, Primitives.NE, BOOL, targetIfNoJump) } else { if (isNull(l)) { // null == expr -> expr eq null - genLoad(r, ObjectReference) - genCZJUMP(success, failure, Primitives.EQ, ObjectReference, targetIfNoJump) + genLoad(r, ObjectRef) + genCZJUMP(success, failure, Primitives.EQ, ObjectRef, targetIfNoJump) } else if (isNull(r)) { // expr == null -> expr eq null - genLoad(l, ObjectReference) - genCZJUMP(success, failure, Primitives.EQ, ObjectReference, targetIfNoJump) + genLoad(l, ObjectRef) + genCZJUMP(success, failure, Primitives.EQ, ObjectRef, targetIfNoJump) } else if (isNonNullExpr(l)) { // SI-7852 Avoid null check if L is statically non-null. - genLoad(l, ObjectReference) - genLoad(r, ObjectReference) + genLoad(l, ObjectRef) + genLoad(r, ObjectRef) genCallMethod(defn.Any_equals, InvokeStyle.Virtual) genCZJUMP(success, failure, Primitives.NE, BOOL, targetIfNoJump) } else { // l == r -> if (l eq null) r eq null else l.equals(r) - val eqEqTempLocal = locals.makeLocal(ObjectReference, nme.EQEQ_LOCAL_VAR.mangledString, defn.ObjectType, r.span) + val eqEqTempLocal = locals.makeLocal(ObjectRef, nme.EQEQ_LOCAL_VAR.mangledString, defn.ObjectType, r.span) val lNull = new asm.Label val lNonNull = new asm.Label - genLoad(l, ObjectReference) - genLoad(r, ObjectReference) + genLoad(l, ObjectRef) + genLoad(r, ObjectRef) locals.store(eqEqTempLocal) - bc dup ObjectReference - genCZJUMP(lNull, lNonNull, Primitives.EQ, ObjectReference, targetIfNoJump = lNull) + bc dup ObjectRef + genCZJUMP(lNull, lNonNull, Primitives.EQ, ObjectRef, targetIfNoJump = lNull) markProgramPoint(lNull) - bc drop ObjectReference + bc drop ObjectRef locals.load(eqEqTempLocal) - genCZJUMP(success, failure, Primitives.EQ, ObjectReference, targetIfNoJump = lNonNull) + genCZJUMP(success, failure, Primitives.EQ, ObjectRef, targetIfNoJump = lNonNull) markProgramPoint(lNonNull) locals.load(eqEqTempLocal) @@ -1753,9 +1752,9 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { val metafactory = if (flags != 0) - lambdaMetaFactoryAltMetafactoryHandle // altMetafactory required to be able to pass the flags and additional arguments if needed + jliLambdaMetaFactoryAltMetafactoryHandle // altMetafactory required to be able to pass the flags and additional arguments if needed else - lambdaMetaFactoryMetafactoryHandle + jliLambdaMetaFactoryMetafactoryHandle bc.jmethod.visitInvokeDynamicInsn(methodName, desc, metafactory, bsmArgs: _*) @@ -1772,27 +1771,5 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { private def isEmittedInterface(sym: Symbol): Boolean = sym.isInterface || sym.is(JavaDefined) && (toDenot(sym).isAnnotation || sym.is(ModuleClass) && (sym.companionClass.is(PureInterface)) || sym.companionClass.is(Trait)) -} -object BCodeBodyBuilder { - val lambdaMetaFactoryMetafactoryHandle = new Handle( - Opcodes.H_INVOKESTATIC, - "java/lang/invoke/LambdaMetafactory", - "metafactory", - "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;", - /* itf = */ false) - - val lambdaMetaFactoryAltMetafactoryHandle = new Handle( - Opcodes.H_INVOKESTATIC, - "java/lang/invoke/LambdaMetafactory", - "altMetafactory", - "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;", - /* itf = */ false) - - val lambdaDeserializeBootstrapHandle = new Handle( - Opcodes.H_INVOKESTATIC, - "scala/runtime/LambdaDeserialize", - "bootstrap", - "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/CallSite;", - /* itf = */ false) } diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala b/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala index f254dea184ed..ffefa254a4c3 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala @@ -132,24 +132,24 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { } /* - * Populates the InnerClasses JVM attribute with `refedInnerClasses`. - * In addition to inner classes mentioned somewhere in `jclass` (where `jclass` is a class file being emitted) - * `refedInnerClasses` should contain those inner classes defined as direct member classes of `jclass` - * but otherwise not mentioned in `jclass`. + * Populates the InnerClasses JVM attribute with `refedInnerClasses`. See also the doc on inner + * classes in BTypes.scala. * - * `refedInnerClasses` may contain duplicates, - * need not contain the enclosing inner classes of each inner class it lists (those are looked up for consistency). + * `refedInnerClasses` may contain duplicates, need not contain the enclosing inner classes of + * each inner class it lists (those are looked up and included). * - * This method serializes in the InnerClasses JVM attribute in an appropriate order, + * This method serializes in the InnerClasses JVM attribute in an appropriate order, * not necessarily that given by `refedInnerClasses`. * * can-multi-thread */ - final def addInnerClassesASM(jclass: asm.ClassVisitor, refedInnerClasses: List[ClassBType]): Unit = { - val allNestedClasses = refedInnerClasses.flatMap(_.enclosingNestedClassesChain).distinct - + final def addInnerClasses(jclass: asm.ClassVisitor, declaredInnerClasses: List[ClassBType], refedInnerClasses: List[ClassBType]): Unit = { // sorting ensures nested classes are listed after their enclosing class thus satisfying the Eclipse Java compiler - for (nestedClass <- allNestedClasses.sortBy(_.internalName.toString)) { + val allNestedClasses = new mutable.TreeSet[ClassBType]()(Ordering.by(_.internalName)) + allNestedClasses ++= declaredInnerClasses + refedInnerClasses.foreach(allNestedClasses ++= _.enclosingNestedClassesChain) + for nestedClass <- allNestedClasses + do { // Extract the innerClassEntry - we know it exists, enclosingNestedClassesChain only returns nested classes. val Some(e) = nestedClass.innerClassAttributeEntry jclass.visitInnerClass(e.name, e.outerName, e.innerName, e.flags) @@ -218,26 +218,16 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { final val emitLines = debugLevel >= 2 final val emitVars = debugLevel >= 3 - /* - * Contains class-symbols that: - * (a) are known to denote inner classes - * (b) are mentioned somewhere in the class being generated. - * - * In other words, the lifetime of `innerClassBufferASM` is associated to "the class being generated". - */ - final val innerClassBufferASM = mutable.Set.empty[ClassBType] - /** - * The class internal name for a given class symbol. If the symbol describes a nested class, the - * ClassBType is added to the innerClassBufferASM. + * The class internal name for a given class symbol. */ final def internalName(sym: Symbol): String = { // For each java class, the scala compiler creates a class and a module (thus a module class). - // If the `sym` is a java module class, we use the java class instead. This ensures that we - // register the class (instead of the module class) in innerClassBufferASM. + // If the `sym` is a java module class, we use the java class instead. This ensures that the + // ClassBType is created from the main class (instead of the module class). // The two symbols have the same name, so the resulting internalName is the same. val classSym = if (sym.is(JavaDefined) && sym.is(ModuleClass)) sym.linkedClass else sym - getClassBTypeAndRegisterInnerClass(classSym).internalName + getClassBType(classSym).internalName } private def assertClassNotArray(sym: Symbol): Unit = { @@ -251,8 +241,7 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { } /** - * The ClassBType for a class symbol. If the class is nested, the ClassBType is added to the - * innerClassBufferASM. + * The ClassBType for a class symbol. * * The class symbol scala.Nothing is mapped to the class scala.runtime.Nothing$. Similarly, * scala.Null is mapped to scala.runtime.Null$. This is because there exist no class files @@ -264,16 +253,12 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { * the class descriptor of the receiver (the implementation class) is obtained by creating the * ClassBType. */ - final def getClassBTypeAndRegisterInnerClass(sym: Symbol): ClassBType = { + final def getClassBType(sym: Symbol): ClassBType = { assertClassNotArrayNotPrimitive(sym) - if (sym == defn.NothingClass) RT_NOTHING - else if (sym == defn.NullClass) RT_NULL - else { - val r = classBTypeFromSymbol(sym) - if (r.isNestedClass) innerClassBufferASM += r - r - } + if (sym == defn.NothingClass) srNothingRef + else if (sym == defn.NullClass) srNullRef + else classBTypeFromSymbol(sym) } /* @@ -288,16 +273,14 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { } /** - * The jvm descriptor of a type. If `t` references a nested class, its ClassBType is added to - * the innerClassBufferASM. + * The jvm descriptor of a type. */ final def typeDescriptor(t: Type): String = { toTypeKind(t).descriptor } /** - * The jvm descriptor for a symbol. If `sym` represents a nested class, its ClassBType is added - * to the innerClassBufferASM. + * The jvm descriptor for a symbol. */ - final def symDescriptor(sym: Symbol): String = { getClassBTypeAndRegisterInnerClass(sym).descriptor } + final def symDescriptor(sym: Symbol): String = getClassBType(sym).descriptor final def toTypeKind(tp: Type): BType = typeToTypeKind(tp)(BCodeHelpers.this)(this) @@ -691,19 +674,18 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { def genMirrorClass(moduleClass: Symbol, cunit: CompilationUnit): asm.tree.ClassNode = { assert(moduleClass.is(ModuleClass)) assert(moduleClass.companionClass == NoSymbol, moduleClass) - innerClassBufferASM.clear() this.cunit = cunit + val bType = mirrorClassBTypeFromSymbol(moduleClass) val moduleName = internalName(moduleClass) // + "$" - val mirrorName = moduleName.substring(0, moduleName.length() - 1) + val mirrorName = bType.internalName - val flags = (asm.Opcodes.ACC_SUPER | asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_FINAL) val mirrorClass = new asm.tree.ClassNode mirrorClass.visit( classfileVersion, - flags, + bType.info.flags, mirrorName, null /* no java-generic-signature */, - ObjectReference.internalName, + ObjectRef.internalName, EMPTY_STRING_ARRAY ) @@ -717,10 +699,6 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { emitAnnotations(mirrorClass, moduleClass.annotations ++ ssa) addForwarders(mirrorClass, mirrorName, moduleClass) - - innerClassBufferASM ++= classBTypeFromSymbol(moduleClass).info.memberClasses - addInnerClassesASM(mirrorClass, innerClassBufferASM.toList) - mirrorClass.visitEnd() moduleClass.name // this side-effect is necessary, really. @@ -754,8 +732,7 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { * must-single-thread */ def legacyAddCreatorCode(clinit: asm.MethodVisitor, cnode: asm.tree.ClassNode, thisName: String): Unit = { - // this tracks the inner class in innerClassBufferASM, if needed. - val androidCreatorType = getClassBTypeAndRegisterInnerClass(AndroidCreatorClass) + val androidCreatorType = getClassBType(AndroidCreatorClass) val tdesc_creator = androidCreatorType.descriptor cnode.visitField( @@ -818,8 +795,7 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { def primitiveOrClassToBType(sym: Symbol): BType = { assert(sym.isClass, sym) assert(sym != defn.ArrayClass || compilingArray, sym) - primitiveTypeMap.getOrElse(sym, - storage.getClassBTypeAndRegisterInnerClass(sym)).asInstanceOf[BType] + primitiveTypeMap.getOrElse(sym, storage.getClassBType(sym)).asInstanceOf[BType] } /** @@ -828,7 +804,7 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { */ def nonClassTypeRefToBType(sym: Symbol): ClassBType = { assert(sym.isType && compilingArray, sym) - ObjectReference.asInstanceOf[ct.bTypes.ClassBType] + ObjectRef.asInstanceOf[ct.bTypes.ClassBType] } tp.widenDealias match { @@ -861,8 +837,8 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { "If possible, please file a bug on https://github.com/lampepfl/dotty/issues") tp match { - case tp: ThisType if tp.cls == defn.ArrayClass => ObjectReference.asInstanceOf[ct.bTypes.ClassBType] // was introduced in 9b17332f11 to fix SI-999, but this code is not reached in its test, or any other test - case tp: ThisType => storage.getClassBTypeAndRegisterInnerClass(tp.cls) + case tp: ThisType if tp.cls == defn.ArrayClass => ObjectRef.asInstanceOf[ct.bTypes.ClassBType] // was introduced in 9b17332f11 to fix SI-999, but this code is not reached in its test, or any other test + case tp: ThisType => storage.getClassBType(tp.cls) // case t: SingletonType => primitiveOrClassToBType(t.classSymbol) case t: SingletonType => typeToTypeKind(t.underlying)(ct)(storage) case t: RefinedType => typeToTypeKind(t.parent)(ct)(storage) //parents.map(_.toTypeKind(ct)(storage).asClassBType).reduceLeft((a, b) => a.jvmWiseLUB(b)) diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeIdiomatic.scala b/compiler/src/dotty/tools/backend/jvm/BCodeIdiomatic.scala index b63a080dcead..02268c2919ba 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeIdiomatic.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeIdiomatic.scala @@ -254,7 +254,7 @@ trait BCodeIdiomatic { case ct: ClassBType if ct.isSubtypeOf(jlCharSequenceRef) => jlCharSequenceRef // Don't match for `ArrayBType(CHAR)`, even though StringBuilder has such an overload: // `"a" + Array('b')` should NOT be "ab", but "a[C@...". - case _: RefBType => ObjectReference + case _: RefBType => ObjectRef // jlStringBuilder does not have overloads for byte and short, but we can just use the int version case BYTE | SHORT => INT case pt: PrimitiveBType => pt @@ -268,8 +268,10 @@ trait BCodeIdiomatic { * can-multi-thread */ final def genStringBuilderEnd: Unit = { - invokevirtual(JavaStringBuilderClassName, "toString", "()Ljava/lang/String;") + invokevirtual(JavaStringBuilderClassName, "toString", genStringBuilderEndDesc) } + // Use ClassBType refs instead of plain string literal to make sure that needed ClassBTypes are initialized and reachable + private lazy val genStringBuilderEndDesc = MethodBType(Nil, StringRef).descriptor /* Concatenate top N arguments on the stack with `StringConcatFactory#makeConcatWithConstants` * (only works for JDK 9+) @@ -284,13 +286,7 @@ trait BCodeIdiomatic { jmethod.visitInvokeDynamicInsn( "makeConcatWithConstants", asm.Type.getMethodDescriptor(StringRef.toASMType, argTypes:_*), - new asm.Handle( - asm.Opcodes.H_INVOKESTATIC, - "java/lang/invoke/StringConcatFactory", - "makeConcatWithConstants", - "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;", - false - ), + coreBTypes.jliStringConcatFactoryMakeConcatWithConstantsHandle, (recipe +: constants):_* ) } diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala index 829b156be428..c79a5543e99a 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala @@ -112,7 +112,6 @@ trait BCodeSkelBuilder extends BCodeHelpers { def genPlainClass(cd0: TypeDef) = cd0 match { case TypeDef(_, impl: Template) => assert(cnode == null, "GenBCode detected nested methods.") - innerClassBufferASM.clear() claszSymbol = cd0.symbol isCZParcelable = isAndroidParcelableClass(claszSymbol) @@ -231,15 +230,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { if (optSerial.isDefined) { addSerialVUID(optSerial.get, cnode)} addClassFields() - - innerClassBufferASM ++= classBTypeFromSymbol(claszSymbol).info.memberClasses - - val companion = claszSymbol.companionClass - if companion.isTopLevelModuleClass then - innerClassBufferASM ++= classBTypeFromSymbol(companion).info.memberClasses - gen(cd.rhs) - addInnerClassesASM(cnode, innerClassBufferASM.toList) if (AsmUtils.traceClassEnabled && cnode.name.contains(AsmUtils.traceClassPattern)) AsmUtils.traceClass(cnode) @@ -255,12 +246,8 @@ trait BCodeSkelBuilder extends BCodeHelpers { private def initJClass(jclass: asm.ClassVisitor): Unit = { val ps = claszSymbol.info.parents - val superClass: String = if (ps.isEmpty) ObjectReference.internalName else internalName(ps.head.typeSymbol) - val interfaceNames0 = classBTypeFromSymbol(claszSymbol).info.interfaces map { - case classBType => - if (classBType.isNestedClass) { innerClassBufferASM += classBType } - classBType.internalName - } + val superClass: String = if (ps.isEmpty) ObjectRef.internalName else internalName(ps.head.typeSymbol) + val interfaceNames0 = classBTypeFromSymbol(claszSymbol).info.interfaces.map(_.internalName) /* To avoid deadlocks when combining objects, lambdas and multi-threading, * lambdas in objects are compiled to instance methods of the module class * instead of static methods (see tests/run/deadlock.scala and @@ -881,7 +868,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { // android creator code if (isCZParcelable) { // add a static field ("CREATOR") to this class to cache android.os.Parcelable$Creator - val andrFieldDescr = getClassBTypeAndRegisterInnerClass(AndroidCreatorClass).descriptor + val andrFieldDescr = classBTypeFromSymbol(AndroidCreatorClass).descriptor cnode.visitField( asm.Opcodes.ACC_STATIC | asm.Opcodes.ACC_FINAL, "CREATOR", diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeSyncAndTry.scala b/compiler/src/dotty/tools/backend/jvm/BCodeSyncAndTry.scala index 5bcc4e0efead..b5ed27511e7e 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeSyncAndTry.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeSyncAndTry.scala @@ -30,7 +30,7 @@ trait BCodeSyncAndTry extends BCodeBodyBuilder { def genSynchronized(tree: Apply, expectedType: BType): BType = (tree: @unchecked) match { case Apply(TypeApply(fun, _), args) => - val monitor = locals.makeLocal(ObjectReference, "monitor", defn.ObjectType, tree.span) + val monitor = locals.makeLocal(ObjectRef, "monitor", defn.ObjectType, tree.span) val monCleanup = new asm.Label // if the synchronized block returns a result, store it in a local variable. @@ -40,7 +40,7 @@ trait BCodeSyncAndTry extends BCodeBodyBuilder { /* ------ (1) pushing and entering the monitor, also keeping a reference to it in a local var. ------ */ genLoadQualifier(fun) - bc dup ObjectReference + bc dup ObjectRef locals.store(monitor) emit(asm.Opcodes.MONITORENTER) @@ -185,7 +185,7 @@ trait BCodeSyncAndTry extends BCodeBodyBuilder { for (CaseDef(pat, _, caseBody) <- catches) yield { pat match { case Typed(Ident(nme.WILDCARD), tpt) => NamelessEH(tpeTK(tpt).asClassBType, caseBody) - case Ident(nme.WILDCARD) => NamelessEH(ThrowableReference, caseBody) + case Ident(nme.WILDCARD) => NamelessEH(jlThrowableRef, caseBody) case Bind(_, _) => BoundEH (pat.symbol, caseBody) } } @@ -324,7 +324,7 @@ trait BCodeSyncAndTry extends BCodeBodyBuilder { nopIfNeeded(startTryBody) val finalHandler = currProgramPoint() // version of the finally-clause reached via unhandled exception. protect(startTryBody, finalHandler, finalHandler, null) - val Local(eTK, _, eIdx, _) = locals(locals.makeLocal(ThrowableReference, "exc", defn.ThrowableType, finalizer.span)) + val Local(eTK, _, eIdx, _) = locals(locals.makeLocal(jlThrowableRef, "exc", defn.ThrowableType, finalizer.span)) bc.store(eIdx, eTK) emitFinalizer(finalizer, null, isDuplicate = true) bc.load(eIdx, eTK) diff --git a/compiler/src/dotty/tools/backend/jvm/BTypes.scala b/compiler/src/dotty/tools/backend/jvm/BTypes.scala index da3bead08835..57bd343b6658 100644 --- a/compiler/src/dotty/tools/backend/jvm/BTypes.scala +++ b/compiler/src/dotty/tools/backend/jvm/BTypes.scala @@ -89,8 +89,8 @@ abstract class BTypes { final def isNonVoidPrimitiveType = isPrimitive && this != UNIT - final def isNullType = this == RT_NULL - final def isNothingType = this == RT_NOTHING + final def isNullType = this == srNullRef + final def isNothingType = this == srNothingRef final def isBoxed = this.isClass && boxedClasses(this.asClassBType) @@ -113,7 +113,7 @@ abstract class BTypes { this match { case ArrayBType(component) => - if (other == ObjectReference || other == jlCloneableReference || other == jioSerializableReference) true + if (other == ObjectRef || other == jlCloneableRef || other == jiSerializableRef) true else other match { case ArrayBType(otherComponoent) => component.conformsTo(otherComponoent) case _ => false @@ -122,7 +122,7 @@ abstract class BTypes { case classType: ClassBType => if (isBoxed) { if (other.isBoxed) this == other - else if (other == ObjectReference) true + else if (other == ObjectRef) true else other match { case otherClassType: ClassBType => classType.isSubtypeOf(otherClassType) // e.g., java/lang/Double conforms to java/lang/Number case _ => false @@ -165,7 +165,7 @@ abstract class BTypes { assert(other.isRef, s"Cannot compute maxType: $this, $other") // Approximate `lub`. The common type of two references is always ObjectReference. - ObjectReference + ObjectRef } /** @@ -668,7 +668,7 @@ abstract class BTypes { if (this == other) return true if (isInterface) { - if (other == ObjectReference) return true // interfaces conform to Object + if (other == ObjectRef) return true // interfaces conform to Object if (!other.isInterface) return false // this is an interface, the other is some class other than object. interfaces cannot extend classes, so the result is false. // else: this and other are both interfaces. continue to (*) } else { @@ -698,13 +698,13 @@ abstract class BTypes { // exercised by test/files/run/t4761.scala if (other.isSubtypeOf(this)) this else if (this.isSubtypeOf(other)) other - else ObjectReference + else ObjectRef case (true, false) => - if (other.isSubtypeOf(this)) this else ObjectReference + if (other.isSubtypeOf(this)) this else ObjectRef case (false, true) => - if (this.isSubtypeOf(other)) other else ObjectReference + if (this.isSubtypeOf(other)) other else ObjectRef case _ => // TODO @lry I don't really understand the reasoning here. diff --git a/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala b/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala index 58e6265376fd..bf275dfa7a62 100644 --- a/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala +++ b/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala @@ -13,6 +13,7 @@ import dotty.tools.dotc.core.Phases._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.transform.SymUtils._ +import dotty.tools.dotc.core.StdNames /** * This class mainly contains the method classBTypeFromSymbol, which extracts the necessary @@ -91,6 +92,20 @@ class BTypesFromSymbols[I <: DottyBackendInterface](val int: I) extends BTypes { }) } + final def mirrorClassBTypeFromSymbol(moduleClassSym: Symbol): ClassBType = { + assert(moduleClassSym.isTopLevelModuleClass, s"not a top-level module class: $moduleClassSym") + val internalName = moduleClassSym.javaBinaryName.stripSuffix(StdNames.str.MODULE_SUFFIX) + val bType = ClassBType(internalName) + bType.info = ClassInfo( + superClass = Some(ObjectRef), + interfaces = Nil, + flags = asm.Opcodes.ACC_SUPER | asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_FINAL, + memberClasses = getMemberClasses(moduleClassSym).map(classBTypeFromSymbol), + nestedInfo = None + ) + bType + } + private def setClassInfo(classSym: Symbol, classBType: ClassBType): ClassBType = { val superClassSym: Symbol = { val t = classSym.asClass.superClass @@ -138,13 +153,6 @@ class BTypesFromSymbols[I <: DottyBackendInterface](val int: I) extends BTypes { /* The InnerClass table of a class C must contain all nested classes of C, even if they are only * declared but not otherwise referenced in C (from the bytecode or a method / field signature). * We collect them here. - * - * Nested classes that are also referenced in C will be added to the innerClassBufferASM during - * code generation, but those duplicates will be eliminated when emitting the InnerClass - * attribute. - * - * Why doe we need to collect classes into innerClassBufferASM at all? To collect references to - * nested classes, but NOT nested in C, that are used within C. */ val nestedClassSymbols = { // The lambdalift phase lifts all nested classes to the enclosing class, so if we collect diff --git a/compiler/src/dotty/tools/backend/jvm/CoreBTypes.scala b/compiler/src/dotty/tools/backend/jvm/CoreBTypes.scala index 88a2bf8faa35..e94bda16fbb8 100644 --- a/compiler/src/dotty/tools/backend/jvm/CoreBTypes.scala +++ b/compiler/src/dotty/tools/backend/jvm/CoreBTypes.scala @@ -5,6 +5,8 @@ package jvm import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.transform.Erasure +import scala.tools.asm.{Handle, Opcodes} +import dotty.tools.dotc.core.StdNames /** * Core BTypes and some other definitions. The initialization of these definitions requies access @@ -54,15 +56,15 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: DottyBackendInterface]](val bTyp defn.DoubleClass -> DOUBLE ) - lazy val BOXED_UNIT : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Void]) - lazy val BOXED_BOOLEAN : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Boolean]) - lazy val BOXED_BYTE : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Byte]) - lazy val BOXED_SHORT : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Short]) - lazy val BOXED_CHAR : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Character]) - lazy val BOXED_INT : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Integer]) - lazy val BOXED_LONG : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Long]) - lazy val BOXED_FLOAT : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Float]) - lazy val BOXED_DOUBLE : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Double]) + private lazy val BOXED_UNIT : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Void]) + private lazy val BOXED_BOOLEAN : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Boolean]) + private lazy val BOXED_BYTE : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Byte]) + private lazy val BOXED_SHORT : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Short]) + private lazy val BOXED_CHAR : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Character]) + private lazy val BOXED_INT : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Integer]) + private lazy val BOXED_LONG : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Long]) + private lazy val BOXED_FLOAT : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Float]) + private lazy val BOXED_DOUBLE : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Double]) /** * Map from primitive types to their boxed class type. Useful when pushing class literals onto the @@ -105,43 +107,79 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: DottyBackendInterface]](val bTyp } /* - * RT_NOTHING and RT_NULL exist at run-time only. They are the bytecode-level manifestation (in - * method signatures only) of what shows up as NothingClass resp. NullClass in Scala ASTs. + * srNothingRef and srNullRef exist at run-time only. They are the bytecode-level manifestation (in + * method signatures only) of what shows up as NothingClass (scala.Nothing) resp. NullClass (scala.Null) in Scala ASTs. * - * Therefore, when RT_NOTHING or RT_NULL are to be emitted, a mapping is needed: the internal + * Therefore, when srNothingRef or srNullRef are to be emitted, a mapping is needed: the internal * names of NothingClass and NullClass can't be emitted as-is. * TODO @lry Once there's a 2.11.3 starr, use the commented argument list. The current starr crashes on the type literal `scala.runtime.Nothing$` */ - lazy val RT_NOTHING : ClassBType = classBTypeFromSymbol(requiredClass("scala.runtime.Nothing$")) // (requiredClass[scala.runtime.Nothing$]) - lazy val RT_NULL : ClassBType = classBTypeFromSymbol(requiredClass("scala.runtime.Null$")) // (requiredClass[scala.runtime.Null$]) - - lazy val ObjectReference : ClassBType = classBTypeFromSymbol(defn.ObjectClass) - lazy val objArrayReference : ArrayBType = ArrayBType(ObjectReference) + lazy val srNothingRef : ClassBType = classBTypeFromSymbol(requiredClass("scala.runtime.Nothing$")) // (requiredClass[scala.runtime.Nothing$]) + lazy val srNullRef : ClassBType = classBTypeFromSymbol(requiredClass("scala.runtime.Null$")) // (requiredClass[scala.runtime.Null$]) + lazy val ObjectRef : ClassBType = classBTypeFromSymbol(defn.ObjectClass) lazy val StringRef : ClassBType = classBTypeFromSymbol(defn.StringClass) lazy val jlStringBuilderRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.StringBuilder]) lazy val jlStringBufferRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.StringBuffer]) lazy val jlCharSequenceRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.CharSequence]) lazy val jlClassRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Class[_]]) - lazy val ThrowableReference : ClassBType = classBTypeFromSymbol(defn.ThrowableClass) - lazy val jlCloneableReference : ClassBType = classBTypeFromSymbol(defn.JavaCloneableClass) // java/lang/Cloneable - lazy val jlNPEReference : ClassBType = classBTypeFromSymbol(defn.NullPointerExceptionClass) // java/lang/NullPointerException - lazy val jioSerializableReference : ClassBType = classBTypeFromSymbol(requiredClass[java.io.Serializable]) // java/io/Serializable - lazy val scalaSerializableReference : ClassBType = classBTypeFromSymbol(requiredClass[scala.Serializable]) // scala/Serializable - lazy val classCastExceptionReference : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.ClassCastException]) // java/lang/ClassCastException - lazy val jlIllegalArgExceptionRef: ClassBType = classBTypeFromSymbol(requiredClass[java.lang.IllegalArgumentException]) - lazy val jliSerializedLambdaRef: ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.SerializedLambda]) - - lazy val srBooleanRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.BooleanRef]) - lazy val srByteRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.ByteRef]) - lazy val srCharRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.CharRef]) - lazy val srIntRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.IntRef]) - lazy val srLongRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.LongRef]) - lazy val srFloatRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.FloatRef]) - lazy val srDoubleRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.DoubleRef]) - - lazy val BoxesRunTime: ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.BoxesRunTime]) - + lazy val jlThrowableRef : ClassBType = classBTypeFromSymbol(defn.ThrowableClass) + lazy val jlCloneableRef : ClassBType = classBTypeFromSymbol(defn.JavaCloneableClass) // java/lang/Cloneable + lazy val jioSerializableRef : ClassBType = classBTypeFromSymbol(requiredClass[java.io.Serializable]) // java/io/Serializable + lazy val jlClassCastExceptionRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.ClassCastException]) // java/lang/ClassCastException + lazy val jlIllegalArgExceptionRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.IllegalArgumentException]) + lazy val jliSerializedLambdaRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.SerializedLambda]) + + lazy val srBoxesRunTimeRef: ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.BoxesRunTime]) + + private lazy val jliCallSiteRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.CallSite]) + private lazy val jliLambdaMetafactoryRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.LambdaMetafactory]) + private lazy val jliMethodHandleRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.MethodHandle]) + private lazy val jliMethodHandlesLookupRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.MethodHandles.Lookup]) + private lazy val jliMethodTypeRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.MethodType]) + private lazy val jliStringConcatFactoryRef : ClassBType = classBTypeFromSymbol(requiredClass("java.lang.invoke.StringConcatFactory")) // since JDK 9 + private lazy val srLambdaDeserialize : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.LambdaDeserialize]) + + lazy val jliLambdaMetaFactoryMetafactoryHandle: Handle = new Handle( + Opcodes.H_INVOKESTATIC, + jliLambdaMetafactoryRef.internalName, + "metafactory", + MethodBType( + List(jliMethodHandlesLookupRef, StringRef, jliMethodTypeRef, jliMethodTypeRef, jliMethodHandleRef, jliMethodTypeRef), + jliCallSiteRef + ).descriptor, + /* itf = */ false) + + lazy val jliLambdaMetaFactoryAltMetafactoryHandle: Handle = new Handle( + Opcodes.H_INVOKESTATIC, + jliLambdaMetafactoryRef.internalName, + "altMetafactory", + MethodBType( + List(jliMethodHandlesLookupRef, StringRef, jliMethodTypeRef, ArrayBType(ObjectRef)), + jliCallSiteRef + ).descriptor, + /* itf = */ false) + + lazy val jliLambdaDeserializeBootstrapHandle: Handle = new Handle( + Opcodes.H_INVOKESTATIC, + srLambdaDeserialize.internalName, + "bootstrap", + MethodBType( + List(jliMethodHandlesLookupRef, StringRef, jliMethodTypeRef, ArrayBType(jliMethodHandleRef)), + jliCallSiteRef + ).descriptor, + /* itf = */ false) + + lazy val jliStringConcatFactoryMakeConcatWithConstantsHandle = new Handle( + Opcodes.H_INVOKESTATIC, + jliStringConcatFactoryRef.internalName, + "makeConcatWithConstants", + MethodBType( + List(jliMethodHandlesLookupRef, StringRef, jliMethodTypeRef, StringRef, ArrayBType(ObjectRef)), + jliCallSiteRef + ).descriptor, + /* itf = */ false) + /** * Methods in scala.runtime.BoxesRuntime */ @@ -157,14 +195,14 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: DottyBackendInterface]](val bTyp ) lazy val asmUnboxTo: Map[BType, MethodNameAndType] = Map( - BOOL -> MethodNameAndType("unboxToBoolean", MethodBType(List(ObjectReference), BOOL)), - BYTE -> MethodNameAndType("unboxToByte", MethodBType(List(ObjectReference), BYTE)), - CHAR -> MethodNameAndType("unboxToChar", MethodBType(List(ObjectReference), CHAR)), - SHORT -> MethodNameAndType("unboxToShort", MethodBType(List(ObjectReference), SHORT)), - INT -> MethodNameAndType("unboxToInt", MethodBType(List(ObjectReference), INT)), - LONG -> MethodNameAndType("unboxToLong", MethodBType(List(ObjectReference), LONG)), - FLOAT -> MethodNameAndType("unboxToFloat", MethodBType(List(ObjectReference), FLOAT)), - DOUBLE -> MethodNameAndType("unboxToDouble", MethodBType(List(ObjectReference), DOUBLE)) + BOOL -> MethodNameAndType("unboxToBoolean", MethodBType(List(ObjectRef), BOOL)), + BYTE -> MethodNameAndType("unboxToByte", MethodBType(List(ObjectRef), BYTE)), + CHAR -> MethodNameAndType("unboxToChar", MethodBType(List(ObjectRef), CHAR)), + SHORT -> MethodNameAndType("unboxToShort", MethodBType(List(ObjectRef), SHORT)), + INT -> MethodNameAndType("unboxToInt", MethodBType(List(ObjectRef), INT)), + LONG -> MethodNameAndType("unboxToLong", MethodBType(List(ObjectRef), LONG)), + FLOAT -> MethodNameAndType("unboxToFloat", MethodBType(List(ObjectRef), FLOAT)), + DOUBLE -> MethodNameAndType("unboxToDouble", MethodBType(List(ObjectRef), DOUBLE)) ) lazy val typeOfArrayOp: Map[Int, BType] = { @@ -178,7 +216,7 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: DottyBackendInterface]](val bTyp (List(LARRAY_LENGTH, LARRAY_GET, LARRAY_SET) map (_ -> LONG)) ++ (List(FARRAY_LENGTH, FARRAY_GET, FARRAY_SET) map (_ -> FLOAT)) ++ (List(DARRAY_LENGTH, DARRAY_GET, DARRAY_SET) map (_ -> DOUBLE)) ++ - (List(OARRAY_LENGTH, OARRAY_GET, OARRAY_SET) map (_ -> ObjectReference)) : _* + (List(OARRAY_LENGTH, OARRAY_GET, OARRAY_SET) map (_ -> ObjectRef)) : _* ) } } @@ -197,12 +235,12 @@ trait CoreBTypesProxyGlobalIndependent[BTS <: BTypes] { def boxedClasses: Set[ClassBType] - def RT_NOTHING : ClassBType - def RT_NULL : ClassBType + def srNothingRef : ClassBType + def srNullRef : ClassBType - def ObjectReference : ClassBType - def jlCloneableReference : ClassBType - def jioSerializableReference : ClassBType + def ObjectRef : ClassBType + def jlCloneableRef : ClassBType + def jiSerializableRef : ClassBType } /** @@ -218,16 +256,6 @@ final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: DottyBackendInterface def primitiveTypeMap: Map[Symbol, PrimitiveBType] = _coreBTypes.primitiveTypeMap - def BOXED_UNIT : ClassBType = _coreBTypes.BOXED_UNIT - def BOXED_BOOLEAN : ClassBType = _coreBTypes.BOXED_BOOLEAN - def BOXED_BYTE : ClassBType = _coreBTypes.BOXED_BYTE - def BOXED_SHORT : ClassBType = _coreBTypes.BOXED_SHORT - def BOXED_CHAR : ClassBType = _coreBTypes.BOXED_CHAR - def BOXED_INT : ClassBType = _coreBTypes.BOXED_INT - def BOXED_LONG : ClassBType = _coreBTypes.BOXED_LONG - def BOXED_FLOAT : ClassBType = _coreBTypes.BOXED_FLOAT - def BOXED_DOUBLE : ClassBType = _coreBTypes.BOXED_DOUBLE - def boxedClasses: Set[ClassBType] = _coreBTypes.boxedClasses def boxedClassOfPrimitive: Map[PrimitiveBType, ClassBType] = _coreBTypes.boxedClassOfPrimitive @@ -236,36 +264,29 @@ final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: DottyBackendInterface def unboxResultType: Map[Symbol, PrimitiveBType] = _coreBTypes.unboxResultType - def RT_NOTHING : ClassBType = _coreBTypes.RT_NOTHING - def RT_NULL : ClassBType = _coreBTypes.RT_NULL - - def ObjectReference : ClassBType = _coreBTypes.ObjectReference - def objArrayReference : ArrayBType = _coreBTypes.objArrayReference + def srNothingRef : ClassBType = _coreBTypes.srNothingRef + def srNullRef : ClassBType = _coreBTypes.srNullRef + def ObjectRef : ClassBType = _coreBTypes.ObjectRef def StringRef : ClassBType = _coreBTypes.StringRef def jlStringBuilderRef : ClassBType = _coreBTypes.jlStringBuilderRef def jlStringBufferRef : ClassBType = _coreBTypes.jlStringBufferRef def jlCharSequenceRef : ClassBType = _coreBTypes.jlCharSequenceRef def jlClassRef : ClassBType = _coreBTypes.jlClassRef - def ThrowableReference : ClassBType = _coreBTypes.ThrowableReference - def jlCloneableReference : ClassBType = _coreBTypes.jlCloneableReference - def jlNPEReference : ClassBType = _coreBTypes.jlNPEReference - def jioSerializableReference : ClassBType = _coreBTypes.jioSerializableReference - def scalaSerializableReference : ClassBType = _coreBTypes.scalaSerializableReference - def classCastExceptionReference : ClassBType = _coreBTypes.classCastExceptionReference + def jlThrowableRef : ClassBType = _coreBTypes.jlThrowableRef + def jlCloneableRef : ClassBType = _coreBTypes.jlCloneableRef + def jiSerializableRef : ClassBType = _coreBTypes.jioSerializableRef + def jlClassCastExceptionRef : ClassBType = _coreBTypes.jlClassCastExceptionRef def jlIllegalArgExceptionRef : ClassBType = _coreBTypes.jlIllegalArgExceptionRef def jliSerializedLambdaRef : ClassBType = _coreBTypes.jliSerializedLambdaRef - def srBooleanRef : ClassBType = _coreBTypes.srBooleanRef - def srByteRef : ClassBType = _coreBTypes.srByteRef - def srCharRef : ClassBType = _coreBTypes.srCharRef - def srIntRef : ClassBType = _coreBTypes.srIntRef - def srLongRef : ClassBType = _coreBTypes.srLongRef - def srFloatRef : ClassBType = _coreBTypes.srFloatRef - def srDoubleRef : ClassBType = _coreBTypes.srDoubleRef - - def BoxesRunTime: ClassBType = _coreBTypes.BoxesRunTime + def srBoxesRuntimeRef: ClassBType = _coreBTypes.srBoxesRunTimeRef + def jliLambdaMetaFactoryMetafactoryHandle : Handle = _coreBTypes.jliLambdaMetaFactoryMetafactoryHandle + def jliLambdaMetaFactoryAltMetafactoryHandle : Handle = _coreBTypes.jliLambdaMetaFactoryAltMetafactoryHandle + def jliLambdaDeserializeBootstrapHandle : Handle = _coreBTypes.jliLambdaDeserializeBootstrapHandle + def jliStringConcatFactoryMakeConcatWithConstantsHandle: Handle = _coreBTypes.jliStringConcatFactoryMakeConcatWithConstantsHandle + def asmBoxTo : Map[BType, MethodNameAndType] = _coreBTypes.asmBoxTo def asmUnboxTo: Map[BType, MethodNameAndType] = _coreBTypes.asmUnboxTo diff --git a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala index b42b0d6e3dc8..6430575f304e 100644 --- a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala +++ b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala @@ -356,6 +356,8 @@ class GenBCodePipeline(val int: DottyBackendInterface, val primitives: DottyPrim * - converting the plain ClassNode to byte array and placing it on queue-3 */ class Worker2 { + import bTypes.ClassBType + import bTypes.coreBTypes.jliLambdaMetaFactoryAltMetafactoryHandle // lazy val localOpt = new LocalOpt(new Settings()) private def localOptimizations(classNode: ClassNode): Unit = { @@ -372,7 +374,7 @@ class GenBCodePipeline(val int: DottyBackendInterface, val primitives: DottyPrim val insn = iter.next() insn match { case indy: InvokeDynamicInsnNode - if indy.bsm == BCodeBodyBuilder.lambdaMetaFactoryAltMetafactoryHandle => + if indy.bsm == jliLambdaMetaFactoryAltMetafactoryHandle => import java.lang.invoke.LambdaMetafactory.FLAG_SERIALIZABLE val metafactoryFlags = indy.bsmArgs(3).asInstanceOf[Integer].toInt val isSerializable = (metafactoryFlags & FLAG_SERIALIZABLE) != 0 @@ -410,7 +412,6 @@ class GenBCodePipeline(val int: DottyBackendInterface, val primitives: DottyPrim */ private def addLambdaDeserialize(classNode: ClassNode, implMethodsArray: Array[Handle]): Unit = { import asm.Opcodes._ - import BCodeBodyBuilder._ import bTypes._ import coreBTypes._ @@ -422,12 +423,12 @@ class GenBCodePipeline(val int: DottyBackendInterface, val primitives: DottyPrim // stack map frames and invokes the `getCommonSuperClass` method. This method expects all // ClassBTypes mentioned in the source code to exist in the map. - val serlamObjDesc = MethodBType(jliSerializedLambdaRef :: Nil, ObjectReference).descriptor + val serlamObjDesc = MethodBType(jliSerializedLambdaRef :: Nil, ObjectRef).descriptor val mv = cw.visitMethod(ACC_PRIVATE + ACC_STATIC + ACC_SYNTHETIC, "$deserializeLambda$", serlamObjDesc, null, null) def emitLambdaDeserializeIndy(targetMethods: Seq[Handle]): Unit = { mv.visitVarInsn(ALOAD, 0) - mv.visitInvokeDynamicInsn("lambdaDeserialize", serlamObjDesc, lambdaDeserializeBootstrapHandle, targetMethods: _*) + mv.visitInvokeDynamicInsn("lambdaDeserialize", serlamObjDesc, jliLambdaDeserializeBootstrapHandle, targetMethods: _*) } val targetMethodGroupLimit = 255 - 1 - 3 // JVM limit. See See MAX_MH_ARITY in CallSite.java @@ -452,6 +453,34 @@ class GenBCodePipeline(val int: DottyBackendInterface, val primitives: DottyPrim mv.visitInsn(ARETURN) } + private def setInnerClasses(classNode: ClassNode): Unit = if (classNode != null) { + classNode.innerClasses.clear() + val (declared, referred) = collectNestedClasses(classNode) + addInnerClasses(classNode, declared, referred) + } + + /** + * Visit the class node and collect all referenced nested classes. + */ + private def collectNestedClasses(classNode: ClassNode): (List[ClassBType], List[ClassBType]) = { + // type InternalName = String + val c = new NestedClassesCollector[ClassBType](nestedOnly = true) { + def declaredNestedClasses(internalName: InternalName): List[ClassBType] = + bTypes.classBTypeFromInternalName(internalName).info.memberClasses + + def getClassIfNested(internalName: InternalName): Option[ClassBType] = { + val c = bTypes.classBTypeFromInternalName(internalName) + Option.when(c.isNestedClass)(c) + } + + def raiseError(msg: String, sig: String, e: Option[Throwable]): Unit = { + // don't crash on invalid generic signatures + } + } + c.visit(classNode) + (c.declaredInnerClasses.toList, c.referredInnerClasses.toList) + } + def run(): Unit = { while (true) { val item = q2.poll @@ -466,6 +495,8 @@ class GenBCodePipeline(val int: DottyBackendInterface, val primitives: DottyPrim val serializableLambdas = collectSerializableLambdas(plainNode) if (serializableLambdas.nonEmpty) addLambdaDeserialize(plainNode, serializableLambdas) + setInnerClasses(plainNode) + setInnerClasses(item.mirror.classNode) addToQ3(item) } catch { case ex: InterruptedException => diff --git a/compiler/src/dotty/tools/backend/jvm/GenericSignatureVisitor.scala b/compiler/src/dotty/tools/backend/jvm/GenericSignatureVisitor.scala new file mode 100644 index 000000000000..e9e532933290 --- /dev/null +++ b/compiler/src/dotty/tools/backend/jvm/GenericSignatureVisitor.scala @@ -0,0 +1,326 @@ +package dotty.tools.backend.jvm + +import scala.language.unsafeNulls + +import scala.tools.asm.{ClassReader, Type, Handle } +import scala.tools.asm.tree._ + +import scala.collection.mutable +import scala.util.control.{NoStackTrace, NonFatal} +import scala.annotation._ +import scala.jdk.CollectionConverters._ + +// Backported from scala/scala, commit sha: 724be0e9425b9ad07c244d25efdad695d75abbcf +// https://github.com/scala/scala/blob/724be0e9425b9ad07c244d25efdad695d75abbcf/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala#L928 +abstract class GenericSignatureVisitor(nestedOnly: Boolean) { + // For performance (`Char => Boolean` is not specialized) + private trait CharBooleanFunction { def apply(c: Char): Boolean } + + final def visitInternalName(internalName: String): Unit = visitInternalName(internalName, 0, if (internalName eq null) 0 else internalName.length) + def visitInternalName(internalName: String, offset: Int, length: Int): Unit + + def raiseError(msg: String, sig: String, e: Option[Throwable] = None): Unit + + def visitClassSignature(sig: String): Unit = if (sig != null) { + val p = new Parser(sig, nestedOnly) + p.safely { p.classSignature() } + } + + def visitMethodSignature(sig: String): Unit = if (sig != null) { + val p = new Parser(sig, nestedOnly) + p.safely { p.methodSignature() } + } + + def visitFieldSignature(sig: String): Unit = if (sig != null) { + val p = new Parser(sig, nestedOnly) + p.safely { p.fieldSignature() } + } + + private final class Parser(sig: String, nestedOnly: Boolean) { + + private var index = 0 + private val end = sig.length + + private val Aborted: Throwable = new NoStackTrace { } + private def abort(): Nothing = throw Aborted + + @inline def safely(f: => Unit): Unit = try f catch { + case Aborted => + case NonFatal(e) => raiseError(s"Exception thrown during signature parsing", sig, Some(e)) + } + + private def current = { + if (index >= end) { + raiseError(s"Out of bounds, $index >= $end", sig) + abort() // Don't continue, even if `notifyInvalidSignature` returns + } + sig.charAt(index) + } + + private def accept(c: Char): Unit = { + if (current != c) { + raiseError(s"Expected $c at $index, found $current", sig) + abort() + } + index += 1 + } + + private def skip(): Unit = { index += 1 } + private def getCurrentAndSkip(): Char = { val c = current; skip(); c } + + private def skipUntil(isDelimiter: CharBooleanFunction): Unit = { + while (!isDelimiter(current)) { index += 1 } + } + private def skipUntilDelimiter(delimiter: Char): Unit = { + sig.indexOf(delimiter, index) match { + case -1 => + raiseError(s"Out of bounds", sig) + abort() // Don't continue, even if `notifyInvalidSignature` returns + case i => + index = i + } + } + + private def appendUntil(builder: java.lang.StringBuilder, isDelimiter: CharBooleanFunction): Unit = { + val start = index + skipUntil(isDelimiter) + builder.append(sig, start, index) + } + + def isBaseType(c: Char): Boolean = c match { + case 'B' | 'C' | 'D' | 'F' | 'I' | 'J' | 'S' | 'Z' => true + case _ => false + } + + private val isClassNameEnd: CharBooleanFunction = (c: Char) => c == '<' || c == '.' || c == ';' + + private def typeArguments(): Unit = if (current == '<') { + skip() + while (current != '>') current match { + case '*' | '+' | '-' => + skip() + case _ => + referenceTypeSignature() + } + accept('>') + } + + @tailrec private def referenceTypeSignature(): Unit = getCurrentAndSkip() match { + case 'L' => + var names: java.lang.StringBuilder = null + + val start = index + var seenDollar = false + while (!isClassNameEnd(current)) { + seenDollar ||= current == '$' + index += 1 + } + if ((current == '.' || seenDollar) || !nestedOnly) { + // OPT: avoid allocations when only a top-level class is encountered + names = new java.lang.StringBuilder(32) + names.append(sig, start, index) + visitInternalName(names.toString) + } + typeArguments() + + while (current == '.') { + skip() + names.append('$') + appendUntil(names, isClassNameEnd) + visitInternalName(names.toString) + typeArguments() + } + accept(';') + + case 'T' => + skipUntilDelimiter(';') + skip() + + case '[' => + if (isBaseType(current)) skip() + else referenceTypeSignature() + } + + private def typeParameters(): Unit = if (current == '<') { + skip() + while (current != '>') { + skipUntilDelimiter(':'); skip() + val c = current + // The ClassBound can be missing, but only if there's an InterfaceBound after. + // This is an assumption that's not in the spec, see https://stackoverflow.com/q/44284928 + if (c != ':' && c != '>') { referenceTypeSignature() } + while (current == ':') { skip(); referenceTypeSignature() } + } + accept('>') + } + + def classSignature(): Unit = { + typeParameters() + while (index < end) referenceTypeSignature() + } + + def methodSignature(): Unit = { + typeParameters() + + accept('(') + while (current != ')') { + if (isBaseType(current)) skip() + else referenceTypeSignature() + } + accept(')') + + if (current == 'V' || isBaseType(current)) skip() + else referenceTypeSignature() + + while (index < end) { + accept('^') + referenceTypeSignature() + } + } + + def fieldSignature(): Unit = if (sig != null) safely { + referenceTypeSignature() + } + } +} + +// Backported from scala/scala, commit sha: 724be0e9425b9ad07c244d25efdad695d75abbcf +// https://github.com/scala/scala/blob/724be0e9425b9ad07c244d25efdad695d75abbcf/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala#L790 +abstract class NestedClassesCollector[T](nestedOnly: Boolean) extends GenericSignatureVisitor(nestedOnly) { + type InternalName = String + + def declaredNestedClasses(internalName: InternalName): List[T] + def getClassIfNested(internalName: InternalName): Option[T] + + val declaredInnerClasses = mutable.Set.empty[T] + val referredInnerClasses = mutable.Set.empty[T] + + def innerClasses: collection.Set[T] = declaredInnerClasses ++ referredInnerClasses + def clear(): Unit = { + declaredInnerClasses.clear() + referredInnerClasses.clear() + } + + def visit(classNode: ClassNode): Unit = { + visitInternalName(classNode.name) + declaredInnerClasses ++= declaredNestedClasses(classNode.name) + + visitInternalName(classNode.superName) + classNode.interfaces.asScala foreach visitInternalName + visitInternalName(classNode.outerClass) + + visitAnnotations(classNode.visibleAnnotations) + visitAnnotations(classNode.visibleTypeAnnotations) + visitAnnotations(classNode.invisibleAnnotations) + visitAnnotations(classNode.invisibleTypeAnnotations) + + visitClassSignature(classNode.signature) + + for (f <- classNode.fields.asScala) { + visitDescriptor(f.desc) + visitAnnotations(f.visibleAnnotations) + visitAnnotations(f.visibleTypeAnnotations) + visitAnnotations(f.invisibleAnnotations) + visitAnnotations(f.invisibleTypeAnnotations) + visitFieldSignature(f.signature) + } + + for (m <- classNode.methods.asScala) { + visitDescriptor(m.desc) + + visitAnnotations(m.visibleAnnotations) + visitAnnotations(m.visibleTypeAnnotations) + visitAnnotations(m.invisibleAnnotations) + visitAnnotations(m.invisibleTypeAnnotations) + visitAnnotationss(m.visibleParameterAnnotations) + visitAnnotationss(m.invisibleParameterAnnotations) + visitAnnotations(m.visibleLocalVariableAnnotations) + visitAnnotations(m.invisibleLocalVariableAnnotations) + + m.exceptions.asScala foreach visitInternalName + for (tcb <- m.tryCatchBlocks.asScala) visitInternalName(tcb.`type`) + + val iter = m.instructions.iterator + while (iter.hasNext) iter.next() match { + case ti: TypeInsnNode => visitInternalNameOrArrayReference(ti.desc) + case fi: FieldInsnNode => visitInternalNameOrArrayReference(fi.owner); visitDescriptor(fi.desc) + case mi: MethodInsnNode => visitInternalNameOrArrayReference(mi.owner); visitDescriptor(mi.desc) + case id: InvokeDynamicInsnNode => visitDescriptor(id.desc); visitHandle(id.bsm); id.bsmArgs foreach visitConstant + case ci: LdcInsnNode => visitConstant(ci.cst) + case ma: MultiANewArrayInsnNode => visitDescriptor(ma.desc) + case _ => + } + + visitMethodSignature(m.signature) + } + } + + private def containsChar(s: String, offset: Int, length: Int, char: Char): Boolean = { + val ix = s.indexOf(char, offset) + !(ix == -1 || ix >= offset + length) + } + + def visitInternalName(internalName: String, offset: Int, length: Int): Unit = if (internalName != null && containsChar(internalName, offset, length, '$')) { + for (c <- getClassIfNested(internalName.substring(offset, length))) + if (!declaredInnerClasses.contains(c)) + referredInnerClasses += c + } + + // either an internal/Name or [[Linternal/Name; -- there are certain references in classfiles + // that are either an internal name (without the surrounding `L;`) or an array descriptor + // `[Linternal/Name;`. + def visitInternalNameOrArrayReference(ref: String): Unit = if (ref != null) { + val bracket = ref.lastIndexOf('[') + if (bracket == -1) visitInternalName(ref) + else if (ref.charAt(bracket + 1) == 'L') visitInternalName(ref, bracket + 2, ref.length - 1) + } + + // we are only interested in the class references in the descriptor, so we can skip over + // primitives and the brackets of array descriptors + def visitDescriptor(desc: String): Unit = (desc.charAt(0): @switch) match { + case '(' => + var i = 1 + while (i < desc.length) { + if (desc.charAt(i) == 'L') { + val start = i + 1 // skip the L + var seenDollar = false + while ({val ch = desc.charAt(i); seenDollar ||= (ch == '$'); ch != ';'}) i += 1 + if (seenDollar) + visitInternalName(desc, start, i) + } + // skips over '[', ')', primitives + i += 1 + } + + case 'L' => + visitInternalName(desc, 1, desc.length - 1) + + case '[' => + visitInternalNameOrArrayReference(desc) + + case _ => // skip over primitive types + } + + def visitConstant(const: AnyRef): Unit = const match { + case t: Type => visitDescriptor(t.getDescriptor) + case _ => + } + + // in principle we could references to annotation types, as they only end up as strings in the + // constant pool, not as class references. however, the java compiler still includes nested + // annotation classes in the innerClass table, so we do the same. explained in detail in the + // large comment in class BTypes. + def visitAnnotation(annot: AnnotationNode): Unit = { + visitDescriptor(annot.desc) + if (annot.values != null) annot.values.asScala foreach visitConstant + } + + def visitAnnotations(annots: java.util.List[_ <: AnnotationNode]) = if (annots != null) annots.asScala foreach visitAnnotation + def visitAnnotationss(annotss: Array[java.util.List[AnnotationNode]]) = if (annotss != null) annotss foreach visitAnnotations + + def visitHandle(handle: Handle): Unit = { + visitInternalNameOrArrayReference(handle.getOwner) + visitDescriptor(handle.getDesc) + } +} +