diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeAsmCommon.scala b/compiler/src/dotty/tools/backend/jvm/BCodeAsmCommon.scala index bdf74558a760..816fec8adaa4 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeAsmCommon.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeAsmCommon.scala @@ -2,12 +2,16 @@ package dotty.tools package backend package jvm +import dotty.tools.dotc.core.Flags._ +import dotty.tools.dotc.core.Symbols._ + /** * This trait contains code shared between GenBCode and GenASM that depends on types defined in * the compiler cake (Global). */ -final class BCodeAsmCommon[I <: BackendInterface](val interface: I) { +final class BCodeAsmCommon[I <: DottyBackendInterface](val interface: I) { import interface._ + import DottyBackendInterface.symExtensions /** * True if `classSym` is an anonymous class or a local class. I.e., false if `classSym` is a @@ -53,7 +57,7 @@ final class BCodeAsmCommon[I <: BackendInterface](val interface: I) { assert(classSym.isClass, classSym) def enclosingMethod(sym: Symbol): Option[Symbol] = { if (sym.isClass || sym == NoSymbol) None - else if (sym.isMethod) Some(sym) + else if (sym.is(Method)) Some(sym) else enclosingMethod(sym.originalOwner.originalLexicallyEnclosingClass) } enclosingMethod(classSym.originalOwner.originalLexicallyEnclosingClass) @@ -85,10 +89,10 @@ final class BCodeAsmCommon[I <: BackendInterface](val interface: I) { def enclosingMethodAttribute(classSym: Symbol, classDesc: Symbol => String, methodDesc: Symbol => String): Option[EnclosingMethodEntry] = { if (isAnonymousOrLocalClass(classSym)) { val methodOpt = enclosingMethodForEnclosingMethodAttribute(classSym) - debuglog(s"enclosing method for $classSym is $methodOpt (in ${methodOpt.map(_.enclClass)})") + ctx.debuglog(s"enclosing method for $classSym is $methodOpt (in ${methodOpt.map(_.enclosingClass)})") Some(EnclosingMethodEntry( classDesc(enclosingClassForEnclosingMethodAttribute(classSym)), - methodOpt.map(_.javaSimpleName.toString).orNull, + methodOpt.map(_.javaSimpleName).orNull, methodOpt.map(methodDesc).orNull)) } else { None diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala b/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala index 54c14db8cc54..ff97bb307d3b 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala @@ -8,6 +8,18 @@ import scala.tools.asm import scala.tools.asm.{Handle, Label, Opcodes} import BCodeHelpers.InvokeStyle +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.CompilationUnit +import dotty.tools.dotc.core.Constants._ +import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.Flags.{Label => LabelFlag, _} +import dotty.tools.dotc.core.Types._ +import dotty.tools.dotc.core.StdNames.{nme, str} +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.transform.Erasure +import dotty.tools.dotc.transform.SymUtils._ +import dotty.tools.dotc.util.Spans._ + /* * * @author Miguel Garcia, http://lamp.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/ @@ -17,11 +29,15 @@ import BCodeHelpers.InvokeStyle trait BCodeBodyBuilder extends BCodeSkelBuilder { // import global._ // import definitions._ + import tpd._ import int._ + import DottyBackendInterface.symExtensions import bTypes._ import coreBTypes._ import BCodeBodyBuilder._ + private val primitives = new DottyPrimitives(ctx) + /* * Functionality to build the body of ASM MethodNode, except for `synchronized` and `try` expressions. */ @@ -57,13 +73,13 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { lineNumber(tree) tree match { - case Assign(lhs @ Select(qual, _), rhs) => + case Assign(lhs @ DesugaredSelect(qual, _), rhs) => val isStatic = lhs.symbol.isStaticMember if (!isStatic) { genLoadQualifier(lhs) } genLoad(rhs, symInfoTK(lhs.symbol)) lineNumber(tree) // receiverClass is used in the bytecode to access the field. using sym.owner may lead to IllegalAccessError - val receiverClass = qual.tpe.typeSymbol + val receiverClass = qual.tpe.widenDealias.typeSymbol fieldStore(lhs.symbol, receiverClass) case Assign(lhs, rhs) => @@ -92,7 +108,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { /* Generate code for primitive arithmetic operations. */ def genArithmeticOp(tree: Tree, code: Int): BType = tree match{ - case Apply(fun @ Select(larg, _), args) => + case Apply(fun @ DesugaredSelect(larg, _), args) => var resKind = tpeTK(larg) assert(resKind.isNumericType || (resKind == BOOL), @@ -148,7 +164,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { /* Generate primitive array operations. */ def genArrayOp(tree: Tree, code: Int, expectedType: BType): BType = tree match{ - case Apply(Select(arrayObj, _), args) => + case Apply(DesugaredSelect(arrayObj, _), args) => import ScalaPrimitivesOps._ val k = tpeTK(arrayObj) genLoad(arrayObj, k) @@ -210,7 +226,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { } def genPrimitiveOp(tree: Apply, expectedType: BType): BType = tree match { - case Apply(fun @ Select(receiver, _), _) => + case Apply(fun @ DesugaredSelect(receiver, _), _) => val sym = tree.symbol val code = primitives.getPrimitive(tree, receiver.tpe) @@ -243,7 +259,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { coercionTo(code) } else abort( - s"Primitive operation not handled yet: ${sym.showFullName}(${fun.symbol.name}) at: ${tree.pos}" + s"Primitive operation not handled yet: ${sym.showFullName}(${fun.symbol.name}) at: ${tree.span}" ) } @@ -258,19 +274,17 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { lineNumber(tree) tree match { - case lblDf @ LabelDef(_, _, _) => genLabelDef(lblDf, expectedType) - - case ValDef(_, `nme_THIS`, _, _) => - debuglog("skipping trivial assign to _$this: " + tree) + case ValDef(nme.THIS, _, _) => + ctx.debuglog("skipping trivial assign to _$this: " + tree) - case ValDef(_, _, _, rhs) => + case tree@ValDef(_, _, _) => val sym = tree.symbol /* most of the time, !locals.contains(sym), unless the current activation of genLoad() is being called while duplicating a finalizer that contains this ValDef. */ val loc = locals.getOrMakeLocal(sym) val Local(tk, _, idx, isSynth) = loc - if (rhs == EmptyTree) { emitZeroOf(tk) } - else { genLoad(rhs, tk) } + if (tree.rhs == tpd.EmptyTree) { emitZeroOf(tk) } + else { genLoad(tree.rhs, tk) } bc.store(idx, tk) val localVarStart = currProgramPoint() if (!isSynth) { // there are case ValDef's emitted by patmat @@ -284,7 +298,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { case t @ Labeled(_, _) => generatedType = genLabeled(t) - case r @ Return(_) => + case r: Return => genReturn(r) generatedType = expectedType @@ -294,17 +308,30 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { case t @ Try(_, _, _) => generatedType = genLoadTry(t) - case Throw(expr) => - generatedType = genThrow(expr) + case t: Apply if t.fun.symbol eq defn.throwMethod => + generatedType = genThrow(t.args.head) case New(tpt) => - abort(s"Unexpected New(${tpt.summaryString}/$tpt) reached GenBCode.\n" + + abort(s"Unexpected New(${tpt.tpe.showSummary()}/$tpt) reached GenBCode.\n" + " Call was genLoad" + ((tree, expectedType))) - case app @ Closure(env, call, functionalInterface) => + case app: Closure => + val env: List[Tree] = app.env + val call: Tree = app.meth + val functionalInterface: Symbol = { + val t = app.tpt.tpe.typeSymbol + if (t.exists) t + else { + val arity = app.meth.tpe.widenDealias.firstParamTypes.size - env.size + val returnsUnit = app.meth.tpe.widenDealias.resultType.classSymbol == defn.UnitClass + if (returnsUnit) ctx.requiredClass(("dotty.runtime.function.JProcedure" + arity)) + else if (arity <= 2) ctx.requiredClass(("dotty.runtime.function.JFunction" + arity)) + else ctx.requiredClass(("scala.Function" + arity)) + } + } val (fun, args) = call match { case Apply(fun, args) => (fun, args) - case t @ Select(_, _) => (t, Nil) + case t @ DesugaredSelect(_, _) => (t, Nil) // TODO: use Select case t @ Ident(_) => (t, Nil) } @@ -315,20 +342,18 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { // but I was able to derrive it by reading // AbstractValidatingLambdaMetafactory.validateMetafactoryArgs - val Select(prefix, _) = fun + val DesugaredSelect(prefix, _) = fun genLoad(prefix) } - genLoadArguments(env, fun.symbol.info.paramTypes map toTypeKind) + genLoadArguments(env, fun.symbol.info.firstParamTypes map toTypeKind) generatedType = genInvokeDynamicLambda(NoSymbol, fun.symbol, env.size, functionalInterface) case app @ Apply(_, _) => generatedType = genApply(app, expectedType) - case ApplyDynamic(qual, args) => sys.error("No invokedynamic support yet.") - case This(qual) => - val symIsModuleClass = tree.symbol.isModuleClass + val symIsModuleClass = tree.symbol.is(ModuleClass) assert(tree.symbol == claszSymbol || symIsModuleClass, s"Trying to access the this of another class: tree.symbol = ${tree.symbol}, class symbol = $claszSymbol compilation unit: $cunit") if (symIsModuleClass && tree.symbol != claszSymbol) { @@ -337,24 +362,24 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { else { mnode.visitVarInsn(asm.Opcodes.ALOAD, 0) generatedType = - if (tree.symbol == ArrayClass) ObjectReference + if (tree.symbol == defn.ArrayClass) ObjectReference else classBTypeFromSymbol(claszSymbol) } - case Select(Ident(`nme_EMPTY_PACKAGE_NAME`), module) => - assert(tree.symbol.isModule, s"Selection of non-module from empty package: $tree sym: ${tree.symbol} at: ${tree.pos}") + case DesugaredSelect(Ident(nme.EMPTY_PACKAGE), module) => + assert(tree.symbol.is(Module), s"Selection of non-module from empty package: $tree sym: ${tree.symbol} at: ${tree.span}") genLoadModule(tree) - case Select(qualifier, _) => + case DesugaredSelect(qualifier, _) => val sym = tree.symbol generatedType = symInfoTK(sym) - val qualSafeToElide = isQualifierSafeToElide(qualifier) + val qualSafeToElide = tpd.isIdempotentExpr(qualifier) def genLoadQualUnlessElidable(): Unit = { if (!qualSafeToElide) { genLoadQualifier(tree) } } // receiverClass is used in the bytecode to access the field. using sym.owner may lead to IllegalAccessError - def receiverClass = qualifier.tpe.typeSymbol - if (sym.isModule) { + def receiverClass = qualifier.tpe.widenDealias.typeSymbol + if (sym.is(Module)) { genLoadQualUnlessElidable() genLoadModule(tree) } else if (sym.isStaticMember) { @@ -370,11 +395,11 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { val tk = symInfoTK(sym) generatedType = tk - val desugared = desugarIdent(t) + val desugared = cachedDesugarIdent(t) desugared match { case None => - if (!sym.hasPackageFlag) { - if (sym.isModule) genLoadModule(sym) + if (!sym.is(Package)) { + if (sym.is(Module)) genLoadModule(sym) else locals.load(sym) } case Some(t) => @@ -394,7 +419,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { genLoad(expr, expectedType) else genBlock(blck, expectedType) - case Typed(Super(_, _), _) => genLoad(This(claszSymbol), expectedType) + case Typed(Super(_, _), _) => genLoad(tpd.This(claszSymbol.asClass), expectedType) case Typed(expr, _) => genLoad(expr, expectedType) @@ -408,13 +433,13 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { case mtch @ Match(_, _) => generatedType = genMatch(mtch) - case EmptyTree => if (expectedType != UNIT) { emitZeroOf(expectedType) } + case tpd.EmptyTree => if (expectedType != UNIT) { emitZeroOf(expectedType) } case t: TypeApply => // dotty specific generatedType = genTypeApply(t) - case _ => abort(s"Unexpected tree in genLoad: $tree/${tree.getClass} at: ${tree.pos}") + case _ => abort(s"Unexpected tree in genLoad: $tree/${tree.getClass} at: ${tree.span}") } // emit conversion @@ -443,7 +468,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { val useSpecificReceiver = specificReceiver != null && !field.isScalaStatic val owner = internalName(if (useSpecificReceiver) specificReceiver else field.owner) - val fieldJName = field.javaSimpleName.toString + val fieldJName = field.javaSimpleName val fieldDescr = symInfoTK(field).descriptor val isStatic = field.isStaticMember val opc = @@ -491,8 +516,12 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { case EnumTag => val sym = const.symbolValue val ownerName = internalName(sym.owner) - val fieldName = sym.javaSimpleName.toString - val fieldDesc = toTypeKind(sym.tpe.underlying).descriptor + val fieldName = sym.javaSimpleName + val underlying = sym.info match { // TODO: Is this actually necessary? Could it be replaced by a call to widen? + case t: TypeProxy => t.underlying + case t => t + } + val fieldDesc = toTypeKind(underlying).descriptor mnode.visitFieldInsn( asm.Opcodes.GETSTATIC, ownerName, @@ -504,18 +533,6 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { } } - private def genLabelDef(lblDf: LabelDef, expectedType: BType): Unit = lblDf match { - case LabelDef(_, _, rhs) => - - assert(int.hasLabelDefs) // scalac - - // duplication of LabelDefs contained in `finally`-clauses is handled when emitting RETURN. No bookkeeping for that required here. - // no need to call index() over lblDf.params, on first access that magic happens (moreover, no LocalVariableTable entries needed for them). - markProgramPoint(programPoint(lblDf.symbol)) - lineNumber(lblDf) - genLoad(rhs, expectedType) - } - private def genLabeled(tree: Labeled): BType = tree match { case Labeled(bind, expr) => @@ -525,8 +542,9 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { resKind } - private def genReturn(r: Return): Unit = r match { - case Return(expr, fromSym) => + private def genReturn(r: Return): Unit = { + val expr: Tree = r.expr + val fromSym: Symbol = if (r.from.symbol.is(LabelFlag)) r.from.symbol else NoSymbol if (NoSymbol == fromSym) { // return from enclosing method @@ -544,7 +562,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { if (saveReturnValue) { // regarding return value, the protocol is: in place of a `return-stmt`, a sequence of `adapt, store, jump` are inserted. if (earlyReturnVar == null) { - earlyReturnVar = locals.makeLocal(returnType, "earlyReturnVar", expr.tpe, expr.pos) + earlyReturnVar = locals.makeLocal(returnType, "earlyReturnVar", expr.tpe, expr.span) } locals.store(earlyReturnVar) } @@ -553,8 +571,8 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { } } else { // return from labeled - assert(fromSym.isLabel, fromSym) - assert(!fromSym.isMethod, fromSym) + assert(fromSym.is(LabelFlag), fromSym) + assert(!fromSym.is(Method), fromSym) /* TODO At the moment, we disregard cleanups, because by construction we don't have return-from-labels * that cross cleanup boundaries. However, in theory such crossings are valid, so we should take care @@ -570,7 +588,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { def genWhileDo(tree: WhileDo): BType = tree match{ case WhileDo(cond, body) => - val isInfinite = cond == EmptyTree + val isInfinite = cond == tpd.EmptyTree val loop = new asm.Label markProgramPoint(loop) @@ -605,15 +623,13 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { } def genTypeApply(t: TypeApply): BType = t match { - case TypeApply(fun@Select(obj, _), targs) => + case TypeApply(fun@DesugaredSelect(obj, _), targs) => val sym = fun.symbol - val cast = sym match { - case Object_isInstanceOf => false - case Object_asInstanceOf => true - case _ => abort(s"Unexpected type application $fun[sym: ${sym.showFullName}] in: $t") - } - + val cast = + if (sym == defn.Any_isInstanceOf) false + else if (sym == defn.Any_asInstanceOf) true + else abort(s"Unexpected type application $fun[sym: ${sym.showFullName}] in: $t") val l = tpeTK(obj) val r = tpeTK(targs.head) genLoadQualifier(fun) @@ -651,7 +667,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { var elemKind = arr.elementType val argsSize = args.length if (argsSize > dims) { - error(app.pos, s"too many arguments for array constructor: found ${args.length} but array has only $dims dimension(s)") + ctx.error(s"too many arguments for array constructor: found ${args.length} but array has only $dims dimension(s)", ctx.source.atSpan(app.span)) } if (argsSize < dims) { /* In one step: @@ -674,29 +690,29 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { var generatedType = expectedType lineNumber(app) app match { - case Apply(_, args) if isSyntheticArrayConstructor(app.symbol) => + case Apply(_, args) if app.symbol eq defn.newArrayMethod => val List(elemClaz, Literal(c: Constant), ArrayValue(_, dims)) = args generatedType = toTypeKind(c.typeValue) mkArrayConstructorCall(generatedType.asArrayBType, app, dims) case Apply(t :TypeApply, _) => generatedType = - if (t.symbol ne Object_synchronized) genTypeApply(t) + if (t.symbol ne defn.Object_synchronized) genTypeApply(t) else genSynchronized(app, expectedType) - case Apply(fun @ Select(Super(_, _), _), args) => + case Apply(fun @ DesugaredSelect(Super(_, _), _), args) => def initModule(): Unit = { // we initialize the MODULE$ field immediately after the super ctor if (!isModuleInitialized && jMethodName == INSTANCE_CONSTRUCTOR_NAME && - fun.symbol.javaSimpleName.toString == INSTANCE_CONSTRUCTOR_NAME && + fun.symbol.javaSimpleName == INSTANCE_CONSTRUCTOR_NAME && claszSymbol.isStaticModuleClass) { isModuleInitialized = true mnode.visitVarInsn(asm.Opcodes.ALOAD, 0) mnode.visitFieldInsn( asm.Opcodes.PUTSTATIC, thisName, - MODULE_INSTANCE_FIELD, + str.MODULE_INSTANCE_FIELD, "L" + thisName + ";" ) } @@ -709,18 +725,18 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { // on the stack (contrary to what the type in the AST says). mnode.visitVarInsn(asm.Opcodes.ALOAD, 0) genLoadArguments(args, paramTKs(app)) - generatedType = genCallMethod(fun.symbol, InvokeStyle.Super, app.pos) + generatedType = genCallMethod(fun.symbol, InvokeStyle.Super, app.span) initModule() // 'new' constructor call: Note: since constructors are // thought to return an instance of what they construct, // we have to 'simulate' it by DUPlicating the freshly created // instance (on JVM, methods return VOID). - case Apply(fun @ Select(New(tpt), `nme_CONSTRUCTOR`), args) => + case Apply(fun @ DesugaredSelect(New(tpt), nme.CONSTRUCTOR), args) => val ctor = fun.symbol assert(ctor.isClassConstructor, s"'new' call to non-constructor: ${ctor.name}") - generatedType = toTypeKind(tpt) + generatedType = toTypeKind(tpt.tpe) assert(generatedType.isRef, s"Non reference type cannot be instantiated: $generatedType") generatedType match { @@ -732,20 +748,20 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { mnode.visitTypeInsn(asm.Opcodes.NEW, rt.internalName) bc dup generatedType genLoadArguments(args, paramTKs(app)) - genCallMethod(ctor, InvokeStyle.Special, app.pos) + genCallMethod(ctor, InvokeStyle.Special, app.span) case _ => abort(s"Cannot instantiate $tpt of kind: $generatedType") } - case Apply(fun, List(expr)) if isBox(fun.symbol) => + case Apply(fun, List(expr)) if Erasure.Boxing.isBox(fun.symbol) && fun.symbol.denot.owner != defn.UnitModuleClass => val nativeKind = tpeTK(expr) genLoad(expr, nativeKind) val MethodNameAndType(mname, methodType) = asmBoxTo(nativeKind) bc.invokestatic(BoxesRunTime.internalName, mname, methodType.descriptor, itf = false) generatedType = boxResultType(fun.symbol) // was toTypeKind(fun.symbol.tpe.resultType) - case Apply(fun, List(expr)) if isUnbox(fun.symbol) => + case Apply(fun, List(expr)) if Erasure.Boxing.isUnbox(fun.symbol) && fun.symbol.denot.owner != defn.UnitModuleClass => genLoad(expr) val boxType = unboxResultType(fun.symbol) // was toTypeKind(fun.symbol.owner.linkedClassOfClass.tpe) generatedType = boxType @@ -755,23 +771,20 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { case app @ Apply(fun, args) => val sym = fun.symbol - if (sym.isLabel) { // jump to a label - assert(int.hasLabelDefs) - genLoadLabelArguments(args, labelDef(sym), app.pos) - bc goTo programPoint(sym) - } else if (isPrimitive(fun)) { // primitive method call + if (isPrimitive(fun)) { // primitive method call generatedType = genPrimitiveOp(app, expectedType) } else { // normal method call val invokeStyle = if (sym.isStaticMember) InvokeStyle.Static - else if (sym.isPrivate || sym.isClassConstructor) InvokeStyle.Special + else if (sym.is(Private) || sym.isClassConstructor) InvokeStyle.Special else InvokeStyle.Virtual if (invokeStyle.hasInstance) genLoadQualifier(fun) genLoadArguments(args, paramTKs(app)) - val Select(qual, _) = fun // fun is a Select, also checked in genLoadQualifier - if (isArrayClone(fun)) { + val DesugaredSelect(qual, name) = fun // fun is a Select, also checked in genLoadQualifier + val isArrayClone = name == nme.clone_ && qual.tpe.widen.isInstanceOf[JavaArrayType] + if (isArrayClone) { // Special-case Array.clone, introduced in 36ef60e. The goal is to generate this call // as "[I.clone" instead of "java/lang/Object.clone". This is consistent with javac. // Arrays have a public method `clone` (jls 10.7). @@ -787,23 +800,23 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { // Emitting `def f(c: C) = c.clone()` as `Object.clone()` gives a VerifyError. val target: String = tpeTK(qual).asRefBType.classOrArrayType val methodBType = asmMethodType(sym) - bc.invokevirtual(target, sym.javaSimpleName.toString, methodBType.descriptor) + bc.invokevirtual(target, sym.javaSimpleName, methodBType.descriptor) generatedType = methodBType.returnType } else { val receiverClass = if (!invokeStyle.isVirtual) null else { // receiverClass is used in the bytecode to as the method receiver. using sym.owner // may lead to IllegalAccessErrors, see 9954eaf / aladdin bug 455. - val qualSym = qual.tpe.typeSymbol - if (qualSym == ArrayClass) { + val qualSym = qual.tpe.widenDealias.typeSymbol + if (qualSym == defn.ArrayClass) { // For invocations like `Array(1).hashCode` or `.wait()`, use Object as receiver // in the bytecode. Using the array descriptor (like we do for clone above) seems // to work as well, but it seems safer not to change this. Javac also uses Object. // Note that array apply/update/length are handled by isPrimitive (above). - assert(sym.owner == ObjectClass, s"unexpected array call: $app") - ObjectClass + assert(sym.owner == defn.ObjectClass, s"unexpected array call: $app") + defn.ObjectClass } else qualSym } - generatedType = genCallMethod(sym, invokeStyle, app.pos, receiverClass) + generatedType = genCallMethod(sym, invokeStyle, app.span, receiverClass) } } } @@ -811,8 +824,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { generatedType } // end of genApply() - private def genArrayValue(av: ArrayValue): BType = av match { - case ArrayValue(tpt, elems) => + private def genArrayValue(av: tpd.JavaSeqLiteral): BType = { val ArrayValue(tpt, elems) = av lineNumber(av) @@ -863,15 +875,15 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { // collect switch blocks and their keys, but don't emit yet any switch-block. for (caze @ CaseDef(pat, guard, body) <- cases) { - assert(guard == EmptyTree, guard) + assert(guard == tpd.EmptyTree, guard) val switchBlockPoint = new asm.Label switchBlocks ::= (switchBlockPoint, body) pat match { case Literal(value) => flatKeys ::= value.intValue targets ::= switchBlockPoint - case Ident(`nme_WILDCARD`) => - assert(default == null, s"multiple default targets in a Match node, at ${tree.pos}") + case Ident(nme.WILDCARD) => + assert(default == null, s"multiple default targets in a Match node, at ${tree.span}") default = switchBlockPoint case Alternative(alts) => alts foreach { @@ -879,10 +891,10 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { flatKeys ::= value.intValue targets ::= switchBlockPoint case _ => - abort(s"Invalid alternative in alternative pattern in Match node: $tree at: ${tree.pos}") + abort(s"Invalid alternative in alternative pattern in Match node: $tree at: ${tree.span}") } case _ => - abort(s"Invalid pattern in Match node: $tree at: ${tree.pos}") + abort(s"Invalid pattern in Match node: $tree at: ${tree.span}") } } @@ -998,9 +1010,9 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { def genLoadQualifier(tree: Tree): Unit = { lineNumber(tree) tree match { - case Select(qualifier, _) => genLoad(qualifier) + case DesugaredSelect(qualifier, _) => genLoad(qualifier) case t: Ident => // dotty specific - desugarIdent(t) match { + cachedDesugarIdent(t) match { case Some(sel) => genLoadQualifier(sel) case None => assert(t.symbol.owner == this.claszSymbol) @@ -1009,44 +1021,14 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { } } - /* Generate code that loads args into label parameters. */ - def genLoadLabelArguments(args: List[Tree], lblDef: LabelDef, gotoPos: Position) = lblDef match { - case LabelDef(_, param, _) => - - val aps = { - val params: List[Symbol] = param - assert(args.length == params.length, s"Wrong number of arguments in call to label at: $gotoPos") - - def isTrivial(kv: (Tree, Symbol)) = kv match { - case (This(_), p) if p.name == nme_THIS => true - case (arg @ Ident(_), p) if arg.symbol == p => true - case _ => false - } - - (args zip params) filterNot isTrivial - } - - // first push *all* arguments. This makes sure multiple uses of the same labelDef-var will all denote the (previous) value. - aps foreach { case (arg, param) => genLoad(arg, locals(param).tk) } // `locals` is known to contain `param` because `genDefDef()` visited `labelDefsAtOrUnder` - - // second assign one by one to the LabelDef's variables. - aps.reverse foreach { - case (_, param) => - // TODO FIXME a "this" param results from tail-call xform. If so, the `else` branch seems perfectly fine. And the `then` branch must be wrong. - if (param.name == nme_THIS) mnode.visitVarInsn(asm.Opcodes.ASTORE, 0) - else locals.store(param) - } - - } - def genLoadArguments(args: List[Tree], btpes: List[BType]): Unit = { (args zip btpes) foreach { case (arg, btpe) => genLoad(arg, btpe) } } def genLoadModule(tree: Tree): BType = { val module = ( - if (!tree.symbol.isPackageClass) tree.symbol - else tree.symbol.info.member(nme_PACKAGE) match { + if (!tree.symbol.is(PackageClass)) tree.symbol + else tree.symbol.info.member(nme.PACKAGE).symbol match { case NoSymbol => abort(s"SI-5604: Cannot use package as value: $tree") case s => abort(s"SI-5604: found package class where package object expected: $tree") } @@ -1065,7 +1047,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { mnode.visitFieldInsn( asm.Opcodes.GETSTATIC, mbt.internalName /* + "$" */ , - MODULE_INSTANCE_FIELD, + str.MODULE_INSTANCE_FIELD, mbt.descriptor // for nostalgics: toTypeKind(module.tpe).descriptor ) } @@ -1107,13 +1089,13 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { // Optimization for expressions of the form "" + x. We can avoid the StringBuilder. case List(Literal(Constant("")), arg) => genLoad(arg, ObjectReference) - genCallMethod(String_valueOf, InvokeStyle.Static) + genCallMethod(defn.String_valueOf_Object, InvokeStyle.Static) case concatenations => bc.genStartConcat for (elem <- concatenations) { val loadedElem = elem match { - case Apply(boxOp, value :: Nil) if isBox(boxOp.symbol) => + case Apply(boxOp, value :: Nil) if Erasure.Boxing.isBox(boxOp.symbol) && boxOp.symbol.denot.owner != defn.UnitModuleClass => // Eliminate boxing of primitive values. Boxing is introduced by erasure because // there's only a single synthetic `+` method "added" to the string class. value @@ -1134,7 +1116,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { * invocation instruction, otherwise `method.owner`. A specific receiver class is needed to * prevent an IllegalAccessError, (aladdin bug 455). */ - def genCallMethod(method: Symbol, style: InvokeStyle, pos: Position = NoPosition, specificReceiver: Symbol = null): BType = { + def genCallMethod(method: Symbol, style: InvokeStyle, pos: Span = NoSpan, specificReceiver: Symbol = null): BType = { val methodOwner = method.owner // the class used in the invocation's method descriptor in the classfile @@ -1142,7 +1124,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { if (specificReceiver != null) assert(style.isVirtual || specificReceiver == methodOwner, s"specificReceiver can only be specified for virtual calls. $method - $specificReceiver") - val useSpecificReceiver = specificReceiver != null && !specificReceiver.isBottomClass && !method.isScalaStatic + val useSpecificReceiver = specificReceiver != null && !defn.isBottomClass(specificReceiver) && !method.isScalaStatic val receiver = if (useSpecificReceiver) specificReceiver else methodOwner // workaround for a JVM bug: https://bugs.openjdk.java.net/browse/JDK-8154587 @@ -1161,10 +1143,10 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { val isTraitMethodOverridingObjectMember = { receiver != methodOwner && // fast path - the boolean is used to pick either of these two, if they are the same it does not matter style.isVirtual && - receiver.isEmittedInterface && - Object_Type.decl(method.name).exists && { // fast path - compute overrideChain on the next line only if necessary - val syms = method.allOverriddenSymbols - !syms.isEmpty && syms.last.owner == ObjectClass + isEmittedInterface(receiver) && + defn.ObjectType.decl(method.name).symbol.exists && { // fast path - compute overrideChain on the next line only if necessary + val syms = method.allOverriddenSymbols.toList + !syms.isEmpty && syms.last.owner == defn.ObjectClass } } if (isTraitMethodOverridingObjectMember) methodOwner else receiver @@ -1173,11 +1155,11 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { receiverClass.info // ensure types the type is up to date; erasure may add lateINTERFACE to traits val receiverName = internalName(receiverClass) - val jname = method.javaSimpleName.toString + val jname = method.javaSimpleName val bmType = asmMethodType(method) val mdescr = bmType.descriptor - val isInterface = receiverClass.isEmittedInterface + val isInterface = isEmittedInterface(receiverClass) import InvokeStyle._ if (style == Super) { // DOTTY: this differ from how super-calls in traits are handled in the scalac backend, @@ -1198,7 +1180,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { /* Generate the scala ## method. */ def genScalaHash(tree: Tree): BType = { genLoad(tree, ObjectReference) - genCallMethod(hashMethodSym, InvokeStyle.Static) + genCallMethod(NoSymbol, InvokeStyle.Static) // used to dispatch ## on primitives to ScalaRuntime.hash. Should be implemented by a miniphase } /* @@ -1206,7 +1188,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { * It turns a chained call like "a".+("b").+("c") into a list of arguments. */ def liftStringConcat(tree: Tree): List[Tree] = tree match { - case tree @ Apply(fun @ Select(larg, method), rarg) => + case tree @ Apply(fun @ DesugaredSelect(larg, method), rarg) => if (isPrimitive(fun) && primitives.getPrimitive(tree, larg.tpe) == ScalaPrimitivesOps.CONCAT) liftStringConcat(larg) ::: rarg @@ -1288,6 +1270,11 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { def genComparisonOp(l: Tree, r: Tree, code: Int): Unit = { val op = testOpForPrimitive(code) + def isNull(t: Tree): Boolean = t match { + 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 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) @@ -1313,8 +1300,8 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { import ScalaPrimitivesOps.{ ZNOT, ZAND, ZOR, EQ } // lhs and rhs of test - lazy val Select(lhs, _) = fun - val rhs = if (args.isEmpty) EmptyTree else args.head // args.isEmpty only for ZNOT + lazy val DesugaredSelect(lhs, _) = fun + val rhs = if (args.isEmpty) tpd.EmptyTree else args.head // args.isEmpty only for ZNOT def genZandOrZor(and: Boolean): Unit = { // reaching "keepGoing" indicates the rhs should be evaluated too (ie not short-circuited). @@ -1363,18 +1350,31 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { * not using the rich equality is possible (their own equals method will do ok.) */ val mustUseAnyComparator: Boolean = { - val areSameFinals = l.tpe.isFinalType && r.tpe.isFinalType && (l.tpe =:= r.tpe) - - !areSameFinals && isMaybeBoxed(l.tpe.typeSymbol) && isMaybeBoxed(r.tpe.typeSymbol) + val areSameFinals = l.tpe.typeSymbol.is(Final) && r.tpe.typeSymbol.is(Final) && (l.tpe =:= r.tpe) + // todo: remove + def isMaybeBoxed(sym: Symbol): Boolean = { + (sym == defn.ObjectClass) || + (sym == defn.JavaSerializableClass) || + (sym == defn.ComparableClass) || + (sym derivesFrom defn.BoxedNumberClass) || + (sym derivesFrom defn.BoxedCharClass) || + (sym derivesFrom defn.BoxedBooleanClass) + } + !areSameFinals && isMaybeBoxed(l.tpe.widenDealias.typeSymbol) && isMaybeBoxed(r.tpe.widenDealias.typeSymbol) + } + def isNull(t: Tree): Boolean = t match { + case Literal(Constant(null)) => true + case _ => false } + def isNonNullExpr(t: Tree): Boolean = t.isInstanceOf[Literal] || ((t.symbol ne null) && t.symbol.is(Module)) if (mustUseAnyComparator) { val equalsMethod: Symbol = { - if (l.tpe <:< BoxedNumberClass.tpe) { - if (r.tpe <:< BoxedNumberClass.tpe) externalEqualsNumNum - else if (r.tpe <:< BoxedCharacterClass.tpe) externalEqualsNumChar - else externalEqualsNumObject - } else externalEquals + if (l.tpe <:< defn.BoxedNumberClass.info) { + if (r.tpe <:< defn.BoxedNumberClass.info) defn.BoxesRunTimeModule.requiredMethod(nme.equalsNumNum) + else if (r.tpe <:< defn.BoxedCharClass.info) NoSymbol // ctx.requiredMethod(BoxesRunTimeTypeRef, nme.equalsNumChar) // this method is private + else defn.BoxesRunTimeModule.requiredMethod(nme.equalsNumObject) + } else defn.BoxesRunTimeModule_externalEquals } genLoad(l, ObjectReference) @@ -1395,11 +1395,11 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { // SI-7852 Avoid null check if L is statically non-null. genLoad(l, ObjectReference) genLoad(r, ObjectReference) - genCallMethod(Object_equals, InvokeStyle.Virtual) + 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, Object_Type, r.pos) + val eqEqTempLocal = locals.makeLocal(ObjectReference, nme.EQEQ_LOCAL_VAR.mangledString, defn.ObjectType, r.span) val lNull = new asm.Label val lNonNull = new asm.Label @@ -1416,7 +1416,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { markProgramPoint(lNonNull) locals.load(eqEqTempLocal) - genCallMethod(Object_equals, InvokeStyle.Virtual) + genCallMethod(defn.Any_equals, InvokeStyle.Virtual) genCZJUMP(success, failure, Primitives.NE, BOOL, targetIfNoJump) } } @@ -1429,38 +1429,48 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { def genInvokeDynamicLambda(ctor: Symbol, lambdaTarget: Symbol, environmentSize: Int, functionalInterface: Symbol): BType = { import java.lang.invoke.LambdaMetafactory.FLAG_SERIALIZABLE - debuglog(s"Using invokedynamic rather than `new ${ctor.owner}`") + ctx.debuglog(s"Using invokedynamic rather than `new ${ctor.owner}`") val generatedType = classBTypeFromSymbol(functionalInterface) // Lambdas should be serializable if they implement a SAM that extends Serializable or if they // implement a scala.Function* class. - val isSerializable = functionalInterface.isSerializable || functionalInterface.isFunctionClass - val isInterface = lambdaTarget.owner.isEmittedInterface + val isSerializable = functionalInterface.isSerializable || defn.isFunctionClass(functionalInterface) + val isInterface = isEmittedInterface(lambdaTarget.owner) val invokeStyle = if (lambdaTarget.isStaticMember) asm.Opcodes.H_INVOKESTATIC - else if (lambdaTarget.isPrivate || lambdaTarget.isClassConstructor) asm.Opcodes.H_INVOKESPECIAL + else if (lambdaTarget.is(Private) || lambdaTarget.isClassConstructor) asm.Opcodes.H_INVOKESPECIAL else if (isInterface) asm.Opcodes.H_INVOKEINTERFACE else asm.Opcodes.H_INVOKEVIRTUAL val targetHandle = new asm.Handle(invokeStyle, classBTypeFromSymbol(lambdaTarget.owner).internalName, - lambdaTarget.name.mangledString, + lambdaTarget.javaSimpleName, asmMethodType(lambdaTarget).descriptor, /* itf = */ isInterface) - val (a,b) = lambdaTarget.info.paramTypes.splitAt(environmentSize) - var (capturedParamsTypes, lambdaParamTypes) = if(int.doLabmdasFollowJVMMetafactoryOrder) (a,b) else (b,a) + val (a,b) = lambdaTarget.info.firstParamTypes.splitAt(environmentSize) + var (capturedParamsTypes, lambdaParamTypes) = (a,b) if (invokeStyle != asm.Opcodes.H_INVOKESTATIC) capturedParamsTypes = lambdaTarget.owner.info :: capturedParamsTypes // Requires https://github.com/scala/scala-java8-compat on the runtime classpath - val returnUnit = lambdaTarget.info.resultType.typeSymbol == UnitClass + val returnUnit = lambdaTarget.info.resultType.widenDealias.typeSymbol == defn.UnitClass val functionalInterfaceDesc: String = generatedType.descriptor val desc = capturedParamsTypes.map(tpe => toTypeKind(tpe)).mkString(("("), "", ")") + functionalInterfaceDesc // TODO specialization - val constrainedType = new MethodBType(lambdaParamTypes.map(p => toTypeKind(p)), toTypeKind(lambdaTarget.tpe.resultType)).toASMType - val abstractMethod = functionalInterface.samMethod() - val methodName = abstractMethod.name.mangledString + val constrainedType = new MethodBType(lambdaParamTypes.map(p => toTypeKind(p)), toTypeKind(lambdaTarget.info.resultType)).toASMType + + val abstractMethod = ctx.atPhase(ctx.erasurePhase) { + val samMethods = toDenot(functionalInterface).info.possibleSamMethods.toList + samMethods match { + case x :: Nil => x.symbol + case Nil => abort(s"${functionalInterface.show} is not a functional interface. It doesn't have abstract methods") + case xs => abort(s"${functionalInterface.show} is not a functional interface. " + + s"It has the following abstract methods: ${xs.map(_.name).mkString(", ")}") + } + } + + val methodName = abstractMethod.javaSimpleName val applyN = { val mt = asmMethodType(abstractMethod) mt.toASMType @@ -1483,6 +1493,16 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { generatedType } } + + /** Does this symbol actually correspond to an interface that will be emitted? + * In the backend, this should be preferred over `isInterface` because it + * also returns true for the symbols of the fake companion objects we + * create for Java-defined classes as well as for Java annotations + * which we represent as classes. + */ + 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 { diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala b/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala index f592e667b831..ddd50a933193 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala @@ -2,11 +2,33 @@ package dotty.tools package backend package jvm +import scala.annotation.threadUnsafe import scala.tools.asm +import scala.tools.asm.AnnotationVisitor import scala.tools.asm.ClassWriter import scala.collection.mutable + +import dotty.tools.dotc.CompilationUnit +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.ast.Trees +import dotty.tools.dotc.core.Annotations.Annotation +import dotty.tools.dotc.core.Constants._ +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.Flags._ +import dotty.tools.dotc.core.Names.Name +import dotty.tools.dotc.core.NameKinds.ExpandedName +import dotty.tools.dotc.core.Signature +import dotty.tools.dotc.core.StdNames._ +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.core.Types +import dotty.tools.dotc.core.Types._ +import dotty.tools.dotc.core.TypeErasure +import dotty.tools.dotc.transform.GenericSignatures import dotty.tools.io.AbstractFile +import dotty.tools.backend.jvm.DottyBackendInterface.symExtensions + /* * Traits encapsulating functionality to convert Scala AST Trees into ASM ClassNodes. * @@ -21,8 +43,19 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { //import bTypes._ //import coreBTypes._ import bTypes._ + import tpd._ import coreBTypes._ import int._ + import DottyBackendInterface._ + + def ScalaATTRName: String = "Scala" + def ScalaSignatureATTRName: String = "ScalaSig" + + @threadUnsafe lazy val AnnotationRetentionAttr: ClassSymbol = ctx.requiredClass("java.lang.annotation.Retention") + @threadUnsafe lazy val AnnotationRetentionSourceAttr: TermSymbol = ctx.requiredClass("java.lang.annotation.RetentionPolicy").linkedClass.requiredValue("SOURCE") + @threadUnsafe lazy val AnnotationRetentionClassAttr: TermSymbol = ctx.requiredClass("java.lang.annotation.RetentionPolicy").linkedClass.requiredValue("CLASS") + @threadUnsafe lazy val AnnotationRetentionRuntimeAttr: TermSymbol = ctx.requiredClass("java.lang.annotation.RetentionPolicy").linkedClass.requiredValue("RUNTIME") + @threadUnsafe lazy val JavaAnnotationClass: ClassSymbol = ctx.requiredClass("java.lang.annotation.Annotation") val bCodeAsmCommon: BCodeAsmCommon[int.type] = new BCodeAsmCommon(int) import bCodeAsmCommon._ @@ -39,10 +72,10 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { */ def getOutFolder(csym: Symbol, cName: String): AbstractFile = { try { - csym.outputDirectory + outputDirectory } catch { case ex: Throwable => - int.error(csym.pos, s"Couldn't create file for class $cName\n${ex.getMessage}") + ctx.error(s"Couldn't create file for class $cName\n${ex.getMessage}", ctx.source.atSpan(csym.span)) null } } @@ -79,7 +112,7 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { * must-single-thread */ def initBytecodeWriter(): BytecodeWriter = { - getSingleOutput match { + (None: Option[AbstractFile] /*getSingleOutput*/) match { // todo: implement case Some(f) if f.hasExtension("jar") => new DirectToJarfileWriter(f.file) case _ => @@ -168,7 +201,7 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { trait BCInnerClassGen { - def debugLevel = int.debuglevel + def debugLevel = 3 // 0 -> no debug info; 1-> filename; 2-> lines; 3-> varnames final val emitSource = debugLevel >= 1 final val emitLines = debugLevel >= 2 @@ -192,13 +225,13 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { // 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. // The two symbols have the same name, so the resulting internalName is the same. - val classSym = if (sym.isJavaDefined && sym.isModuleClass) sym.linkedClassOfClass else sym + val classSym = if (sym.is(JavaDefined) && sym.is(ModuleClass)) sym.linkedClass else sym getClassBTypeAndRegisterInnerClass(classSym).internalName } private def assertClassNotArray(sym: Symbol): Unit = { assert(sym.isClass, sym) - assert(sym != ArrayClass || isCompilingArray, sym) + assert(sym != defn.ArrayClass || compilingArray, sym) } private def assertClassNotArrayNotPrimitive(sym: Symbol): Unit = { @@ -223,8 +256,8 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { final def getClassBTypeAndRegisterInnerClass(sym: Symbol): ClassBType = { assertClassNotArrayNotPrimitive(sym) - if (sym == NothingClass) RT_NOTHING - else if (sym == NullClass) RT_NULL + if (sym == defn.NothingClass) RT_NOTHING + else if (sym == defn.NullClass) RT_NULL else { val r = classBTypeFromSymbol(sym) if (r.isNestedClass) innerClassBufferASM += r @@ -236,11 +269,11 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { * must-single-thread */ final def asmMethodType(msym: Symbol): MethodBType = { - assert(msym.isMethod, s"not a method-symbol: $msym") + assert(msym.is(Method), s"not a method-symbol: $msym") val resT: BType = if (msym.isClassConstructor || msym.isConstructor) UNIT - else toTypeKind(msym.tpe.resultType) - MethodBType(msym.tpe.paramTypes map toTypeKind, resT) + else toTypeKind(msym.info.resultType) + MethodBType(msym.info.firstParamTypes map toTypeKind, resT) } /** @@ -255,7 +288,7 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { */ final def symDescriptor(sym: Symbol): String = { getClassBTypeAndRegisterInnerClass(sym).descriptor } - final def toTypeKind(tp: Type): BType = tp.toTypeKind(BCodeHelpers.this)(this) + final def toTypeKind(tp: Type): BType = typeToTypeKind(tp)(BCodeHelpers.this)(this) } // end of trait BCInnerClassGen @@ -264,40 +297,196 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { /* * must-single-thread */ - def emitAnnotations(cw: asm.ClassVisitor, annotations: List[Annotation]) = - int.emitAnnotations(cw, annotations, BCodeHelpers.this)(this) + def emitAnnotations(cw: asm.ClassVisitor, annotations: List[Annotation]): Unit = + for(annot <- annotations; if shouldEmitAnnotation(annot)) { + val typ = annot.tree.tpe + val assocs = assocsFromApply(annot.tree) + val av = cw.visitAnnotation(typeDescriptor(typ), isRuntimeVisible(annot)) + emitAssocs(av, assocs, BCodeHelpers.this)(this) + } /* * must-single-thread */ - def emitAnnotations(mw: asm.MethodVisitor, annotations: List[Annotation]) = - int.emitAnnotations(mw, annotations, BCodeHelpers.this)(this) + def emitAnnotations(mw: asm.MethodVisitor, annotations: List[Annotation]): Unit = + for(annot <- annotations; if shouldEmitAnnotation(annot)) { + val typ = annot.tree.tpe + val assocs = assocsFromApply(annot.tree) + val av = mw.visitAnnotation(typeDescriptor(typ), isRuntimeVisible(annot)) + emitAssocs(av, assocs, BCodeHelpers.this)(this) + } /* * must-single-thread */ - def emitAnnotations(fw: asm.FieldVisitor, annotations: List[Annotation]) = - int.emitAnnotations(fw, annotations, BCodeHelpers.this)(this) + def emitAnnotations(fw: asm.FieldVisitor, annotations: List[Annotation]): Unit = + for(annot <- annotations; if shouldEmitAnnotation(annot)) { + val typ = annot.tree.tpe + val assocs = assocsFromApply(annot.tree) + val av = fw.visitAnnotation(typeDescriptor(typ), isRuntimeVisible(annot)) + emitAssocs(av, assocs, BCodeHelpers.this)(this) + } /* * must-single-thread */ - def emitParamAnnotations(jmethod: asm.MethodVisitor, pannotss: List[List[Annotation]]) = - int.emitParamAnnotations(jmethod, pannotss, BCodeHelpers.this)(this) + def emitParamAnnotations(jmethod: asm.MethodVisitor, pannotss: List[List[Annotation]]): Unit = + val annotationss = pannotss map (_ filter shouldEmitAnnotation) + if (annotationss forall (_.isEmpty)) return + for ((annots, idx) <- annotationss.zipWithIndex; + annot <- annots) { + val typ = annot.tree.tpe + val assocs = assocsFromApply(annot.tree) + val pannVisitor: asm.AnnotationVisitor = jmethod.visitParameterAnnotation(idx, typeDescriptor(typ.asInstanceOf[Type]), isRuntimeVisible(annot)) + emitAssocs(pannVisitor, assocs, BCodeHelpers.this)(this) + } + + private def shouldEmitAnnotation(annot: Annotation): Boolean = { + annot.symbol.is(JavaDefined) && + retentionPolicyOf(annot) != AnnotationRetentionSourceAttr + } + + private def emitAssocs(av: asm.AnnotationVisitor, assocs: List[(Name, Object)], bcodeStore: BCodeHelpers) + (innerClasesStore: bcodeStore.BCInnerClassGen) = { + for ((name, value) <- assocs) + emitArgument(av, name.mangledString, value.asInstanceOf[Tree], bcodeStore)(innerClasesStore) + av.visitEnd() + } + + private def emitArgument(av: AnnotationVisitor, + name: String, + arg: Tree, bcodeStore: BCodeHelpers)(innerClasesStore: bcodeStore.BCInnerClassGen): Unit = { + val narg = normalizeArgument(arg) + // Transformation phases are not run on annotation trees, so we need to run + // `constToLiteral` at this point. + val t = constToLiteral(narg)(ctx.withPhase(ctx.erasurePhase)) + t match { + case Literal(const @ Constant(_)) => + const.tag match { + case BooleanTag | ByteTag | ShortTag | CharTag | IntTag | LongTag | FloatTag | DoubleTag => av.visit(name, const.value) + case StringTag => + assert(const.value != null, const) // TODO this invariant isn't documented in `case class Constant` + av.visit(name, const.stringValue) // `stringValue` special-cases null, but that execution path isn't exercised for a const with StringTag + case ClazzTag => av.visit(name, typeToTypeKind(const.typeValue)(bcodeStore)(innerClasesStore).toASMType) + case EnumTag => + val edesc = innerClasesStore.typeDescriptor(const.tpe) // the class descriptor of the enumeration class. + val evalue = const.symbolValue.javaSimpleName // value the actual enumeration value. + av.visitEnum(name, edesc, evalue) + } + case t: TypeApply if (t.fun.symbol == defn.Predef_classOf) => + av.visit(name, typeToTypeKind(t.args.head.tpe.classSymbol.denot.info)(bcodeStore)(innerClasesStore).toASMType) + case Ident(nme.WILDCARD) => + // An underscore argument indicates that we want to use the default value for this parameter, so do not emit anything + case t: tpd.RefTree if t.symbol.denot.owner.isAllOf(JavaEnumTrait) => + val edesc = innerClasesStore.typeDescriptor(t.tpe) // the class descriptor of the enumeration class. + val evalue = t.symbol.javaSimpleName // value the actual enumeration value. + av.visitEnum(name, edesc, evalue) + case t: SeqLiteral => + val arrAnnotV: AnnotationVisitor = av.visitArray(name) + for (arg <- t.elems) { emitArgument(arrAnnotV, null, arg, bcodeStore)(innerClasesStore) } + arrAnnotV.visitEnd() + + case Apply(fun, args) if fun.symbol == defn.ArrayClass.primaryConstructor || + toDenot(fun.symbol).owner == defn.ArrayClass.linkedClass && fun.symbol.name == nme.apply => + val arrAnnotV: AnnotationVisitor = av.visitArray(name) + + var actualArgs = if (fun.tpe.isImplicitMethod) { + // generic array method, need to get implicit argument out of the way + fun.asInstanceOf[Apply].args + } else args + + val flatArgs = actualArgs.flatMap { arg => + normalizeArgument(arg) match { + case t: tpd.SeqLiteral => t.elems + case e => List(e) + } + } + for(arg <- flatArgs) { + emitArgument(arrAnnotV, null, arg, bcodeStore)(innerClasesStore) + } + arrAnnotV.visitEnd() + /* + case sb @ ScalaSigBytes(bytes) => + // see http://www.scala-lang.org/sid/10 (Storage of pickled Scala signatures in class files) + // also JVMS Sec. 4.7.16.1 The element_value structure and JVMS Sec. 4.4.7 The CONSTANT_Utf8_info Structure. + if (sb.fitsInOneString) { + av.visit(name, BCodeAsmCommon.strEncode(sb)) + } else { + val arrAnnotV: asm.AnnotationVisitor = av.visitArray(name) + for(arg <- BCodeAsmCommon.arrEncode(sb)) { arrAnnotV.visit(name, arg) } + arrAnnotV.visitEnd() + } // for the lazy val in ScalaSigBytes to be GC'ed, the invoker of emitAnnotations() should hold the ScalaSigBytes in a method-local var that doesn't escape. + */ + case t @ Apply(constr, args) if t.tpe.derivesFrom(JavaAnnotationClass) => + val typ = t.tpe.classSymbol.denot.info + val assocs = assocsFromApply(t) + val desc = innerClasesStore.typeDescriptor(typ) // the class descriptor of the nested annotation class + val nestedVisitor = av.visitAnnotation(name, desc) + emitAssocs(nestedVisitor, assocs, bcodeStore)(innerClasesStore) + + case t => + ctx.error(ex"Annotation argument is not a constant", t.sourcePos) + } + } + + private def normalizeArgument(arg: Tree): Tree = arg match { + case Trees.NamedArg(_, arg1) => normalizeArgument(arg1) + case Trees.Typed(arg1, _) => normalizeArgument(arg1) + case _ => arg + } + + private def isRuntimeVisible(annot: Annotation): Boolean = + if (toDenot(annot.tree.tpe.typeSymbol).hasAnnotation(AnnotationRetentionAttr)) + retentionPolicyOf(annot) == AnnotationRetentionRuntimeAttr + else { + // SI-8926: if the annotation class symbol doesn't have a @RetentionPolicy annotation, the + // annotation is emitted with visibility `RUNTIME` + // dotty bug: #389 + true + } + + private def retentionPolicyOf(annot: Annotation): Symbol = + annot.tree.tpe.typeSymbol.getAnnotation(AnnotationRetentionAttr). + flatMap(_.argumentConstant(0).map(_.symbolValue)).getOrElse(AnnotationRetentionClassAttr) + + private def assocsFromApply(tree: Tree): List[(Name, Tree)] = { + tree match { + case Block(_, expr) => assocsFromApply(expr) + case Apply(fun, args) => + fun.tpe.widen match { + case MethodType(names) => + (names zip args).filter { + case (_, t: tpd.Ident) if (t.tpe.normalizedPrefix eq NoPrefix) => false + case _ => true + } + } + } + } } // end of trait BCAnnotGen trait BCJGenSigGen { + import int._ def getCurrentCUnit(): CompilationUnit - /* @return - * - `null` if no Java signature is to be added (`null` is what ASM expects in these cases). - * - otherwise the signature in question + /** + * Generates the generic signature for `sym` before erasure. * - * must-single-thread + * @param sym The symbol for which to generate a signature. + * @param owner The owner of `sym`. + * @return The generic signature of `sym` before erasure, as specified in the Java Virtual + * Machine Specification, §4.3.4, or `null` if `sym` doesn't need a generic signature. + * @see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.4 */ - def getGenericSignature(sym: Symbol, owner: Symbol): String = int.getGenericSignature(sym, owner) + def getGenericSignature(sym: Symbol, owner: Symbol): String = { + ctx.atPhase(ctx.erasurePhase) { + val memberTpe = + if (sym.is(Method)) sym.denot.info + else owner.denot.thisType.memberInfo(sym) + getGenericSignatureHelper(sym, owner, memberTpe).orNull + } + } } // end of trait BCJGenSigGen @@ -310,7 +499,7 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { private def addForwarder(jclass: asm.ClassVisitor, module: Symbol, m: Symbol): Unit = { val moduleName = internalName(module) val methodInfo = module.thisType.memberInfo(m) - val paramJavaTypes: List[BType] = methodInfo.paramTypes map toTypeKind + val paramJavaTypes: List[BType] = methodInfo.firstParamTypes map toTypeKind // val paramNames = 0 until paramJavaTypes.length map ("x_" + _) /* Forwarders must not be marked final, @@ -320,17 +509,17 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { // TODO: evaluate the other flags we might be dropping on the floor here. // TODO: ACC_SYNTHETIC ? val flags = GenBCodeOps.PublicStatic | ( - if (m.isVarargsMethod) asm.Opcodes.ACC_VARARGS else 0 + if (m.is(JavaVarargs)) asm.Opcodes.ACC_VARARGS else 0 ) // TODO needed? for(ann <- m.annotations) { ann.symbol.initialize } val jgensig = getStaticForwarderGenericSignature(m, module) - val (throws, others) = m.annotations partition (_.symbol == ThrowsClass) + val (throws, others) = m.annotations partition (_.tree.symbol == defn.ThrowsAnnot) val thrownExceptions: List[String] = getExceptions(throws) val jReturnType = toTypeKind(methodInfo.resultType) val mdesc = MethodBType(paramJavaTypes, jReturnType).descriptor - val mirrorMethodName = m.javaSimpleName.toString + val mirrorMethodName = m.javaSimpleName val mirrorMethod: asm.MethodVisitor = jclass.visitMethod( flags, mirrorMethodName, @@ -340,11 +529,16 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { ) emitAnnotations(mirrorMethod, others) - emitParamAnnotations(mirrorMethod, m.info.params.map(_.annotations)) + val params: List[Symbol] = Nil // backend uses this to emit annotations on parameter lists of forwarders + // to static methods of companion class + // Old assumption: in Dotty this link does not exists: there is no way to get from method type + // to inner symbols of DefDef + // TODO: now we have paramSymss and could use it here. + emitParamAnnotations(mirrorMethod, params.map(_.annotations)) mirrorMethod.visitCode() - mirrorMethod.visitFieldInsn(asm.Opcodes.GETSTATIC, moduleName, MODULE_INSTANCE_FIELD, symDescriptor(module)) + mirrorMethod.visitFieldInsn(asm.Opcodes.GETSTATIC, moduleName, str.MODULE_INSTANCE_FIELD, symDescriptor(module)) var index = 0 for(jparamType <- paramJavaTypes) { @@ -369,32 +563,46 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { * must-single-thread */ def addForwarders(jclass: asm.ClassVisitor, jclassName: String, moduleClass: Symbol): Unit = { - assert(moduleClass.isModuleClass, moduleClass) - debuglog(s"Dumping mirror class for object: $moduleClass") + assert(moduleClass.is(ModuleClass), moduleClass) + ctx.debuglog(s"Dumping mirror class for object: $moduleClass") val linkedClass = moduleClass.companionClass lazy val conflictingNames: Set[Name] = { - (linkedClass.info.members collect { case sym if sym.name.isTermName => sym.name }).toSet + (linkedClass.info.allMembers.collect { case d if d.name.isTermName => d.name }).toSet } - debuglog(s"Potentially conflicting names for forwarders: $conflictingNames") + ctx.debuglog(s"Potentially conflicting names for forwarders: $conflictingNames") - for (m0 <- moduleClass.info.sortedMembersBasedOnFlags(required = Flag_METHOD, excluded = ExcludedForwarderFlags)) { - val m = if (m0.isBridge) m0.nextOverriddenSymbol else m0 + for (m0 <- sortedMembersBasedOnFlags(moduleClass.info, required = Method, excluded = ExcludedForwarder)) { + val m = if (m0.is(Bridge)) m0.nextOverriddenSymbol else m0 if (m == NoSymbol) - log(s"$m0 is a bridge method that overrides nothing, something went wrong in a previous phase.") - else if (m.isType || m.isDeferred || (m.owner eq ObjectClass) || m.isConstructor || m.isExpanded) - debuglog(s"No forwarder for '$m' from $jclassName to '$moduleClass'") + ctx.log(s"$m0 is a bridge method that overrides nothing, something went wrong in a previous phase.") + else if (m.isType || m.is(Deferred) || (m.owner eq defn.ObjectClass) || m.isConstructor || m.name.is(ExpandedName)) + ctx.debuglog(s"No forwarder for '$m' from $jclassName to '$moduleClass'") else if (conflictingNames(m.name)) - log(s"No forwarder for $m due to conflict with ${linkedClass.info.member(m.name)}") - else if (m.hasAccessBoundary) - log(s"No forwarder for non-public member $m") + ctx.log(s"No forwarder for $m due to conflict with ${linkedClass.info.member(m.name)}") + else if (m.accessBoundary(defn.RootClass) ne defn.RootClass) + ctx.log(s"No forwarder for non-public member $m") else { - log(s"Adding static forwarder for '$m' from $jclassName to '$moduleClass'") + ctx.log(s"Adding static forwarder for '$m' from $jclassName to '$moduleClass'") addForwarder(jclass, moduleClass, m) } } } + /** The members of this type that have all of `required` flags but none of `excluded` flags set. + * The members are sorted by name and signature to guarantee a stable ordering. + */ + private def sortedMembersBasedOnFlags(tp: Type, required: Flag, excluded: FlagSet): List[Symbol] = { + // The output of `memberNames` is a Set, sort it to guarantee a stable ordering. + val names = tp.memberNames(takeAllFilter).toSeq.sorted + val buffer = mutable.ListBuffer[Symbol]() + names.foreach { name => + buffer ++= tp.memberBasedOnFlags(name, required, excluded) + .alternatives.sortBy(_.signature)(Signature.lexicographicOrdering).map(_.symbol) + } + buffer.toList + } + /* * Quoting from JVMS 4.7.5 The Exceptions Attribute * "The Exceptions attribute indicates which checked exceptions a method may throw. @@ -406,8 +614,10 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { * must-single-thread */ def getExceptions(excs: List[Annotation]): List[String] = { - for (ThrownException(exc) <- excs.distinct) - yield internalName(exc) + // TODO: implement ThrownException + // for (ThrownException(exc) <- excs.distinct) + // yield internalName(exc) + Nil } } // end of trait BCForwardersGen @@ -458,7 +668,7 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { * must-single-thread */ def genMirrorClass(moduleClass: Symbol, cunit: CompilationUnit): asm.tree.ClassNode = { - assert(moduleClass.isModuleClass) + assert(moduleClass.is(ModuleClass)) assert(moduleClass.companionClass == NoSymbol, moduleClass) innerClassBufferASM.clear() this.cunit = cunit @@ -477,11 +687,11 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { ) if (emitSource) { - mirrorClass.visitSource("" + sourceFileFor(cunit), + mirrorClass.visitSource("" + cunit.source.file.name, null /* SourceDebugExtension */) } - val ssa = getAnnotPickle(mirrorName, moduleClass.companionSymbol) + val ssa = None // getAnnotPickle(mirrorName, if (moduleClass.is(Module)) moduleClass.companionClass else moduleClass.companionModule) mirrorClass.visitAttribute(if (ssa.isDefined) pickleMarkerLocal else pickleMarkerForeign) emitAnnotations(mirrorClass, moduleClass.annotations ++ ssa) @@ -507,17 +717,17 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { * Classes implementing the `Parcelable` interface must also have a static field called `CREATOR`, * which is an object implementing the `Parcelable.Creator` interface. */ - val androidFieldName = newTermName("CREATOR") + val androidFieldName = "CREATOR".toTermName - lazy val AndroidParcelableInterface : Symbol = getClassIfDefined("android.os.Parcelable") - lazy val AndroidCreatorClass : Symbol = getClassIfDefined("android.os.Parcelable$Creator") + lazy val AndroidParcelableInterface : Symbol = NoSymbol // getClassIfDefined("android.os.Parcelable") + lazy val AndroidCreatorClass : Symbol = NoSymbol // getClassIfDefined("android.os.Parcelable$Creator") /* * must-single-thread */ def isAndroidParcelableClass(sym: Symbol) = (AndroidParcelableInterface != NoSymbol) && - (sym.parentSymbols contains AndroidParcelableInterface) + (sym.info.parents.map(_.typeSymbol) contains AndroidParcelableInterface) /* * must-single-thread @@ -541,7 +751,7 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { clinit.visitFieldInsn( asm.Opcodes.GETSTATIC, moduleName, - MODULE_INSTANCE_FIELD, + str.MODULE_INSTANCE_FIELD, "L" + moduleName + ";" ) @@ -565,6 +775,163 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { } } // end of trait JAndroidBuilder + + /** + * This method returns the BType for a type reference, for example a parameter type. + * + * If the result is a ClassBType for a nested class, it is added to the innerClassBufferASM. + * + * If `t` references a class, toTypeKind ensures that the class is not an implementation class. + * See also comment on getClassBTypeAndRegisterInnerClass, which is invoked for implementation + * classes. + */ + private def typeToTypeKind(tp: Type)(ct: BCodeHelpers)(storage: ct.BCInnerClassGen): ct.bTypes.BType = { + import ct.bTypes._ + val defn = ctx.definitions + import coreBTypes._ + import Types._ + /** + * Primitive types are represented as TypeRefs to the class symbol of, for example, scala.Int. + * The `primitiveTypeMap` maps those class symbols to the corresponding PrimitiveBType. + */ + def primitiveOrClassToBType(sym: Symbol): BType = { + assert(sym.isClass, sym) + assert(sym != defn.ArrayClass || compilingArray, sym) + primitiveTypeMap.getOrElse(sym, + storage.getClassBTypeAndRegisterInnerClass(sym)).asInstanceOf[BType] + } + + /** + * When compiling Array.scala, the type parameter T is not erased and shows up in method + * signatures, e.g. `def apply(i: Int): T`. A TyperRef to T is replaced by ObjectReference. + */ + def nonClassTypeRefToBType(sym: Symbol): ClassBType = { + assert(sym.isType && compilingArray, sym) + ObjectReference.asInstanceOf[ct.bTypes.ClassBType] + } + + tp.widenDealias match { + case JavaArrayType(el) =>ArrayBType(typeToTypeKind(el)(ct)(storage)) // Array type such as Array[Int] (kept by erasure) + case t: TypeRef => + t.info match { + + case _ => + if (!t.symbol.isClass) nonClassTypeRefToBType(t.symbol) // See comment on nonClassTypeRefToBType + else primitiveOrClassToBType(t.symbol) // Common reference to a type such as scala.Int or java.lang.String + } + case Types.ClassInfo(_, sym, _, _, _) => primitiveOrClassToBType(sym) // We get here, for example, for genLoadModule, which invokes toTypeKind(moduleClassSymbol.info) + + /* AnnotatedType should (probably) be eliminated by erasure. However we know it happens for + * meta-annotated annotations (@(ann @getter) val x = 0), so we don't emit a warning. + * The type in the AnnotationInfo is an AnnotatedTpe. Tested in jvm/annotations.scala. + */ + case a @ AnnotatedType(t, _) => + ctx.debuglog(s"typeKind of annotated type $a") + typeToTypeKind(t)(ct)(storage) + + /* The cases below should probably never occur. They are kept for now to avoid introducing + * new compiler crashes, but we added a warning. The compiler / library bootstrap and the + * test suite don't produce any warning. + */ + + case tp => + ctx.warning( + s"an unexpected type representation reached the compiler backend while compiling ${ctx.compilationUnit}: $tp. " + + "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 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)) + } + } + } + + private def getGenericSignatureHelper(sym: Symbol, owner: Symbol, memberTpe: Type)(implicit ctx: Context): Option[String] = { + if (needsGenericSignature(sym)) { + val erasedTypeSym = TypeErasure.fullErasure(sym.denot.info).typeSymbol + if (erasedTypeSym.isPrimitiveValueClass) { + // Suppress signatures for symbols whose types erase in the end to primitive + // value types. This is needed to fix #7416. + None + } else { + val jsOpt = GenericSignatures.javaSig(sym, memberTpe) + if (ctx.settings.XverifySignatures.value) { + jsOpt.foreach(verifySignature(sym, _)) + } + + jsOpt + } + } else { + None + } + } + + private def verifySignature(sym: Symbol, sig: String)(implicit ctx: Context): Unit = { + import scala.tools.asm.util.CheckClassAdapter + def wrap(body: => Unit): Unit = { + try body + catch { + case ex: Throwable => + ctx.error(i"""|compiler bug: created invalid generic signature for $sym in ${sym.denot.owner.showFullName} + |signature: $sig + |if this is reproducible, please report bug at https://github.com/lampepfl/dotty/issues + """.trim, sym.sourcePos) + throw ex + } + } + + wrap { + if (sym.is(Method)) { + CheckClassAdapter.checkMethodSignature(sig) + } + else if (sym.isTerm) { + CheckClassAdapter.checkFieldSignature(sig) + } + else { + CheckClassAdapter.checkClassSignature(sig) + } + } + } + + // @M don't generate java generics sigs for (members of) implementation + // classes, as they are monomorphic (TODO: ok?) + private final def needsGenericSignature(sym: Symbol): Boolean = !( + // pp: this condition used to include sym.hasexpandedname, but this leads + // to the total loss of generic information if a private member is + // accessed from a closure: both the field and the accessor were generated + // without it. This is particularly bad because the availability of + // generic information could disappear as a consequence of a seemingly + // unrelated change. + ctx.base.settings.YnoGenericSig.value + || sym.is(Artifact) + || sym.isAllOf(LiftedMethod) + || sym.is(Bridge) + ) + + private def getStaticForwarderGenericSignature(sym: Symbol, moduleClass: Symbol): String = { + // scala/bug#3452 Static forwarder generation uses the same erased signature as the method if forwards to. + // By rights, it should use the signature as-seen-from the module class, and add suitable + // primitive and value-class boxing/unboxing. + // But for now, just like we did in mixin, we just avoid writing a wrong generic signature + // (one that doesn't erase to the actual signature). See run/t3452b for a test case. + + val memberTpe = ctx.atPhase(ctx.erasurePhase) { moduleClass.denot.thisType.memberInfo(sym) } + val erasedMemberType = TypeErasure.erasure(memberTpe) + if (erasedMemberType =:= sym.denot.info) + getGenericSignatureHelper(sym, moduleClass, memberTpe).orNull + else null + } + + def abort(msg: String): Nothing = { + ctx.error(msg) + throw new RuntimeException(msg) + } + + private def compilingArray(using ctx: Context) = + ctx.compilationUnit.source.file.name == "Array.scala" } object BCodeHelpers { @@ -585,4 +952,5 @@ object BCodeHelpers { val Special = new InvokeStyle(2) // InvokeSpecial (private methods, constructors) val Super = new InvokeStyle(3) // InvokeSpecial (super calls) } + } diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeIdiomatic.scala b/compiler/src/dotty/tools/backend/jvm/BCodeIdiomatic.scala index 81e3284e1fff..a68f9f4bc14a 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeIdiomatic.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeIdiomatic.scala @@ -16,14 +16,14 @@ import scala.tools.asm.tree.MethodInsnNode * */ trait BCodeIdiomatic { - val int: BackendInterface + val int: DottyBackendInterface final lazy val bTypes = new BTypesFromSymbols[int.type](int) import int._ import bTypes._ import coreBTypes._ - lazy val classfileVersion: Int = targetPlatform match { + lazy val classfileVersion: Int = ctx.settings.target.value match { case "jvm-1.5" => asm.Opcodes.V1_5 case "jvm-1.6" => asm.Opcodes.V1_6 case "jvm-1.7" => asm.Opcodes.V1_7 @@ -584,6 +584,11 @@ trait BCodeIdiomatic { jmethod.visitTypeInsn(Opcodes.CHECKCAST, tk.classOrArrayType) } + def abort(msg: String): Nothing = { + ctx.error(msg) + throw new RuntimeException(msg) + } + } // end of class JCodeMethodN /* Constant-valued val-members of JCodeMethodN at the companion object, so as to avoid re-initializing them multiple times. */ diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala index a16f3bed5057..e86f8455a8c8 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala @@ -9,6 +9,16 @@ import scala.tools.asm import scala.tools.asm.util.{TraceMethodVisitor, ASMifier} import java.io.PrintWriter +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.CompilationUnit +import dotty.tools.dotc.core.Annotations.Annotation +import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.Flags._ +import dotty.tools.dotc.core.StdNames.str +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.core.Types.Type +import dotty.tools.dotc.util.Spans._ + /* * * @author Miguel Garcia, http://lamp.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/ @@ -17,10 +27,14 @@ import java.io.PrintWriter */ trait BCodeSkelBuilder extends BCodeHelpers { import int._ + import DottyBackendInterface.{symExtensions, _} + import tpd._ import bTypes._ import coreBTypes._ import bCodeAsmCommon._ + lazy val NativeAttr: Symbol = requiredClass[scala.native] + /* * There's a dedicated PlainClassBuilder for each CompilationUnit, * which simplifies the initialization of per-class data structures in `genPlainClass()` which in turn delegates to `initJClass()` @@ -65,7 +79,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { def paramTKs(app: Apply, take: Int = -1): List[BType] = app match { case Apply(fun, _) => val funSym = fun.symbol - (funSym.info.paramTypes map toTypeKind) // this tracks mentioned inner classes (in innerClassBufferASM) + (funSym.info.firstParamTypes map toTypeKind) // this tracks mentioned inner classes (in innerClassBufferASM) } def symInfoTK(sym: Symbol): BType = { @@ -78,8 +92,8 @@ trait BCodeSkelBuilder extends BCodeHelpers { /* ---------------- helper utils for generating classes and fields ---------------- */ - def genPlainClass(cd: ClassDef) = cd match { - case ClassDef(_, _, _, impl) => + def genPlainClass(cd: TypeDef) = cd match { + case TypeDef(_, impl) => assert(cnode == null, "GenBCode detected nested methods.") innerClassBufferASM.clear() @@ -92,7 +106,8 @@ trait BCodeSkelBuilder extends BCodeHelpers { initJClass(cnode) - val hasStaticCtor = cd.symbol.methodSymbols exists (_.isStaticConstructor) + val methodSymbols = for (f <- cd.symbol.info.decls.toList if f.is(Method) && f.isTerm && !f.is(Module)) yield f + val hasStaticCtor = methodSymbols exists (_.isStaticConstructor) if (!hasStaticCtor) { // but needs one ... if (isCZStaticModule || isCZParcelable) { @@ -100,7 +115,19 @@ trait BCodeSkelBuilder extends BCodeHelpers { } } - val optSerial: Option[Long] = claszSymbol.serialVUID + val optSerial: Option[Long] = + claszSymbol.getAnnotation(defn.SerialVersionUIDAnnot).flatMap { annot => + if (claszSymbol.is(Trait)) { + ctx.error("@SerialVersionUID does nothing on a trait", annot.tree.sourcePos) + None + } else { + val vuid = annot.argumentConstant(0).map(_.longValue) + if (vuid.isEmpty) + ctx.error("The argument passed to @SerialVersionUID must be a constant", + annot.argument(0).getOrElse(annot.tree).sourcePos) + vuid + } + } if (optSerial.isDefined) { addSerialVUID(optSerial.get, cnode)} addClassFields() @@ -123,7 +150,7 @@ 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 superClass: String = if (ps.isEmpty) ObjectReference.internalName else internalName(ps.head.widenDealias.typeSymbol) val interfaceNames = classBTypeFromSymbol(claszSymbol).info.interfaces map { case classBType => if (classBType.isNestedClass) { innerClassBufferASM += classBType } @@ -138,7 +165,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { superClass, interfaceNames.toArray) if (emitSource) { - cnode.visitSource(sourceFileFor(cunit), null /* SourceDebugExtension */) + cnode.visitSource(cunit.source.file.name, null /* SourceDebugExtension */) } enclosingMethodAttribute(claszSymbol, internalName, asmMethodType(_).descriptor) match { @@ -147,7 +174,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { case _ => () } - val ssa = getAnnotPickle(thisName, claszSymbol) + val ssa = None // TODO: inlined form `getAnnotPickle(thisName, claszSymbol)`. Should something be done on Dotty? cnode.visitAttribute(if (ssa.isDefined) pickleMarkerLocal else pickleMarkerForeign) emitAnnotations(cnode, claszSymbol.annotations ++ ssa) @@ -157,15 +184,15 @@ trait BCodeSkelBuilder extends BCodeHelpers { } else { - val skipStaticForwarders = (claszSymbol.isInterface || claszSymbol.isModule || noForwarders) + val skipStaticForwarders = (claszSymbol.isInterface || claszSymbol.is(Module) || ctx.settings.XnoForwarders.value) if (!skipStaticForwarders) { val lmoc = claszSymbol.companionModule // add static forwarders if there are no name conflicts; see bugs #363 and #1735 if (lmoc != NoSymbol) { // it must be a top level class (name contains no $s) - val isCandidateForForwarders = lmoc.shouldEmitForwarders + val isCandidateForForwarders = (lmoc.is(Module)) && lmoc.isStatic if (isCandidateForForwarders) { - log(s"Adding static forwarders from '$claszSymbol' to implementations in '$lmoc'") + ctx.log(s"Adding static forwarders from '$claszSymbol' to implementations in '$lmoc'") addForwarders(cnode, thisName, lmoc.moduleClass) } } @@ -183,7 +210,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { private def addModuleInstanceField(): Unit = { val fv = cnode.visitField(GenBCodeOps.PublicStaticFinal, // TODO confirm whether we really don't want ACC_SYNTHETIC nor ACC_DEPRECATED - MODULE_INSTANCE_FIELD, + str.MODULE_INSTANCE_FIELD, "L" + thisName + ";", null, // no java-generic-signature null // no initial value @@ -227,16 +254,16 @@ trait BCodeSkelBuilder extends BCodeHelpers { * backend emits them as static). * No code is needed for this module symbol. */ - for (f <- claszSymbol.fieldSymbols) { + for (f <- claszSymbol.info.decls.filter(p => p.isTerm && !p.is(Method))) { val javagensig = getGenericSignature(f, claszSymbol) val flags = javaFieldFlags(f) - assert(!f.isStaticMember || !claszSymbol.isInterface || !f.isMutable, + assert(!f.isStaticMember || !claszSymbol.isInterface || !f.is(Mutable), s"interface $claszSymbol cannot have non-final static field $f") val jfield = new asm.tree.FieldNode( flags, - f.javaSimpleName.toString, + f.javaSimpleName, symInfoTK(f).descriptor, javagensig, null // no initial value @@ -278,7 +305,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { */ var jumpDest: immutable.Map[ /* Labeled or LabelDef */ Symbol, asm.Label ] = null def programPoint(labelSym: Symbol): asm.Label = { - assert(labelSym.isLabel, s"trying to map a non-label symbol to an asm.Label, at: ${labelSym.pos}") + assert(labelSym.is(Label), s"trying to map a non-label symbol to an asm.Label, at: ${labelSym.span}") jumpDest.getOrElse(labelSym, { val pp = new asm.Label jumpDest += (labelSym -> pp) @@ -359,9 +386,9 @@ trait BCodeSkelBuilder extends BCodeHelpers { /* Make a fresh local variable, ensuring a unique name. * The invoker must make sure inner classes are tracked for the sym's tpe. */ - def makeLocal(tk: BType, name: String, tpe: Type, pos: Position): Symbol = { + def makeLocal(tk: BType, name: String, tpe: Type, pos: Span): Symbol = { - val locSym = methSymbol.freshLocal(cunit, name, tpe, pos, Flag_SYNTHETIC) // setInfo tpe + val locSym = ctx.newSymbol(methSymbol, name.toTermName, Synthetic, tpe, NoSymbol, pos) makeLocal(locSym, tk) locSym } @@ -377,10 +404,10 @@ trait BCodeSkelBuilder extends BCodeHelpers { private def makeLocal(sym: Symbol, tk: BType): Local = { assert(nxtIdx != -1, "not a valid start index") - val loc = Local(tk, sym.javaSimpleName.toString, nxtIdx, sym.isSynthetic) + val loc = Local(tk, sym.javaSimpleName, nxtIdx, sym.is(Synthetic)) val existing = slots.put(sym, loc) if (existing.isDefined) - error(sym.pos, "attempt to create duplicate local var.") + ctx.error("attempt to create duplicate local var.", ctx.source.atSpan(sym.span)) assert(tk.size > 0, "makeLocal called for a symbol whose type is Unit.") nxtIdx += tk.size loc @@ -401,30 +428,6 @@ trait BCodeSkelBuilder extends BCodeHelpers { /* ---------------- Part 2 of program points, ie Labels in the ASM world ---------------- */ - /* - * The semantics of try-with-finally and synchronized-expr require their cleanup code - * to be present in three forms in the emitted bytecode: - * (a) as normal-exit code, reached via fall-through from the last program point being protected, - * (b) as code reached upon early-return from an enclosed return statement. - * The only difference between (a) and (b) is their next program-point: - * the former must continue with fall-through while - * the latter must continue to the next early-return cleanup (if any, otherwise return from the method). - * Otherwise they are identical. - * (c) as exception-handler, reached via exceptional control flow, - * which rethrows the caught exception once it's done with the cleanup code. - * - * A particular cleanup may in general contain LabelDefs. Care is needed when duplicating such jump-targets, - * so as to preserve agreement wit the (also duplicated) jump-sources. - * This is achieved based on the bookkeeping provided by two maps: - * - `labelDefsAtOrUnder` lists all LabelDefs enclosed by a given Tree node (the key) - * - `labelDef` provides the LabelDef node whose symbol is used as key. - * As a sidenote, a related map is `jumpDest`: it has the same keys as `labelDef` but its values are asm.Labels not LabelDef nodes. - * - * Details in `emitFinalizer()`, which is invoked from `genLoadTry()` and `genSynchronized()`. - */ - var labelDefsAtOrUnder: scala.collection.Map[Tree, List[LabelDef]] = null - var labelDef: scala.collection.Map[Symbol, LabelDef] = null// (LabelDef-sym -> LabelDef) - // 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) @@ -453,8 +456,8 @@ trait BCodeSkelBuilder extends BCodeHelpers { case _ => false } ) } def lineNumber(tree: Tree): Unit = { - if (!emitLines || !tree.pos.isDefined) return; - val nr = tree.pos.finalPosition.line + if (!emitLines || !tree.span.exists) return; + val nr = ctx.source.atSpan(tree.span).line + 1 if (nr != lastEmittedLineNr) { lastEmittedLineNr = nr lastInsn match { @@ -468,15 +471,11 @@ trait BCodeSkelBuilder extends BCodeHelpers { } // on entering a method - def resetMethodBookkeeping(dd: DefDef) = dd match { - case DefDef(_, _, _, _, _, rhs) => + def resetMethodBookkeeping(dd: DefDef) = { + val rhs = dd.rhs locals.reset(isStaticMethod = methSymbol.isStaticMember) jumpDest = immutable.Map.empty[ /* LabelDef */ Symbol, asm.Label ] - // populate labelDefsAtOrUnder - val ldf = getLabelDefOwners(rhs) - labelDefsAtOrUnder = ldf.withDefaultValue(Nil) - labelDef = labelDefsAtOrUnder(rhs).map(ld => (ld.symbol -> ld)).toMap // check previous invocation of genDefDef exited as many varsInScope as it entered. assert(varsInScope == null, "Unbalanced entering/exiting of GenBCode's genBlock().") // check previous invocation of genDefDef unregistered as many cleanups as it registered. @@ -492,15 +491,17 @@ trait BCodeSkelBuilder extends BCodeHelpers { def gen(tree: Tree): Unit = { tree match { - case EmptyTree => () + case tpd.EmptyTree => () - case ModuleDef(_, _, _) => abort(s"Modules should have been eliminated by refchecks: $tree") + case ValDef(name, tpt, rhs) => () // fields are added in `genPlainClass()`, via `addClassFields()` - case ValDef(mods, name, tpt, rhs) => () // fields are added in `genPlainClass()`, via `addClassFields()` + case dd: DefDef => genDefDef(dd) - case dd @ DefDef(_, _, _, _, _, _) => genDefDef(dd.asInstanceOf[DefDef]) - - case Template(_, _, body) => body foreach gen + case tree: Template => + val body = + if (tree.constr.rhs.isEmpty) tree.body + else tree.constr :: tree.body + body foreach gen case _ => abort(s"Illegal tree in gen: $tree") } @@ -512,7 +513,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { def initJMethod(flags: Int, paramAnnotations: List[List[Annotation]]): Unit = { val jgensig = getGenericSignature(methSymbol, claszSymbol) - val (excs, others) = methSymbol.annotations partition (_.symbol == ThrowsClass) + val (excs, others) = methSymbol.annotations partition (_.tree.symbol == defn.ThrowsAnnot) val thrownExceptions: List[String] = getExceptions(excs) val bytecodeName = @@ -536,14 +537,15 @@ trait BCodeSkelBuilder extends BCodeHelpers { } // end of method initJMethod - def genDefDef(dd: DefDef): Unit = dd match { - case DefDef(_, _, _, vparamss, _, rhs) => + def genDefDef(dd: DefDef): Unit = { + val rhs = dd.rhs + val vparamss = dd.vparamss // the only method whose implementation is not emitted: getClass() - if (dd.symbol.isGetClass) { return } + if (dd.symbol eq defn.Any_getClass) { return } assert(mnode == null, "GenBCode detected nested method.") methSymbol = dd.symbol - jMethodName = methSymbol.javaSimpleName.toString + jMethodName = methSymbol.javaSimpleName returnType = asmMethodType(dd.symbol).returnType isMethSymStaticCtor = methSymbol.isStaticConstructor @@ -558,37 +560,22 @@ trait BCodeSkelBuilder extends BCodeHelpers { if (params.size > MaximumJvmParameters) { // SI-7324 - error(methSymbol.pos, s"Platform restriction: a parameter list's length cannot exceed $MaximumJvmParameters.") + ctx.error(s"Platform restriction: a parameter list's length cannot exceed $MaximumJvmParameters.", ctx.source.atSpan(methSymbol.span)) return } val isNative = methSymbol.hasAnnotation(NativeAttr) - val isAbstractMethod = (methSymbol.isDeferred || (methSymbol.owner.isInterface && !methSymbol.isJavaDefaultMethod)) + val isAbstractMethod = (methSymbol.is(Deferred) || (methSymbol.owner.isInterface && ((methSymbol.is(Deferred)) || methSymbol.isClassConstructor))) val flags = GenBCodeOps.mkFlags( javaFlags(methSymbol), if (isAbstractMethod) asm.Opcodes.ACC_ABSTRACT else 0, - if (methSymbol.isStrictFP) asm.Opcodes.ACC_STRICT else 0, + if (false /*methSymbol.isStrictFP*/) asm.Opcodes.ACC_STRICT else 0, if (isNative) asm.Opcodes.ACC_NATIVE else 0 // native methods of objects are generated in mirror classes ) // TODO needed? for(ann <- m.symbol.annotations) { ann.symbol.initialize } initJMethod(flags, params.map(p => p.symbol.annotations)) - /* Add method-local vars for LabelDef-params. - * - * This makes sure that: - * (1) upon visiting any "forward-jumping" Apply (ie visited before its target LabelDef), and after - * (2) grabbing the corresponding param symbols, - * those param-symbols can be used to access method-local vars. - * - * When duplicating a finally-contained LabelDef, another program-point is needed for the copy (each such copy has its own asm.Label), - * but the same vars (given by the LabelDef's params) can be reused, - * because no LabelDef ends up nested within itself after such duplication. - */ - for(ld <- labelDefsAtOrUnder(rhs); LabelDef(_, ldpl ,_) = ld; ldp <- ldpl; if !locals.contains(ldp)) { - // the tail-calls xform results in symbols shared btw method-params and labelDef-params, thus the guard above. - locals.makeLocal(ldp) - } if (!isAbstractMethod && !isNative) { @@ -597,11 +584,13 @@ trait BCodeSkelBuilder extends BCodeHelpers { genLoad(rhs, returnType) rhs match { - case Return(_) | Block(_, Return(_)) | Throw(_) | Block(_, Throw(_)) => () - case EmptyTree => - error(NoPosition, "Concrete method has no definition: " + dd + ( - if (settings_debug) "(found: " + methSymbol.owner.info.decls.toList.mkString(", ") + ")" - else "") + case (_: Return) | Block(_, (_: Return)) => () + case (_: Apply) | Block(_, (_: Apply)) if rhs.symbol eq defn.throwMethod => () + case tpd.EmptyTree => + ctx.error("Concrete method has no definition: " + dd + ( + if (ctx.settings.Ydebug.value) "(found: " + methSymbol.owner.info.decls.toList.mkString(", ") + ")" + else ""), + ctx.source.atSpan(NoSpan) ) case _ => bc emitRETURN returnType @@ -666,11 +655,11 @@ trait BCodeSkelBuilder extends BCodeHelpers { // call object's private ctor from static ctor if (isCZStaticModule) { // NEW `moduleName` - val className = internalName(methSymbol.enclClass) + val className = internalName(methSymbol.enclosingClass) insnModA = new asm.tree.TypeInsnNode(asm.Opcodes.NEW, className) // INVOKESPECIAL - val callee = methSymbol.enclClass.primaryConstructor - val jname = callee.javaSimpleName.toString + val callee = methSymbol.enclosingClass.primaryConstructor + val jname = callee.javaSimpleName val jowner = internalName(callee.owner) val jtype = asmMethodType(callee).descriptor insnModB = new asm.tree.MethodInsnNode(asm.Opcodes.INVOKESPECIAL, jowner, jname, jtype, false) @@ -690,9 +679,9 @@ trait BCodeSkelBuilder extends BCodeHelpers { null ) // INVOKESTATIC CREATOR(): android.os.Parcelable$Creator; -- TODO where does this Android method come from? - val callee = claszSymbol.companionModule.info.member(androidFieldName) + val callee = claszSymbol.companionModule.info.member(androidFieldName).symbol val jowner = internalName(callee.owner) - val jname = callee.javaSimpleName.toString + val jname = callee.javaSimpleName val jtype = asmMethodType(callee).descriptor insnParcA = new asm.tree.MethodInsnNode(asm.Opcodes.INVOKESTATIC, jowner, jname, jtype, false) // PUTSTATIC `thisName`.CREATOR; diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeSyncAndTry.scala b/compiler/src/dotty/tools/backend/jvm/BCodeSyncAndTry.scala index 606e3ca8c1c3..60ceda5fa8b2 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeSyncAndTry.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeSyncAndTry.scala @@ -5,6 +5,12 @@ package jvm import scala.collection.immutable import scala.tools.asm +import dotty.tools.dotc.CompilationUnit +import dotty.tools.dotc.core.StdNames.nme +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.ast.tpd.TreeOps + /* * * @author Miguel Garcia, http://lamp.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/ @@ -13,6 +19,7 @@ import scala.tools.asm */ trait BCodeSyncAndTry extends BCodeBodyBuilder { import int._ + import tpd._ import bTypes._ import coreBTypes._ /* @@ -22,13 +29,13 @@ trait BCodeSyncAndTry extends BCodeBodyBuilder { def genSynchronized(tree: Apply, expectedType: BType): BType = tree match { case Apply(TypeApply(fun, _), args) => - val monitor = locals.makeLocal(ObjectReference, "monitor", Object_Type, tree.pos) + val monitor = locals.makeLocal(ObjectReference, "monitor", defn.ObjectType, tree.span) val monCleanup = new asm.Label // 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", Object_Type, tree.pos) else null + val monitorResult: Symbol = 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) @@ -176,8 +183,8 @@ trait BCodeSyncAndTry extends BCodeBodyBuilder { val caseHandlers: List[EHClause] = 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 Typed(Ident(nme.WILDCARD), tpt) => NamelessEH(tpeTK(tpt).asClassBType, caseBody) + case Ident(nme.WILDCARD) => NamelessEH(ThrowableReference, caseBody) case Bind(_, _) => BoundEH (pat.symbol, caseBody) } } @@ -195,7 +202,7 @@ trait BCodeSyncAndTry extends BCodeBodyBuilder { */ val postHandlers = new asm.Label - val hasFinally = (finalizer != EmptyTree) + val hasFinally = (finalizer != tpd.EmptyTree) /* * used in the finally-clause reached via fall-through from try-catch, if any. @@ -206,7 +213,7 @@ trait BCodeSyncAndTry extends BCodeBodyBuilder { * please notice `tmp` has type tree.tpe, while `earlyReturnVar` has the method return type. * Because those two types can be different, dedicated vars are needed. */ - val tmp = if (guardResult) locals.makeLocal(tpeTK(tree), "tmp", tree.tpe, tree.pos) else null + val tmp = if (guardResult) locals.makeLocal(tpeTK(tree), "tmp", tree.tpe, tree.span) else null /* * upon early return from the try-body or one of its EHs (but not the EH-version of the finally-clause) @@ -316,7 +323,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", Throwable_Type, finalizer.pos)) + val Local(eTK, _, eIdx, _) = locals(locals.makeLocal(ThrowableReference, "exc", defn.ThrowableType, finalizer.span)) bc.store(eIdx, eTK) emitFinalizer(finalizer, null, isDuplicate = true) bc.load(eIdx, eTK) @@ -392,9 +399,6 @@ trait BCodeSyncAndTry extends BCodeBodyBuilder { var saved: immutable.Map[ /* LabelDef */ Symbol, asm.Label ] = null if (isDuplicate) { saved = jumpDest - for(ldef <- labelDefsAtOrUnder(finalizer)) { - jumpDest -= ldef.symbol - } } // when duplicating, the above guarantees new asm.Labels are used for LabelDefs contained in the finalizer (their vars are reused, that's ok) if (tmp != null) { locals.store(tmp) } @@ -406,11 +410,11 @@ trait BCodeSyncAndTry extends BCodeBodyBuilder { } /* Does this tree have a try-catch block? */ - def mayCleanStack(tree: Tree): Boolean = tree exists { t => t match { + def mayCleanStack(tree: Tree): Boolean = tree.find { t => t match { // TODO: use existsSubTree case Try(_, _, _) => true case _ => false } - } + }.isDefined trait EHClause case class NamelessEH(typeToDrop: ClassBType, caseBody: Tree) extends EHClause diff --git a/compiler/src/dotty/tools/backend/jvm/BTypes.scala b/compiler/src/dotty/tools/backend/jvm/BTypes.scala index 998981bfbec1..7df2e674f3a5 100644 --- a/compiler/src/dotty/tools/backend/jvm/BTypes.scala +++ b/compiler/src/dotty/tools/backend/jvm/BTypes.scala @@ -14,7 +14,7 @@ import scala.tools.asm */ abstract class BTypes { - val int: BackendInterface + val int: DottyBackendInterface import int._ /** * A map from internal names to ClassBTypes. Every ClassBType is added to this map on its @@ -610,7 +610,7 @@ abstract class BTypes { assert(!ClassBType.isInternalPhantomType(internalName), s"Cannot create ClassBType for phantom type $this") assert( - if (info.superClass.isEmpty) { isJLO(this) || (int.isCompilingPrimitive && ClassBType.hasNoSuper(internalName)) } + if (info.superClass.isEmpty) { isJLO(this) || (DottyBackendInterface.isCompilingPrimitive && ClassBType.hasNoSuper(internalName)) } else if (isInterface) isJLO(info.superClass.get) else !isJLO(this) && ifInit(info.superClass.get)(!_.isInterface), s"Invalid superClass in $this: ${info.superClass}" diff --git a/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala b/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala index 62cdac980254..9bf597ce99fb 100644 --- a/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala +++ b/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala @@ -4,6 +4,14 @@ package jvm import scala.tools.asm import scala.annotation.threadUnsafe +import scala.collection.mutable +import scala.collection.generic.Clearable + +import dotty.tools.dotc.core.Flags._ +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.core.Phases.Phase +import dotty.tools.dotc.transform.SymUtils._ +import dotty.tools.dotc.util.WeakHashSet /** * This class mainly contains the method classBTypeFromSymbol, which extracts the necessary @@ -17,8 +25,12 @@ import scala.annotation.threadUnsafe * of the core btypes. They are declared in BTypes as abstract members. Note that BTypes does * not have access to the compiler instance. */ -class BTypesFromSymbols[I <: BackendInterface](val int: I) extends BTypes { +class BTypesFromSymbols[I <: DottyBackendInterface](val int: I) extends BTypes { import int._ + import DottyBackendInterface.{symExtensions, _} + + lazy val TransientAttr = requiredClass[scala.transient] + lazy val VolatileAttr = requiredClass[scala.volatile] val bCodeAsmCommon: BCodeAsmCommon[int.type ] = new BCodeAsmCommon(int) import bCodeAsmCommon._ @@ -31,6 +43,25 @@ class BTypesFromSymbols[I <: BackendInterface](val int: I) extends BTypes { coreBTypes.setBTypes(new CoreBTypes[this.type](this)) } + private[this] val perRunCaches: Caches = new Caches { + def newAnyRefMap[K <: AnyRef, V](): mutable.AnyRefMap[K, V] = new mutable.AnyRefMap[K, V]() + def newWeakMap[K, V](): mutable.WeakHashMap[K, V] = new mutable.WeakHashMap[K, V]() + def recordCache[T <: Clearable](cache: T): T = cache + def newWeakSet[K >: Null <: AnyRef](): WeakHashSet[K] = new WeakHashSet[K]() + def newMap[K, V](): mutable.HashMap[K, V] = new mutable.HashMap[K, V]() + def newSet[K](): mutable.Set[K] = new mutable.HashSet[K] + } + + // TODO remove abstraction + private abstract class Caches { + def recordCache[T <: Clearable](cache: T): T + def newWeakMap[K, V](): collection.mutable.WeakHashMap[K, V] + def newMap[K, V](): collection.mutable.HashMap[K, V] + def newSet[K](): collection.mutable.Set[K] + def newWeakSet[K >: Null <: AnyRef](): dotty.tools.dotc.util.WeakHashSet[K] + def newAnyRefMap[K <: AnyRef, V](): collection.mutable.AnyRefMap[K, V] + } + @threadUnsafe protected lazy val classBTypeFromInternalNameMap = { perRunCaches.recordCache(collection.concurrent.TrieMap.empty[String, ClassBType]) } @@ -48,7 +79,7 @@ class BTypesFromSymbols[I <: BackendInterface](val int: I) extends BTypes { assert(classSym.isClass, s"Cannot create ClassBType from non-class symbol $classSym") assert( (!primitiveTypeMap.contains(classSym) || isCompilingPrimitive) && - (classSym != NothingClass && classSym != NullClass), + (classSym != defn.NothingClass && classSym != defn.NullClass), s"Cannot create ClassBType for special class symbol ${classSym.showFullName}") convertedClasses.getOrElse(classSym, { @@ -62,12 +93,22 @@ class BTypesFromSymbols[I <: BackendInterface](val int: I) extends BTypes { } private def setClassInfo(classSym: Symbol, classBType: ClassBType): ClassBType = { - val superClassSym = classSym.superClass + val superClassSym: Symbol = { + val t = classSym.asClass.superClass + if (t.exists) t + else if (classSym.is(ModuleClass)) { + // workaround #371 + + println(s"Warning: mocking up superclass for $classSym") + defn.ObjectClass + } + else t + } assert( - if (classSym == ObjectClass) + if (classSym == defn.ObjectClass) superClassSym == NoSymbol else if (classSym.isInterface) - superClassSym == ObjectClass + superClassSym == defn.ObjectClass else // A ClassBType for a primitive class (scala.Boolean et al) is only created when compiling these classes. ((superClassSym != NoSymbol) && !superClassSym.isInterface) || (isCompilingPrimitive && primitiveTypeMap.contains(classSym)), @@ -76,6 +117,21 @@ class BTypesFromSymbols[I <: BackendInterface](val int: I) extends BTypes { val superClass = if (superClassSym == NoSymbol) None else Some(classBTypeFromSymbol(superClassSym)) + /** + * All interfaces implemented by a class, except for those inherited through the superclass. + * Redundant interfaces are removed unless there is a super call to them. + */ + def (sym: Symbol).superInterfaces: List[Symbol] = { + val directlyInheritedTraits = sym.directlyInheritedTraits + val directlyInheritedTraitsSet = directlyInheritedTraits.toSet + val allBaseClasses = directlyInheritedTraits.iterator.flatMap(_.asClass.baseClasses.drop(1)).toSet + val superCalls = superCallsMap.getOrElse(sym, Set.empty) + val additional = (superCalls -- directlyInheritedTraitsSet).filter(_.is(Trait)) +// if (additional.nonEmpty) +// println(s"$fullName: adding supertraits $additional") + directlyInheritedTraits.filter(t => !allBaseClasses(t) || superCalls(t)) ++ additional + } + val interfaces = classSym.superInterfaces.map(classBTypeFromSymbol) val flags = javaFlags(classSym) @@ -95,7 +151,7 @@ class BTypesFromSymbols[I <: BackendInterface](val int: I) extends BTypes { // The lambdalift phase lifts all nested classes to the enclosing class, so if we collect // member classes right after lambdalift, we obtain all nested classes, including local and // anonymous ones. - val nestedClasses = classSym.nestedClasses + val nestedClasses = getNestedClasses(classSym) // If this is a top-level class, and it has a companion object, the member classes of the // companion are added as members of the class. For example: @@ -110,7 +166,10 @@ class BTypesFromSymbols[I <: BackendInterface](val int: I) extends BTypes { // For consistency, the InnerClass entry for D needs to be present in C - to Java it looks // like D is a member of C, not C$. val linkedClass = classSym.linkedClass - val companionModuleMembers = classSym.companionModuleMembers + val companionModuleMembers = { + if (classSym.linkedClass.isTopLevelModuleClass) getMemberClasses(classSym.linkedClass) + else Nil + } nestedClasses ++ companionModuleMembers } @@ -122,7 +181,7 @@ class BTypesFromSymbols[I <: BackendInterface](val int: I) extends BTypes { * Here we get rid of the module class B, making sure that the class B is present. */ val nestedClassSymbolsNoJavaModuleClasses = nestedClassSymbols.filter(s => { - if (s.isJavaDefined && s.isModuleClass) { + if (s.is(JavaDefined) && s.is(ModuleClass)) { // We could also search in nestedClassSymbols for s.linkedClassOfClass, but sometimes that // returns NoSymbol, so it doesn't work. val nb = nestedClassSymbols.count(mc => mc.name == s.name && mc.owner == s.owner) @@ -140,28 +199,53 @@ class BTypesFromSymbols[I <: BackendInterface](val int: I) extends BTypes { classBType } + /** For currently compiled classes: All locally defined classes including local classes. + * The empty list for classes that are not currently compiled. + */ + private def getNestedClasses(sym: Symbol): List[Symbol] = definedClasses(sym, ctx.flattenPhase) + + /** For currently compiled classes: All classes that are declared as members of this class + * (but not inherited ones). The empty list for classes that are not currently compiled. + */ + private def getMemberClasses(sym: Symbol): List[Symbol] = definedClasses(sym, ctx.lambdaLiftPhase) + + private def definedClasses(sym: Symbol, phase: Phase) = + if (sym.isDefinedInCurrentRun) + ctx.atPhase(phase) { + toDenot(sym).info.decls.filter(_.isClass) + } + else Nil private def buildNestedInfo(innerClassSym: Symbol): Option[NestedInfo] = { assert(innerClassSym.isClass, s"Cannot build NestedInfo for non-class symbol $innerClassSym") - val isNested = !innerClassSym.rawowner.isPackageClass + val isNested = !innerClassSym.originalOwner.originalLexicallyEnclosingClass.is(PackageClass) if (!isNested) None else { // See comment in BTypes, when is a class marked static in the InnerClass table. val isStaticNestedClass = innerClassSym.originalOwner.originalLexicallyEnclosingClass.isOriginallyStaticOwner // After lambdalift (which is where we are), the rawowoner field contains the enclosing class. - val enclosingClassSym = innerClassSym.enclosingClassSym + val enclosingClassSym = { + if (innerClassSym.isClass) { + val ct = ctx.withPhase(ctx.flattenPhase.prev) + toDenot(innerClassSym)(ct).owner.enclosingClass(ct) + } + else innerClassSym.enclosingClass(ctx.withPhase(ctx.flattenPhase.prev)) + } //todo is handled specially for JavaDefined symbols in scalac + val enclosingClass: ClassBType = classBTypeFromSymbol(enclosingClassSym) val outerName: Option[String] = { if (isAnonymousOrLocalClass(innerClassSym)) { None } else { - val outerName = innerClassSym.rawowner.javaBinaryName + val outerName = innerClassSym.originalOwner.originalLexicallyEnclosingClass.javaBinaryName + def dropModule(str: String): String = + if (!str.isEmpty && str.last == '$') str.take(str.length - 1) else str // Java compatibility. See the big comment in BTypes that summarizes the InnerClass spec. val outerNameModule = - if (innerClassSym.rawowner.isTopLevelModuleClass) dropModule(outerName) + if (innerClassSym.originalOwner.originalLexicallyEnclosingClass.isTopLevelModuleClass) dropModule(outerName) else outerName Some(outerNameModule.toString) } @@ -169,13 +253,28 @@ class BTypesFromSymbols[I <: BackendInterface](val int: I) extends BTypes { val innerName: Option[String] = { if (innerClassSym.isAnonymousClass || innerClassSym.isAnonymousFunction) None - else Some(innerClassSym.rawname + innerClassSym.moduleSuffix) // moduleSuffix for module classes + else { + val original = innerClassSym.initial + Some(innerClassSym.name(ctx.withPhase(original.validFor.phaseId)).mangledString) // moduleSuffix for module classes + } } Some(NestedInfo(enclosingClass, outerName, innerName, isStaticNestedClass)) } } + /** + * This is basically a re-implementation of sym.isStaticOwner, but using the originalOwner chain. + * + * The problem is that we are interested in a source-level property. Various phases changed the + * symbol's properties in the meantime, mostly lambdalift modified (destructively) the owner. + * Therefore, `sym.isStatic` is not what we want. For example, in + * object T { def f { object U } } + * the owner of U is T, so UModuleClass.isStatic is true. Phase travel does not help here. + */ + private def (sym: Symbol).isOriginallyStaticOwner: Boolean = + sym.is(PackageClass) || sym.is(ModuleClass) && sym.originalOwner.originalLexicallyEnclosingClass.isOriginallyStaticOwner + /** * Return the Java modifiers for the given symbol. * Java modifiers for classes: @@ -197,14 +296,14 @@ class BTypesFromSymbols[I <: BackendInterface](val int: I) extends BTypes { final def javaFlags(sym: Symbol): Int = { - val privateFlag = sym.getsJavaPrivateFlag + val privateFlag = sym.is(Private) || (sym.isPrimaryConstructor && sym.owner.isTopLevelModuleClass) - val finalFlag = sym.getsJavaFinalFlag + val finalFlag = sym.is(Final) && !toDenot(sym).isClassConstructor && !(sym.is(Mutable)) && !(sym.enclosingClass.is(Trait)) import asm.Opcodes._ GenBCodeOps.mkFlags( if (privateFlag) ACC_PRIVATE else ACC_PUBLIC, - if (sym.isDeferred || sym.hasAbstractFlag) ACC_ABSTRACT else 0, + if (sym.is(Deferred) || sym.isOneOf(AbstractOrTrait)) ACC_ABSTRACT else 0, if (sym.isInterface) ACC_INTERFACE else 0, if (finalFlag && @@ -212,19 +311,19 @@ class BTypesFromSymbols[I <: BackendInterface](val int: I) extends BTypes { // without having to provide any implementations, but that is an // illegal combination of modifiers at the bytecode level so // suppress final if abstract if present. - !sym.hasAbstractFlag && + !sym.isOneOf(AbstractOrTrait) && // Mixin forwarders are bridges and can be final, but final bridges confuse some frameworks - !sym.isBridge) + !sym.is(Bridge)) ACC_FINAL else 0, if (sym.isStaticMember) ACC_STATIC else 0, - if (sym.isBridge) ACC_BRIDGE | ACC_SYNTHETIC else 0, - if (sym.isArtifact) ACC_SYNTHETIC else 0, + if (sym.is(Bridge)) ACC_BRIDGE | ACC_SYNTHETIC else 0, + if (sym.is(Artifact)) ACC_SYNTHETIC else 0, if (sym.isClass && !sym.isInterface) ACC_SUPER else 0, - if (sym.hasEnumFlag) ACC_ENUM else 0, - if (sym.isVarargsMethod) ACC_VARARGS else 0, - if (sym.isSynchronized) ACC_SYNCHRONIZED else 0, - if (sym.isDeprecated) asm.Opcodes.ACC_DEPRECATED else 0, - if (sym.isEnum) asm.Opcodes.ACC_ENUM else 0 + if (sym.isAllOf(JavaEnumTrait)) ACC_ENUM else 0, + if (sym.is(JavaVarargs)) ACC_VARARGS else 0, + if (sym.is(Synchronized)) ACC_SYNCHRONIZED else 0, + if (false /*sym.isDeprecated*/) asm.Opcodes.ACC_DEPRECATED else 0, // TODO: add an isDeprecated method in SymUtils + if (sym.is(Enum)) asm.Opcodes.ACC_ENUM else 0 ) } @@ -232,7 +331,7 @@ class BTypesFromSymbols[I <: BackendInterface](val int: I) extends BTypes { javaFlags(sym) | GenBCodeOps.mkFlags( if (sym hasAnnotation TransientAttr) asm.Opcodes.ACC_TRANSIENT else 0, if (sym hasAnnotation VolatileAttr) asm.Opcodes.ACC_VOLATILE else 0, - if (sym.isMutable) 0 else asm.Opcodes.ACC_FINAL + if (sym.is(Mutable)) 0 else asm.Opcodes.ACC_FINAL ) } } diff --git a/compiler/src/dotty/tools/backend/jvm/BackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/BackendInterface.scala deleted file mode 100644 index e04809cb510d..000000000000 --- a/compiler/src/dotty/tools/backend/jvm/BackendInterface.scala +++ /dev/null @@ -1,831 +0,0 @@ -package dotty.tools -package backend -package jvm - -import scala.collection.generic.Clearable -import scala.reflect.ClassTag -import dotty.tools.io.AbstractFile -import scala.language.implicitConversions -import scala.tools.asm - -/* Interface to abstract over frontend inside backend. - * Intended to be implemented by both scalac and dotc - */ -abstract class BackendInterface extends BackendInterfaceDefinitions { - type Flags = Long - - type Constant >: Null <: AnyRef - type Symbol >: Null <: AnyRef - type Type >: Null <: AnyRef - type Annotation >: Null <: AnyRef - type Tree >: Null <: AnyRef - type Modifiers >: Null <: AnyRef - type TypeDef >: Null <: Tree - type Apply >: Null <: Tree - type Select >: Null <: Tree - type TypeApply >: Null <: Tree - type ClassDef >: Null <: Tree - type Try >: Null <: Tree - type Assign >: Null <: Tree - type Ident >: Null <: Tree - type If >: Null <: Tree - type LabelDef >: Null <: Tree - type ValDef >: Null <: Tree - type Throw >: Null <: Tree - type Labeled >: Null <: Tree - type Return >: Null <: Tree - type WhileDo >: Null <: Tree - type Literal >: Null <: Tree - type Block >: Null <: Tree - type Typed >: Null <: Tree - type ArrayValue >: Null <: Tree - type Match >: Null <: Tree - type This >: Null <: Tree - type CaseDef >: Null <: Tree - type Alternative >: Null <: Tree - type DefDef >: Null <: Tree - type ModuleDef >: Null <: Tree - type Template >: Null <: Tree - type Name >: Null <: AnyRef - type Position - type CompilationUnit <: AnyRef - type Bind >: Null <: Tree - type New >: Null <: Tree - type ApplyDynamic >: Null <: Tree - type Super >: Null <: Tree - type Closure >: Null <: Tree - - - implicit val TypeDefTag: ClassTag[TypeDef] - implicit val ApplyTag: ClassTag[Apply] - implicit val SelectTag: ClassTag[Select] - - implicit val TypeApplyTag: ClassTag[TypeApply] - implicit val ClassDefTag: ClassTag[ClassDef] - implicit val TryTag: ClassTag[Try] - implicit val AssignTag: ClassTag[Assign] - implicit val IdentTag: ClassTag[Ident] - implicit val IfTag: ClassTag[If] - implicit val LabelDefTag: ClassTag[LabelDef] - implicit val ValDefTag: ClassTag[ValDef] - implicit val ThrowTag: ClassTag[Throw] - implicit val LabeledTag: ClassTag[Labeled] - implicit val ReturnTag: ClassTag[Return] - implicit val WhileDoTag: ClassTag[WhileDo] - implicit val LiteralTag: ClassTag[Literal] - implicit val BlockTag: ClassTag[Block] - implicit val TypedTag: ClassTag[Typed] - implicit val ArrayValueTag: ClassTag[ArrayValue] - implicit val MatchTag: ClassTag[Match] - implicit val CaseDefTag: ClassTag[CaseDef] - implicit val ThisTag: ClassTag[This] - implicit val AlternativeTag: ClassTag[Alternative] - implicit val DefDefTag: ClassTag[DefDef] - implicit val ModuleDefTag: ClassTag[ModuleDef] - implicit val NameTag: ClassTag[Name] - implicit val TemplateTag: ClassTag[Template] - implicit val BindTag: ClassTag[Bind] - implicit val NewTag: ClassTag[New] - implicit val ApplyDynamicTag: ClassTag[ApplyDynamic] - implicit val SuperTag: ClassTag[Super] - implicit val ConstantClassTag: ClassTag[Constant] - implicit val ClosureTag: ClassTag[Closure] - - type ConstantTag = Int - - val UnitTag: ConstantTag - val IntTag: ConstantTag - val FloatTag: ConstantTag - val NullTag: ConstantTag - val BooleanTag: ConstantTag - val ByteTag: ConstantTag - val ShortTag: ConstantTag - val CharTag: ConstantTag - val DoubleTag: ConstantTag - val LongTag: ConstantTag - val StringTag: ConstantTag - val ClazzTag: ConstantTag - val EnumTag: ConstantTag - - val primitives: Primitives - - - val nme_This: Name - val nme_EMPTY_PACKAGE_NAME: Name - val nme_CONSTRUCTOR: Name - val nme_WILDCARD: Name - val nme_THIS: Name - val nme_PACKAGE: Name - val nme_EQEQ_LOCAL_VAR: Name - val nme_apply: Name - - /* methods used to box&unbox primitives ?and value classes? */ - def boxMethods: Map[Symbol, Symbol] // (class, method) - def unboxMethods: Map[Symbol, Symbol] - - /* dotty specific, see dotty.runtime.Arrays */ - def isSyntheticArrayConstructor(s: Symbol) = false - - /* - * Collects all LabelDef nodes enclosed (directly or not) by each node. - * - * In other words, this prepares a map giving - * all labelDefs (the entry-value) having a Tree node (the entry-key) as ancestor. - * The entry-value for a LabelDef entry-key always contains the entry-key. - */ - def getLabelDefOwners(t: Tree): Map[Tree, List[LabelDef]] - - /* - * Implementation of those methods is very specific to how annotations are represented - * representations for Dotc and Scalac are to different to abstract over them - */ - def emitAnnotations(cw: asm.ClassVisitor, annotations: List[Annotation], bcodeStore: BCodeHelpers)(innerClasesStore: bcodeStore.BCInnerClassGen): Unit - def emitAnnotations(mw: asm.MethodVisitor, annotations: List[Annotation], bcodeStore: BCodeHelpers)(innerClasesStore: bcodeStore.BCInnerClassGen): Unit - def emitAnnotations(fw: asm.FieldVisitor, annotations: List[Annotation], bcodeStore: BCodeHelpers)(innerClasesStore: bcodeStore.BCInnerClassGen): Unit - def emitParamAnnotations(jmethod: asm.MethodVisitor, pannotss: List[List[Annotation]], bcodeStore: BCodeHelpers)(innerClasesStore: bcodeStore.BCInnerClassGen): Unit - - /* means of getting class symbols from compiler */ - def requiredClass[T: ClassTag]: Symbol - def requiredModule[T: ClassTag]: Symbol - def getRequiredClass(fullname: String): Symbol - def getClassIfDefined(fullname: String): Symbol - - def isQualifierSafeToElide(qual: Tree): Boolean - def desugarIdent(i: Ident): Option[Select] - - /* various configuration options used by backend */ - def emitAsmp: Option[String] - def dumpClasses: Option[String] - def noForwarders: Boolean - def debuglevel: Int - def settings_debug: Boolean - def targetPlatform: String - def sourceFileFor(cu: CompilationUnit): String - def informProgress(msg: String): Unit - def hasLabelDefs: Boolean // whether this compiler uses LabelDefs (i.e., scalac) - - /* backend actually uses free names to generate stuff. This should NOT mangled */ - def newTermName(prefix: String): Name - - def getGenericSignature(sym: Symbol, owner:Symbol): String - def getStaticForwarderGenericSignature(sym: Symbol, moduleClass: Symbol): String - - def isBox(sym: Symbol): Boolean - def isUnbox(sym: Symbol): Boolean - def isMaybeBoxed(sym: Symbol): Boolean - - /** Whether an annotation should be emitted as a Java annotation - * .initialize: if 'annot' is read from pickle, atp might be un-initialized - */ - def shouldEmitAnnotation(annot: Annotation): Boolean - - def isRuntimeVisible(annot: Annotation): Boolean - - def getSingleOutput: Option[AbstractFile] - - implicit def symHelper(sym: Symbol): SymbolHelper - implicit def typeHelper(tp: Type): TypeHelper - implicit def nameHelper(n: Name): NameHelper - implicit def annotHelper(a: Annotation): AnnotationHelper - implicit def treeHelper(a: Tree): TreeHelper - - implicit def constantHelper(a: Constant): ConstantHelper - implicit def positionHelper(a: Position): PositionHelper - - - val Assign: AssignDeconstructor - val Select: SelectDeconstructor - val Apply: ApplyDeconstructor - val If: IfDeconstructor - val ValDef: ValDefDeconstructor - val Throw: ThrowDeconstructor - val New: NewDeconstructor - val ApplyDynamic: ApplyDynamicDeconstructor - val This: ThisDeconstructor - val Ident: IdentDeconstructor - val Try: TryDeconstructor - val Labeled: LabeledDeconstructor - val Return: ReturnDeconstructor - val WhileDo: WhileDoDeconstructor - val LabelDef: LabelDeconstructor - val Literal: LiteralDeconstructor - val Typed: TypedDeconstrutor - val Super: SuperDeconstructor - val ArrayValue: ArrayValueDeconstructor - val Match: MatchDeconstructor - val Block: BlockDeconstructor - val TypeApply: TypeApplyDeconstructor - val CaseDef: CaseDeconstructor - val Alternative: AlternativeDeconstructor - val Constant: ConstantDeconstructor - val ThrownException: ThrownException - val DefDef: DefDefDeconstructor - val ModuleDef: ModuleDefDeconstructor - val Template: TemplateDeconstructor - val Bind: BindDeconstructor - val ClassDef: ClassDefDeconstructor - val Closure: ClosureDeconstructor - - abstract class DeconstructorCommon[T >: Null <: AnyRef] { - var field: T = null - def get: this.type = this - def isEmpty: Boolean = field eq null - def isDefined = !isEmpty - def unapply(s: T): this.type ={ - field = s - this - } - } - - abstract class Deconstructor1Common[T >: Null <: AnyRef, R]{ - var field: T = _ - def get: R - def isEmpty: Boolean = field eq null - def isDefined = !isEmpty - def unapply(s: T): this.type ={ - field = s - this - } - } - - abstract class ClassDefDeconstructor extends DeconstructorCommon[ClassDef] { - def _1: Modifiers - def _2: Name - def _3: List[TypeDef] - def _4: Template - } - - abstract class BindDeconstructor extends DeconstructorCommon[Bind]{ - def _1: Name - def _2: Tree - } - - abstract class TemplateDeconstructor extends DeconstructorCommon[Template]{ - def _1: List[Tree] - def _2: ValDef - def _3: List[Tree] - } - - abstract class ModuleDefDeconstructor extends DeconstructorCommon[ModuleDef]{ - def _1: Modifiers - def _2: Name - def _3: Tree - } - - abstract class DefDefDeconstructor extends DeconstructorCommon[DefDef]{ - def _1: Modifiers - def _2: Name - def _3: List[TypeDef] - def _4: List[List[ValDef]] - def _5: Tree - def _6: Tree - } - - abstract class ClosureDeconstructor extends DeconstructorCommon[Closure]{ - def _1: List[Tree] // environment - def _2: Tree // meth - def _3: Symbol // functionalInterface - } - - abstract class ThisDeconstructor extends Deconstructor1Common[This, Name]{ - def apply(s: Symbol): Tree - } - - abstract class IdentDeconstructor extends Deconstructor1Common[Ident, Name]{ - } - - abstract class LabeledDeconstructor extends DeconstructorCommon[Labeled]{ - def _1: Bind // bind - def _2: Tree // expr - } - - abstract class ReturnDeconstructor extends DeconstructorCommon[Return]{ - def _1: Tree // expr - def _2: Symbol // target label, NoSymbol if return to method - } - - abstract class WhileDoDeconstructor extends DeconstructorCommon[WhileDo]{ - def _1: Tree // cond - def _2: Tree // body - } - - abstract class ThrownException { - def unapply(a: Annotation): Option[Symbol] - } - - abstract class ThrowDeconstructor extends Deconstructor1Common[Throw, Tree]{ - } - - abstract class ConstantDeconstructor extends Deconstructor1Common[Constant, Any]{ - } - - abstract class NewDeconstructor extends Deconstructor1Common[New, Type]{ - } - - abstract class AlternativeDeconstructor extends Deconstructor1Common[Alternative, List[Tree]]{ - } - - abstract class BlockDeconstructor extends DeconstructorCommon[Block]{ - def _1: List[Tree] - def _2: Tree - } - - abstract class CaseDeconstructor extends DeconstructorCommon[CaseDef]{ - def _1: Tree - def _2: Tree - def _3: Tree - } - - abstract class MatchDeconstructor extends DeconstructorCommon[Match]{ - def _1: Tree - def _2: List[Tree] - } - - abstract class LiteralDeconstructor extends Deconstructor1Common[Literal, Constant]{ - } - - abstract class AssignDeconstructor extends DeconstructorCommon[Assign]{ - def _1: Tree - def _2: Tree - } - - abstract class SelectDeconstructor extends DeconstructorCommon[Select]{ - def _1: Tree - def _2: Name - } - - abstract class ApplyDeconstructor extends DeconstructorCommon[Apply] { - def _1: Tree - def _2: List[Tree] - } - - abstract class IfDeconstructor extends DeconstructorCommon[If]{ - def _1: Tree - def _2: Tree - def _3: Tree - } - - abstract class ValDefDeconstructor extends DeconstructorCommon[ValDef]{ - def _1: Modifiers - def _2: Name - def _3: Tree - def _4: Tree - } - - - abstract class ApplyDynamicDeconstructor extends DeconstructorCommon[ApplyDynamic]{ - def _1: Tree - def _2: List[Tree] - } - - - abstract class TryDeconstructor extends DeconstructorCommon[Try]{ - def _1: Tree - def _2: List[Tree] - def _3: Tree - } - - abstract class LabelDeconstructor extends DeconstructorCommon[LabelDef]{ - def _1: Name - def _2: List[Symbol] - def _3: Tree - } - - abstract class TypedDeconstrutor extends DeconstructorCommon[Typed]{ - def _1: Tree - def _2: Tree - } - - abstract class SuperDeconstructor extends DeconstructorCommon[Super]{ - def _1: Tree - def _2: Name - } - - abstract class ArrayValueDeconstructor extends DeconstructorCommon[ArrayValue]{ - def _1: Type - def _2: List[Tree] - } - - abstract class TypeApplyDeconstructor extends DeconstructorCommon[TypeApply]{ - def _1: Tree - def _2: List[Tree] - } - - abstract class PositionHelper { - def isDefined: Boolean - def finalPosition: Position - def line: Int - } - - abstract class ConstantHelper { - def tag: ConstantTag - def longValue: Long - def doubleValue: Double - def charValue: Char - def stringValue: String - def byteValue: Byte - def booleanValue: Boolean - def shortValue: Short - def intValue: Int - def value: Any - def floatValue: Float - def typeValue: Type - def symbolValue: Symbol - } - - abstract class TreeHelper{ - def symbol: Symbol - def tpe: Type - def isEmpty: Boolean - def pos: Position - def exists(pred: Tree => Boolean): Boolean - } - - abstract class SymbolHelper { - def exists: Boolean - - // names - def showFullName: String - def javaSimpleName: String - def javaBinaryName: String - def javaClassName: String - def name: Name - def rawname: String - - // types - def info: Type - def tpe: Type // todo whats the differentce between tpe and info? - def thisType: Type - - /** Does this symbol actually correspond to an interface that will be emitted? - * In the backend, this should be preferred over `isInterface` because it - * also returns true for the symbols of the fake companion objects we - * create for Java-defined classes as well as for Java annotations - * which we represent as classes. - */ - final def isEmittedInterface: Boolean = isInterface || - isJavaDefined && (isAnnotation || isModuleClass && companionClass.isInterface) - - // tests - def isClass: Boolean - def isType: Boolean - def isAnonymousClass: Boolean - def isConstructor: Boolean - def isExpanded: Boolean - def isAnonymousFunction: Boolean - def isMethod: Boolean - def isPublic: Boolean - def isSynthetic: Boolean - def isPackageClass: Boolean - def isModuleClass: Boolean - def isModule: Boolean - def isStrictFP: Boolean - def isLabel: Boolean - def hasPackageFlag: Boolean - def isInterface: Boolean - def isGetter: Boolean - def isSetter: Boolean - def isGetClass: Boolean - def isJavaDefined: Boolean - def isDeferred: Boolean - def isPrivate: Boolean - def getsJavaPrivateFlag: Boolean - def isFinal: Boolean - def getsJavaFinalFlag: Boolean - def isScalaStatic: Boolean - def isStaticMember: Boolean - def isBottomClass: Boolean - def isBridge: Boolean - def isArtifact: Boolean - def hasEnumFlag: Boolean - def hasAccessBoundary: Boolean - def isVarargsMethod: Boolean - def isDeprecated: Boolean - def isMutable: Boolean - def hasAbstractFlag: Boolean - def hasModuleFlag: Boolean - def isSynchronized: Boolean - def isNonBottomSubClass(sym: Symbol): Boolean - def hasAnnotation(sym: Symbol): Boolean - def shouldEmitForwarders: Boolean - def isJavaDefaultMethod: Boolean - def isClassConstructor: Boolean - def isAnnotation: Boolean - def isSerializable: Boolean - def isEnum: Boolean - - /** - * True for module classes of modules that are top-level or owned only by objects. Module classes - * for such objects will get a MODULE$ flag and a corresponding static initializer. - */ - def isStaticModuleClass: Boolean - - def isStaticConstructor: Boolean - - - // navigation - def owner: Symbol - def rawowner: Symbol // todo ??? - def originalOwner: Symbol - def parentSymbols: List[Symbol] - def superClass: Symbol - def enclClass: Symbol - def linkedClassOfClass: Symbol - def linkedClass: Symbol - def companionClass: Symbol - def companionModule: Symbol - def companionSymbol: Symbol - def moduleClass: Symbol - def enclosingClassSym: Symbol - def originalLexicallyEnclosingClass: Symbol - def nextOverriddenSymbol: Symbol - def allOverriddenSymbols: List[Symbol] - - - // members - def primaryConstructor: Symbol - def nestedClasses: List[Symbol] - def memberClasses: List[Symbol] - def annotations: List[Annotation] - def companionModuleMembers: List[Symbol] - def fieldSymbols: List[Symbol] - def methodSymbols: List[Symbol] - def serialVUID: Option[Long] - - - def freshLocal(cunit: CompilationUnit, name: String, tpe: Type, pos: Position, flags: Flags): Symbol - - def getter(clz: Symbol): Symbol - def setter(clz: Symbol): Symbol - - def moduleSuffix: String - def outputDirectory: AbstractFile - def pos: Position - - def throwsAnnotations: List[Symbol] - - /** - * All interfaces implemented by a class, except for those inherited through the superclass. - * - */ - def superInterfaces: List[Symbol] - - /** - * True for module classes of package level objects. The backend will generate a mirror class for - * such objects. - */ - def isTopLevelModuleClass: Boolean - - /** - * This is basically a re-implementation of sym.isStaticOwner, but using the originalOwner chain. - * - * The problem is that we are interested in a source-level property. Various phases changed the - * symbol's properties in the meantime, mostly lambdalift modified (destructively) the owner. - * Therefore, `sym.isStatic` is not what we want. For example, in - * object T { def f { object U } } - * the owner of U is T, so UModuleClass.isStatic is true. Phase travel does not help here. - */ - def isOriginallyStaticOwner: Boolean = - isPackageClass || isModuleClass && originalOwner.originalLexicallyEnclosingClass.isOriginallyStaticOwner - - def samMethod(): Symbol - - /** Is this the symbol of one of the `scala.Function*` class ? - * Note that this will return false for subclasses of these classes. - */ - def isFunctionClass: Boolean - } - - abstract class TypeHelper { - def <:<(other: Type): Boolean - def =:=(other: Type): Boolean - def paramTypes: List[Type] - def params: List[Symbol] - def resultType: Type - def memberInfo(s: Symbol): Type - - /** The members of this type that have all of `required` flags but none of `excluded` flags set. - * The members are sorted by name and signature to guarantee a stable ordering. - */ - def sortedMembersBasedOnFlags(required: Flags, excluded: Flags): List[Symbol] - def members: List[Symbol] - def decl(name: Name): Symbol - def decls: List[Symbol] - def underlying: Type - def parents: List[Type] - def summaryString: String - def typeSymbol: Symbol - def member(string: Name): Symbol - /** - * This method returns the BType for a type reference, for example a parameter type. - * - * If the result is a ClassBType for a nested class, it is added to the innerClassBufferASM. - * - * If `t` references a class, toTypeKind ensures that the class is not an implementation class. - * See also comment on getClassBTypeAndRegisterInnerClass, which is invoked for implementation - * classes. - */ - def toTypeKind(ctx: BCodeHelpers)(storage: ctx.BCInnerClassGen): ctx.bTypes.BType - - def isFinalType: Boolean - } - - abstract class Primitives { - def getPrimitive(app: Apply, receiver: Type): Int - def isPrimitive(fun: Tree): Boolean - def getPrimitive(sym: Symbol): Int - } - - abstract class NameHelper { - def isTypeName: Boolean - def isTermName: Boolean - def startsWith(s: String): Boolean - def mangledString: String - } - - abstract class AnnotationHelper{ - def atp: Type - def symbol: Symbol - def args: List[Tree] - def assocs: List[(Name, /* ClassfileAnnotArg*/ Object)] - } - - def debuglog(msg: => String): Unit - def log(msg: => String): Unit - def error(pos: Position, msg: String): Unit // reporter.error - def warning(pos: Position, msg: String): Unit // reporter.warning - def abort(msg: String): Nothing - - val ExcludedForwarderFlags: Flags - val Flag_METHOD: Flags - val Flag_SYNTHETIC: Flags - - abstract class Caches { - def recordCache[T <: Clearable](cache: T): T - def newWeakMap[K, V](): collection.mutable.WeakHashMap[K, V] - def newMap[K, V](): collection.mutable.HashMap[K, V] - def newSet[K](): collection.mutable.Set[K] - def newWeakSet[K >: Null <: AnyRef](): dotty.tools.dotc.util.WeakHashSet[K] - def newAnyRefMap[K <: AnyRef, V](): collection.mutable.AnyRefMap[K, V] - } - - def perRunCaches: Caches - - def MODULE_INSTANCE_FIELD: String - - def dropModule(str: String): String - -/* Returns a ScalaSignature annotation if it must be added to this class, none otherwise. - * This annotation must be added to the class' annotations list when generating them. - * - * Depending on whether the returned option is defined, it adds to `jclass` one of: - * (a) the ScalaSig marker attribute - * (indicating that a scala-signature-annotation aka pickle is present in this class); or - * (b) the Scala marker attribute - * (indicating that a scala-signature-annotation aka pickle is to be found in another file). - * - * - * @param jclassName The class file that is being readied. - * @param sym The symbol for which the signature has been entered in the symData map. - * This is different than the symbol - * that is being generated in the case of a mirror class. - * @return An option that is: - * - defined and contains an AnnotationInfo of the ScalaSignature type, - * instantiated with the pickle signature for sym. - * - empty if the jclass/sym pair must not contain a pickle. - * - * must-single-thread - */ - def getAnnotPickle(jclassName: String, sym: Symbol): Option[Annotation] -} - -abstract class BackendInterfaceDefinitions { self: BackendInterface => - val nme_valueOf: Name - - /* magic instances */ - val NoPosition: Position - val NoSymbol: Symbol - val EmptyTree: Tree - val NothingClass: Symbol - val NullClass: Symbol - val ObjectClass: Symbol - val Object_isInstanceOf: Symbol - val Object_asInstanceOf: Symbol - val Object_synchronized: Symbol - val Object_equals: Symbol - val ArrayClass: Symbol - - val UnitClass: Symbol - val BooleanClass: Symbol - val CharClass: Symbol - val ShortClass: Symbol - val ClassClass: Symbol - val ByteClass: Symbol - val IntClass: Symbol - val LongClass: Symbol - val FloatClass: Symbol - val DoubleClass: Symbol - - // Class symbols used in backend. - // Vals because they are to frequent in scala programs so that they are already loaded by backend - - lazy val NativeAttr: Symbol = requiredClass[scala.native] - lazy val TransientAttr = requiredClass[scala.transient] - lazy val VolatileAttr = requiredClass[scala.volatile] - lazy val LambdaMetaFactory = getClassIfDefined("java.lang.invoke.LambdaMetafactory") - lazy val MethodHandle = getClassIfDefined("java.lang.invoke.MethodHandle") - - val ScalaATTRName: String = "Scala" - val ScalaSignatureATTRName: String = "ScalaSig" - - def doLabmdasFollowJVMMetafactoryOrder: Boolean = true - - val BoxedBooleanClass: Symbol = requiredClass[java.lang.Boolean] - val BoxedByteClass: Symbol = requiredClass[java.lang.Byte] - val BoxedShortClass: Symbol = requiredClass[java.lang.Short] - val BoxedCharacterClass: Symbol = requiredClass[java.lang.Character] - val BoxedIntClass: Symbol = requiredClass[java.lang.Integer] - val BoxedLongClass: Symbol = requiredClass[java.lang.Long] - val BoxedFloatClass: Symbol = requiredClass[java.lang.Float] - val BoxedDoubleClass: Symbol = requiredClass[java.lang.Double] - val StringClass: Symbol = requiredClass[java.lang.String] - val JavaStringBuilderClass: Symbol = requiredClass[java.lang.StringBuilder] - val JavaStringBufferClass: Symbol = requiredClass[java.lang.StringBuffer] - val JavaCharSequenceClass: Symbol = requiredClass[java.lang.CharSequence] - val ThrowableClass: Symbol = requiredClass[java.lang.Throwable] - val JavaCloneableClass: Symbol = requiredClass[java.lang.Cloneable] - val NullPointerExceptionClass: Symbol = requiredClass[java.lang.NullPointerException] - val JavaSerializableClass: Symbol = requiredClass[java.io.Serializable] - val SerializableClass: Symbol = requiredClass[scala.Serializable] - val ClassCastExceptionClass: Symbol = requiredClass[java.lang.ClassCastException] - val IllegalArgExceptionClass: Symbol = requiredClass[java.lang.IllegalArgumentException] - val SerializedLambdaClass: Symbol = requiredClass[java.lang.invoke.SerializedLambda] - - val ClassfileAnnotationClass: Symbol = requiredClass[scala.annotation.ClassfileAnnotation] - val BoxedNumberClass: Symbol = requiredClass[java.lang.Number] - val ThrowsClass: Symbol = requiredClass[scala.throws[_]] - - // Module symbols used in backend - val StringModule: Symbol = StringClass.linkedClassOfClass - val ScalaRunTimeModule: Symbol = requiredModule[scala.runtime.ScalaRunTime.type] - - - // types used in backend - - val Object_Type: Type - val Throwable_Type: Type - // methods used in backend - - def isArrayClone(fun: Tree): Boolean - val hashMethodSym: Symbol - val externalEqualsNumNum: Symbol - val externalEqualsNumChar: Symbol - val externalEqualsNumObject: Symbol - val externalEquals: Symbol - - val MaxFunctionArity: Int - val FunctionClass: Array[Symbol] - val AbstractFunctionClass: Array[Symbol] - val PartialFunctionClass: Symbol - val AbstractPartialFunctionClass: Symbol - - /* The Object => String overload. */ - val String_valueOf: Symbol - - def isNull(t: Tree): Boolean = t match { - case Literal(Constant(null)) => true - case _ => false - } - def isLiteral(t: Tree): Boolean = t match { - case Literal(_) => true - case _ => false - } - def isNonNullExpr(t: Tree): Boolean = isLiteral(t) || ((t.symbol ne null) && t.symbol.isModule) - def ifOneIsNull(l: Tree, r: Tree): Tree = if (isNull(l)) r else if (isNull(r)) l else null - - private val primitiveCompilationUnits = Set( - "Unit.scala", - "Boolean.scala", - "Char.scala", - "Byte.scala", - "Short.scala", - "Int.scala", - "Float.scala", - "Long.scala", - "Double.scala" - ) - - def currentUnit: CompilationUnit - - /** - * True if the current compilation unit is of a primitive class (scala.Boolean et al). - * Used only in assertions. - */ - def isCompilingPrimitive = { - primitiveCompilationUnits(sourceFileFor(currentUnit)) - } - - def isCompilingArray = { - sourceFileFor(currentUnit) == "Array.scala" - } -} diff --git a/compiler/src/dotty/tools/backend/jvm/BytecodeWriters.scala b/compiler/src/dotty/tools/backend/jvm/BytecodeWriters.scala index 43c9e252cb57..513e2d7f127e 100644 --- a/compiler/src/dotty/tools/backend/jvm/BytecodeWriters.scala +++ b/compiler/src/dotty/tools/backend/jvm/BytecodeWriters.scala @@ -15,7 +15,7 @@ class FileConflictException(msg: String, val file: AbstractFile) extends IOExcep * files, jars, and disassembled/javap output. */ trait BytecodeWriters { - val int: BackendInterface + val int: DottyBackendInterface import int._ /** @@ -31,11 +31,11 @@ trait BytecodeWriters { ensureDirectory(dir) fileNamed pathParts.last + suffix } def getFile(sym: Symbol, clsName: String, suffix: String): AbstractFile = - getFile(sym.outputDirectory, clsName, suffix) + getFile(outputDirectory, clsName, suffix) def factoryNonJarBytecodeWriter(): BytecodeWriter = { - val emitAsmp = int.emitAsmp - val doDump = int.dumpClasses + val emitAsmp = None + val doDump = dumpClasses (emitAsmp.isDefined, doDump.isDefined) match { case (false, false) => new ClassBytecodeWriter { } case (false, true ) => new ClassBytecodeWriter with DumpBytecodeWriter { } @@ -61,7 +61,7 @@ trait BytecodeWriters { try out.write(jclassBytes, 0, jclassBytes.length) finally out.flush() - informProgress("added " + label + path + " to jar") + ctx.informProgress("added " + label + path + " to jar") } override def close() = writer.close() } @@ -77,7 +77,7 @@ trait BytecodeWriters { trait AsmpBytecodeWriter extends BytecodeWriter { import scala.tools.asm - private val baseDir = Directory(int.emitAsmp.get).createDirectory() + private val baseDir = Directory(None.get).createDirectory() // FIXME missing directoy private def emitAsmp(jclassBytes: Array[Byte], asmpFile: dotty.tools.io.File): Unit = { val pw = asmpFile.printWriter() @@ -111,12 +111,12 @@ trait BytecodeWriters { try outstream.write(jclassBytes, 0, jclassBytes.length) finally outstream.close() - informProgress("wrote '" + label + "' to " + outfile) + ctx.informProgress("wrote '" + label + "' to " + outfile) } } trait DumpBytecodeWriter extends BytecodeWriter { - val baseDir = Directory(int.dumpClasses.get).createDirectory() + val baseDir = Directory(dumpClasses.get).createDirectory() abstract override def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], outfile: AbstractFile): Unit = { super.writeClass(label, jclassName, jclassBytes, outfile) @@ -130,4 +130,8 @@ trait BytecodeWriters { finally outstream.close() } } + + private def dumpClasses: Option[String] = + if (ctx.settings.Ydumpclasses.isDefault) None + else Some(ctx.settings.Ydumpclasses.value) } diff --git a/compiler/src/dotty/tools/backend/jvm/CoreBTypes.scala b/compiler/src/dotty/tools/backend/jvm/CoreBTypes.scala index cd04f6f7d1c8..2d4aec72d0b1 100644 --- a/compiler/src/dotty/tools/backend/jvm/CoreBTypes.scala +++ b/compiler/src/dotty/tools/backend/jvm/CoreBTypes.scala @@ -4,6 +4,10 @@ package jvm import scala.annotation.switch +import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.transform.Erasure + /** * Core BTypes and some other definitions. The initialization of these definitions requies access * to symbols / types (global). @@ -27,9 +31,10 @@ import scala.annotation.switch * added when the ClassBTypes are created. The per run cache removes them, so they would be missing * in the second run. */ -class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: BackendInterface]](val bTypes: BTFS) { +class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: DottyBackendInterface]](val bTypes: BTFS) { import bTypes._ import int._ + import DottyBackendInterface._ //import global._ //import rootMirror.{requiredClass, getClassIfDefined} @@ -40,26 +45,26 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: BackendInterface]](val bTypes: B * the first use of `classBTypeFromSymbol` because that method looks at the map. */ lazy val primitiveTypeMap: Map[Symbol, PrimitiveBType] = Map( - UnitClass -> UNIT, - BooleanClass -> BOOL, - CharClass -> CHAR, - ByteClass -> BYTE, - ShortClass -> SHORT, - IntClass -> INT, - LongClass -> LONG, - FloatClass -> FLOAT, - DoubleClass -> DOUBLE + defn.UnitClass -> UNIT, + defn.BooleanClass -> BOOL, + defn.CharClass -> CHAR, + defn.ByteClass -> BYTE, + defn.ShortClass -> SHORT, + defn.IntClass -> INT, + defn.LongClass -> LONG, + defn.FloatClass -> FLOAT, + defn.DoubleClass -> DOUBLE ) lazy val BOXED_UNIT : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Void]) - lazy val BOXED_BOOLEAN : ClassBType = classBTypeFromSymbol(BoxedBooleanClass) - lazy val BOXED_BYTE : ClassBType = classBTypeFromSymbol(BoxedByteClass) - lazy val BOXED_SHORT : ClassBType = classBTypeFromSymbol(BoxedShortClass) - lazy val BOXED_CHAR : ClassBType = classBTypeFromSymbol(BoxedCharacterClass) - lazy val BOXED_INT : ClassBType = classBTypeFromSymbol(BoxedIntClass) - lazy val BOXED_LONG : ClassBType = classBTypeFromSymbol(BoxedLongClass) - lazy val BOXED_FLOAT : ClassBType = classBTypeFromSymbol(BoxedFloatClass) - lazy val BOXED_DOUBLE : ClassBType = classBTypeFromSymbol(BoxedDoubleClass) + 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]) /** * Map from primitive types to their boxed class type. Useful when pushing class literals onto the @@ -84,6 +89,9 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: BackendInterface]](val bTypes: B * method symbol for `Byte.box()` is mapped to the ClassBType `java/lang/Byte`. */ lazy val boxResultType: Map[Symbol, ClassBType] = { + val boxMethods = defn.ScalaValueClasses().map{x => // @darkdimius Are you sure this should be a def? + (x, Erasure.Boxing.boxMethod(x.asClass)) + }.toMap for ((valueClassSym, boxMethodSym) <- boxMethods) yield boxMethodSym -> boxedClassOfPrimitive(primitiveTypeMap(valueClassSym)) } @@ -92,6 +100,8 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: BackendInterface]](val bTypes: B * Maps the method symbol for an unbox method to the primitive type of the result. * For example, the method symbol for `Byte.unbox()`) is mapped to the PrimitiveBType BYTE. */ lazy val unboxResultType: Map[Symbol, PrimitiveBType] = { + val unboxMethods: Map[Symbol, Symbol] = + defn.ScalaValueClasses().map(x => (x, Erasure.Boxing.unboxMethod(x.asClass))).toMap for ((valueClassSym, unboxMethodSym) <- unboxMethods) yield unboxMethodSym -> primitiveTypeMap(valueClassSym) } @@ -104,24 +114,24 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: BackendInterface]](val bTypes: B * 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(getRequiredClass("scala.runtime.Nothing$")) // (requiredClass[scala.runtime.Nothing$]) - lazy val RT_NULL : ClassBType = classBTypeFromSymbol(getRequiredClass("scala.runtime.Null$")) // (requiredClass[scala.runtime.Null$]) + lazy val RT_NOTHING : ClassBType = classBTypeFromSymbol(ctx.requiredClass("scala.runtime.Nothing$")) // (requiredClass[scala.runtime.Nothing$]) + lazy val RT_NULL : ClassBType = classBTypeFromSymbol(ctx.requiredClass("scala.runtime.Null$")) // (requiredClass[scala.runtime.Null$]) - lazy val ObjectReference : ClassBType = classBTypeFromSymbol(ObjectClass) + lazy val ObjectReference : ClassBType = classBTypeFromSymbol(defn.ObjectClass) lazy val objArrayReference : ArrayBType = ArrayBType(ObjectReference) - lazy val StringRef : ClassBType = classBTypeFromSymbol(StringClass) - lazy val jlStringBuilderRef : ClassBType = classBTypeFromSymbol(JavaStringBuilderClass) - lazy val jlStringBufferRef : ClassBType = classBTypeFromSymbol(JavaStringBufferClass) - lazy val jlCharSequenceRef : ClassBType = classBTypeFromSymbol(JavaCharSequenceClass) - lazy val ThrowableReference : ClassBType = classBTypeFromSymbol(ThrowableClass) - lazy val jlCloneableReference : ClassBType = classBTypeFromSymbol(JavaCloneableClass) // java/lang/Cloneable - lazy val jlNPEReference : ClassBType = classBTypeFromSymbol(NullPointerExceptionClass) // java/lang/NullPointerException - lazy val jioSerializableReference : ClassBType = classBTypeFromSymbol(JavaSerializableClass) // java/io/Serializable - lazy val scalaSerializableReference : ClassBType = classBTypeFromSymbol(SerializableClass) // scala/Serializable - lazy val classCastExceptionReference : ClassBType = classBTypeFromSymbol(ClassCastExceptionClass) // java/lang/ClassCastException - lazy val jlIllegalArgExceptionRef: ClassBType = classBTypeFromSymbol(IllegalArgExceptionClass) - lazy val jliSerializedLambdaRef: ClassBType = classBTypeFromSymbol(SerializedLambdaClass) + 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 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]) @@ -199,7 +209,7 @@ trait CoreBTypesProxyGlobalIndependent[BTS <: BTypes] { /** * See comment in class [[CoreBTypes]]. */ -final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: BackendInterface]](val bTypes: BTFS) extends CoreBTypesProxyGlobalIndependent[BTFS] { +final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: DottyBackendInterface]](val bTypes: BTFS) extends CoreBTypesProxyGlobalIndependent[BTFS] { import bTypes._ import bTypes.int._ diff --git a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index 895daa2a8980..467d25d0ab83 100644 --- a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -3,7 +3,7 @@ package dotty.tools.backend.jvm import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.ast.Trees import dotty.tools.dotc -import dotty.tools.dotc.core.Flags.{termFlagSet} +import dotty.tools.dotc.core.Flags._ import dotty.tools.dotc.transform.{Erasure, GenericSignatures} import dotty.tools.dotc.transform.SymUtils._ import java.io.{File => _} @@ -24,416 +24,21 @@ import Phases._ import dotty.tools.dotc.util import dotty.tools.dotc.util.Spans import Decorators._ +import Constants._ import tpd._ import scala.tools.asm import StdNames.{nme, str} import NameKinds.{DefaultGetterName, ExpandedName} import Names.TermName +import Annotations.Annotation +import Names.Name -class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Map[Symbol, Set[ClassSymbol]])(implicit ctx: Context) extends BackendInterface{ - import Symbols.{toDenot, toClassDenot} - // Dotty deviation: Need to (re-)import implicit decorators here because otherwise - // they would be shadowed by the more deeply nested `symHelper` decorator. - - type Symbol = Symbols.Symbol - type Type = Types.Type - type Tree = tpd.Tree - type CompilationUnit = dotc.CompilationUnit - type Constant = Constants.Constant - type Literal = tpd.Literal - type Position = Spans.Span - type Name = Names.Name - type ClassDef = tpd.TypeDef - type TypeDef = tpd.TypeDef - type Apply = tpd.Apply - type TypeApply = tpd.TypeApply - type Try = tpd.Try - type Assign = tpd.Assign - type Ident = tpd.Ident - type If = tpd.If - type ValDef = tpd.ValDef - type Throw = tpd.Apply - type Labeled = tpd.Labeled - type Return = tpd.Return - type WhileDo = tpd.WhileDo - type Block = tpd.Block - type Typed = tpd.Typed - type Match = tpd.Match - type This = tpd.This - type CaseDef = tpd.CaseDef - type Alternative = tpd.Alternative - type DefDef = tpd.DefDef - type Template = tpd.Template - type Select = tpd.Tree // Actually tpd.Select || tpd.Ident - type Bind = tpd.Bind - type New = tpd.New - type Super = tpd.Super - type Modifiers = Null - type Annotation = Annotations.Annotation - type ArrayValue = tpd.JavaSeqLiteral - type ApplyDynamic = Null - type ModuleDef = Null - type LabelDef = Null - type Closure = tpd.Closure - - val NoSymbol: Symbol = Symbols.NoSymbol - val NoPosition: Position = Spans.NoSpan - val EmptyTree: Tree = tpd.EmptyTree - - - val UnitTag: ConstantTag = Constants.UnitTag - val IntTag: ConstantTag = Constants.IntTag - val FloatTag: ConstantTag = Constants.FloatTag - val NullTag: ConstantTag = Constants.NullTag - val BooleanTag: ConstantTag = Constants.BooleanTag - val ByteTag: ConstantTag = Constants.ByteTag - val ShortTag: ConstantTag = Constants.ShortTag - val CharTag: ConstantTag = Constants.CharTag - val DoubleTag: ConstantTag = Constants.DoubleTag - val LongTag: ConstantTag = Constants.LongTag - val StringTag: ConstantTag = Constants.StringTag - val ClazzTag: ConstantTag = Constants.ClazzTag - val EnumTag: ConstantTag = Constants.EnumTag - - val nme_This: Name = StdNames.nme.This - val nme_EMPTY_PACKAGE_NAME: Name = StdNames.nme.EMPTY_PACKAGE - val nme_CONSTRUCTOR: Name = StdNames.nme.CONSTRUCTOR - val nme_WILDCARD: Name = StdNames.nme.WILDCARD - val nme_THIS: Name = StdNames.nme.THIS - val nme_PACKAGE: Name = StdNames.nme.PACKAGE - val nme_EQEQ_LOCAL_VAR: Name = StdNames.nme.EQEQ_LOCAL_VAR - - // require LambdaMetafactory: scalac uses getClassIfDefined, but we need those always. - @threadUnsafe override lazy val LambdaMetaFactory: ClassSymbol = ctx.requiredClass("java.lang.invoke.LambdaMetafactory") - @threadUnsafe override lazy val MethodHandle: ClassSymbol = ctx.requiredClass("java.lang.invoke.MethodHandle") - - val nme_valueOf: Name = StdNames.nme.valueOf - val nme_apply: TermName = StdNames.nme.apply - val NothingClass: Symbol = defn.NothingClass - val NullClass: Symbol = defn.NullClass - val ObjectClass: Symbol = defn.ObjectClass - val Object_Type: Type = defn.ObjectType - val Throwable_Type: Type = defn.ThrowableType - val Object_isInstanceOf: Symbol = defn.Any_isInstanceOf - val Object_asInstanceOf: Symbol = defn.Any_asInstanceOf - val Object_synchronized: Symbol = defn.Object_synchronized - val Object_equals: Symbol = defn.Any_equals - val ArrayClass: Symbol = defn.ArrayClass - val UnitClass: Symbol = defn.UnitClass - val BooleanClass: Symbol = defn.BooleanClass - val CharClass: Symbol = defn.CharClass - val ShortClass: Symbol = defn.ShortClass - val ClassClass: Symbol = defn.ClassClass - val ByteClass: Symbol = defn.ByteClass - val IntClass: Symbol = defn.IntClass - val LongClass: Symbol = defn.LongClass - val FloatClass: Symbol = defn.FloatClass - val DoubleClass: Symbol = defn.DoubleClass - def isArrayClone(tree: Tree): Boolean = tree match { - case Select(qual, StdNames.nme.clone_) if qual.tpe.widen.isInstanceOf[JavaArrayType] => true - case _ => false - } - - val hashMethodSym: Symbol = NoSymbol // used to dispatch ## on primitives to ScalaRuntime.hash. Should be implemented by a miniphase - val externalEqualsNumNum: Symbol = defn.BoxesRunTimeModule.requiredMethod(nme.equalsNumNum) - val externalEqualsNumChar: Symbol = NoSymbol // ctx.requiredMethod(BoxesRunTimeTypeRef, nme.equalsNumChar) // this method is private - val externalEqualsNumObject: Symbol = defn.BoxesRunTimeModule.requiredMethod(nme.equalsNumObject) - val externalEquals: Symbol = defn.BoxesRunTimeModule.info.decl(nme.equals_).suchThat(toDenot(_).info.firstParamTypes.size == 2).symbol - val MaxFunctionArity: Int = Definitions.MaxImplementedFunctionArity - val FunctionClass: Array[Symbol] = defn.FunctionClassPerRun() - val AbstractFunctionClass: Array[Symbol] = defn.AbstractFunctionClassPerRun() - val PartialFunctionClass: Symbol = defn.PartialFunctionClass - val AbstractPartialFunctionClass: Symbol = defn.AbstractPartialFunctionClass - val String_valueOf: Symbol = defn.String_valueOf_Object - @threadUnsafe lazy val Predef_classOf: Symbol = defn.ScalaPredefModule.requiredMethod(nme.classOf) - - @threadUnsafe lazy val AnnotationRetentionAttr: ClassSymbol = ctx.requiredClass("java.lang.annotation.Retention") - @threadUnsafe lazy val AnnotationRetentionSourceAttr: TermSymbol = ctx.requiredClass("java.lang.annotation.RetentionPolicy").linkedClass.requiredValue("SOURCE") - @threadUnsafe lazy val AnnotationRetentionClassAttr: TermSymbol = ctx.requiredClass("java.lang.annotation.RetentionPolicy").linkedClass.requiredValue("CLASS") - @threadUnsafe lazy val AnnotationRetentionRuntimeAttr: TermSymbol = ctx.requiredClass("java.lang.annotation.RetentionPolicy").linkedClass.requiredValue("RUNTIME") - @threadUnsafe lazy val JavaAnnotationClass: ClassSymbol = ctx.requiredClass("java.lang.annotation.Annotation") - - def boxMethods: Map[Symbol, Symbol] = defn.ScalaValueClasses().map{x => // @darkdimius Are you sure this should be a def? - (x, Erasure.Boxing.boxMethod(x.asClass)) - }.toMap - def unboxMethods: Map[Symbol, Symbol] = - defn.ScalaValueClasses().map(x => (x, Erasure.Boxing.unboxMethod(x.asClass))).toMap - - override def isSyntheticArrayConstructor(s: Symbol): Boolean = { - s eq defn.newArrayMethod - } - - def isBox(sym: Symbol): Boolean = - Erasure.Boxing.isBox(sym) && sym.denot.owner != defn.UnitModuleClass - def isUnbox(sym: Symbol): Boolean = - Erasure.Boxing.isUnbox(sym) && sym.denot.owner != defn.UnitModuleClass - - val primitives: Primitives = new Primitives { - val primitives = new DottyPrimitives(ctx) - def getPrimitive(app: Apply, receiver: Type): Int = primitives.getPrimitive(app, receiver) - - def getPrimitive(sym: Symbol): Int = primitives.getPrimitive(sym) - - def isPrimitive(fun: Tree): Boolean = primitives.isPrimitive(fun) - } - implicit val TypeDefTag: ClassTag[TypeDef] = ClassTag[TypeDef](classOf[TypeDef]) - implicit val ApplyTag: ClassTag[Apply] = ClassTag[Apply](classOf[Apply]) - implicit val SelectTag: ClassTag[Select] = ClassTag[Select](classOf[Select]) - implicit val TypeApplyTag: ClassTag[TypeApply] = ClassTag[TypeApply](classOf[TypeApply]) - implicit val ClassDefTag: ClassTag[ClassDef] = ClassTag[TypeDef](classOf[TypeDef]) - implicit val TryTag: ClassTag[Try] = ClassTag[Try](classOf[Try]) - implicit val AssignTag: ClassTag[Assign] = ClassTag[Assign](classOf[Assign]) - implicit val IdentTag: ClassTag[Ident] = ClassTag[Ident](classOf[Ident]) - implicit val IfTag: ClassTag[If] = ClassTag[If](classOf[If]) - implicit val LabelDefTag: ClassTag[LabelDef] = ClassTag[LabelDef](classOf[LabelDef]) - implicit val ValDefTag: ClassTag[ValDef] = ClassTag[ValDef](classOf[ValDef]) - implicit val ThrowTag: ClassTag[Throw] = ClassTag[Throw](classOf[Throw]) - implicit val LabeledTag: ClassTag[Labeled] = ClassTag[Labeled](classOf[Labeled]) - implicit val ReturnTag: ClassTag[Return] = ClassTag[Return](classOf[Return]) - implicit val WhileDoTag: ClassTag[WhileDo] = ClassTag[WhileDo](classOf[WhileDo]) - implicit val LiteralTag: ClassTag[Literal] = ClassTag[Literal](classOf[Literal]) - implicit val BlockTag: ClassTag[Block] = ClassTag[Block](classOf[Block]) - implicit val TypedTag: ClassTag[Typed] = ClassTag[Typed](classOf[Typed]) - implicit val ArrayValueTag: ClassTag[ArrayValue] = ClassTag[ArrayValue](classOf[ArrayValue]) - implicit val MatchTag: ClassTag[Match] = ClassTag[Match](classOf[Match]) - implicit val CaseDefTag: ClassTag[CaseDef] = ClassTag[CaseDef](classOf[CaseDef]) - implicit val ThisTag: ClassTag[This] = ClassTag[This](classOf[This]) - implicit val AlternativeTag: ClassTag[Alternative] = ClassTag[Alternative](classOf[Alternative]) - implicit val DefDefTag: ClassTag[DefDef] = ClassTag[DefDef](classOf[DefDef]) - implicit val ModuleDefTag: ClassTag[ModuleDef] = ClassTag[ModuleDef](classOf[ModuleDef]) - implicit val NameTag: ClassTag[Name] = ClassTag[Name](classOf[Name]) - implicit val TemplateTag: ClassTag[Template] = ClassTag[Template](classOf[Template]) - implicit val BindTag: ClassTag[Bind] = ClassTag[Bind](classOf[Bind]) - implicit val NewTag: ClassTag[New] = ClassTag[New](classOf[New]) - implicit val ApplyDynamicTag: ClassTag[ApplyDynamic] = ClassTag[ApplyDynamic](classOf[ApplyDynamic]) - implicit val SuperTag: ClassTag[Super] = ClassTag[Super](classOf[Super]) - implicit val ConstantClassTag: ClassTag[Constant] = ClassTag[Constant](classOf[Constant]) - implicit val ClosureTag: ClassTag[Closure] = ClassTag[Closure](classOf[Closure]) - - def isRuntimeVisible(annot: Annotation): Boolean = - if (toDenot(annot.atp.typeSymbol).hasAnnotation(AnnotationRetentionAttr)) - retentionPolicyOf(annot) == AnnotationRetentionRuntimeAttr - else { - // SI-8926: if the annotation class symbol doesn't have a @RetentionPolicy annotation, the - // annotation is emitted with visibility `RUNTIME` - // dotty bug: #389 - true - } - - def shouldEmitAnnotation(annot: Annotation): Boolean = { - annot.symbol.isJavaDefined && - retentionPolicyOf(annot) != AnnotationRetentionSourceAttr && - annot.args.isEmpty - } - - private def retentionPolicyOf(annot: Annotation): Symbol = - annot.atp.typeSymbol.getAnnotation(AnnotationRetentionAttr). - flatMap(_.argumentConstant(0).map(_.symbolValue)).getOrElse(AnnotationRetentionClassAttr) - - private def normalizeArgument(arg: Tree): Tree = arg match { - case Trees.NamedArg(_, arg1) => normalizeArgument(arg1) - case Trees.Typed(arg1, _) => normalizeArgument(arg1) - case _ => arg - } - - private def emitArgument(av: AnnotationVisitor, - name: String, - arg: Tree, bcodeStore: BCodeHelpers)(innerClasesStore: bcodeStore.BCInnerClassGen): Unit = { - val narg = normalizeArgument(arg) - // Transformation phases are not run on annotation trees, so we need to run - // `constToLiteral` at this point. - val t = constToLiteral(narg)(ctx.withPhase(ctx.erasurePhase)) - t match { - case Literal(const @ Constant(_)) => - const.tag match { - case BooleanTag | ByteTag | ShortTag | CharTag | IntTag | LongTag | FloatTag | DoubleTag => av.visit(name, const.value) - case StringTag => - assert(const.value != null, const) // TODO this invariant isn't documented in `case class Constant` - av.visit(name, const.stringValue) // `stringValue` special-cases null, but that execution path isn't exercised for a const with StringTag - case ClazzTag => av.visit(name, const.typeValue.toTypeKind(bcodeStore)(innerClasesStore).toASMType) - case EnumTag => - val edesc = innerClasesStore.typeDescriptor(const.tpe.asInstanceOf[bcodeStore.int.Type]) // the class descriptor of the enumeration class. - val evalue = const.symbolValue.name.mangledString // value the actual enumeration value. - av.visitEnum(name, edesc, evalue) - } - case t: TypeApply if (t.fun.symbol == Predef_classOf) => - av.visit(name, t.args.head.tpe.classSymbol.denot.info.toTypeKind(bcodeStore)(innerClasesStore).toASMType) - case Ident(nme.WILDCARD) => - // An underscore argument indicates that we want to use the default value for this parameter, so do not emit anything - case t: tpd.RefTree if t.symbol.denot.owner.isAllOf(Flags.JavaEnumTrait) => - val edesc = innerClasesStore.typeDescriptor(t.tpe.asInstanceOf[bcodeStore.int.Type]) // the class descriptor of the enumeration class. - val evalue = t.symbol.name.mangledString // value the actual enumeration value. - av.visitEnum(name, edesc, evalue) - case t: SeqLiteral => - val arrAnnotV: AnnotationVisitor = av.visitArray(name) - for (arg <- t.elems) { emitArgument(arrAnnotV, null, arg, bcodeStore)(innerClasesStore) } - arrAnnotV.visitEnd() - - case Apply(fun, args) if fun.symbol == defn.ArrayClass.primaryConstructor || - toDenot(fun.symbol).owner == defn.ArrayClass.linkedClass && fun.symbol.name == nme_apply => - val arrAnnotV: AnnotationVisitor = av.visitArray(name) - - var actualArgs = if (fun.tpe.isImplicitMethod) { - // generic array method, need to get implicit argument out of the way - fun.asInstanceOf[Apply].args - } else args - - val flatArgs = actualArgs.flatMap { arg => - normalizeArgument(arg) match { - case t: tpd.SeqLiteral => t.elems - case e => List(e) - } - } - for(arg <- flatArgs) { - emitArgument(arrAnnotV, null, arg, bcodeStore)(innerClasesStore) - } - arrAnnotV.visitEnd() -/* - case sb @ ScalaSigBytes(bytes) => - // see http://www.scala-lang.org/sid/10 (Storage of pickled Scala signatures in class files) - // also JVMS Sec. 4.7.16.1 The element_value structure and JVMS Sec. 4.4.7 The CONSTANT_Utf8_info Structure. - if (sb.fitsInOneString) { - av.visit(name, BCodeAsmCommon.strEncode(sb)) - } else { - val arrAnnotV: asm.AnnotationVisitor = av.visitArray(name) - for(arg <- BCodeAsmCommon.arrEncode(sb)) { arrAnnotV.visit(name, arg) } - arrAnnotV.visitEnd() - } // for the lazy val in ScalaSigBytes to be GC'ed, the invoker of emitAnnotations() should hold the ScalaSigBytes in a method-local var that doesn't escape. -*/ - case t @ Apply(constr, args) if t.tpe.derivesFrom(JavaAnnotationClass) => - val typ = t.tpe.classSymbol.denot.info - val assocs = assocsFromApply(t) - val desc = innerClasesStore.typeDescriptor(typ.asInstanceOf[bcodeStore.int.Type]) // the class descriptor of the nested annotation class - val nestedVisitor = av.visitAnnotation(name, desc) - emitAssocs(nestedVisitor, assocs, bcodeStore)(innerClasesStore) - - case t => - ctx.error(ex"Annotation argument is not a constant", t.sourcePos) - } - } - - override def emitAnnotations(cw: asm.ClassVisitor, annotations: List[Annotation], bcodeStore: BCodeHelpers) - (innerClasesStore: bcodeStore.BCInnerClassGen): Unit = { - for(annot <- annotations; if shouldEmitAnnotation(annot)) { - val typ = annot.atp - val assocs = annot.assocs - val av = cw.visitAnnotation(innerClasesStore.typeDescriptor(typ.asInstanceOf[bcodeStore.int.Type]), isRuntimeVisible(annot)) - emitAssocs(av, assocs, bcodeStore)(innerClasesStore) - } - } - - private def emitAssocs(av: asm.AnnotationVisitor, assocs: List[(Name, Object)], bcodeStore: BCodeHelpers) - (innerClasesStore: bcodeStore.BCInnerClassGen) = { - for ((name, value) <- assocs) - emitArgument(av, name.mangledString, value.asInstanceOf[Tree], bcodeStore)(innerClasesStore) - av.visitEnd() - } - - override def emitAnnotations(mw: asm.MethodVisitor, annotations: List[Annotation], bcodeStore: BCodeHelpers) - (innerClasesStore: bcodeStore.BCInnerClassGen): Unit = { - for(annot <- annotations; if shouldEmitAnnotation(annot)) { - val typ = annot.atp - val assocs = annot.assocs - val av = mw.visitAnnotation(innerClasesStore.typeDescriptor(typ.asInstanceOf[bcodeStore.int.Type]), isRuntimeVisible(annot)) - emitAssocs(av, assocs, bcodeStore)(innerClasesStore) - } - } - - override def emitAnnotations(fw: asm.FieldVisitor, annotations: List[Annotation], bcodeStore: BCodeHelpers) - (innerClasesStore: bcodeStore.BCInnerClassGen): Unit = { - for(annot <- annotations; if shouldEmitAnnotation(annot)) { - val typ = annot.atp - val assocs = annot.assocs - val av = fw.visitAnnotation(innerClasesStore.typeDescriptor(typ.asInstanceOf[bcodeStore.int.Type]), isRuntimeVisible(annot)) - emitAssocs(av, assocs, bcodeStore)(innerClasesStore) - } - } - - override def emitParamAnnotations(jmethod: asm.MethodVisitor, pannotss: List[List[Annotation]], bcodeStore: BCodeHelpers) - (innerClasesStore: bcodeStore.BCInnerClassGen): Unit = { - val annotationss = pannotss map (_ filter shouldEmitAnnotation) - if (annotationss forall (_.isEmpty)) return - for ((annots, idx) <- annotationss.zipWithIndex; - annot <- annots) { - val typ = annot.atp - val assocs = annot.assocs - val pannVisitor: asm.AnnotationVisitor = jmethod.visitParameterAnnotation(idx, innerClasesStore.typeDescriptor(typ.asInstanceOf[bcodeStore.int.Type]), isRuntimeVisible(annot)) - emitAssocs(pannVisitor, assocs, bcodeStore)(innerClasesStore) - } - } - - def getAnnotPickle(jclassName: String, sym: Symbol): Option[Annotation] = None - - - def getRequiredClass(fullname: String): Symbol = ctx.requiredClass(fullname) - - def getClassIfDefined(fullname: String): Symbol = NoSymbol // used only for android. todo: implement - - private def erasureString(clazz: Class[_]): String = { - if (clazz.isArray) "Array[" + erasureString(clazz.getComponentType) + "]" - else clazz.getName - } - - def requiredClass[T](implicit evidence: ClassTag[T]): Symbol = - ctx.requiredClass(erasureString(evidence.runtimeClass)) - - def requiredModule[T](implicit evidence: ClassTag[T]): Symbol = { - val moduleName = erasureString(evidence.runtimeClass) - val className = if (moduleName.endsWith("$")) moduleName.dropRight(1) else moduleName - ctx.requiredModule(className) - } - - def debuglog(msg: => String): Unit = ctx.debuglog(msg) - def informProgress(msg: String): Unit = ctx.informProgress(msg) - def log(msg: => String): Unit = ctx.log(msg) - def error(pos: Position, msg: String): Unit = ctx.error(msg, sourcePos(pos)) - def warning(pos: Position, msg: String): Unit = ctx.warning(msg, sourcePos(pos)) - def abort(msg: String): Nothing = { - ctx.error(msg) - throw new RuntimeException(msg) - } - def sourcePos(pos: Position)(implicit ctx: Context): util.SourcePosition = - ctx.source.atSpan(pos) - - def emitAsmp: Option[String] = None - - def hasLabelDefs: Boolean = false - - def dumpClasses: Option[String] = - if (ctx.settings.Ydumpclasses.isDefault) None - else Some(ctx.settings.Ydumpclasses.value) - - def noForwarders: Boolean = ctx.settings.XnoForwarders.value - def debuglevel: Int = 3 // 0 -> no debug info; 1-> filename; 2-> lines; 3-> varnames - def settings_debug: Boolean = ctx.settings.Ydebug.value - def targetPlatform: String = ctx.settings.target.value - - val perRunCaches: Caches = new Caches { - def newAnyRefMap[K <: AnyRef, V](): mutable.AnyRefMap[K, V] = new mutable.AnyRefMap[K, V]() - def newWeakMap[K, V](): mutable.WeakHashMap[K, V] = new mutable.WeakHashMap[K, V]() - def recordCache[T <: Clearable](cache: T): T = cache - def newWeakSet[K >: Null <: AnyRef](): WeakHashSet[K] = new WeakHashSet[K]() - def newMap[K, V](): mutable.HashMap[K, V] = new mutable.HashMap[K, V]() - def newSet[K](): mutable.Set[K] = new mutable.HashSet[K] - } - - val MODULE_INSTANCE_FIELD: String = str.MODULE_INSTANCE_FIELD - - def dropModule(str: String): String = - if (!str.isEmpty && str.last == '$') str.take(str.length - 1) else str - - def newTermName(prefix: String): Name = prefix.toTermName - - val Flag_SYNTHETIC: Flags = Flags.Synthetic.bits - val Flag_METHOD: Flags = Flags.Method.bits - val ExcludedForwarderFlags: Flags = DottyBackendInterface.ExcludedForwarderFlags.bits - - def isQualifierSafeToElide(qual: Tree): Boolean = tpd.isIdempotentExpr(qual) +class DottyBackendInterface(val outputDirectory: AbstractFile, val superCallsMap: Map[Symbol, Set[ClassSymbol]])(implicit val ctx: Context) { private val desugared = new java.util.IdentityHashMap[Type, tpd.Select] - def desugarIdent(i: Ident): Option[tpd.Select] = { + def cachedDesugarIdent(i: Ident): Option[tpd.Select] = { var found = desugared.get(i.tpe) if (found == null) { tpd.desugarIdent(i) match { @@ -446,271 +51,87 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma if (found == null) None else Some(found) } - def getLabelDefOwners(tree: Tree): Map[Tree, List[LabelDef]] = Map.empty + object DesugaredSelect extends DeconstructorCommon[tpd.Tree] { - // todo: remove - def isMaybeBoxed(sym: Symbol): Boolean = { - (sym == ObjectClass) || - (sym == JavaSerializableClass) || - (sym == defn.ComparableClass) || - (sym derivesFrom BoxedNumberClass) || - (sym derivesFrom BoxedCharacterClass) || - (sym derivesFrom BoxedBooleanClass) - } + var desugared: tpd.Select = null - def getSingleOutput: Option[AbstractFile] = None // todo: implement + override def isEmpty: Boolean = + desugared eq null - // @M don't generate java generics sigs for (members of) implementation - // classes, as they are monomorphic (TODO: ok?) - private final def needsGenericSignature(sym: Symbol): Boolean = !( - // pp: this condition used to include sym.hasexpandedname, but this leads - // to the total loss of generic information if a private member is - // accessed from a closure: both the field and the accessor were generated - // without it. This is particularly bad because the availability of - // generic information could disappear as a consequence of a seemingly - // unrelated change. - ctx.base.settings.YnoGenericSig.value - || sym.is(Flags.Artifact) - || sym.isAllOf(Flags.LiftedMethod) - || sym.is(Flags.Bridge) - ) + def _1: Tree = desugared.qualifier - private def verifySignature(sym: Symbol, sig: String)(implicit ctx: Context): Unit = { - import scala.tools.asm.util.CheckClassAdapter - def wrap(body: => Unit): Unit = { - try body - catch { - case ex: Throwable => - ctx.error(i"""|compiler bug: created invalid generic signature for $sym in ${sym.denot.owner.showFullName} - |signature: $sig - |if this is reproducible, please report bug at https://github.com/lampepfl/dotty/issues - """.trim, sym.sourcePos) - throw ex - } - } + def _2: Name = desugared.name - wrap { - if (sym.is(Flags.Method)) { - CheckClassAdapter.checkMethodSignature(sig) - } - else if (sym.isTerm) { - CheckClassAdapter.checkFieldSignature(sig) - } - else { - CheckClassAdapter.checkClassSignature(sig) + override def unapply(s: tpd.Tree): this.type = { + s match { + case t: tpd.Select => desugared = t + case t: Ident => + cachedDesugarIdent(t) match { + case Some(t) => desugared = t + case None => desugared = null + } + case _ => desugared = null } - } - } - /** - * Generates the generic signature for `sym` before erasure. - * - * @param sym The symbol for which to generate a signature. - * @param owner The owner of `sym`. - * @return The generic signature of `sym` before erasure, as specified in the Java Virtual - * Machine Specification, §4.3.4, or `null` if `sym` doesn't need a generic signature. - * @see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.4 - */ - def getGenericSignature(sym: Symbol, owner: Symbol): String = { - ctx.atPhase(ctx.erasurePhase) { - val memberTpe = - if (sym.is(Flags.Method)) sym.denot.info - else owner.denot.thisType.memberInfo(sym) - getGenericSignature(sym, owner, memberTpe).orNull + this } } - def getStaticForwarderGenericSignature(sym: Symbol, moduleClass: Symbol): String = { - // scala/bug#3452 Static forwarder generation uses the same erased signature as the method if forwards to. - // By rights, it should use the signature as-seen-from the module class, and add suitable - // primitive and value-class boxing/unboxing. - // But for now, just like we did in mixin, we just avoid writing a wrong generic signature - // (one that doesn't erase to the actual signature). See run/t3452b for a test case. - - val memberTpe = ctx.atPhase(ctx.erasurePhase) { moduleClass.denot.thisType.memberInfo(sym) } - val erasedMemberType = TypeErasure.erasure(memberTpe) - if (erasedMemberType =:= sym.denot.info) - getGenericSignature(sym, moduleClass, memberTpe).orNull - else null - } - - private def getGenericSignature(sym: Symbol, owner: Symbol, memberTpe: Type)(implicit ctx: Context): Option[String] = - if (needsGenericSignature(sym)) { - val erasedTypeSym = TypeErasure.fullErasure(sym.denot.info).typeSymbol - if (erasedTypeSym.isPrimitiveValueClass) { - // Suppress signatures for symbols whose types erase in the end to primitive - // value types. This is needed to fix #7416. - None - } else { - val jsOpt = GenericSignatures.javaSig(sym, memberTpe) - if (ctx.settings.XverifySignatures.value) { - jsOpt.foreach(verifySignature(sym, _)) - } - - jsOpt - } - } else { - None + object ArrayValue extends DeconstructorCommon[tpd.JavaSeqLiteral] { + def _1: Type = field.tpe match { + case JavaArrayType(elem) => elem + case _ => + ctx.error(s"JavaSeqArray with type ${field.tpe} reached backend: $field", ctx.source.atSpan(field.span)) + UnspecifiedErrorType } - - - - def sourceFileFor(cu: CompilationUnit): String = cu.source.file.name - - - - implicit def positionHelper(a: Position): PositionHelper = new PositionHelper { - def isDefined: Boolean = a.exists - def line: Int = sourcePos(a).line + 1 - def finalPosition: Position = a - } - - implicit def constantHelper(a: Constant): ConstantHelper = new ConstantHelper { - def booleanValue: Boolean = a.booleanValue - def longValue: Long = a.longValue - def byteValue: Byte = a.byteValue - def stringValue: String = a.stringValue - def symbolValue: Symbol = a.symbolValue - def floatValue: Float = a.floatValue - def value: Any = a.value - def tag: ConstantTag = a.tag - def typeValue: Type = a.typeValue - def shortValue: Short = a.shortValue - def intValue: Int = a.intValue - def doubleValue: Double = a.doubleValue - def charValue: Char = a.charValue + def _2: List[Tree] = field.elems } - - implicit def treeHelper(a: Tree): TreeHelper = new TreeHelper { - def symbol: Symbol = a.symbol - - def pos: Position = a.span - - def isEmpty: Boolean = a.isEmpty - - def tpe: Type = a.tpe - - def exists(pred: (Tree) => Boolean): Boolean = a.find(pred).isDefined + abstract class DeconstructorCommon[T >: Null <: AnyRef] { + var field: T = null + def get: this.type = this + def isEmpty: Boolean = field eq null + def isDefined = !isEmpty + def unapply(s: T): this.type ={ + field = s + this + } } +} - implicit def annotHelper(a: Annotation): AnnotationHelper = new AnnotationHelper { - def atp: Type = a.tree.tpe - - def assocs: List[(Name, Tree)] = assocsFromApply(a.tree) - - def symbol: Symbol = a.tree.symbol +object DottyBackendInterface { - def args: List[Tree] = List.empty // those arguments to scala-defined annotations. they are never emitted + private def erasureString(clazz: Class[_]): String = { + if (clazz.isArray) "Array[" + erasureString(clazz.getComponentType) + "]" + else clazz.getName } - def assocsFromApply(tree: Tree): List[(Name, Tree)] = { - tree match { - case Block(_, expr) => assocsFromApply(expr) - case Apply(fun, args) => - fun.tpe.widen match { - case MethodType(names) => - (names zip args).filter { - case (_, t: tpd.Ident) if (t.tpe.normalizedPrefix eq NoPrefix) => false - case _ => true - } - } - } - } + def requiredClass[T](implicit evidence: ClassTag[T], ctx: Context): Symbol = + ctx.requiredClass(erasureString(evidence.runtimeClass)) - implicit def nameHelper(n: Name): NameHelper = new NameHelper { - def isTypeName: Boolean = n.isTypeName - def isTermName: Boolean = n.isTermName - def startsWith(s: String): Boolean = n.startsWith(s) - def mangledString: String = n.mangledString + def requiredModule[T](implicit evidence: ClassTag[T], ctx: Context): Symbol = { + val moduleName = erasureString(evidence.runtimeClass) + val className = if (moduleName.endsWith("$")) moduleName.dropRight(1) else moduleName + ctx.requiredModule(className) } - implicit def symHelper(sym: Symbol): SymbolHelper = new SymbolHelper { - def exists: Boolean = sym.exists + extension symExtensions on (sym: Symbol) { - // names - def showFullName: String = sym.showFullName - def javaSimpleName: String = toDenot(sym).name.mangledString // addModuleSuffix(simpleName.dropLocal) - def javaBinaryName: String = javaClassName.replace('.', '/') // TODO: can we make this a string? addModuleSuffix(fullNameInternal('/')) - def javaClassName: String = toDenot(sym).fullName.mangledString // addModuleSuffix(fullNameInternal('.')).toString - def name: Name = sym.name - def rawname: String = { - val original = toDenot(sym).initial - sym.name(ctx.withPhase(original.validFor.phaseId)).mangledString - } + def isInterface(using Context): Boolean = (sym.is(PureInterface)) || sym.is(Trait) - // types - def info: Type = toDenot(sym).info - def tpe: Type = toDenot(sym).info // todo whats the differentce between tpe and info? - def thisType: Type = toDenot(sym).thisType + def isStaticConstructor(using Context): Boolean = (sym.isStaticMember && sym.isClassConstructor) || (sym.name eq nme.STATIC_CONSTRUCTOR) - // tests - def isClass: Boolean = { - sym.isPackageObject || (sym.isClass) - } - def isType: Boolean = sym.isType - def isAnonymousClass: Boolean = toDenot(sym).isAnonymousClass - def isConstructor: Boolean = toDenot(sym).isConstructor - def isExpanded: Boolean = sym.name.is(ExpandedName) - def isAnonymousFunction: Boolean = toDenot(sym).isAnonymousFunction - def isMethod: Boolean = sym.is(Flags.Method) - def isPublic: Boolean = !sym.flags.isOneOf(Flags.Private | Flags.Protected) - def isSynthetic: Boolean = sym.is(Flags.Synthetic) - def isPackageClass: Boolean = sym.is(Flags.PackageClass) - def isModuleClass: Boolean = sym.is(Flags.ModuleClass) - def isModule: Boolean = sym.is(Flags.Module) - def isStrictFP: Boolean = false // todo: implement - def isLabel: Boolean = sym.is(Flags.Label) - def hasPackageFlag: Boolean = sym.is(Flags.Package) - def isInterface: Boolean = (sym.is(Flags.PureInterface)) || (sym.is(Flags.Trait)) - def isGetter: Boolean = toDenot(sym).isGetter - def isSetter: Boolean = toDenot(sym).isSetter - def isGetClass: Boolean = sym eq defn.Any_getClass - def isJavaDefined: Boolean = sym.is(Flags.JavaDefined) - def isJavaDefaultMethod: Boolean = !((sym.is(Flags.Deferred)) || toDenot(sym).isClassConstructor) - def isDeferred: Boolean = sym.is(Flags.Deferred) - def isPrivate: Boolean = sym.is(Flags.Private) - def getsJavaFinalFlag: Boolean = - isFinal && !toDenot(sym).isClassConstructor && !(sym.is(Flags.Mutable)) && !(sym.enclosingClass.is(Flags.Trait)) - - def getsJavaPrivateFlag: Boolean = - isPrivate || (sym.isPrimaryConstructor && sym.owner.isTopLevelModuleClass) - - def isFinal: Boolean = sym.is(Flags.Final) - def isScalaStatic: Boolean = - toDenot(sym).hasAnnotation(ctx.definitions.ScalaStaticAnnot) - def isStaticMember: Boolean = (sym ne NoSymbol) && - (sym.is(Flags.JavaStatic) || isScalaStatic) + def isStaticMember(using Context): Boolean = (sym ne NoSymbol) && + (sym.is(JavaStatic) || sym.isScalaStatic) // guard against no sumbol cause this code is executed to select which call type(static\dynamic) to use to call array.clone - def isBottomClass: Boolean = (sym eq defn.NullClass) || (sym eq defn.NothingClass) - def isBridge: Boolean = sym.is(Flags.Bridge) - def isArtifact: Boolean = sym.is(Flags.Artifact) - def hasEnumFlag: Boolean = sym.isAllOf(Flags.JavaEnumTrait) - def hasAccessBoundary: Boolean = sym.accessBoundary(defn.RootClass) ne defn.RootClass - def isVarargsMethod: Boolean = sym.is(Flags.JavaVarargs) - def isDeprecated: Boolean = false - def isMutable: Boolean = sym.is(Flags.Mutable) - def hasAbstractFlag: Boolean = sym.isOneOf(Flags.AbstractOrTrait) - def hasModuleFlag: Boolean = sym.is(Flags.Module) - def isSynchronized: Boolean = sym.is(Flags.Synchronized) - def isNonBottomSubClass(other: Symbol): Boolean = sym.derivesFrom(other) - def hasAnnotation(ann: Symbol): Boolean = toDenot(sym).hasAnnotation(ann) - def shouldEmitForwarders: Boolean = - (sym.is(Flags.Module)) && sym.isStatic - def isEnum = sym.is(Flags.Enum) - - def isClassConstructor: Boolean = toDenot(sym).isClassConstructor - def isAnnotation: Boolean = toDenot(sym).isAnnotation - def isSerializable: Boolean = toDenot(sym).isSerializable - /** * True for module classes of modules that are top-level or owned only by objects. Module classes * for such objects will get a MODULE$ flag and a corresponding static initializer. */ - def isStaticModuleClass: Boolean = - (sym.is(Flags.Module)) && { + def isStaticModuleClass(using Context): Boolean = + (sym.is(Module)) && { // scalac uses atPickling here // this would not work if modules are created after pickling // for example by specialization @@ -720,42 +141,9 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma toDenot(sym)(shiftedContext).isStatic(shiftedContext) } - def isStaticConstructor: Boolean = (isStaticMember && isClassConstructor) || (sym.name eq nme.STATIC_CONSTRUCTOR) - - // navigation - def owner: Symbol = toDenot(sym).owner - def rawowner: Symbol = { - originalOwner.originalLexicallyEnclosingClass - } - def originalOwner: Symbol = toDenot(sym).originalOwner - def parentSymbols: List[Symbol] = toDenot(sym).info.parents.map(_.typeSymbol) - def superClass: Symbol = { - val t = toDenot(sym).asClass.superClass - if (t.exists) t - else if (sym.is(Flags.ModuleClass)) { - // workaround #371 - println(s"Warning: mocking up superclass for $sym") - ObjectClass - } - else t - } - def enclClass: Symbol = toDenot(sym).enclosingClass - def linkedClassOfClass: Symbol = linkedClass - def linkedClass: Symbol = toDenot(sym)(ctx).linkedClass(ctx) //exitingPickler(sym.linkedClassOfClass) - def companionClass: Symbol = toDenot(sym).companionClass - def companionModule: Symbol = toDenot(sym).companionModule - def companionSymbol: Symbol = if (sym.is(Flags.Module)) companionClass else companionModule - def moduleClass: Symbol = toDenot(sym).moduleClass - def enclosingClassSym: Symbol = { - if (this.isClass) { - val ct = ctx.withPhase(ctx.flattenPhase.prev) - toDenot(sym)(ct).owner.enclosingClass(ct) - } - else sym.enclosingClass(ctx.withPhase(ctx.flattenPhase.prev)) - } //todo is handled specially for JavaDefined symbols in scalac - def originalLexicallyEnclosingClass: Symbol = + def originalLexicallyEnclosingClass(using Context): Symbol = // used to populate the EnclosingMethod attribute. // it is very tricky in presence of classes(and annonymous classes) defined inside supper calls. if (sym.exists) { @@ -763,451 +151,40 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma val shiftedContext = ctx.withPhase(validity.phaseId) toDenot(sym)(shiftedContext).lexicallyEnclosingClass(shiftedContext) } else NoSymbol - def nextOverriddenSymbol: Symbol = toDenot(sym).nextOverriddenSymbol - def allOverriddenSymbols: List[Symbol] = toDenot(sym).allOverriddenSymbols.toList - - // members - def primaryConstructor: Symbol = toDenot(sym).primaryConstructor - - /** For currently compiled classes: All locally defined classes including local classes. - * The empty list for classes that are not currently compiled. - - */ - def nestedClasses: List[Symbol] = definedClasses(ctx.flattenPhase) - - /** For currently compiled classes: All classes that are declared as members of this class - * (but not inherited ones). The empty list for classes that are not currently compiled. - */ - def memberClasses: List[Symbol] = definedClasses(ctx.lambdaLiftPhase) - - private def definedClasses(phase: Phase) = - if (sym.isDefinedInCurrentRun) - ctx.atPhase(phase) { - toDenot(sym).info.decls.filter(_.isClass) - } - else Nil - - def annotations: List[Annotation] = toDenot(sym).annotations - def companionModuleMembers: List[Symbol] = { - // phase travel to exitingPickler: this makes sure that memberClassesOf only sees member classes, - // not local classes of the companion module (E in the example) that were lifted by lambdalift. - if (linkedClass.isTopLevelModuleClass) /*exitingPickler*/ linkedClass.memberClasses - else Nil - } - def fieldSymbols: List[Symbol] = { - toDenot(sym).info.decls.filter(p => p.isTerm && !p.is(Flags.Method)) - } - def methodSymbols: List[Symbol] = - for (f <- toDenot(sym).info.decls.toList if f.isMethod && f.isTerm && !f.isModule) yield f - def serialVUID: Option[Long] = - sym.getAnnotation(defn.SerialVersionUIDAnnot).flatMap { annot => - if (sym.is(Flags.Trait)) { - ctx.error("@SerialVersionUID does nothing on a trait", annot.tree.sourcePos) - None - } else { - val vuid = annot.argumentConstant(0).map(_.longValue) - if (vuid.isEmpty) - ctx.error("The argument passed to @SerialVersionUID must be a constant", - annot.argument(0).getOrElse(annot.tree).sourcePos) - vuid - } - } - - def freshLocal(cunit: CompilationUnit, name: String, tpe: Type, pos: Position, flags: Flags): Symbol = { - ctx.newSymbol(sym, name.toTermName, termFlagSet(flags), tpe, NoSymbol, pos) - } - - def getter(clz: Symbol): Symbol = decorateSymbol(sym).getter - def setter(clz: Symbol): Symbol = decorateSymbol(sym).setter - - def moduleSuffix: String = "" // todo: validate that names already have $ suffix - def outputDirectory: AbstractFile = DottyBackendInterface.this.outputDirectory - def pos: Position = sym.span - - def throwsAnnotations: List[Symbol] = Nil - - /** - * All interfaces implemented by a class, except for those inherited through the superclass. - * Redundant interfaces are removed unless there is a super call to them. - */ - def superInterfaces: List[Symbol] = { - val directlyInheritedTraits = decorateSymbol(sym).directlyInheritedTraits - val directlyInheritedTraitsSet = directlyInheritedTraits.toSet - val allBaseClasses = directlyInheritedTraits.iterator.flatMap(_.asClass.baseClasses.drop(1)).toSet - val superCalls = superCallsMap.getOrElse(sym, Set.empty) - val additional = (superCalls -- directlyInheritedTraitsSet).filter(_.is(Flags.Trait)) -// if (additional.nonEmpty) -// println(s"$fullName: adding supertraits $additional") - directlyInheritedTraits.filter(t => !allBaseClasses(t) || superCalls(t)) ++ additional - } /** * True for module classes of package level objects. The backend will generate a mirror class for * such objects. */ - def isTopLevelModuleClass: Boolean = sym.isModuleClass && + def isTopLevelModuleClass(using Context): Boolean = + sym.is(ModuleClass) && ctx.atPhase(ctx.flattenPhase) { - toDenot(sym).owner.is(Flags.PackageClass) - } - - def addRemoteRemoteExceptionAnnotation: Unit = () - - def samMethod(): Symbol = ctx.atPhase(ctx.erasurePhase) { - val samMethods = toDenot(sym).info.possibleSamMethods.toList - samMethods match { - case x :: Nil => x.symbol - case Nil => abort(s"${sym.show} is not a functional interface. It doesn't have abstract methods") - case xs => abort(s"${sym.show} is not a functional interface. " + - s"It has the following abstract methods: ${xs.map(_.name).mkString(", ")}") - } - } - - def isFunctionClass: Boolean = - defn.isFunctionClass(sym) - } - - - implicit def typeHelper(tp: Type): TypeHelper = new TypeHelper { - def member(string: Name): Symbol = tp.member(string.toTermName).symbol - - def isFinalType: Boolean = tp.typeSymbol.is(Flags.Final) //in scalac checks for type parameters. Why? Aren't they gone by backend? - - def underlying: Type = tp match { - case t: TypeProxy => t.underlying - case _ => tp - } - - def paramTypes: List[Type] = tp.firstParamTypes - - def <:<(other: Type): Boolean = tp <:< other - - def memberInfo(s: Symbol): Type = tp.memberInfo(s) - - def decl(name: Name): Symbol = tp.decl(name).symbol - - def decls: List[Symbol] = tp.decls.toList - - def members: List[Symbol] = tp.allMembers.map(_.symbol).toList - - def typeSymbol: Symbol = tp.widenDealias.typeSymbol - - def =:=(other: Type): Boolean = tp =:= other - - def sortedMembersBasedOnFlags(required: Flags, excluded: Flags): List[Symbol] = { - val requiredFlagSet = termFlagSet(required) - val excludedFlagSet = termFlagSet(excluded) - // The output of `memberNames` is a Set, sort it to guarantee a stable ordering. - val names = tp.memberNames(takeAllFilter).toSeq.sorted - val buffer = mutable.ListBuffer[Symbol]() - names.foreach { name => - buffer ++= tp.memberBasedOnFlags(name, requiredFlagSet, excludedFlagSet) - .alternatives.sortBy(_.signature)(Signature.lexicographicOrdering).map(_.symbol) + toDenot(sym).owner.is(PackageClass) } - buffer.toList - } - - def resultType: Type = tp.resultType - - def toTypeKind(ct: BCodeHelpers)(storage: ct.BCInnerClassGen): ct.bTypes.BType = { - import ct.bTypes._ - val defn = ctx.definitions - import coreBTypes._ - import Types._ - /** - * Primitive types are represented as TypeRefs to the class symbol of, for example, scala.Int. - * The `primitiveTypeMap` maps those class symbols to the corresponding PrimitiveBType. - */ - def primitiveOrClassToBType(sym: Symbol): BType = { - assert(sym.isClass, sym) - assert(sym != ArrayClass || isCompilingArray, sym) - primitiveTypeMap.getOrElse(sym.asInstanceOf[ct.bTypes.coreBTypes.bTypes.int.Symbol], - storage.getClassBTypeAndRegisterInnerClass(sym.asInstanceOf[ct.int.Symbol])).asInstanceOf[BType] - } - - /** - * When compiling Array.scala, the type parameter T is not erased and shows up in method - * signatures, e.g. `def apply(i: Int): T`. A TyperRef to T is replaced by ObjectReference. - */ - def nonClassTypeRefToBType(sym: Symbol): ClassBType = { - assert(sym.isType && isCompilingArray, sym) - ObjectReference.asInstanceOf[ct.bTypes.ClassBType] - } - - tp.widenDealias match { - case JavaArrayType(el) =>ArrayBType(el.toTypeKind(ct)(storage)) // Array type such as Array[Int] (kept by erasure) - case t: TypeRef => - t.info match { - - case _ => - if (!t.symbol.isClass) nonClassTypeRefToBType(t.symbol) // See comment on nonClassTypeRefToBType - else primitiveOrClassToBType(t.symbol) // Common reference to a type such as scala.Int or java.lang.String - } - case Types.ClassInfo(_, sym, _, _, _) => primitiveOrClassToBType(sym) // We get here, for example, for genLoadModule, which invokes toTypeKind(moduleClassSymbol.info) - - /* AnnotatedType should (probably) be eliminated by erasure. However we know it happens for - * meta-annotated annotations (@(ann @getter) val x = 0), so we don't emit a warning. - * The type in the AnnotationInfo is an AnnotatedTpe. Tested in jvm/annotations.scala. - */ - case a @ AnnotatedType(t, _) => - debuglog(s"typeKind of annotated type $a") - t.toTypeKind(ct)(storage) - - /* ExistentialType should (probably) be eliminated by erasure. We know they get here for - * classOf constants: - * class C[T] - * class T { final val k = classOf[C[_]] } - */ - /* case e @ ExistentialType(_, t) => - debuglog(s"typeKind of existential type $e") - t.toTypeKind(ctx)(storage)*/ - - /* The cases below should probably never occur. They are kept for now to avoid introducing - * new compiler crashes, but we added a warning. The compiler / library bootstrap and the - * test suite don't produce any warning. - */ - - case tp => - ctx.warning( - s"an unexpected type representation reached the compiler backend while compiling $currentUnit: $tp. " + - "If possible, please file a bug on https://github.com/lampepfl/dotty/issues") - - tp match { - case tp: ThisType if tp.cls == 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.asInstanceOf[ct.int.Symbol]) - // case t: SingletonType => primitiveOrClassToBType(t.classSymbol) - case t: SingletonType => t.underlying.toTypeKind(ct)(storage) - case t: RefinedType => t.parent.toTypeKind(ct)(storage) //parents.map(_.toTypeKind(ct)(storage).asClassBType).reduceLeft((a, b) => a.jvmWiseLUB(b)) - } - } - } - - def summaryString: String = tp.showSummary() - - def params: List[Symbol] = - Nil // backend uses this to emit annotations on parameter lists of forwarders - // to static methods of companion class - // in Dotty this link does not exists: there is no way to get from method type - // to inner symbols of DefDef - // todo: somehow handle. - def parents: List[Type] = tp.parents + def javaSimpleName(using Context): String = toDenot(sym).name.mangledString + def javaClassName(using Context): String = toDenot(sym).fullName.mangledString + def javaBinaryName(using Context): String = javaClassName.replace('.', '/') } - object Assign extends AssignDeconstructor { - def _1: Tree = field.lhs - def _2: Tree = field.rhs - } - - object Select extends SelectDeconstructor { - - var desugared: tpd.Select = null - - override def isEmpty: Boolean = - desugared eq null - - def _1: Tree = desugared.qualifier - - def _2: Name = desugared.name - - override def unapply(s: Select): this.type = { - s match { - case t: tpd.Select => desugared = t - case t: Ident => - desugarIdent(t) match { - case Some(t) => desugared = t - case None => desugared = null - } - case _ => desugared = null - } - - this - } - } - - object Apply extends ApplyDeconstructor { - def _1: Tree = field.fun - def _2: List[Tree] = field.args - } - - object If extends IfDeconstructor { - def _1: Tree = field.cond - def _2: Tree = field.thenp - def _3: Tree = field.elsep - } - - object ValDef extends ValDefDeconstructor { - def _1: Modifiers = null - def _2: Name = field.name - def _3: Tree = field.tpt - def _4: Tree = field.rhs - } - - object ApplyDynamic extends ApplyDynamicDeconstructor { - def _1: Tree = ??? - def _2: List[Tree] = ??? - } - - // todo: this product1s should also eventually become name-based pattn matching - object Literal extends LiteralDeconstructor { - def get: Constant = field.const - } - - object Throw extends ThrowDeconstructor { - def get: Tree = field.args.head - - override def unapply(s: Throw): Throw.type = { - if (s.fun.symbol eq defn.throwMethod) { - field = s - } else { - field = null - } - this - } - } - - object New extends NewDeconstructor { - def get: Type = field.tpt.tpe - } - - object This extends ThisDeconstructor { - def get: Name = field.qual.name - def apply(s: Symbol): This = tpd.This(s.asClass) - } - - object Labeled extends LabeledDeconstructor { - def _1: Bind = field.bind - def _2: Tree = field.expr - } - - object Return extends ReturnDeconstructor { - def _1: Tree = field.expr - def _2: Symbol = if (field.from.symbol.isLabel) field.from.symbol else NoSymbol - } - - object WhileDo extends WhileDoDeconstructor { - def _1: Tree = field.cond - def _2: Tree = field.body - } - - object Ident extends IdentDeconstructor { - def get: Name = field.name - } - - object Alternative extends AlternativeDeconstructor { - def get: List[Tree] = field.trees - } - - object Constant extends ConstantDeconstructor { - def get: Any = field.value - } - object ThrownException extends ThrownException { - def unapply(a: Annotation): Option[Symbol] = None // todo - } - - object Try extends TryDeconstructor { - def _1: Tree = field.expr - def _2: List[Tree] = field.cases - def _3: Tree = field.finalizer - } - - object LabelDef extends LabelDeconstructor { - def _1: Name = ??? - def _2: List[Symbol] = ??? - def _3: Tree = ??? - } - - object Typed extends TypedDeconstrutor { - def _1: Tree = field.expr - def _2: Tree = field.tpt - } - object Super extends SuperDeconstructor { - def _1: Tree = field.qual - def _2: Name = field.mix.name - } - object ArrayValue extends ArrayValueDeconstructor { - def _1: Type = field.tpe match { - case JavaArrayType(elem) => elem - case _ => - error(field.span, s"JavaSeqArray with type ${field.tpe} reached backend: $field") - UnspecifiedErrorType - } - def _2: List[Tree] = field.elems - } - object Match extends MatchDeconstructor { - def _1: Tree = field.selector - def _2: List[Tree] = field.cases - } - object Block extends BlockDeconstructor { - def _1: List[Tree] = field.stats - def _2: Tree = field.expr - } - object TypeApply extends TypeApplyDeconstructor { - def _1: Tree = field.fun - def _2: List[Tree] = field.args - } - object CaseDef extends CaseDeconstructor { - def _1: Tree = field.pat - def _2: Tree = field.guard - def _3: Tree = field.body - } - - object DefDef extends DefDefDeconstructor { - def _1: Modifiers = null - def _2: Name = field.name - def _3: List[TypeDef] = field.tparams - def _4: List[List[ValDef]] = field.vparamss - def _5: Tree = field.tpt - def _6: Tree = field.rhs - } - - object ModuleDef extends ModuleDefDeconstructor { - def _1: Modifiers = ??? - def _2: Name = ??? - def _3: Tree = ??? - } - - object Template extends TemplateDeconstructor { - def _1: List[Tree] = field.parents - def _2: ValDef = field.self - def _3: List[Tree] = - if (field.constr.rhs.isEmpty) field.body - else field.constr :: field.body - } - - object Bind extends BindDeconstructor { - def _1: Name = field.name - def _2: Tree = field.body - } - - object ClassDef extends ClassDefDeconstructor { - def _1: Modifiers = null - def _2: Name = field.name - def _4: Template = field.rhs.asInstanceOf[Template] - def _3: List[TypeDef] = Nil - } + private val primitiveCompilationUnits = Set( + "Unit.scala", + "Boolean.scala", + "Char.scala", + "Byte.scala", + "Short.scala", + "Int.scala", + "Float.scala", + "Long.scala", + "Double.scala" + ) - object Closure extends ClosureDeconstructor { - def _1: List[Tree] = field.env - def _2: Tree = field.meth - def _3: Symbol = { - val t = field.tpt.tpe.typeSymbol - if (t.exists) t - else { - val arity = field.meth.tpe.widenDealias.paramTypes.size - _1.size - val returnsUnit = field.meth.tpe.widenDealias.resultType.classSymbol == UnitClass - if (returnsUnit) ctx.requiredClass(("dotty.runtime.function.JProcedure" + arity)) - else if (arity <= 2) ctx.requiredClass(("dotty.runtime.function.JFunction" + arity)) - else ctx.requiredClass(("scala.Function" + arity)) - } - } + /** + * True if the current compilation unit is of a primitive class (scala.Boolean et al). + * Used only in assertions. + */ + def isCompilingPrimitive(using ctx: Context) = { + primitiveCompilationUnits(ctx.compilationUnit.source.file.name) } - def currentUnit: CompilationUnit = ctx.compilationUnit -} - -object DottyBackendInterface { - val ExcludedForwarderFlags: Flags.FlagSet = { - Flags.Specialized | Flags.Lifted | Flags.Protected | Flags.JavaStatic | - Flags.Private | Flags.Macro - } } diff --git a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala index e68c54d93e56..49fd86aef855 100644 --- a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala +++ b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala @@ -70,6 +70,7 @@ object GenBCode { } class GenBCodePipeline(val int: DottyBackendInterface)(implicit ctx: Context) extends BCodeSyncAndTry { + import DottyBackendInterface.symExtensions private var tree: Tree = _ @@ -184,7 +185,7 @@ class GenBCodePipeline(val int: DottyBackendInterface)(implicit ctx: Context) ex try { /*withCurrentUnit(item.cunit)*/(visit(item)) } catch { case ex: Throwable => - println(s"Error while emitting ${int.sourceFileFor(item.cunit)}") + println(s"Error while emitting ${item.cunit.source.file.name}") throw ex } } @@ -203,7 +204,7 @@ class GenBCodePipeline(val int: DottyBackendInterface)(implicit ctx: Context) ex // -------------- mirror class, if needed -------------- val mirrorC = - if (int.symHelper(claszSymbol).isTopLevelModuleClass) { + if (claszSymbol.isTopLevelModuleClass) { if (claszSymbol.companionClass == NoSymbol) { mirrorCodeGen.genMirrorClass(claszSymbol, cunit) } else { @@ -530,7 +531,7 @@ class GenBCodePipeline(val int: DottyBackendInterface)(implicit ctx: Context) ex case PackageDef(_, stats) => stats foreach gen case ValDef(name, tpt, rhs) => () // module val not emitted case cd: TypeDef => - q1 add Item1(arrivalPos, cd, int.currentUnit) + q1 add Item1(arrivalPos, cd, int.ctx.compilationUnit) arrivalPos += 1 } } diff --git a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala index 89915e44eb16..c17e7b881bf2 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala @@ -548,9 +548,8 @@ class JSCodeGen()(implicit ctx: Context) { }.toSet val members = { - import dotty.tools.backend.jvm.DottyBackendInterface.ExcludedForwarderFlags moduleClass.info.membersBasedOnFlags(required = Flags.Method, - excluded = ExcludedForwarderFlags).map(_.symbol) + excluded = Flags.ExcludedForwarder).map(_.symbol) } def isExcluded(m: Symbol): Boolean = { diff --git a/compiler/src/dotty/tools/backend/sjs/JSEncoding.scala b/compiler/src/dotty/tools/backend/sjs/JSEncoding.scala index eefa11436bbb..aa96e9121226 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSEncoding.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSEncoding.scala @@ -30,6 +30,8 @@ import ScopedVar.withScopedVars import JSDefinitions._ import JSInterop._ +import dotty.tools.backend.jvm.DottyBackendInterface.symExtensions + /** Encoding of symbol names for JavaScript * * Some issues that this encoding solves: @@ -119,7 +121,7 @@ object JSEncoding { js.LabelIdent(freshLabelName(base)) def labelSymbolName(sym: Symbol)(implicit ctx: Context): LabelName = - labelSymbolNames.getOrElseUpdate(sym, freshLabelName(sym.name.mangledString)) + labelSymbolNames.getOrElseUpdate(sym, freshLabelName(sym.javaSimpleName)) def getEnclosingReturnLabel()(implicit pos: ir.Position): js.LabelIdent = { if (returnLabelName.isEmpty) @@ -157,7 +159,7 @@ object JSEncoding { require(sym.owner.isClass && sym.isTerm && !sym.is(Flags.Method) && !sym.is(Flags.Module), "encodeFieldSym called with non-field symbol: " + sym) - val name0 = sym.name.mangledString + val name0 = sym.javaSimpleName val name = if (name0.charAt(name0.length()-1) != ' ') name0 else name0.substring(0, name0.length()-1) @@ -247,7 +249,7 @@ object JSEncoding { */ ir.Names.BoxedUnitClass } else { - ClassName(sym1.fullName.mangledString) + ClassName(sym1.javaClassName) } } diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 6dba2ed925c4..325638fe5094 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -399,6 +399,7 @@ class Definitions { @tu lazy val ScalaRuntime__hashCode: Symbol = ScalaRuntimeModule.requiredMethod(nme._hashCode_) @tu lazy val BoxesRunTimeModule: Symbol = ctx.requiredModule("scala.runtime.BoxesRunTime") + @tu lazy val BoxesRunTimeModule_externalEquals: Symbol = BoxesRunTimeModule.info.decl(nme.equals_).suchThat(toDenot(_).info.firstParamTypes.size == 2).symbol @tu lazy val ScalaStaticsModule: Symbol = ctx.requiredModule("scala.runtime.Statics") def staticsMethodRef(name: PreName): TermRef = ScalaStaticsModule.requiredMethodRef(name) def staticsMethod(name: PreName): TermSymbol = ScalaStaticsModule.requiredMethod(name) diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 0b2aa4133983..b1ddc12b2ed4 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -536,6 +536,7 @@ object Flags { val FinalOrInline: FlagSet = Final | Inline val FinalOrModuleClass: FlagSet = Final | ModuleClass // A module class or a final class val EffectivelyFinalFlags: FlagSet = Final | Private + val ExcludedForwarder: Flags.FlagSet = Specialized | Lifted | Protected | JavaStatic | Private | Macro val FinalOrSealed: FlagSet = Final | Sealed val GivenOrImplicit: FlagSet = Given | Implicit val GivenOrImplicitVal: FlagSet = GivenOrImplicit.toTermFlags diff --git a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala index e4970b430506..f1c87957496b 100644 --- a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala +++ b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala @@ -20,6 +20,8 @@ import parsing.Parsers.OutlineParser import reporting.trace import ast.desugar +import dotty.tools.backend.jvm.DottyBackendInterface.symExtensions + object SymbolLoaders { import ast.untpd._ @@ -279,7 +281,7 @@ object SymbolLoaders { if (!sourceModule.isCompleted) sourceModule.completer.complete(sourceModule) - val packageName = if (root.isEffectiveRoot) "" else root.fullName.mangledString + val packageName = if (root.isEffectiveRoot) "" else root.symbol.javaClassName enterFlatClasses = Some { ctx => enterFlatClasses = None diff --git a/compiler/src/dotty/tools/dotc/transform/CheckStatic.scala b/compiler/src/dotty/tools/dotc/transform/CheckStatic.scala index 395527ffd3fe..11040f4872cc 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckStatic.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckStatic.scala @@ -10,6 +10,8 @@ import dotty.tools.dotc.ast.tpd import Decorators._ import reporting.messages._ +import dotty.tools.dotc.transform.SymUtils._ + /** A transformer that check that requirements of Static fields\methods are implemented: * 1. Only objects can have members annotated with `@static` * 2. The fields annotated with `@static` should precede any non-`@static` fields. @@ -31,7 +33,7 @@ class CheckStatic extends MiniPhase { val defns = tree.body.collect{case t: ValOrDefDef => t} var hadNonStaticField = false for (defn <- defns) - if (defn.symbol.hasAnnotation(ctx.definitions.ScalaStaticAnnot)) { + if (defn.symbol.isScalaStatic) { if (!ctx.owner.is(Module)) ctx.error(StaticFieldsOnlyAllowedInObjects(defn.symbol), defn.sourcePos) diff --git a/compiler/src/dotty/tools/dotc/transform/SelectStatic.scala b/compiler/src/dotty/tools/dotc/transform/SelectStatic.scala index a0a6f26508ca..226bed2f66ce 100644 --- a/compiler/src/dotty/tools/dotc/transform/SelectStatic.scala +++ b/compiler/src/dotty/tools/dotc/transform/SelectStatic.scala @@ -9,6 +9,7 @@ import dotty.tools.dotc.core.Flags._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core._ import dotty.tools.dotc.transform.MegaPhase._ +import dotty.tools.dotc.transform.SymUtils._ /** Removes selects that would be compiled into GetStatic * otherwise backend needs to be aware that some qualifiers need to be dropped. @@ -25,7 +26,7 @@ class SelectStatic extends MiniPhase with IdentityDenotTransformer { def isStaticMember = (sym is Flags.Module) && sym.initial.maybeOwner.initial.isStaticOwner || (sym is Flags.JavaStatic) || - sym.hasAnnotation(ctx.definitions.ScalaStaticAnnot) + sym.isScalaStatic val isStaticRef = !sym.is(Package) && !sym.maybeOwner.is(Package) && isStaticMember val tree1 = if (isStaticRef && !tree.qualifier.symbol.isAllOf(JavaModule) && !tree.qualifier.isType) diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index ff7890f62a4c..cb2751fe120f 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -227,4 +227,8 @@ class SymUtils(val self: Symbol) extends AnyVal { def isCollectiveExtensionClass(using Context): Boolean = self.is(ModuleClass) && self.sourceModule.is(Extension, butNot = Method) + + def isScalaStatic(using Context): Boolean = + self.hasAnnotation(ctx.definitions.ScalaStaticAnnot) + } diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index 3b518c7227b1..047c010c0d9e 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -23,6 +23,7 @@ import scala.internal.Chars._ import collection.mutable import ProtoTypes._ +import dotty.tools.backend.jvm.DottyBackendInterface.symExtensions import scala.util.control.NonFatal @@ -50,7 +51,7 @@ class TreeChecker extends Phase with SymTransformer { val NoSuperClassFlags: FlagSet = Trait | Package def testDuplicate(sym: Symbol, registry: mutable.Map[String, Symbol], typ: String)(using Context): Unit = { - val name = sym.fullName.mangledString + val name = sym.javaClassName val isDuplicate = this.flatClasses && registry.contains(name) assert(!isDuplicate, s"$typ defined twice $sym ${sym.id} ${registry(name).id}") registry(name) = sym