diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala b/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala index 862ec9275bdd..b814ab82ef43 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala @@ -2,7 +2,6 @@ package dotty.tools package backend package jvm -import scala.language.unsafeNulls import scala.annotation.{switch, tailrec} import scala.collection.mutable.SortedMap import scala.tools.asm @@ -202,7 +201,7 @@ trait BCodeBodyBuilder(val primitives: ScalaPrimitives) extends BCodeSkelBuilder if (isArrayGet(code)) { // load argument on stack - assert(args.length == 1, s"Too many arguments for array get operation: $tree"); + assert(args.length == 1, s"Too many arguments for array get operation: $tree") stack.push(k) genLoad(args.head, INT) stack.pop() @@ -335,7 +334,7 @@ trait BCodeBodyBuilder(val primitives: ScalaPrimitives) extends BCodeSkelBuilder bc.store(idx, tk) val localVarStart = currProgramPoint() if (!isSynth) { // there are case ValDef's emitted by patmat - varsInScope ::= (sym -> localVarStart) + varsInScope = (sym -> localVarStart) :: varsInScope.nn } generatedType = UNIT @@ -547,7 +546,7 @@ trait BCodeBodyBuilder(val primitives: ScalaPrimitives) extends BCodeSkelBuilder private def fieldOp(field: Symbol, isLoad: Boolean, specificReceiver: Symbol | Null)(using Context): Unit = { val useSpecificReceiver = specificReceiver != null && !field.isScalaStatic - val owner = bTypeLoader.classBTypeFromSymbol(if (useSpecificReceiver) specificReceiver else field.owner).internalName + val owner = bTypeLoader.classBTypeFromSymbol(if (useSpecificReceiver) specificReceiver.nn else field.owner).internalName val fieldJName = field.javaSimpleName val fieldDescr = symInfoTK(field).descriptor val isStatic = field.isStaticMember @@ -653,7 +652,7 @@ trait BCodeBodyBuilder(val primitives: ScalaPrimitives) extends BCodeSkelBuilder if (earlyReturnVar == null) { earlyReturnVar = locals.makeLocal(returnType, "earlyReturnVar", expr.tpe, expr.span) } - locals.store(earlyReturnVar) + locals.store(earlyReturnVar.nn) } bc.goTo(nextCleanup) shouldEmitCleanup = true @@ -986,7 +985,7 @@ trait BCodeBodyBuilder(val primitives: ScalaPrimitives) extends BCodeSkelBuilder var flatKeys: List[Int] = Nil var targets: List[asm.Label] = Nil - var default: asm.Label = null + var default: asm.Label | Null = null var switchBlocks: List[(asm.Label, Tree)] = Nil genLoad(selector, INT) @@ -1020,7 +1019,7 @@ trait BCodeBodyBuilder(val primitives: ScalaPrimitives) extends BCodeSkelBuilder if !hasDefault then default = new asm.Label - bc.emitSWITCH(mkArrayReverse(flatKeys), mkArrayL(targets.reverse), default, MIN_SWITCH_DENSITY) + bc.emitSWITCH(mkArrayReverse(flatKeys), mkArrayL(targets.reverse), default.nn, MIN_SWITCH_DENSITY) // emit switch-blocks. for (sb <- switchBlocks.reverse) { @@ -1030,7 +1029,7 @@ trait BCodeBodyBuilder(val primitives: ScalaPrimitives) extends BCodeSkelBuilder } if !hasDefault then - markProgramPoint(default) + markProgramPoint(default.nn) emitThrowMatchError() } else { @@ -1042,13 +1041,12 @@ trait BCodeBodyBuilder(val primitives: ScalaPrimitives) extends BCodeSkelBuilder * This mirrors the way that Java compiles `switch` on Strings. */ - var default: asm.Label = null - var indirectBlocks: List[(asm.Label, Tree)] = Nil + var default: asm.Label | Null = null + var indirectBlocks: List[(asm.Label, Tree | Null)] = Nil // Cases grouped by their hashCode val casesByHash = SortedMap.empty[Int, List[(String, Either[asm.Label, Tree])]] - var caseFallback: Tree = null for (caze @ CaseDef(pat, guard, body) <- cases) { assert(guard == tpd.EmptyTree, guard) @@ -1062,7 +1060,7 @@ trait BCodeBodyBuilder(val primitives: ScalaPrimitives) extends BCodeSkelBuilder case Ident(nme.WILDCARD) => assert(default == null, s"multiple default targets in a Match node, at ${tree.span}") default = new asm.Label - indirectBlocks ::= (default, body) + indirectBlocks ::= (default.nn, body) case Alternative(alts) => // We need an extra basic block since multiple strings can lead to this code val indirectCaseGroupLabel = new asm.Label @@ -1097,7 +1095,7 @@ trait BCodeBodyBuilder(val primitives: ScalaPrimitives) extends BCodeSkelBuilder val hasDefault = default != null if !hasDefault then default = new asm.Label - indirectBlocks ::= (default, null) + indirectBlocks ::= (default.nn, null) // Push the hashCode of the string (or `0` it is `null`) onto the stack and switch on it genLoadIfTo( @@ -1109,7 +1107,7 @@ trait BCodeBodyBuilder(val primitives: ScalaPrimitives) extends BCodeSkelBuilder INT, LoadDestination.FallThrough ) - bc.emitSWITCH(mkArrayReverse(flatKeys), mkArrayL(targets.reverse), default, MIN_SWITCH_DENSITY) + bc.emitSWITCH(mkArrayReverse(flatKeys), mkArrayL(targets.reverse), default.nn, MIN_SWITCH_DENSITY) // emit blocks for each hash case for ((hashLabel, caseAlternatives) <- hashBlocks.reverse) { @@ -1130,7 +1128,7 @@ trait BCodeBodyBuilder(val primitives: ScalaPrimitives) extends BCodeSkelBuilder } markProgramPoint(keepGoing) } - bc.goTo(default) + bc.goTo(default.nn) } // emit blocks for common patterns @@ -1165,7 +1163,7 @@ trait BCodeBodyBuilder(val primitives: ScalaPrimitives) extends BCodeSkelBuilder def emitLocalVarScopes(): Unit = if (BackendUtils.emitVars) { val end = currProgramPoint() - for ((sym, start) <- varsInScope.reverse) { + for ((sym, start) <- varsInScope.nn.reverse) { emitLocalVarScope(sym, start, end) } } @@ -1445,7 +1443,7 @@ trait BCodeBodyBuilder(val primitives: ScalaPrimitives) extends BCodeSkelBuilder assert(style.isVirtual || style.isSuper || specificReceiver == methodOwner, s"specificReceiver can only be specified for virtual and super calls. $method - $specificReceiver") val useSpecificReceiver = specificReceiver != null && !defn.isBottomClass(specificReceiver) && !method.isScalaStatic - val receiver = if (useSpecificReceiver) specificReceiver else methodOwner + val receiver: Symbol = if (useSpecificReceiver) specificReceiver.nn else methodOwner // TODO this JVM bug was resolved a very long time ago, workaround could be removed? // workaround for a JVM bug: https://bugs.openjdk.java.net/browse/JDK-8154587 @@ -1603,7 +1601,7 @@ trait BCodeBodyBuilder(val primitives: ScalaPrimitives) extends BCodeSkelBuilder case Literal(Constant(null)) => true case _ => false } - def ifOneIsNull(l: Tree, r: Tree): Tree = if (isNull(l)) r else if (isNull(r)) l else null + def ifOneIsNull(l: Tree, r: Tree): Tree | Null = if (isNull(l)) r else if (isNull(r)) l else null 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) diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala b/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala index 55059a5c2456..f1b162d74819 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala @@ -2,7 +2,6 @@ package dotty.tools package backend package jvm -import scala.language.unsafeNulls import scala.tools.asm import scala.tools.asm.{AnnotationVisitor, ClassWriter, Opcodes} import scala.collection.mutable @@ -205,7 +204,7 @@ trait BCodeHelpers(val bTypeLoader: BTypeLoader, val bTypes: WellKnownBTypes) ex } private def emitArgument(av: AnnotationVisitor, - name: String, + name: String | Null, arg: Tree)(using Context): Unit = { val narg = normalizeArgument(arg) // Transformation phases are not run on annotation trees, so we need to run @@ -648,7 +647,7 @@ trait BCodeHelpers(val bTypeLoader: BTypeLoader, val bTypes: WellKnownBTypes) ex val erasedMemberType = ElimErasedValueType.elimEVT(TypeErasure.transformInfo(sym, memberTpe)) if (erasedMemberType =:= sym.denot.info) val gensig = getGenericSignatureHelper(sym, moduleClass, memberTpe) - if gensig == null || descriptor.contentEquals(gensig) then null + if gensig == null || (descriptor != null && descriptor.contentEquals(gensig)) then null else gensig.toString else null else null diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeIdiomatic.scala b/compiler/src/dotty/tools/backend/jvm/BCodeIdiomatic.scala index dfe7da286239..edb2d626b1fb 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeIdiomatic.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeIdiomatic.scala @@ -2,7 +2,6 @@ package dotty.tools package backend package jvm -import scala.language.unsafeNulls import scala.tools.asm import scala.annotation.switch import scala.tools.asm.tree.MethodInsnNode diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala index b5dc3ba140c7..945da843a17f 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala @@ -2,7 +2,6 @@ package dotty.tools package backend package jvm -import scala.language.unsafeNulls import scala.annotation.tailrec import scala.collection.{immutable, mutable} import scala.tools.asm @@ -23,6 +22,8 @@ import SymbolUtils.given import dotty.tools.dotc.core.NameOps.isStaticConstructorName import tpd.* +import scala.compiletime.uninitialized + /* * * @author Miguel Garcia, http://lamp.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/ @@ -132,14 +133,14 @@ trait BCodeSkelBuilder extends BCodeHelpers { with BCJGenSigGen { // Strangely I can't find this in the asm code 255, but reserving 1 for "this" - inline val MaximumJvmParameters = 254 + private inline val MaximumJvmParameters = 254 // current class - var cnode: ClassNode1 = null - var thisName: String = null // the internal name of the class being emitted + private var cnode: ClassNode1 = uninitialized + private var thisName: String = uninitialized // the internal name of the class being emitted - var claszSymbol: Symbol = null - var isCZStaticModule = false + protected var claszSymbol: Symbol = uninitialized + private var isCZStaticModule = false // keep track of interfaces that are used in super calls, as they need to be directly inherited even if they are also indirectly inherited val superCallTargets = mutable.LinkedHashSet[ClassBType]() @@ -160,7 +161,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { /* ---------------- helper utils for generating classes and fields ---------------- */ - def genPlainClass(cd0: TypeDef)(using Context) = (cd0: @unchecked) match { + def genPlainClass(cd0: TypeDef)(using Context): ClassNode1 = (cd0: @unchecked) match { case TypeDef(_, impl: Template) => assert(cnode == null, "GenBCode detected nested methods.") @@ -288,7 +289,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { TraceUtils.traceClassIfRequested(cnode) assert(cd.symbol == claszSymbol, "Someone messed up BCodePhase.claszSymbol during genPlainClass().") - + cnode } // end of method genPlainClass() /* @@ -386,7 +387,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { .addFlagIf(!sym.is(Mutable), ACC_FINAL) } - def addClassField(f: Symbol)(using Context): Unit = { + private def addClassField(f: Symbol)(using Context): Unit = { val descriptor = symInfoTK(f).descriptor val javagensig = getGenericSignature(f, claszSymbol, descriptor) val flags = javaFieldFlags(f) @@ -405,7 +406,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { emitAnnotations(jfield, f.annotations) } - def addClassFields()(using Context): Unit = + private def addClassFields()(using Context): Unit = /* Non-method term members are fields, except for module members. Module * members can only happen on .NET (no flatten) for inner traits. There, * a module symbol is generated (transformInfo in mixin) which is used @@ -416,18 +417,18 @@ trait BCodeSkelBuilder extends BCodeHelpers { claszSymbol.info.decls.filter(p => p.isTerm && !p.is(Method)).foreach(addClassField) // current method - var mnode: MethodNode1 = null - var jMethodName: String = null - var isMethSymStaticCtor = false - var returnType: BType = null - var methSymbol: Symbol = null + var mnode: MethodNode1 = uninitialized + var jMethodName: String = uninitialized + private var isMethSymStaticCtor = false + var returnType: BType = uninitialized + var methSymbol: Symbol = uninitialized // used by genLoadTry() and genSynchronized() - var earlyReturnVar: Symbol = null + var earlyReturnVar: Symbol | Null = null var shouldEmitCleanup = false // stack tracking val stack = new BTypesStack // line numbers - var lastEmittedLineNr = -1 + private var lastEmittedLineNr = -1 object bc extends JCodeMethodN { override def jmethod = PlainSkelBuilder.this.mnode @@ -440,7 +441,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { * The `jumpDest` map is used to find the `LoadDestination` at the end of the `Labeled` block, as well as the * corresponding expected type. The `LoadDestination` can never be `FallThrough` here. */ - var jumpDest: immutable.Map[ /* Labeled */ Symbol, (BType, LoadDestination) ] = null + var jumpDest: immutable.Map[ /* Labeled */ Symbol, (BType, LoadDestination) ] = immutable.Map.empty def registerJumpDest(labelSym: Symbol, expectedType: BType, dest: LoadDestination)(using Context): Unit = { assert(labelSym.is(Label), s"trying to register a jump-dest for a non-label symbol, at: ${labelSym.span}") assert(dest != LoadDestination.FallThrough, s"trying to register a FallThrough dest for label, at: ${labelSym.span}") @@ -484,10 +485,10 @@ trait BCodeSkelBuilder extends BCodeHelpers { * emitted for that purpose as described in `genLoadTry()` and `genSynchronized()`. */ var cleanups: List[asm.Label] = Nil - def registerCleanup(finCleanup: asm.Label): Unit = { + def registerCleanup(finCleanup: asm.Label | Null): Unit = { if (finCleanup != null) { cleanups = finCleanup :: cleanups } } - def unregisterCleanup(finCleanup: asm.Label): Unit = { + def unregisterCleanup(finCleanup: asm.Label | Null): Unit = { if (finCleanup != null) { assert(cleanups.head eq finCleanup, s"Bad nesting of cleanup operations: $cleanups trying to unregister: $finCleanup") @@ -585,7 +586,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { /* ---------------- Part 2 of program points, ie Labels in the ASM world ---------------- */ // bookkeeping the scopes of non-synthetic local vars, to emit debug info (`emitVars`). - var varsInScope: List[(Symbol, asm.Label)] = null // (local-var-sym -> start-of-scope) + var varsInScope: List[(Symbol, asm.Label)] | Null = null // (local-var-sym -> start-of-scope) // helpers around program-points. def lastInsn: asm.tree.AbstractInsnNode = mnode.instructions.getLast @@ -806,7 +807,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { .withAttachment(BCodeHelpers.UseInvokeSpecial, ()) }) - def genDefDef(dd: DefDef)(using Context): Unit = { + private def genDefDef(dd: DefDef)(using Context): Unit = { val rhs = dd.rhs val vparamss = dd.termParamss // the only method whose implementation is not emitted: getClass() @@ -924,7 +925,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { TraceUtils.traceMethodIfRequested(mnode) - mnode = null + mnode = null.asInstanceOf[MethodNode1] // for GC } // end of method genDefDef() def emitLocalVarScope(sym: Symbol, start: asm.Label, end: asm.Label, force: Boolean = false): Unit = { diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeSyncAndTry.scala b/compiler/src/dotty/tools/backend/jvm/BCodeSyncAndTry.scala index 1df91692ee12..8c7f632c76d6 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeSyncAndTry.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeSyncAndTry.scala @@ -2,7 +2,6 @@ package dotty.tools package backend package jvm -import scala.language.unsafeNulls import scala.collection.immutable import scala.tools.asm import dotty.tools.dotc.core.StdNames.nme @@ -31,7 +30,7 @@ trait BCodeSyncAndTry extends BCodeBodyBuilder { // if the synchronized block returns a result, store it in a local variable. // Just leaving it on the stack is not valid in MSIL (stack is cleaned when leaving try-blocks). val hasResult = expectedType != UNIT - val monitorResult: Symbol = if (hasResult) locals.makeLocal(tpeTK(args.head), "monitorResult", defn.ObjectType, tree.span) else null + val monitorResult: Symbol | Null = if (hasResult) locals.makeLocal(tpeTK(args.head), "monitorResult", defn.ObjectType, tree.span) else null /* ------ (1) pushing and entering the monitor, also keeping a reference to it in a local var. ------ */ genLoadQualifier(fun) @@ -50,7 +49,7 @@ trait BCodeSyncAndTry extends BCodeBodyBuilder { registerCleanup(monCleanup) genLoad(args.head, expectedType /* toTypeKind(tree.tpe.resultType) */) unregisterCleanup(monCleanup) - if (hasResult) { locals.store(monitorResult) } + if (monitorResult ne null) { locals.store(monitorResult) } nopIfNeeded(startProtected) val endProtected = currProgramPoint() @@ -61,7 +60,7 @@ trait BCodeSyncAndTry extends BCodeBodyBuilder { */ locals.load(monitor) emit(asm.Opcodes.MONITOREXIT) - if (hasResult) { locals.load(monitorResult) } + if (monitorResult ne null) { locals.load(monitorResult) } val postHandler = new asm.Label bc.goTo(postHandler) @@ -276,10 +275,10 @@ trait BCodeSyncAndTry extends BCodeBodyBuilder { * here makes sure that `shouldEmitCleanup` is only propagated outwards, not inwards to * nested `finally` blocks. */ - def withFreshCleanupScope(body: => Unit): Unit = { + def withFreshCleanupScope(body: () => Unit): Unit = { val savedShouldEmitCleanup = shouldEmitCleanup shouldEmitCleanup = false - body + body() shouldEmitCleanup = savedShouldEmitCleanup || shouldEmitCleanup } @@ -291,12 +290,12 @@ trait BCodeSyncAndTry extends BCodeBodyBuilder { * ------ */ - for (ch <- caseHandlers) withFreshCleanupScope { + for (ch <- caseHandlers) withFreshCleanupScope { () => // (2.a) emit case clause proper val startHandler = currProgramPoint() - var endHandler: asm.Label = null - var excType: ClassBType = null + var endHandler: asm.Label | Null = null + var excType: ClassBType | Null = null registerCleanup(finCleanup) ch match { case NamelessEH(typeToDrop, caseBody) => @@ -338,7 +337,7 @@ trait BCodeSyncAndTry extends BCodeBodyBuilder { // a note on terminology: this is not "postHandlers", despite appearances. // "postHandlers" as in the source-code view. And from that perspective, both (3.A) and (3.B) are invisible implementation artifacts. - if (hasFinally) withFreshCleanupScope { + if (hasFinally) withFreshCleanupScope { () => nopIfNeeded(startTryBody) val finalHandler = currProgramPoint() // version of the finally-clause reached via unhandled exception. protect(startTryBody, finalHandler, finalHandler, null) @@ -367,7 +366,7 @@ trait BCodeSyncAndTry extends BCodeBodyBuilder { // `shouldEmitCleanup` can be set, and at the same time this try expression may lack a finally-clause. // In other words, all combinations of (hasFinally, shouldEmitCleanup) are valid. if (hasFinally && currentFinallyBlockNeedsCleanup) { - markProgramPoint(finCleanup) + markProgramPoint(finCleanup.nn) // regarding return value, the protocol is: in place of a `return-stmt`, a sequence of `adapt, store, jump` are inserted. emitFinalizer(finalizer, null, isDuplicate = true) pendingCleanups() @@ -426,8 +425,8 @@ trait BCodeSyncAndTry extends BCodeBodyBuilder { cleanups match { case Nil => if (earlyReturnVar != null) { - locals.load(earlyReturnVar) - bc.emitRETURN(locals(earlyReturnVar).tk) + locals.load(earlyReturnVar.nn) + bc.emitRETURN(locals(earlyReturnVar.nn).tk) } else { bc.emitRETURN(UNIT) } @@ -438,8 +437,8 @@ trait BCodeSyncAndTry extends BCodeBodyBuilder { } } - private def protect(start: asm.Label, end: asm.Label, handler: asm.Label, excType: ClassBType): Unit = { - val excInternalName: String = + private def protect(start: asm.Label, end: asm.Label, handler: asm.Label, excType: ClassBType | Null): Unit = { + val excInternalName: String | Null = if (excType == null) null else excType.internalName assert(start != end, "protecting a range of zero instructions leads to illegal class format. Solution: add a NOP to that range.") @@ -447,8 +446,8 @@ trait BCodeSyncAndTry extends BCodeBodyBuilder { } /* `tmp` (if non-null) is the symbol of the local-var used to preserve the result of the try-body, see `guardResult` */ - private def emitFinalizer(finalizer: Tree, tmp: Symbol, isDuplicate: Boolean)(using Context): Unit = { - var saved: immutable.Map[ /* Labeled */ Symbol, (BType, LoadDestination) ] = null + private def emitFinalizer(finalizer: Tree, tmp: Symbol | Null, isDuplicate: Boolean)(using Context): Unit = { + var saved: immutable.Map[ /* Labeled */ Symbol, (BType, LoadDestination) ] | Null = null if (isDuplicate) { saved = jumpDest } @@ -456,7 +455,7 @@ trait BCodeSyncAndTry extends BCodeBodyBuilder { if (tmp != null) { locals.store(tmp) } genLoad(finalizer, UNIT) if (tmp != null) { locals.load(tmp) } - if (isDuplicate) { + if (saved ne null) { jumpDest = saved } } diff --git a/compiler/src/dotty/tools/backend/jvm/BTypes.scala b/compiler/src/dotty/tools/backend/jvm/BTypes.scala index 584e97907759..4ec8a6719606 100644 --- a/compiler/src/dotty/tools/backend/jvm/BTypes.scala +++ b/compiler/src/dotty/tools/backend/jvm/BTypes.scala @@ -2,16 +2,13 @@ package dotty.tools package backend package jvm -import scala.language.unsafeNulls import java.util.concurrent.ConcurrentHashMap import scala.tools.asm import dotty.tools.backend.jvm.BTypes.InternalName import dotty.tools.backend.jvm.opt.OptimizerWarning -import dotty.tools.dotc.core.Symbols.ClassSymbol import scala.collection.SortedMap import scala.tools.asm.Opcodes -import scala.tools.asm.tree.{ClassNode, ModuleNode} /** @@ -592,7 +589,7 @@ case class NestedInfo(enclosingClass: ClassBType, * @param innerName The simple name of the inner class, may be null. * @param flags The flags for this class in the InnerClass entry. */ -case class InnerClassEntry(name: String, outerName: String, innerName: String, flags: Int) +case class InnerClassEntry(name: String, outerName: String | Null, innerName: String | Null, flags: Int) /** diff --git a/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala b/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala index 47d7df6eb006..c9f5783d53e4 100644 --- a/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala +++ b/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala @@ -14,7 +14,6 @@ import java.util.concurrent.ConcurrentHashMap import scala.annotation.switch import scala.collection.{BitSet, mutable} import scala.jdk.CollectionConverters.* -import scala.language.unsafeNulls import scala.tools.asm import scala.tools.asm.tree.* import scala.tools.asm.{Handle, Opcodes, Type} @@ -179,43 +178,44 @@ class BackendUtils(val ts: WellKnownBTypes) { // ============================================================================================== - def primitiveAsmTypeToBType(primitiveType: asm.Type): PrimitiveBType = (primitiveType.getSort: @switch) match { - case asm.Type.BOOLEAN => BOOL - case asm.Type.BYTE => BYTE - case asm.Type.CHAR => CHAR - case asm.Type.SHORT => SHORT - case asm.Type.INT => INT - case asm.Type.LONG => LONG - case asm.Type.FLOAT => FLOAT - case asm.Type.DOUBLE => DOUBLE - case _ => null - } + val primitiveAsmTypeSortToBType: Map[Int, PrimitiveBType] = Map( + asm.Type.BOOLEAN -> BOOL, + asm.Type.BYTE -> BYTE, + asm.Type.CHAR -> CHAR, + asm.Type.SHORT -> SHORT, + asm.Type.INT -> INT, + asm.Type.LONG -> LONG, + asm.Type.FLOAT -> FLOAT, + asm.Type.DOUBLE -> DOUBLE + ) - def isScalaBox(insn: MethodInsnNode): Boolean = { + def isScalaBox(insn: MethodInsnNode): Boolean = insn.owner == ts.srBoxesRuntimeRef.internalName && { val args = asm.Type.getArgumentTypes(insn.desc) - args.length == 1 && (ts.srBoxesRuntimeBoxToMethods.get(primitiveAsmTypeToBType(args(0))) match { - case Some(MethodNameAndType(name, tp)) => name == insn.name && tp.descriptor == insn.desc - case _ => false - }) + args.length == 1 && (primitiveAsmTypeSortToBType.get(args(0).getSort) match + case Some(prim) => + val MethodNameAndType(name, tp) = ts.srBoxesRuntimeBoxToMethods(prim) + name == insn.name && tp.descriptor == insn.desc + case None => false) } - } def getScalaBox(primitiveType: asm.Type): MethodInsnNode = { - val bType = primitiveAsmTypeToBType(primitiveType) + val bType = primitiveAsmTypeSortToBType(primitiveType.getSort) val MethodNameAndType(name, methodBType) = ts.srBoxesRuntimeBoxToMethods(bType) new MethodInsnNode(Opcodes.INVOKESTATIC, ts.srBoxesRuntimeRef.internalName, name, methodBType.descriptor, /*itf =*/ false) } def getScalaUnbox(primitiveType: asm.Type): MethodInsnNode = { - val bType = primitiveAsmTypeToBType(primitiveType) + val bType = primitiveAsmTypeSortToBType(primitiveType.getSort) val MethodNameAndType(name, methodBType) = ts.srBoxesRuntimeUnboxToMethods(bType) new MethodInsnNode(Opcodes.INVOKESTATIC, ts.srBoxesRuntimeRef.internalName, name, methodBType.descriptor, /*itf =*/ false) } def isScalaUnbox(insn: MethodInsnNode): Boolean = { - insn.owner == ts.srBoxesRuntimeRef.internalName && (ts.srBoxesRuntimeUnboxToMethods.get(primitiveAsmTypeToBType(asm.Type.getReturnType(insn.desc))) match { - case Some(MethodNameAndType(name, tp)) => name == insn.name && tp.descriptor == insn.desc + insn.owner == ts.srBoxesRuntimeRef.internalName && (primitiveAsmTypeSortToBType.get(asm.Type.getReturnType(insn.desc).getSort) match { + case Some(prim) => + val MethodNameAndType(name, tp) = ts.srBoxesRuntimeUnboxToMethods(prim) + name == insn.name && tp.descriptor == insn.desc case _ => false }) } @@ -349,7 +349,7 @@ class BackendUtils(val ts: WellKnownBTypes) { var numBoxConv = 0 var numCallsOrNew = 0 - var callMi: MethodInsnNode = null + var callMi: MethodInsnNode | Null = null val it = method.instructions.iterator while (it.hasNext && numCallsOrNew < 2) { val i = it.next() @@ -378,7 +378,7 @@ class BackendUtils(val ts: WellKnownBTypes) { } if (numCallsOrNew > 1 || numBoxConv > paramTypes.length + 1) -1 else if (numCallsOrNew == 0) if (numBoxConv == 0) 1 else 3 - else if (callMi.name == GenBCode.INSTANCE_CONSTRUCTOR_NAME) 2 + else if (callMi.nn.name == GenBCode.INSTANCE_CONSTRUCTOR_NAME) 2 // if numCallsOrNew > 0 then callMi is nonnull else if (numBoxConv > 0) 3 else 4 } diff --git a/compiler/src/dotty/tools/backend/jvm/CodeGen.scala b/compiler/src/dotty/tools/backend/jvm/CodeGen.scala index 1947e16a9313..8081f27ce035 100644 --- a/compiler/src/dotty/tools/backend/jvm/CodeGen.scala +++ b/compiler/src/dotty/tools/backend/jvm/CodeGen.scala @@ -137,7 +137,6 @@ class CodeGen(val backendUtils: BackendUtils, val primitives: ScalaPrimitives, v private def genClass(cd: TypeDef)(using Context): ClassNode = { val b = new impl.SyncAndTryBuilder b.genPlainClass(cd) - b.cnode } } diff --git a/compiler/src/dotty/tools/backend/jvm/GeneratedClassHandler.scala b/compiler/src/dotty/tools/backend/jvm/GeneratedClassHandler.scala index eefeca894576..17fb71add11f 100644 --- a/compiler/src/dotty/tools/backend/jvm/GeneratedClassHandler.scala +++ b/compiler/src/dotty/tools/backend/jvm/GeneratedClassHandler.scala @@ -73,22 +73,23 @@ private[jvm] object GeneratedClassHandler { private val processingUnits = ListBuffer.empty[CompilationUnitInPostProcess] def process(unit: GeneratedCompilationUnit): Unit = { - val unitInPostProcess = new CompilationUnitInPostProcess(unit.classes, unit.tasty, unit.sourceFile) - postProcessUnit(unitInPostProcess) - processingUnits += unitInPostProcess - } + given ExecutionContext = executionContext - private val executionContext: ExecutionContextExecutor = ExecutionContext.fromExecutor(javaExecutor) + // We want to release these for GC as soon as their processing is done, even if the Future is still referenced + val classesRef = scala.runtime.ObjectRef(unit.classes) + val tastyRef = scala.runtime.ObjectRef(unit.tasty) - private def postProcessUnit(unitInPostProcess: CompilationUnitInPostProcess): Unit = { - given ExecutionContext = executionContext - unitInPostProcess.task = Future: - // we 'take' classes to reduce the memory pressure - // as soon as the class is consumed and written, we release its data - unitInPostProcess.takeClasses().foreach(postProcessor.sendToDisk) - unitInPostProcess.takeTasty().foreach(postProcessor.sendToDisk) + val task = Future: + classesRef.elem.foreach(postProcessor.sendToDisk) + classesRef.elem = null + tastyRef.elem.foreach(postProcessor.sendToDisk) + tastyRef.elem = null + + processingUnits += new CompilationUnitInPostProcess(unit.sourceFile, task) } + private val executionContext: ExecutionContextExecutor = ExecutionContext.fromExecutor(javaExecutor) + private def takeProcessingUnits(): List[CompilationUnitInPostProcess] = { val result = processingUnits.result() processingUnits.clear() @@ -154,24 +155,7 @@ private[jvm] object GeneratedClassHandler { /** * State for a compilation unit being post-processed. - * - Holds the classes to post-process (released for GC when no longer used) - * - Keeps a reference to the future that runs the post-processor - * - Buffers messages reported during post-processing + * Keeps a reference to the future that runs the post-processor. */ - final private class CompilationUnitInPostProcess(private var classes: List[GeneratedClass], private var tasty: List[GeneratedTasty], val sourceFile: AbstractFile) { - def takeClasses(): List[GeneratedClass] = { - val c = classes - classes = Nil - c - } - - def takeTasty(): List[GeneratedTasty] = { - val v = tasty - tasty = Nil - v - } - - /** the main async task submitted onto the scheduler */ - var task: Future[Unit] = uninitialized - } + final private class CompilationUnitInPostProcess(val sourceFile: AbstractFile, val task: Future[Unit]) } diff --git a/compiler/src/dotty/tools/backend/jvm/GenericSignatureVisitor.scala b/compiler/src/dotty/tools/backend/jvm/GenericSignatureVisitor.scala index dfca8cbecf1b..477978e913f6 100644 --- a/compiler/src/dotty/tools/backend/jvm/GenericSignatureVisitor.scala +++ b/compiler/src/dotty/tools/backend/jvm/GenericSignatureVisitor.scala @@ -1,8 +1,6 @@ package dotty.tools.backend.jvm -import scala.language.unsafeNulls - -import scala.tools.asm.{ClassReader, Type, Handle } +import scala.tools.asm.{Type, Handle} import scala.tools.asm.tree.* import scala.collection.mutable @@ -108,7 +106,7 @@ abstract class GenericSignatureVisitor(nestedOnly: Boolean) { @tailrec private def referenceTypeSignature(): Unit = getCurrentAndSkip() match { case 'L' => - var names: java.lang.StringBuilder = null + var names: java.lang.StringBuilder | Null = null val start = index var seenDollar = false @@ -126,8 +124,8 @@ abstract class GenericSignatureVisitor(nestedOnly: Boolean) { while (current == '.') { skip() - names.append('$') - appendUntil(names, isClassNameEnd) + names.nn.append('$') + appendUntil(names.nn, isClassNameEnd) visitInternalName(names.toString) typeArguments() } diff --git a/compiler/src/dotty/tools/backend/jvm/opt/CopyProp.scala b/compiler/src/dotty/tools/backend/jvm/opt/CopyProp.scala index 05d43b7e1ffc..b22ab2113854 100644 --- a/compiler/src/dotty/tools/backend/jvm/opt/CopyProp.scala +++ b/compiler/src/dotty/tools/backend/jvm/opt/CopyProp.scala @@ -487,7 +487,7 @@ class CopyProp(backendUtils: BackendUtils, callGraph: CallGraph, inliner: Inline val receiver = if (methodInsn.getOpcode == INVOKESTATIC) 0 else 1 handleInputs(prod, Type.getArgumentTypes(methodInsn.desc).length + receiver) } else if (backendUtils.isScalaUnbox(methodInsn)) { - val tp = backendUtils.primitiveAsmTypeToBType(Type.getReturnType(methodInsn.desc)) + val tp = backendUtils.primitiveAsmTypeSortToBType(Type.getReturnType(methodInsn.desc).getSort) val boxTp = ts.boxedClassOfPrimitive(tp) toInsertBefore(methodInsn) = List(new TypeInsnNode(CHECKCAST, boxTp.internalName), new InsnNode(POP)) toRemove += prod