aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/backend
diff options
context:
space:
mode:
authorFelix Mulder <felix.mulder@gmail.com>2016-11-02 11:08:28 +0100
committerGuillaume Martres <smarter@ubuntu.com>2016-11-22 01:35:07 +0100
commit8a61ff432543a29234193cd1f7c14abd3f3d31a0 (patch)
treea8147561d307af862c295cfc8100d271063bb0dd /compiler/src/dotty/tools/backend
parent6a455fe6da5ff9c741d91279a2dc6fe2fb1b472f (diff)
downloaddotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.gz
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.bz2
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.zip
Move compiler and compiler tests to compiler dir
Diffstat (limited to 'compiler/src/dotty/tools/backend')
-rw-r--r--compiler/src/dotty/tools/backend/jvm/CollectEntryPoints.scala125
-rw-r--r--compiler/src/dotty/tools/backend/jvm/CollectSuperCalls.scala42
-rw-r--r--compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala1102
-rw-r--r--compiler/src/dotty/tools/backend/jvm/GenBCode.scala433
-rw-r--r--compiler/src/dotty/tools/backend/jvm/LabelDefs.scala223
-rw-r--r--compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala417
6 files changed, 2342 insertions, 0 deletions
diff --git a/compiler/src/dotty/tools/backend/jvm/CollectEntryPoints.scala b/compiler/src/dotty/tools/backend/jvm/CollectEntryPoints.scala
new file mode 100644
index 000000000..2ee1b6011
--- /dev/null
+++ b/compiler/src/dotty/tools/backend/jvm/CollectEntryPoints.scala
@@ -0,0 +1,125 @@
+package dotty.tools.backend.jvm
+
+import dotty.tools.dotc.ast.tpd
+import dotty.tools.dotc.core.Contexts.Context
+import dotty.tools.dotc.core.Types
+import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, MiniPhase, MiniPhaseTransform}
+import dotty.tools.dotc.ast.tpd
+import dotty.tools.dotc
+import dotty.tools.dotc.backend.jvm.DottyPrimitives
+import dotty.tools.dotc.core.Flags.FlagSet
+import dotty.tools.dotc.transform.Erasure
+import dotty.tools.dotc.transform.SymUtils._
+import java.io.{File => JFile}
+
+import scala.collection.generic.Clearable
+import scala.collection.mutable
+import scala.reflect.ClassTag
+import scala.reflect.internal.util.WeakHashSet
+import scala.reflect.io.{Directory, PlainDirectory, AbstractFile}
+import scala.tools.asm.{ClassVisitor, FieldVisitor, MethodVisitor}
+import scala.tools.nsc.backend.jvm.{BCodeHelpers, BackendInterface}
+import dotty.tools.dotc.core._
+import Periods._
+import SymDenotations._
+import Contexts._
+import Types._
+import Symbols._
+import Denotations._
+import Phases._
+import java.lang.AssertionError
+import dotty.tools.dotc.util.Positions.Position
+import Decorators._
+import tpd._
+import StdNames.nme
+
+/**
+ * Created by dark on 26/11/14.
+ */
+class CollectEntryPoints extends MiniPhaseTransform {
+ def phaseName: String = "Collect entry points"
+
+ override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
+ if ((tree.symbol ne NoSymbol) && CollectEntryPoints.isJavaEntryPoint(tree.symbol)) {
+ ctx.genBCodePhase.asInstanceOf[GenBCode].registerEntryPoint(tree.symbol)
+ }
+ tree
+ }
+}
+
+object CollectEntryPoints{
+ def isJavaMainMethod(sym: Symbol)(implicit ctx: Context) = {
+ (sym.name == nme.main) && (sym.info match {
+ case r@MethodType(_, List(defn.ArrayOf(t))) =>
+ (t.widenDealias =:= defn.StringType) && (
+ r.resultType.widenDealias =:= defn.UnitType)
+ case _ => false
+ })
+ }
+
+ def isJavaEntryPoint(sym: Symbol)(implicit ctx: Context): Boolean = {
+ import Types.MethodType
+ val d = ctx.definitions
+ val StringType = d.StringType
+ // The given class has a main method.
+ def hasJavaMainMethod(sym: Symbol): Boolean =
+ (toDenot(sym).info member nme.main).alternatives exists(x => isJavaMainMethod(x.symbol))
+
+ def fail(msg: String, pos: Position = sym.pos) = {
+ ctx.warning( sym.name +
+ s" has a main method with parameter type Array[String], but ${toDenot(sym).fullName} will not be a runnable program.\n Reason: $msg",
+ sourcePos(sym.pos)
+ // TODO: make this next claim true, if possible
+ // by generating valid main methods as static in module classes
+ // not sure what the jvm allows here
+ // + " You can still run the program by calling it as " + javaName(sym) + " instead."
+ )
+ false
+ }
+ def failNoForwarder(msg: String) = {
+ fail(s"$msg, which means no static forwarder can be generated.\n")
+ }
+ val possibles = if (sym.flags is Flags.Module) (toDenot(sym).info nonPrivateMember nme.main).alternatives else Nil
+ val hasApproximate = possibles exists { m =>
+ m.info match {
+ case MethodType(_, p :: Nil) =>
+ p.typeSymbol == defn.ArrayClass
+ case _ => false
+ }
+ }
+ // At this point it's a module with a main-looking method, so either succeed or warn that it isn't.
+ hasApproximate && {
+ // Before erasure so we can identify generic mains.
+ {
+ // implicit val c = ctx.withPhase(ctx.erasurePhase)
+
+ val companion = sym.asClass.moduleClass
+
+ if (hasJavaMainMethod(companion))
+ failNoForwarder("companion contains its own main method")
+ else if (toDenot(companion).info.member(nme.main) != NoDenotation)
+ // this is only because forwarders aren't smart enough yet
+ failNoForwarder("companion contains its own main method (implementation restriction: no main is allowed, regardless of signature)")
+ else if (companion.flags is Flags.Trait)
+ failNoForwarder("companion is a trait")
+ // Now either succeed, or issue some additional warnings for things which look like
+ // attempts to be java main methods.
+ else (possibles exists(x=> isJavaMainMethod(x.symbol))) || {
+ possibles exists { m =>
+ toDenot(m.symbol).info match {
+ case t: PolyType =>
+ fail("main methods cannot be generic.")
+ case t@MethodType(paramNames, paramTypes) =>
+ if (t.resultType :: paramTypes exists (_.typeSymbol.isAbstractType))
+ fail("main methods cannot refer to type parameters or abstract types.", m.symbol.pos)
+ else
+ isJavaMainMethod(m.symbol) || fail("main method must have exact signature (Array[String])Unit", m.symbol.pos)
+ case tp =>
+ fail(s"don't know what this is: $tp", m.symbol.pos)
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/compiler/src/dotty/tools/backend/jvm/CollectSuperCalls.scala b/compiler/src/dotty/tools/backend/jvm/CollectSuperCalls.scala
new file mode 100644
index 000000000..8285bfe4b
--- /dev/null
+++ b/compiler/src/dotty/tools/backend/jvm/CollectSuperCalls.scala
@@ -0,0 +1,42 @@
+package dotty.tools.backend.jvm
+
+import dotty.tools.dotc.ast.tpd
+import dotty.tools.dotc.ast.Trees._
+import dotty.tools.dotc.core.Contexts.Context
+import dotty.tools.dotc.core.Symbols._
+import dotty.tools.dotc.core.Flags.Trait
+import dotty.tools.dotc.transform.TreeTransforms.{MiniPhaseTransform, TransformerInfo}
+
+/** Collect all super calls to trait members.
+ *
+ * For each super reference to trait member, register a call from the current class to the
+ * owner of the referenced member.
+ *
+ * This information is used to know if it is safe to remove a redundant mixin class.
+ * A redundant mixin class is one that is implemented by another mixin class. As the
+ * methods in a redundant mixin class could be implemented with a default abstract method,
+ * the redundant mixin class could be required as a parent by the JVM.
+ */
+class CollectSuperCalls extends MiniPhaseTransform {
+ import tpd._
+
+ def phaseName: String = "collectSuperCalls"
+
+ override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo): Tree = {
+ tree.qualifier match {
+ case sup: Super =>
+ if (tree.symbol.owner.is(Trait))
+ registerSuperCall(ctx.owner.enclosingClass.asClass, tree.symbol.owner.asClass)
+ case _ =>
+ }
+ tree
+ }
+
+ private def registerSuperCall(sym: ClassSymbol, calls: ClassSymbol)(implicit ctx: Context) = {
+ ctx.genBCodePhase match {
+ case genBCodePhase: GenBCode =>
+ genBCodePhase.registerSuperCall(sym, calls)
+ case _ =>
+ }
+ }
+}
diff --git a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala
new file mode 100644
index 000000000..a7c449947
--- /dev/null
+++ b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala
@@ -0,0 +1,1102 @@
+package dotty.tools.backend.jvm
+
+import dotty.tools.dotc.ast.tpd
+import dotty.tools.dotc
+import dotty.tools.dotc.backend.jvm.DottyPrimitives
+import dotty.tools.dotc.core.Flags.FlagSet
+import dotty.tools.dotc.transform.Erasure
+import dotty.tools.dotc.transform.SymUtils._
+import java.io.{File => JFile}
+
+import scala.collection.generic.Clearable
+import scala.collection.mutable
+import scala.reflect.ClassTag
+import scala.reflect.internal.util.WeakHashSet
+import scala.reflect.io.{AbstractFile, Directory, PlainDirectory}
+import scala.tools.asm.{AnnotationVisitor, ClassVisitor, FieldVisitor, MethodVisitor}
+import scala.tools.nsc.backend.jvm.{BCodeHelpers, BackendInterface}
+import dotty.tools.dotc.core._
+import Periods._
+import SymDenotations._
+import Contexts._
+import Types._
+import Symbols._
+import Denotations._
+import Phases._
+import java.lang.AssertionError
+
+import dotty.tools.dotc.util.{DotClass, Positions}
+import Decorators._
+import tpd._
+
+import scala.tools.asm
+import NameOps._
+import StdNames.nme
+import NameOps._
+import dotty.tools.dotc.core
+import dotty.tools.dotc.core.Names.TypeName
+
+import scala.annotation.tailrec
+
+class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Map[Symbol, Set[ClassSymbol]])(implicit ctx: Context) extends BackendInterface{
+ 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 = Positions.Position
+ 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 Return = tpd.Return
+ 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 = tpd.DefDef
+ type Closure = tpd.Closure
+
+ val NoSymbol = Symbols.NoSymbol
+ val NoPosition: Position = Positions.NoPosition
+ 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.
+ override lazy val LambdaMetaFactory = ctx.requiredClass("java.lang.invoke.LambdaMetafactory")
+ override lazy val MethodHandle = ctx.requiredClass("java.lang.invoke.MethodHandle")
+
+ val nme_valueOf: Name = StdNames.nme.valueOf
+ val nme_apply = 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_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) = 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.BoxesRunTimeClass.info.decl(nme.equals_).suchThat(toDenot(_).info.firstParamTypes.size == 2).symbol
+ val MaxFunctionArity: Int = Definitions.MaxFunctionArity
+ 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
+ lazy val Predef_classOf: Symbol = defn.ScalaPredefModule.requiredMethod(nme.classOf)
+
+ lazy val AnnotationRetentionAttr = ctx.requiredClass("java.lang.annotation.Retention")
+ lazy val AnnotationRetentionSourceAttr = ctx.requiredClass("java.lang.annotation.RetentionPolicy").linkedClass.requiredValue("SOURCE")
+ lazy val AnnotationRetentionClassAttr = ctx.requiredClass("java.lang.annotation.RetentionPolicy").linkedClass.requiredValue("CLASS")
+ lazy val AnnotationRetentionRuntimeAttr = ctx.requiredClass("java.lang.annotation.RetentionPolicy").linkedClass.requiredValue("RUNTIME")
+ lazy val JavaAnnotationClass = 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) = {
+ s eq defn.newArrayMethod
+ }
+
+ def isBox(sym: Symbol): Boolean = Erasure.Boxing.isBox(sym)
+ def isUnbox(sym: Symbol): Boolean = Erasure.Boxing.isUnbox(sym)
+
+ val primitives: Primitives = new Primitives {
+ val primitives = new DottyPrimitives(ctx)
+ def getPrimitive(app: Apply, reciever: Type): Int = primitives.getPrimitive(app, reciever)
+
+ 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 ReturnTag: ClassTag[Return] = ClassTag[Return](classOf[Return])
+ 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])
+
+ /* dont emit any annotations for now*/
+ def isRuntimeVisible(annot: Annotation): Boolean = {
+ annot.atp.typeSymbol.getAnnotation(AnnotationRetentionAttr) match {
+ case Some(retentionAnnot) =>
+ retentionAnnot.tree.find(_.symbol == AnnotationRetentionRuntimeAttr).isDefined
+ case _ =>
+ // 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(_.argument(0).map(_.symbol)).getOrElse(AnnotationRetentionClassAttr)
+
+ private def emitArgument(av: AnnotationVisitor,
+ name: String,
+ arg: Tree, bcodeStore: BCodeHelpers)(innerClasesStore: bcodeStore.BCInnerClassGen): Unit = {
+ (arg: @unchecked) 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.toString // 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 t: tpd.Select =>
+ if (t.symbol.denot.is(Flags.Enum)) {
+ val edesc = innerClasesStore.typeDescriptor(t.tpe.asInstanceOf[bcodeStore.int.Type]) // the class descriptor of the enumeration class.
+ val evalue = t.symbol.name.toString // value the actual enumeration value.
+ av.visitEnum(name, edesc, evalue)
+ } else {
+ assert(toDenot(t.symbol).name.toTermName.defaultGetterIndex >= 0) // this should be default getter. do not emmit.
+ }
+ 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.isInstanceOf[ImplicitMethodType]) {
+ // generic array method, need to get implicit argument out of the way
+ fun.asInstanceOf[Apply].args
+ } else args
+
+ val flatArgs = actualArgs.flatMap {
+ 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)
+ }
+ }
+
+ override def emitAnnotations(cw: asm.ClassVisitor, annotations: List[Annotation], bcodeStore: BCodeHelpers)
+ (innerClasesStore: bcodeStore.BCInnerClassGen) = {
+ 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.toString, value.asInstanceOf[Tree], bcodeStore)(innerClasesStore)
+ av.visitEnd()
+ }
+
+ override def emitAnnotations(mw: asm.MethodVisitor, annotations: List[Annotation], bcodeStore: BCodeHelpers)
+ (innerClasesStore: bcodeStore.BCInnerClassGen) = {
+ 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) = {
+ 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.toTermName)
+
+ 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).toTermName)
+ }
+
+ 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.toTermName)
+ }
+
+
+ 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, pos)
+ def warning(pos: Position, msg: String): Unit = ctx.warning(msg, pos)
+ def abort(msg: String): Nothing = {
+ ctx.error(msg)
+ throw new RuntimeException(msg)
+ }
+
+ def emitAsmp: Option[String] = None
+
+ def shouldEmitJumpAfterLabels = true
+
+ def dumpClasses: Option[String] =
+ if (ctx.settings.Ydumpclasses.isDefault) None
+ else Some(ctx.settings.Ydumpclasses.value)
+
+ def mainClass: Option[String] =
+ if (ctx.settings.mainClass.isDefault) None
+ else Some(ctx.settings.mainClass.value)
+ def setMainClass(name: String): Unit = ctx.settings.mainClass.update(name)
+
+
+ def noForwarders: Boolean = ctx.settings.noForwarders.value
+ def debuglevel: Int = 3 // 0 -> no debug info; 1-> filename; 2-> lines; 3-> varnames
+ def settings_debug: Boolean = ctx.settings.debug.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 <: 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 = nme.MODULE_INSTANCE_FIELD.toString
+
+ def internalNameString(offset: Int, length: Int): String = new String(Names.chrs, offset, length)
+
+ def newTermName(prefix: String): Name = prefix.toTermName
+
+ val Flag_SYNTHETIC: Flags = Flags.Synthetic.bits
+ val Flag_METHOD: Flags = Flags.Method.bits
+ val ExcludedForwarderFlags: Flags = {
+ Flags.Specialized | Flags.Lifted | Flags.Protected | Flags.JavaStatic |
+ Flags.ExpandedName | Flags.Bridge | Flags.VBridge | Flags.Private | Flags.Macro
+ }.bits
+
+
+ def isQualifierSafeToElide(qual: Tree): Boolean = tpd.isIdempotentExpr(qual)
+ def desugarIdent(i: Ident): Option[tpd.Select] = {
+ i.tpe match {
+ case TermRef(prefix: TermRef, name) =>
+ Some(tpd.ref(prefix).select(i.symbol))
+ case TermRef(prefix: ThisType, name) =>
+ Some(tpd.This(prefix.cls).select(i.symbol))
+ case TermRef(NoPrefix, name) =>
+ if (i.symbol is Flags.Method) Some(This(i.symbol.topLevelClass).select(i.symbol)) // workaround #342 todo: remove after fixed
+ else None
+ case _ => None
+ }
+ }
+ def getLabelDefOwners(tree: Tree): Map[Tree, List[LabelDef]] = {
+ // for each rhs of a defdef returns LabelDefs inside this DefDef
+ val res = new collection.mutable.HashMap[Tree, List[LabelDef]]()
+
+ val t = new TreeTraverser {
+ var outerRhs: Tree = tree
+
+ def traverse(tree: tpd.Tree)(implicit ctx: Context): Unit = tree match {
+ case t: DefDef =>
+ if (t.symbol is Flags.Label)
+ res.put(outerRhs, t :: res.getOrElse(outerRhs, Nil))
+ else outerRhs = t
+ traverseChildren(t)
+ case _ => traverseChildren(tree)
+ }
+ }
+
+ t.traverse(tree)
+ res.toMap
+ }
+
+ // todo: remove
+ def isMaybeBoxed(sym: Symbol) = {
+ (sym == ObjectClass) ||
+ (sym == JavaSerializableClass) ||
+ (sym == defn.ComparableClass) ||
+ (sym derivesFrom BoxedNumberClass) ||
+ (sym derivesFrom BoxedCharacterClass) ||
+ (sym derivesFrom BoxedBooleanClass)
+ }
+
+ def getSingleOutput: Option[AbstractFile] = None // todo: implement
+
+
+ def getGenericSignature(sym: Symbol, owner: Symbol): String = null // todo: implement
+
+ def getStaticForwarderGenericSignature(sym: Symbol, moduleClass: Symbol): String = null // todo: implement
+
+
+ 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
+ }
+
+
+ implicit def treeHelper(a: Tree): TreeHelper = new TreeHelper {
+ def symbol: Symbol = a.symbol
+
+ def pos: Position = a.pos
+
+ def isEmpty: Boolean = a.isEmpty
+
+ def tpe: Type = a.tpe
+
+ def exists(pred: (Tree) => Boolean): Boolean = a.find(pred).isDefined
+ }
+
+
+ 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
+
+ def args: List[Tree] = List.empty // those arguments to scala-defined annotations. they are never emmited
+ }
+
+ def assocsFromApply(tree: Tree) = {
+ tree match {
+ case Apply(fun, args) =>
+ fun.tpe.widen match {
+ case MethodType(names, _) =>
+ names zip args
+ }
+ }
+ }
+
+
+ implicit def nameHelper(n: Name): NameHelper = new NameHelper {
+ def toTypeName: Name = n.toTypeName
+ def isTypeName: Boolean = n.isTypeName
+ def toTermName: Name = n.toTermName
+ def dropModule: Name = n.stripModuleClassSuffix
+
+ def len: Int = n.length
+ def offset: Int = n.start
+ def isTermName: Boolean = n.isTermName
+ def startsWith(s: String): Boolean = n.startsWith(s)
+ }
+
+
+ implicit def symHelper(sym: Symbol): SymbolHelper = new SymbolHelper {
+ // names
+ def fullName(sep: Char): String = sym.showFullName
+ def fullName: String = sym.showFullName
+ def simpleName: Name = sym.name
+ def javaSimpleName: Name = toDenot(sym).name // addModuleSuffix(simpleName.dropLocal)
+ def javaBinaryName: Name = toDenot(sym).fullNameSeparated("/") // addModuleSuffix(fullNameInternal('/'))
+ def javaClassName: String = toDenot(sym).fullName.toString// addModuleSuffix(fullNameInternal('.')).toString
+ def name: Name = sym.name
+ def rawname: Name = sym.name // todo ????
+
+ // 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
+
+ // 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 isAnonymousFunction: Boolean = toDenot(sym).isAnonymousFunction
+ def isMethod: Boolean = sym is Flags.Method
+ def isPublic: Boolean = sym.flags.is(Flags.EmptyFlags, 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 isImplClass: Boolean = sym is Flags.ImplClass
+ def isInterface: Boolean = (sym is Flags.PureInterface) || (sym is Flags.Trait)
+ def hasGetter: Boolean = false // used only for generaration of beaninfo todo: implement
+ 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 isStaticMember: Boolean = (sym ne NoSymbol) &&
+ ((sym is Flags.JavaStatic) || (owner is Flags.ImplClass) || toDenot(sym).hasAnnotation(ctx.definitions.ScalaStaticAnnot))
+ // 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 ne defn.NullClass) && (sym ne defn.NothingClass)
+ def isBridge: Boolean = sym is Flags.Bridge
+ def isArtifact: Boolean = sym is Flags.Artifact
+ def hasEnumFlag: Boolean = sym is Flags.Enum
+ 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 is Flags.Abstract) || (sym is Flags.JavaInterface) || (sym is Flags.Trait)
+ 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 is Flags.ImplClass) && sym.isStatic
+ def isJavaEntryPoint: Boolean = CollectEntryPoints.isJavaEntryPoint(sym)
+
+ def isClassConstructor: Boolean = toDenot(sym).isClassConstructor
+
+ /**
+ * 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) && {
+ // scalac uses atPickling here
+ // this would not work if modules are created after pickling
+ // for example by specialization
+ val original = toDenot(sym).initial
+ val validity = original.validFor
+ val shiftedContext = ctx.withPhase(validity.phaseId)
+ toDenot(sym)(shiftedContext).isStatic(shiftedContext)
+ }
+
+ def isStaticConstructor: Boolean = (isStaticMember && isClassConstructor) || (sym.name eq core.Names.STATIC_CONSTRUCTOR)
+
+
+ // navigation
+ def owner: Symbol = toDenot(sym).owner
+ def rawowner: Symbol = {
+ originalOwner
+ }
+ def originalOwner: 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) {
+ val original = toDenot(sym).initial
+ val validity = original.validFor
+ val shiftedContext = ctx.withPhase(validity.phaseId)
+ val r = toDenot(sym)(shiftedContext).maybeOwner.lexicallyEnclosingClass(shiftedContext)
+ r
+ } else NoSymbol
+ 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
+
+
+
+ // 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) { implicit ctx =>
+ toDenot(sym).info.decls.filter(_.isClass).toList
+ }
+ else Nil
+
+ def annotations: List[Annotation] = Nil
+ 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 exmaple) 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)).toList
+ }
+ def methodSymbols: List[Symbol] =
+ for (f <- toDenot(sym).info.decls.toList if f.isMethod && f.isTerm && !f.isModule) yield f
+ def serialVUID: Option[Long] = None
+
+
+ def freshLocal(cunit: CompilationUnit, name: String, tpe: Type, pos: Position, flags: Flags): Symbol = {
+ ctx.newSymbol(sym, name.toTermName, FlagSet(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.pos
+
+ 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(_.symbol.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 && sym.isStatic
+
+ /**
+ * 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 = sym.isStatic
+
+
+ def addRemoteRemoteExceptionAnnotation: Unit = ()
+
+ def samMethod(): Symbol =
+ toDenot(sym).info.abstractTermMembers.headOption.getOrElse(toDenot(sym).info.member(nme.apply)).symbol
+ }
+
+
+ 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 decls: List[Symbol] = tp.decls.map(_.symbol).toList
+
+ def members: List[Symbol] =
+ tp.memberDenots(takeAllFilter, (name, buf) => buf ++= tp.member(name).alternatives).map(_.symbol).toList
+
+ def typeSymbol: Symbol = tp.widenDealias.typeSymbol
+
+ def =:=(other: Type): Boolean = tp =:= other
+
+ def membersBasedOnFlags(excludedFlags: Flags, requiredFlags: Flags): List[Symbol] =
+ tp.membersBasedOnFlags(FlagSet(requiredFlags), FlagSet(excludedFlags)).map(_.symbol).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)
+
+ case t: MethodType => // triggers for LabelDefs
+ t.resultType.toTypeKind(ct)(storage)
+
+ /* 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 issues.scala-lang.org.")
+
+ 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 emmit 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
+ }
+
+
+
+ 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 = field.const
+ }
+
+ object Throw extends ThrowDeconstructor {
+ def get = field.args.head
+
+ override def unapply(s: Throw): DottyBackendInterface.this.Throw.type = {
+ if (s.fun.symbol eq defn.throwMethod) {
+ field = s
+ } else {
+ field = null
+ }
+ this
+ }
+ }
+
+ object New extends NewDeconstructor {
+ def get = field.tpt.tpe
+ }
+
+ object This extends ThisDeconstructor {
+ def get = field.qual.name
+ def apply(s: Symbol): This = tpd.This(s.asClass)
+ }
+
+ object Return extends ReturnDeconstructor {
+ def get = field.expr
+ }
+
+ object Ident extends IdentDeconstructor {
+ def get = field.name
+ }
+
+ object Alternative extends AlternativeDeconstructor {
+ def get = field.trees
+ }
+
+ object Constant extends ConstantDeconstructor {
+ def get = 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 = field.name
+ def _2: List[Symbol] = field.vparamss.flatMap(_.map(_.symbol))
+ def _3: Tree = field.rhs
+
+ override def unapply(s: LabelDef): DottyBackendInterface.this.LabelDef.type = {
+ if (s.symbol is Flags.Label) this.field = s
+ else this.field = null
+ this
+ }
+ }
+
+ 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 _ =>
+ ctx.error(s"JavaSeqArray with type ${field.tpe} reached backend: $field", field.pos)
+ ErrorType
+ }
+ 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] = 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
+ }
+
+ object Closure extends ClosureDeconstructor {
+ def _1 = field.env
+ def _2 = field.meth
+ def _3 = {
+ 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(("scala.compat.java8.JProcedure" + arity).toTermName)
+ else ctx.requiredClass(("scala.compat.java8.JFunction" + arity).toTermName)
+ }
+ }
+ }
+
+ def currentUnit = ctx.compilationUnit
+}
diff --git a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala
new file mode 100644
index 000000000..65dcb6c79
--- /dev/null
+++ b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala
@@ -0,0 +1,433 @@
+package dotty.tools.backend.jvm
+
+import dotty.tools.dotc.CompilationUnit
+import dotty.tools.dotc.ast.Trees.{ValDef, PackageDef}
+import dotty.tools.dotc.ast.tpd
+import dotty.tools.dotc.core.Phases.Phase
+import dotty.tools.dotc.core.Names.TypeName
+
+import scala.collection.mutable
+import scala.tools.asm.{CustomAttr, ClassVisitor, MethodVisitor, FieldVisitor}
+import scala.tools.nsc.Settings
+import scala.tools.nsc.backend.jvm._
+import dotty.tools.dotc
+import dotty.tools.dotc.backend.jvm.DottyPrimitives
+import dotty.tools.dotc.transform.Erasure
+
+import dotty.tools.dotc.interfaces
+import java.util.Optional
+
+import scala.reflect.ClassTag
+import dotty.tools.dotc.core._
+import Periods._
+import SymDenotations._
+import Contexts._
+import Types._
+import Symbols._
+import Denotations._
+import Phases._
+import java.lang.AssertionError
+import java.io.{ File => JFile }
+import scala.tools.asm
+import scala.tools.asm.tree._
+import dotty.tools.dotc.util.{Positions, DotClass}
+import tpd._
+import StdNames._
+import scala.reflect.io.{Directory, PlainDirectory, AbstractFile}
+
+import scala.tools.nsc.backend.jvm.opt.LocalOpt
+
+class GenBCode extends Phase {
+ def phaseName: String = "genBCode"
+ private val entryPoints = new mutable.HashSet[Symbol]()
+ def registerEntryPoint(sym: Symbol) = entryPoints += sym
+
+ private val superCallsMap = new mutable.HashMap[Symbol, Set[ClassSymbol]]()
+ def registerSuperCall(sym: Symbol, calls: ClassSymbol) = {
+ val old = superCallsMap.getOrElse(sym, Set.empty)
+ superCallsMap.put(sym, old + calls)
+ }
+
+ def outputDir(implicit ctx: Context): AbstractFile =
+ new PlainDirectory(new Directory(new JFile(ctx.settings.d.value)))
+
+ def run(implicit ctx: Context): Unit = {
+ new GenBCodePipeline(entryPoints.toList,
+ new DottyBackendInterface(outputDir, superCallsMap.toMap)(ctx))(ctx).run(ctx.compilationUnit.tpdTree)
+ entryPoints.clear()
+ }
+}
+
+class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInterface)(implicit val ctx: Context) extends BCodeSyncAndTry{
+
+ var tree: Tree = _
+
+ val sourceFile = ctx.compilationUnit.source
+
+ /** Convert a `scala.reflect.io.AbstractFile` into a
+ * `dotty.tools.dotc.interfaces.AbstractFile`.
+ */
+ private[this] def convertAbstractFile(absfile: scala.reflect.io.AbstractFile): interfaces.AbstractFile =
+ new interfaces.AbstractFile {
+ override def name = absfile.name
+ override def path = absfile.path
+ override def jfile = Optional.ofNullable(absfile.file)
+ }
+
+ final class PlainClassBuilder(cunit: CompilationUnit) extends SyncAndTryBuilder(cunit)
+
+
+// class BCodePhase() {
+
+ private var bytecodeWriter : BytecodeWriter = null
+ private var mirrorCodeGen : JMirrorBuilder = null
+ private var beanInfoCodeGen : JBeanInfoBuilder = null
+
+ /* ---------------- q1 ---------------- */
+
+ case class Item1(arrivalPos: Int, cd: TypeDef, cunit: CompilationUnit) {
+ def isPoison = { arrivalPos == Int.MaxValue }
+ }
+ private val poison1 = Item1(Int.MaxValue, null, ctx.compilationUnit)
+ private val q1 = new java.util.LinkedList[Item1]
+
+ /* ---------------- q2 ---------------- */
+
+ case class Item2(arrivalPos: Int,
+ mirror: asm.tree.ClassNode,
+ plain: asm.tree.ClassNode,
+ bean: asm.tree.ClassNode,
+ outFolder: scala.tools.nsc.io.AbstractFile) {
+ def isPoison = { arrivalPos == Int.MaxValue }
+ }
+
+ private val poison2 = Item2(Int.MaxValue, null, null, null, null)
+ private val q2 = new _root_.java.util.LinkedList[Item2]
+
+ /* ---------------- q3 ---------------- */
+
+ /*
+ * An item of queue-3 (the last queue before serializing to disk) contains three of these
+ * (one for each of mirror, plain, and bean classes).
+ *
+ * @param jclassName internal name of the class
+ * @param jclassBytes bytecode emitted for the class SubItem3 represents
+ */
+ case class SubItem3(
+ jclassName: String,
+ jclassBytes: Array[Byte]
+ )
+
+ case class Item3(arrivalPos: Int,
+ mirror: SubItem3,
+ plain: SubItem3,
+ bean: SubItem3,
+ outFolder: scala.tools.nsc.io.AbstractFile) {
+
+ def isPoison = { arrivalPos == Int.MaxValue }
+ }
+ private val i3comparator = new java.util.Comparator[Item3] {
+ override def compare(a: Item3, b: Item3) = {
+ if (a.arrivalPos < b.arrivalPos) -1
+ else if (a.arrivalPos == b.arrivalPos) 0
+ else 1
+ }
+ }
+ private val poison3 = Item3(Int.MaxValue, null, null, null, null)
+ private val q3 = new java.util.PriorityQueue[Item3](1000, i3comparator)
+
+ /*
+ * Pipeline that takes ClassDefs from queue-1, lowers them into an intermediate form, placing them on queue-2
+ */
+ class Worker1(needsOutFolder: Boolean) {
+
+ val caseInsensitively = scala.collection.mutable.Map.empty[String, Symbol]
+
+ def run(): Unit = {
+ while (true) {
+ val item = q1.poll
+ if (item.isPoison) {
+ q2 add poison2
+ return
+ }
+ else {
+ try { /*withCurrentUnit(item.cunit)*/(visit(item)) }
+ catch {
+ case ex: Throwable =>
+ ex.printStackTrace()
+ ctx.error(s"Error while emitting ${int.sourceFileFor(item.cunit)}\n${ex.getMessage}")
+ }
+ }
+ }
+ }
+
+ /*
+ * Checks for duplicate internal names case-insensitively,
+ * builds ASM ClassNodes for mirror, plain, and bean classes;
+ * enqueues them in queue-2.
+ *
+ */
+ def visit(item: Item1) = {
+ val Item1(arrivalPos, cd, cunit) = item
+ val claszSymbol = cd.symbol
+
+ // GenASM checks this before classfiles are emitted, https://github.com/scala/scala/commit/e4d1d930693ac75d8eb64c2c3c69f2fc22bec739
+ // todo: add back those checks
+ /*val lowercaseJavaClassName = claszSymbol.javaClassName.toLowerCase
+ caseInsensitively.get(lowercaseJavaClassName) match {
+ case None =>
+ caseInsensitively.put(lowercaseJavaClassName, claszSymbol)
+ case Some(dupClassSym) =>
+ reporter.warning(
+ claszSymbol.pos,
+ s"Class ${claszSymbol.javaClassName} differs only in case from ${dupClassSym.javaClassName}. " +
+ "Such classes will overwrite one another on case-insensitive filesystems."
+ )
+ }*/
+
+ // -------------- mirror class, if needed --------------
+ val mirrorC =
+ if (int.symHelper(claszSymbol).isTopLevelModuleClass) {
+ if (claszSymbol.companionClass == NoSymbol) {
+ mirrorCodeGen.genMirrorClass(claszSymbol, cunit)
+ } else {
+ ctx.log(s"No mirror class for module with linked class: ${claszSymbol.fullName}")
+ null
+ }
+ } else null
+
+ // -------------- "plain" class --------------
+ val pcb = new PlainClassBuilder(cunit)
+ pcb.genPlainClass(cd)
+ val outF = if (needsOutFolder) getOutFolder(claszSymbol, pcb.thisName) else null;
+ val plainC = pcb.cnode
+
+ if (claszSymbol.isClass) // @DarkDimius is this test needed here?
+ for (binary <- ctx.compilationUnit.pickled.get(claszSymbol.asClass)) {
+ val dataAttr = new CustomAttr(nme.TASTYATTR.toString, binary)
+ (if (mirrorC ne null) mirrorC else plainC).visitAttribute(dataAttr)
+ }
+
+ // -------------- bean info class, if needed --------------
+ val beanC =
+ if (claszSymbol hasAnnotation int.BeanInfoAttr) {
+ beanInfoCodeGen.genBeanInfoClass(
+ claszSymbol, cunit,
+ int.symHelper(claszSymbol).fieldSymbols,
+ int.symHelper(claszSymbol).methodSymbols
+ )
+ } else null
+
+ // ----------- hand over to pipeline-2
+
+ val item2 =
+ Item2(arrivalPos,
+ mirrorC, plainC, beanC,
+ outF)
+
+ q2 add item2 // at the very end of this method so that no Worker2 thread starts mutating before we're done.
+
+ } // end of method visit(Item1)
+
+ } // end of class BCodePhase.Worker1
+
+ /*
+ * Pipeline that takes ClassNodes from queue-2. The unit of work depends on the optimization level:
+ *
+ * (a) no optimization involves:
+ * - converting the plain ClassNode to byte array and placing it on queue-3
+ */
+ class Worker2 {
+ lazy val localOpt = new LocalOpt(new Settings())
+
+ def localOptimizations(classNode: ClassNode): Unit = {
+ /*BackendStats.timed(BackendStats.methodOptTimer)*/(localOpt.methodOptimizations(classNode))
+ }
+
+ def run(): Unit = {
+ while (true) {
+ val item = q2.poll
+ if (item.isPoison) {
+ q3 add poison3
+ return
+ }
+ else {
+ try {
+ localOptimizations(item.plain)
+ addToQ3(item)
+ } catch {
+ case ex: Throwable =>
+ ex.printStackTrace()
+ ctx.error(s"Error while emitting ${item.plain.name}\n${ex.getMessage}")
+ }
+ }
+ }
+ }
+
+ private def addToQ3(item: Item2) = {
+
+ def getByteArray(cn: asm.tree.ClassNode): Array[Byte] = {
+ val cw = new CClassWriter(extraProc)
+ cn.accept(cw)
+ cw.toByteArray
+ }
+
+ val Item2(arrivalPos, mirror, plain, bean, outFolder) = item
+
+ val mirrorC = if (mirror == null) null else SubItem3(mirror.name, getByteArray(mirror))
+ val plainC = SubItem3(plain.name, getByteArray(plain))
+ val beanC = if (bean == null) null else SubItem3(bean.name, getByteArray(bean))
+
+ if (AsmUtils.traceSerializedClassEnabled && plain.name.contains(AsmUtils.traceSerializedClassPattern)) {
+ if (mirrorC != null) AsmUtils.traceClass(mirrorC.jclassBytes)
+ AsmUtils.traceClass(plainC.jclassBytes)
+ if (beanC != null) AsmUtils.traceClass(beanC.jclassBytes)
+ }
+
+ q3 add Item3(arrivalPos, mirrorC, plainC, beanC, outFolder)
+
+ }
+
+ } // end of class BCodePhase.Worker2
+
+ var arrivalPos = 0
+
+ /*
+ * A run of the BCodePhase phase comprises:
+ *
+ * (a) set-up steps (most notably supporting maps in `BCodeTypes`,
+ * but also "the" writer where class files in byte-array form go)
+ *
+ * (b) building of ASM ClassNodes, their optimization and serialization.
+ *
+ * (c) tear down (closing the classfile-writer and clearing maps)
+ *
+ */
+ def run(t: Tree) = {
+ this.tree = t
+
+ // val bcodeStart = Statistics.startTimer(BackendStats.bcodeTimer)
+
+ // val initStart = Statistics.startTimer(BackendStats.bcodeInitTimer)
+ arrivalPos = 0 // just in case
+ // scalaPrimitives.init()
+ bTypes.intializeCoreBTypes()
+ // Statistics.stopTimer(BackendStats.bcodeInitTimer, initStart)
+
+ // initBytecodeWriter invokes fullName, thus we have to run it before the typer-dependent thread is activated.
+ bytecodeWriter = initBytecodeWriter(entryPoints)
+ mirrorCodeGen = new JMirrorBuilder
+ beanInfoCodeGen = new JBeanInfoBuilder
+
+ val needsOutfileForSymbol = bytecodeWriter.isInstanceOf[ClassBytecodeWriter]
+ buildAndSendToDisk(needsOutfileForSymbol)
+
+ // closing output files.
+ bytecodeWriter.close()
+ // Statistics.stopTimer(BackendStats.bcodeTimer, bcodeStart)
+
+ if (ctx.compilerCallback != null)
+ ctx.compilerCallback.onSourceCompiled(sourceFile)
+
+ /* TODO Bytecode can be verified (now that all classfiles have been written to disk)
+ *
+ * (1) asm.util.CheckAdapter.verify()
+ * public static void verify(ClassReader cr, ClassLoader loader, boolean dump, PrintWriter pw)
+ * passing a custom ClassLoader to verify inter-dependent classes.
+ * Alternatively,
+ * - an offline-bytecode verifier could be used (e.g. Maxine brings one as separate tool).
+ * - -Xverify:all
+ *
+ * (2) if requested, check-java-signatures, over and beyond the syntactic checks in `getGenericSignature()`
+ *
+ */
+ }
+
+ /*
+ * Sequentially:
+ * (a) place all ClassDefs in queue-1
+ * (b) dequeue one at a time from queue-1, convert it to ASM ClassNode, place in queue-2
+ * (c) dequeue one at a time from queue-2, convert it to byte-array, place in queue-3
+ * (d) serialize to disk by draining queue-3.
+ */
+ private def buildAndSendToDisk(needsOutFolder: Boolean) = {
+
+ feedPipeline1()
+ // val genStart = Statistics.startTimer(BackendStats.bcodeGenStat)
+ (new Worker1(needsOutFolder)).run()
+ // Statistics.stopTimer(BackendStats.bcodeGenStat, genStart)
+
+ (new Worker2).run()
+
+ // val writeStart = Statistics.startTimer(BackendStats.bcodeWriteTimer)
+ drainQ3()
+ // Statistics.stopTimer(BackendStats.bcodeWriteTimer, writeStart)
+
+ }
+
+ /* Feed pipeline-1: place all ClassDefs on q1, recording their arrival position. */
+ private def feedPipeline1() = {
+ def gen(tree: Tree): Unit = {
+ tree match {
+ case EmptyTree => ()
+ 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)
+ arrivalPos += 1
+ }
+ }
+ gen(tree)
+ q1 add poison1
+ }
+
+ /* Pipeline that writes classfile representations to disk. */
+ private def drainQ3() = {
+
+ def sendToDisk(cfr: SubItem3, outFolder: scala.tools.nsc.io.AbstractFile): Unit = {
+ if (cfr != null){
+ val SubItem3(jclassName, jclassBytes) = cfr
+ try {
+ val outFile =
+ if (outFolder == null) null
+ else getFileForClassfile(outFolder, jclassName, ".class")
+ bytecodeWriter.writeClass(jclassName, jclassName, jclassBytes, outFile)
+
+ val className = jclassName.replace('/', '.')
+ if (ctx.compilerCallback != null)
+ ctx.compilerCallback.onClassGenerated(sourceFile, convertAbstractFile(outFile), className)
+ if (ctx.sbtCallback != null)
+ ctx.sbtCallback.generatedClass(sourceFile.jfile.orElse(null), outFile.file, className)
+ }
+ catch {
+ case e: FileConflictException =>
+ ctx.error(s"error writing $jclassName: ${e.getMessage}")
+ }
+ }
+ }
+
+ var moreComing = true
+ // `expected` denotes the arrivalPos whose Item3 should be serialized next
+ var expected = 0
+
+ while (moreComing) {
+ val incoming = q3.poll
+ moreComing = !incoming.isPoison
+ if (moreComing) {
+ val item = incoming
+ val outFolder = item.outFolder
+ sendToDisk(item.mirror, outFolder)
+ sendToDisk(item.plain, outFolder)
+ sendToDisk(item.bean, outFolder)
+ expected += 1
+ }
+ }
+
+ // we're done
+ assert(q1.isEmpty, s"Some ClassDefs remained in the first queue: $q1")
+ assert(q2.isEmpty, s"Some classfiles remained in the second queue: $q2")
+ assert(q3.isEmpty, s"Some classfiles weren't written to disk: $q3")
+
+ }
+ //} // end of class BCodePhase
+}
diff --git a/compiler/src/dotty/tools/backend/jvm/LabelDefs.scala b/compiler/src/dotty/tools/backend/jvm/LabelDefs.scala
new file mode 100644
index 000000000..371396e36
--- /dev/null
+++ b/compiler/src/dotty/tools/backend/jvm/LabelDefs.scala
@@ -0,0 +1,223 @@
+package dotty.tools.backend.jvm
+
+import dotty.tools.dotc.ast.Trees.Thicket
+import dotty.tools.dotc.ast.{Trees, tpd}
+import dotty.tools.dotc.core.Contexts.Context
+import dotty.tools.dotc.core.Types
+import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, MiniPhase, MiniPhaseTransform}
+import dotty.tools.dotc
+import dotty.tools.dotc.backend.jvm.DottyPrimitives
+import dotty.tools.dotc.core.Flags.FlagSet
+import dotty.tools.dotc.transform.Erasure
+import dotty.tools.dotc.transform.SymUtils._
+import java.io.{File => JFile}
+
+import scala.collection.generic.Clearable
+import scala.collection.mutable
+import scala.collection.mutable.{ListBuffer, ArrayBuffer}
+import scala.reflect.ClassTag
+import scala.reflect.internal.util.WeakHashSet
+import scala.reflect.io.{Directory, PlainDirectory, AbstractFile}
+import scala.tools.asm.{ClassVisitor, FieldVisitor, MethodVisitor}
+import scala.tools.nsc.backend.jvm.{BCodeHelpers, BackendInterface}
+import dotty.tools.dotc.core._
+import Periods._
+import SymDenotations._
+import Contexts._
+import Types._
+import Symbols._
+import Denotations._
+import Phases._
+import java.lang.AssertionError
+import dotty.tools.dotc.util.Positions.Position
+import Decorators._
+import tpd._
+import Flags._
+import StdNames.nme
+
+/**
+ * Verifies that each Label DefDef has only a single address to jump back and
+ * reorders them such that they are not nested and this address is a fall-through address for JVM
+ *
+ * ei such code
+ *
+ *
+ * <label> def foo(i: Int) = {
+ * <label> def bar = 0
+ * <label> def dough(i: Int) = if (i == 0) bar else foo(i-1)
+ * dough(i)
+ * }
+ *
+ * foo(100)
+ *
+ * will get rewritten to
+ *
+ * \
+ * <label> def foo(i: Int) = dough(i)
+ * <label> def dough(i: Int) = if (i == 0) bar else foo(i-1)
+ * <label> def bar = 2
+ * foo(100)
+ *
+ * Proposed way to generate this pattern in backend is:
+ *
+ * foo(100)
+ * <jump foo>
+ * <label> def foo(i: Int) = dough(i)
+ * // <jump a> // unreachable
+ * <label> def dough(i: Int) = if (i == 0) bar else foo(i-1)
+ * // <jump a> // unreachable
+ * <label> def bar = 2
+ * // <jump a> // unreachable
+ * <asm point a>
+ *
+ * Unreachable jumps will be eliminated by local dead code analysis.
+ * After JVM is smart enough to remove next-line jumps
+ *
+ * Note that Label DefDefs can be only nested in Block, otherwise no one would be able to call them
+ * Other DefDefs are eliminated
+ */
+class LabelDefs extends MiniPhaseTransform {
+ def phaseName: String = "labelDef"
+
+ val queue = new ArrayBuffer[Tree]()
+ val beingAppended = new mutable.HashSet[Symbol]()
+ var labelLevel = 0
+
+ override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
+ if (tree.symbol is Flags.Label) tree
+ else {
+ collectLabelDefs.clear
+ val newRhs = collectLabelDefs.transform(tree.rhs)
+ val labelCalls = collectLabelDefs.labelCalls
+ var entryPoints = collectLabelDefs.parentLabelCalls
+ var labelDefs = collectLabelDefs.labelDefs
+ var callCounts = collectLabelDefs.callCounts
+
+ // make sure that for every label there's a single location it should return and single entry point
+ // if theres already a location that it returns to that's a failure
+ val disallowed = new mutable.HashMap[Symbol, Tree]()
+ queue.sizeHint(labelCalls.size + entryPoints.size)
+
+ def putLabelDefsNearCallees = new TreeMap() {
+
+ override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = {
+ tree match {
+ case t: Apply if (entryPoints.contains(t)) =>
+ entryPoints = entryPoints - t
+ labelLevel = labelLevel + 1
+ val r = Block(moveLabels(t), t)
+ labelLevel = labelLevel - 1
+ if (labelLevel == 0) beingAppended.clear()
+ r
+ case _ => if (entryPoints.nonEmpty && labelDefs.nonEmpty) super.transform(tree) else tree
+ }
+
+ }
+ }
+
+ def moveLabels(entryPoint: Apply): List[Tree] = {
+ val entrySym = entryPoint.symbol
+ if ((entrySym is Flags.Label) && labelDefs.contains(entrySym)) {
+ val visitedNow = new mutable.HashMap[Symbol, Tree]()
+ val treesToAppend = new ArrayBuffer[Tree]() // order matters. parents should go first
+ treesToAppend += labelDefs(entrySym)
+ queue.clear()
+
+ var visited = 0
+ queue += entryPoint
+ while (visited < queue.size) {
+ val owningLabelDefSym = queue(visited).symbol
+ for (call <- labelCalls(owningLabelDefSym)) {
+ val callSym = call.symbol
+ if (!beingAppended.contains(callSym)) {
+ if (disallowed.contains(callSym)) {
+ val oldCall = disallowed(callSym)
+ ctx.error(s"Multiple return locations for Label $oldCall and $call", callSym.pos)
+ } else {
+ if ((!visitedNow.contains(callSym)) && labelDefs.contains(callSym)) {
+ val defTree = labelDefs(callSym)
+ visitedNow.put(callSym, defTree)
+ val callCount = callCounts(callSym)
+ if (callCount > 1) {
+ if (!treesToAppend.contains(defTree)) {
+ treesToAppend += defTree
+ queue += call
+
+ }
+ } else if (entryPoint.symbol ne callSym) entryPoints += call
+ }
+ }
+ }
+ }
+
+ visited += 1
+ }
+ beingAppended ++= treesToAppend.map(_.symbol)
+ treesToAppend.toList.map(putLabelDefsNearCallees.transform)
+ } else Nil
+ }
+
+
+ val res = cpy.DefDef(tree)(rhs = putLabelDefsNearCallees.transform(newRhs))
+
+ res
+ }
+ }
+
+ object collectLabelDefs extends TreeMap() {
+
+ // label calls from this DefDef
+ var parentLabelCalls: mutable.Set[Tree] = new mutable.HashSet[Tree]()
+ var callCounts: mutable.Map[Symbol, Int] = new mutable.HashMap[Symbol, Int]().withDefaultValue(0)
+
+ def shouldMoveLabel = true
+
+ // labelSymbol -> Defining tree
+ val labelDefs = new mutable.HashMap[Symbol, Tree]()
+ // owner -> all calls by this owner
+ val labelCalls = new mutable.HashMap[Symbol, mutable.Set[Tree]]()
+ var owner: Symbol = null
+
+ def clear = {
+ parentLabelCalls.clear()
+ labelDefs.clear()
+ labelCalls.clear()
+ }
+
+ override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match {
+ case t: Template => t
+ case t: Block =>
+ val r = super.transform(t)
+ r match {
+ case t: Block if t.stats.isEmpty => t.expr
+ case _ => r
+ }
+ case t: DefDef =>
+ assert(t.symbol is Flags.Label)
+
+ val st = parentLabelCalls
+ parentLabelCalls = new mutable.HashSet[Tree]()
+ val symt = owner
+ owner = t.symbol
+
+ val r = super.transform(tree)
+
+ owner = symt
+ labelCalls(r.symbol) = parentLabelCalls
+ parentLabelCalls = st
+
+ if (shouldMoveLabel) {
+ labelDefs(r.symbol) = r
+ EmptyTree
+ } else r
+ case t: Apply if t.symbol is Flags.Label =>
+ val sym = t.symbol
+ parentLabelCalls = parentLabelCalls + t
+ if (owner != sym) callCounts(sym) = callCounts(sym) + 1
+ super.transform(tree)
+ case _ =>
+ super.transform(tree)
+
+ }
+ }
+}
diff --git a/compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala b/compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala
new file mode 100644
index 000000000..0027defa7
--- /dev/null
+++ b/compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala
@@ -0,0 +1,417 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2012 LAMP/EPFL
+ * @author Martin Odersky
+ */
+
+package dotty.tools.dotc
+package backend.jvm
+
+import dotty.tools.backend.jvm.GenBCodePipeline
+import dotty.tools.dotc.ast.Trees.Select
+import dotty.tools.dotc.ast.tpd._
+import dotty.tools.dotc.core.Names.TermName
+import dotty.tools.dotc.core.StdNames
+import dotty.tools.dotc.core.StdNames._
+import dotty.tools.dotc.core.Types.{JavaArrayType, ErrorType, Type}
+
+import scala.collection.{ mutable, immutable }
+
+import core.Contexts.Context
+import core.Symbols.{Symbol, NoSymbol}
+
+/** Scala primitive operations are represented as methods in `Any` and
+ * `AnyVal` subclasses. Here we demultiplex them by providing a mapping
+ * from their symbols to integers. Different methods exist for
+ * different value types, but with the same meaning (like plus, minus,
+ * etc.). They will all be mapped to the same int.
+ *
+ * Note: The three equal methods have the following semantics:
+ * - `"=="` checks for `null`, and if non-null, calls
+ * `java.lang.Object.equals`
+ * `(class: Any; modifier: final)`. Primitive: `EQ`
+ * - `"eq"` usual reference comparison
+ * `(class: AnyRef; modifier: final)`. Primitive: `ID`
+ * - `"equals"` user-defined equality (Java semantics)
+ * `(class: Object; modifier: none)`. Primitive: `EQUALS`
+ *
+ * Inspired from the `scalac` compiler.
+ */
+class DottyPrimitives(ctx: Context) {
+ import scala.tools.nsc.backend.ScalaPrimitives._
+
+ private lazy val primitives: immutable.Map[Symbol, Int] = init
+
+ /** Return the code for the given symbol. */
+ def getPrimitive(sym: Symbol): Int = {
+ primitives(sym)
+ }
+
+ /**
+ * Return the primitive code of the given operation. If the
+ * operation is an array get/set, we inspect the type of the receiver
+ * to demux the operation.
+ *
+ * @param fun The method symbol
+ * @param tpe The type of the receiver object. It is used only for array
+ * operations
+ */
+ def getPrimitive(app: Apply, tpe: Type)(implicit ctx: Context): Int = {
+ val fun = app.fun.symbol
+ val defn = ctx.definitions
+ val code = app.fun match {
+ case Select(_, nme.primitive.arrayLength) =>
+ LENGTH
+ case Select(_, nme.primitive.arrayUpdate) =>
+ UPDATE
+ case Select(_, nme.primitive.arrayApply) =>
+ APPLY
+ case _ => getPrimitive(fun)
+ }
+
+ def elementType: Type = tpe.widenDealias match {
+ case defn.ArrayOf(el) => el
+ case JavaArrayType(el) => el
+ case _ =>
+ ctx.error(s"expected Array $tpe")
+ ErrorType
+ }
+
+ code match {
+
+ case APPLY =>
+ defn.scalaClassName(elementType) match {
+ case tpnme.Boolean => ZARRAY_GET
+ case tpnme.Byte => BARRAY_GET
+ case tpnme.Short => SARRAY_GET
+ case tpnme.Char => CARRAY_GET
+ case tpnme.Int => IARRAY_GET
+ case tpnme.Long => LARRAY_GET
+ case tpnme.Float => FARRAY_GET
+ case tpnme.Double => DARRAY_GET
+ case _ => OARRAY_GET
+ }
+
+ case UPDATE =>
+ defn.scalaClassName(elementType) match {
+ case tpnme.Boolean => ZARRAY_SET
+ case tpnme.Byte => BARRAY_SET
+ case tpnme.Short => SARRAY_SET
+ case tpnme.Char => CARRAY_SET
+ case tpnme.Int => IARRAY_SET
+ case tpnme.Long => LARRAY_SET
+ case tpnme.Float => FARRAY_SET
+ case tpnme.Double => DARRAY_SET
+ case _ => OARRAY_SET
+ }
+
+ case LENGTH =>
+ defn.scalaClassName(elementType) match {
+ case tpnme.Boolean => ZARRAY_LENGTH
+ case tpnme.Byte => BARRAY_LENGTH
+ case tpnme.Short => SARRAY_LENGTH
+ case tpnme.Char => CARRAY_LENGTH
+ case tpnme.Int => IARRAY_LENGTH
+ case tpnme.Long => LARRAY_LENGTH
+ case tpnme.Float => FARRAY_LENGTH
+ case tpnme.Double => DARRAY_LENGTH
+ case _ => OARRAY_LENGTH
+ }
+
+ case _ =>
+ code
+ }
+ }
+
+ /** Initialize the primitive map */
+ private def init: immutable.Map[Symbol, Int] = {
+
+ implicit val ctx: Context = this.ctx
+
+ import core.Symbols.defn
+ val primitives = new mutable.HashMap[Symbol, Int]()
+
+ /** Add a primitive operation to the map */
+ def addPrimitive(s: Symbol, code: Int): Unit = {
+ assert(!(primitives contains s), "Duplicate primitive " + s)
+ primitives(s) = code
+ }
+
+ def addPrimitives(cls: Symbol, method: TermName, code: Int)(implicit ctx: Context): Unit = {
+ val alts = cls.info.member(method).alternatives.map(_.symbol)
+ if (alts.isEmpty)
+ ctx.error(s"Unknown primitive method $cls.$method")
+ else alts foreach (s =>
+ addPrimitive(s,
+ s.info.paramTypess match {
+ case List(tp :: _) if code == ADD && tp =:= ctx.definitions.StringType => CONCAT
+ case _ => code
+ }
+ )
+ )
+ }
+
+ // scala.Any
+ addPrimitive(defn.Any_==, EQ)
+ addPrimitive(defn.Any_!=, NE)
+ addPrimitive(defn.Any_isInstanceOf, IS)
+ addPrimitive(defn.Any_asInstanceOf, AS)
+ addPrimitive(defn.Any_##, HASH)
+
+ // java.lang.Object
+ addPrimitive(defn.Object_eq, ID)
+ addPrimitive(defn.Object_ne, NI)
+ /* addPrimitive(defn.Any_==, EQ)
+ addPrimitive(defn.Any_!=, NE)*/
+ addPrimitive(defn.Object_synchronized, SYNCHRONIZED)
+ /*addPrimitive(defn.Any_isInstanceOf, IS)
+ addPrimitive(defn.Any_asInstanceOf, AS)*/
+
+ // java.lang.String
+ addPrimitive(defn.String_+, CONCAT)
+
+ import core.StdNames.nme
+
+ // scala.Array
+ lazy val ArrayClass = defn.ArrayClass
+ addPrimitives(ArrayClass, nme.length, LENGTH)
+ addPrimitives(ArrayClass, nme.apply, APPLY)
+ addPrimitives(ArrayClass, nme.update, UPDATE)
+
+ // scala.Boolean
+ lazy val BooleanClass = defn.BooleanClass
+ addPrimitives(BooleanClass, nme.EQ, EQ)
+ addPrimitives(BooleanClass, nme.NE, NE)
+ addPrimitives(BooleanClass, nme.UNARY_!, ZNOT)
+ addPrimitives(BooleanClass, nme.ZOR, ZOR)
+ addPrimitives(BooleanClass, nme.ZAND, ZAND)
+ addPrimitives(BooleanClass, nme.OR, OR)
+ addPrimitives(BooleanClass, nme.AND, AND)
+ addPrimitives(BooleanClass, nme.XOR, XOR)
+
+ // scala.Byte
+ lazy val ByteClass = defn.ByteClass
+ addPrimitives(ByteClass, nme.EQ, EQ)
+ addPrimitives(ByteClass, nme.NE, NE)
+ addPrimitives(ByteClass, nme.ADD, ADD)
+ addPrimitives(ByteClass, nme.SUB, SUB)
+ addPrimitives(ByteClass, nme.MUL, MUL)
+ addPrimitives(ByteClass, nme.DIV, DIV)
+ addPrimitives(ByteClass, nme.MOD, MOD)
+ addPrimitives(ByteClass, nme.LT, LT)
+ addPrimitives(ByteClass, nme.LE, LE)
+ addPrimitives(ByteClass, nme.GT, GT)
+ addPrimitives(ByteClass, nme.GE, GE)
+ addPrimitives(ByteClass, nme.XOR, XOR)
+ addPrimitives(ByteClass, nme.OR, OR)
+ addPrimitives(ByteClass, nme.AND, AND)
+ addPrimitives(ByteClass, nme.LSL, LSL)
+ addPrimitives(ByteClass, nme.LSR, LSR)
+ addPrimitives(ByteClass, nme.ASR, ASR)
+ // conversions
+ addPrimitives(ByteClass, nme.toByte, B2B)
+ addPrimitives(ByteClass, nme.toShort, B2S)
+ addPrimitives(ByteClass, nme.toChar, B2C)
+ addPrimitives(ByteClass, nme.toInt, B2I)
+ addPrimitives(ByteClass, nme.toLong, B2L)
+ // unary methods
+ addPrimitives(ByteClass, nme.UNARY_+, POS)
+ addPrimitives(ByteClass, nme.UNARY_-, NEG)
+ addPrimitives(ByteClass, nme.UNARY_~, NOT)
+
+ addPrimitives(ByteClass, nme.toFloat, B2F)
+ addPrimitives(ByteClass, nme.toDouble, B2D)
+
+ // scala.Short
+ lazy val ShortClass = defn.ShortClass
+ addPrimitives(ShortClass, nme.EQ, EQ)
+ addPrimitives(ShortClass, nme.NE, NE)
+ addPrimitives(ShortClass, nme.ADD, ADD)
+ addPrimitives(ShortClass, nme.SUB, SUB)
+ addPrimitives(ShortClass, nme.MUL, MUL)
+ addPrimitives(ShortClass, nme.DIV, DIV)
+ addPrimitives(ShortClass, nme.MOD, MOD)
+ addPrimitives(ShortClass, nme.LT, LT)
+ addPrimitives(ShortClass, nme.LE, LE)
+ addPrimitives(ShortClass, nme.GT, GT)
+ addPrimitives(ShortClass, nme.GE, GE)
+ addPrimitives(ShortClass, nme.XOR, XOR)
+ addPrimitives(ShortClass, nme.OR, OR)
+ addPrimitives(ShortClass, nme.AND, AND)
+ addPrimitives(ShortClass, nme.LSL, LSL)
+ addPrimitives(ShortClass, nme.LSR, LSR)
+ addPrimitives(ShortClass, nme.ASR, ASR)
+ // conversions
+ addPrimitives(ShortClass, nme.toByte, S2B)
+ addPrimitives(ShortClass, nme.toShort, S2S)
+ addPrimitives(ShortClass, nme.toChar, S2C)
+ addPrimitives(ShortClass, nme.toInt, S2I)
+ addPrimitives(ShortClass, nme.toLong, S2L)
+ // unary methods
+ addPrimitives(ShortClass, nme.UNARY_+, POS)
+ addPrimitives(ShortClass, nme.UNARY_-, NEG)
+ addPrimitives(ShortClass, nme.UNARY_~, NOT)
+
+ addPrimitives(ShortClass, nme.toFloat, S2F)
+ addPrimitives(ShortClass, nme.toDouble, S2D)
+
+ // scala.Char
+ lazy val CharClass = defn.CharClass
+ addPrimitives(CharClass, nme.EQ, EQ)
+ addPrimitives(CharClass, nme.NE, NE)
+ addPrimitives(CharClass, nme.ADD, ADD)
+ addPrimitives(CharClass, nme.SUB, SUB)
+ addPrimitives(CharClass, nme.MUL, MUL)
+ addPrimitives(CharClass, nme.DIV, DIV)
+ addPrimitives(CharClass, nme.MOD, MOD)
+ addPrimitives(CharClass, nme.LT, LT)
+ addPrimitives(CharClass, nme.LE, LE)
+ addPrimitives(CharClass, nme.GT, GT)
+ addPrimitives(CharClass, nme.GE, GE)
+ addPrimitives(CharClass, nme.XOR, XOR)
+ addPrimitives(CharClass, nme.OR, OR)
+ addPrimitives(CharClass, nme.AND, AND)
+ addPrimitives(CharClass, nme.LSL, LSL)
+ addPrimitives(CharClass, nme.LSR, LSR)
+ addPrimitives(CharClass, nme.ASR, ASR)
+ // conversions
+ addPrimitives(CharClass, nme.toByte, C2B)
+ addPrimitives(CharClass, nme.toShort, C2S)
+ addPrimitives(CharClass, nme.toChar, C2C)
+ addPrimitives(CharClass, nme.toInt, C2I)
+ addPrimitives(CharClass, nme.toLong, C2L)
+ // unary methods
+ addPrimitives(CharClass, nme.UNARY_+, POS)
+ addPrimitives(CharClass, nme.UNARY_-, NEG)
+ addPrimitives(CharClass, nme.UNARY_~, NOT)
+ addPrimitives(CharClass, nme.toFloat, C2F)
+ addPrimitives(CharClass, nme.toDouble, C2D)
+
+ // scala.Int
+ lazy val IntClass = defn.IntClass
+ addPrimitives(IntClass, nme.EQ, EQ)
+ addPrimitives(IntClass, nme.NE, NE)
+ addPrimitives(IntClass, nme.ADD, ADD)
+ addPrimitives(IntClass, nme.SUB, SUB)
+ addPrimitives(IntClass, nme.MUL, MUL)
+ addPrimitives(IntClass, nme.DIV, DIV)
+ addPrimitives(IntClass, nme.MOD, MOD)
+ addPrimitives(IntClass, nme.LT, LT)
+ addPrimitives(IntClass, nme.LE, LE)
+ addPrimitives(IntClass, nme.GT, GT)
+ addPrimitives(IntClass, nme.GE, GE)
+ addPrimitives(IntClass, nme.XOR, XOR)
+ addPrimitives(IntClass, nme.OR, OR)
+ addPrimitives(IntClass, nme.AND, AND)
+ addPrimitives(IntClass, nme.LSL, LSL)
+ addPrimitives(IntClass, nme.LSR, LSR)
+ addPrimitives(IntClass, nme.ASR, ASR)
+ // conversions
+ addPrimitives(IntClass, nme.toByte, I2B)
+ addPrimitives(IntClass, nme.toShort, I2S)
+ addPrimitives(IntClass, nme.toChar, I2C)
+ addPrimitives(IntClass, nme.toInt, I2I)
+ addPrimitives(IntClass, nme.toLong, I2L)
+ // unary methods
+ addPrimitives(IntClass, nme.UNARY_+, POS)
+ addPrimitives(IntClass, nme.UNARY_-, NEG)
+ addPrimitives(IntClass, nme.UNARY_~, NOT)
+ addPrimitives(IntClass, nme.toFloat, I2F)
+ addPrimitives(IntClass, nme.toDouble, I2D)
+
+ // scala.Long
+ lazy val LongClass = defn.LongClass
+ addPrimitives(LongClass, nme.EQ, EQ)
+ addPrimitives(LongClass, nme.NE, NE)
+ addPrimitives(LongClass, nme.ADD, ADD)
+ addPrimitives(LongClass, nme.SUB, SUB)
+ addPrimitives(LongClass, nme.MUL, MUL)
+ addPrimitives(LongClass, nme.DIV, DIV)
+ addPrimitives(LongClass, nme.MOD, MOD)
+ addPrimitives(LongClass, nme.LT, LT)
+ addPrimitives(LongClass, nme.LE, LE)
+ addPrimitives(LongClass, nme.GT, GT)
+ addPrimitives(LongClass, nme.GE, GE)
+ addPrimitives(LongClass, nme.XOR, XOR)
+ addPrimitives(LongClass, nme.OR, OR)
+ addPrimitives(LongClass, nme.AND, AND)
+ addPrimitives(LongClass, nme.LSL, LSL)
+ addPrimitives(LongClass, nme.LSR, LSR)
+ addPrimitives(LongClass, nme.ASR, ASR)
+ // conversions
+ addPrimitives(LongClass, nme.toByte, L2B)
+ addPrimitives(LongClass, nme.toShort, L2S)
+ addPrimitives(LongClass, nme.toChar, L2C)
+ addPrimitives(LongClass, nme.toInt, L2I)
+ addPrimitives(LongClass, nme.toLong, L2L)
+ // unary methods
+ addPrimitives(LongClass, nme.UNARY_+, POS)
+ addPrimitives(LongClass, nme.UNARY_-, NEG)
+ addPrimitives(LongClass, nme.UNARY_~, NOT)
+ addPrimitives(LongClass, nme.toFloat, L2F)
+ addPrimitives(LongClass, nme.toDouble, L2D)
+
+ // scala.Float
+ lazy val FloatClass = defn.FloatClass
+ addPrimitives(FloatClass, nme.EQ, EQ)
+ addPrimitives(FloatClass, nme.NE, NE)
+ addPrimitives(FloatClass, nme.ADD, ADD)
+ addPrimitives(FloatClass, nme.SUB, SUB)
+ addPrimitives(FloatClass, nme.MUL, MUL)
+ addPrimitives(FloatClass, nme.DIV, DIV)
+ addPrimitives(FloatClass, nme.MOD, MOD)
+ addPrimitives(FloatClass, nme.LT, LT)
+ addPrimitives(FloatClass, nme.LE, LE)
+ addPrimitives(FloatClass, nme.GT, GT)
+ addPrimitives(FloatClass, nme.GE, GE)
+ // conversions
+ addPrimitives(FloatClass, nme.toByte, F2B)
+ addPrimitives(FloatClass, nme.toShort, F2S)
+ addPrimitives(FloatClass, nme.toChar, F2C)
+ addPrimitives(FloatClass, nme.toInt, F2I)
+ addPrimitives(FloatClass, nme.toLong, F2L)
+ addPrimitives(FloatClass, nme.toFloat, F2F)
+ addPrimitives(FloatClass, nme.toDouble, F2D)
+ // unary methods
+ addPrimitives(FloatClass, nme.UNARY_+, POS)
+ addPrimitives(FloatClass, nme.UNARY_-, NEG)
+
+ // scala.Double
+ lazy val DoubleClass = defn.DoubleClass
+ addPrimitives(DoubleClass, nme.EQ, EQ)
+ addPrimitives(DoubleClass, nme.NE, NE)
+ addPrimitives(DoubleClass, nme.ADD, ADD)
+ addPrimitives(DoubleClass, nme.SUB, SUB)
+ addPrimitives(DoubleClass, nme.MUL, MUL)
+ addPrimitives(DoubleClass, nme.DIV, DIV)
+ addPrimitives(DoubleClass, nme.MOD, MOD)
+ addPrimitives(DoubleClass, nme.LT, LT)
+ addPrimitives(DoubleClass, nme.LE, LE)
+ addPrimitives(DoubleClass, nme.GT, GT)
+ addPrimitives(DoubleClass, nme.GE, GE)
+ // conversions
+ addPrimitives(DoubleClass, nme.toByte, D2B)
+ addPrimitives(DoubleClass, nme.toShort, D2S)
+ addPrimitives(DoubleClass, nme.toChar, D2C)
+ addPrimitives(DoubleClass, nme.toInt, D2I)
+ addPrimitives(DoubleClass, nme.toLong, D2L)
+ addPrimitives(DoubleClass, nme.toFloat, D2F)
+ addPrimitives(DoubleClass, nme.toDouble, D2D)
+ // unary methods
+ addPrimitives(DoubleClass, nme.UNARY_+, POS)
+ addPrimitives(DoubleClass, nme.UNARY_-, NEG)
+
+
+ primitives.toMap
+ }
+
+ def isPrimitive(fun: Tree): Boolean = {
+ (primitives contains fun.symbol(ctx)) ||
+ (fun.symbol(ctx) == NoSymbol // the only trees that do not have a symbol assigned are array.{update,select,length,clone}}
+ && (fun match {
+ case Select(_, StdNames.nme.clone_) => false // but array.clone is NOT a primitive op.
+ case _ => true
+ }))
+ }
+
+}
+