diff options
Diffstat (limited to 'src/dotty')
18 files changed, 2030 insertions, 21 deletions
diff --git a/src/dotty/tools/backend/jvm/CollectEntryPoints.scala b/src/dotty/tools/backend/jvm/CollectEntryPoints.scala new file mode 100644 index 000000000..802100bf2 --- /dev/null +++ b/src/dotty/tools/backend/jvm/CollectEntryPoints.scala @@ -0,0 +1,120 @@ +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.isJavaEntyPoint(tree.symbol)) { + ctx.genBCodePhase.asInstanceOf[GenBCode].registerEntryPoint(tree.symbol) + } + tree + } +} + +object CollectEntryPoints{ + def isJavaEntyPoint(sym: Symbol)(implicit ctx: Context): Boolean = { + import Types.MethodType + val d = ctx.definitions + val StringType = d.StringType + def isJavaMainMethod(sym: Symbol) = (sym.name == nme.main) && (toDenot(sym).info match { + case r@ MethodType(_, List(d.ArrayType(StringType))) => r.resultType eq d.UnitType + case _ => false + }) + // 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 succeeed, 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/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala new file mode 100644 index 000000000..672e00a5c --- /dev/null +++ b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -0,0 +1,847 @@ +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.{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, DotClass} +import Decorators._ +import tpd._ +import StdNames.nme + +class DottyBackendInterface()(implicit ctx: Context) extends BackendInterface{ + trait NonExistentTree extends tpd.Tree + 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.Throw + 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.Select + type Bind = tpd.Bind + type New = tpd.New + type Super = tpd.Super + type Modifiers = tpd.Modifiers + type Annotation = NonExistentTree + type ArrayValue = tpd.JavaSeqLiteral + type ApplyDynamic = NonExistentTree + type ModuleDef = NonExistentTree + 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 + + val BoxesRunTimeModule = ctx.requiredModule("scala.runtime.BoxesRunTime") + val BoxesRunTimeClass = toDenot(BoxesRunTimeModule).moduleClass.asClass + + 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 = ctx.requiredMethod(BoxesRunTimeClass, nme.equalsNumNum) + lazy val externalEqualsNumChar: Symbol = ??? // ctx.requiredMethod(BoxesRunTimeClass, nme.equalsNumChar) // this method is private + val externalEqualsNumObject: Symbol = ctx.requiredMethod(BoxesRunTimeClass, nme.equalsNumObject) + val externalEquals: Symbol = ctx.requiredMethod(BoxesRunTimeClass, nme.equals_) + val MaxFunctionArity: Int = Definitions.MaxFunctionArity + val FunctionClass: Array[Symbol] = defn.FunctionClass.asInstanceOf[Array[Symbol]] + val AbstractFunctionClass: Array[Symbol] = defn.AbstractFunctionClass.asInstanceOf[Array[Symbol]] + val PartialFunctionClass: Symbol = defn.PartialFunctionClass + val AbstractPartialFunctionClass: Symbol = defn.AbstractPartialFunctionClass + val String_valueOf: Symbol = defn.String_valueOf_Object + + def boxMethods: Map[Symbol, Symbol] = defn.ScalaValueClasses.map{x => + (x, Erasure.Boxing.boxMethod(x.asClass)) + }.toMap + def unboxMethods: Map[Symbol, Symbol] = defn.ScalaValueClasses.map(x => (x, Erasure.Boxing.unboxMethod(x.asClass))).toMap + + private val mkArrayNames: Set[String] = Set("Byte", "Float", "Char", "Double", "Boolean", "Unit", "Long", "Int", "Short", "Ref") + + override lazy val syntheticArrayConstructors: Set[Symbol] = mkArrayNames.map(nm => ctx.requiredMethod(toDenot(defn.DottyArraysModule).moduleClass.asClass, s"new${nm}Array")) + + 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 = false + def shouldEmitAnnotation(annot: Annotation): Boolean = false + + def emitAnnotations(cw: ClassVisitor, annotations: List[Annotation], bcodeStore: BCodeHelpers)(innerClasesStore: bcodeStore.BCInnerClassGen): Unit = () + def emitAnnotations(mw: MethodVisitor, annotations: List[Annotation], bcodeStore: BCodeHelpers)(innerClasesStore: bcodeStore.BCInnerClassGen): Unit = () + def emitAnnotations(fw: FieldVisitor, annotations: List[Annotation], bcodeStore: BCodeHelpers)(innerClasesStore: bcodeStore.BCInnerClassGen): Unit = () + def emitParamAnnotations(jmethod: MethodVisitor, pannotss: List[List[Annotation]], bcodeStore: BCodeHelpers)(innerClasesStore: bcodeStore.BCInnerClassGen): Unit = () + 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[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 _ => 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): 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 + 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, Object)] = ??? + + def symbol: Symbol = a.tree.symbol + + def args: List[Tree] = ??? + } + + + 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 = ??? + + 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.isClass && (sym.isPackageObject || !(sym is Flags.Package)) + } + 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.JavaInterface) + + 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)) + // 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) + def hasModuleFlag: Boolean = sym is Flags.Module + def isSynchronized: Boolean = sym is Flags.Synchronized + def isNonBottomSubClass(other: Symbol): Boolean = sym.derivesFrom(other) + def hasAnnotation(sym: Symbol): Boolean = false + def shouldEmitForwarders: Boolean = //exitingPickler { !(sym.name.toString contains '$') + (sym is Flags.Module) && !(sym is Flags.ImplClass) /// !sym.isNestedClass + def isJavaEntryPoint: Boolean = CollectEntryPoints.isJavaEntyPoint(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.isStatic && (sym is Flags.Module) + + def isStaticConstructor: Boolean = isStaticMember && isClassConstructor + + + // navigation + def owner: Symbol = toDenot(sym).owner + def rawowner: Symbol = owner + def originalOwner: Symbol = { + try { + if (sym.exists) { + val original = toDenot(sym).initial + val validity = original.validFor + val shiftedContext = ctx.withPhase(validity.phaseId) + val r = toDenot(sym)(shiftedContext).maybeOwner + if(r is Flags.Package) NoSymbol + else r + } else NoSymbol + } catch { + case e: NotDefinedHere => NoSymbol // todo: do we have a method to tests this? + } + } + def parentSymbols: List[Symbol] = toDenot(sym).info.parents.map(_.typeSymbol) + def superClass: Symbol = toDenot(sym).superClass + def enclClass: Symbol = toDenot(sym).enclosingClass + def linkedClassOfClass: Symbol = linkedClass + def linkedClass: Symbol = toDenot(sym).linkedClass //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 = enclClass //todo is handled specially for JavaDefined symbols in scalac + + + + // members + def primaryConstructor: Symbol = toDenot(sym).primaryConstructor + def nestedClasses: List[Symbol] = memberClasses //exitingPhase(currentRun.lambdaliftPhase)(sym.memberClasses) + def memberClasses: List[Symbol] = toDenot(sym).info.memberClasses.map(_.symbol).toList + 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.memberClasses.map(_.symbol).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 = new PlainDirectory(new Directory(new JFile(ctx.settings.d.value))) + def pos: Position = sym.pos + + def throwsAnnotations: List[Symbol] = Nil + + /** + * All interfaces implemented by a class, except for those inherited through the superclass. + * + */ + def superInterfaces: List[Symbol] = decorateSymbol(sym).superInterfaces + + /** + * 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 = () + } + + + 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 ++= 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, storage.getClassBTypeAndRegisterInnerClass(sym.asInstanceOf[ct.int.Symbol])) + } + + /** + * 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 + } + + 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 ThisType(ArrayClass) => ObjectReference // was introduced in 9b17332f11 to fix SI-999, but this code is not reached in its test, or any other test + case ThisType(sym) => storage.getClassBTypeAndRegisterInnerClass(sym.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 // used only for emmiting annotations + + def parents: List[Type] = tp.parents + } + + + + object Assign extends AssignDeconstructor { + def _1: Tree = field.lhs + def _2: Tree = field.rhs + } + + object Select extends SelectDeconstructor { + def _1: Tree = field.qualifier + def _2: Name = field.name + } + + 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 = field.mods + 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.expr + } + + object New extends NewDeconstructor { + def get = field.tpt.tpe + } + + object This extends ThisDeconstructor { + def get = field.qual + 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 + } + 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 = field.mods + 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 = field.mods + 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 +}
\ No newline at end of file diff --git a/src/dotty/tools/backend/jvm/GenBCode.scala b/src/dotty/tools/backend/jvm/GenBCode.scala new file mode 100644 index 000000000..a53e910ae --- /dev/null +++ b/src/dotty/tools/backend/jvm/GenBCode.scala @@ -0,0 +1,390 @@ +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 scala.collection.mutable +import scala.tools.asm.{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 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 scala.tools.asm +import scala.tools.asm.tree._ +import dotty.tools.dotc.util.{Positions, DotClass} +import tpd._ + +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 + + + def run(implicit ctx: Context): Unit = { + new GenBCodePipeline(entryPoints.toList, new DottyBackendInterface()(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 = _ + + 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() { + 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 + + // -------------- 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() { + 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) + + /* 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) { + tree match { + case EmptyTree => () + case PackageDef(_, stats) => stats foreach gen + case ValDef(name, tpt, rhs) => () // module val not emmited + 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) { + 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) + } + 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/src/dotty/tools/backend/jvm/LabelDefs.scala b/src/dotty/tools/backend/jvm/LabelDefs.scala new file mode 100644 index 000000000..0e50e9366 --- /dev/null +++ b/src/dotty/tools/backend/jvm/LabelDefs.scala @@ -0,0 +1,185 @@ +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.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 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]() + + + + override def transformBlock(tree: tpd.Block)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { + collectLabelDefs.clear + val newStats = collectLabelDefs.transformStats(tree.stats) + val newExpr = collectLabelDefs.transform(tree.expr) + val labelCalls = collectLabelDefs.labelCalls + val entryPoints = collectLabelDefs.parentLabelCalls + val labelDefs = collectLabelDefs.labelDefs + + // 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 moveLabels(entryPoint: Tree): List[Tree] = { + if((entryPoint.symbol is Flags.Label) && labelDefs.contains(entryPoint.symbol)) { + val visitedNow = new mutable.HashMap[Symbol, Tree]() + val treesToAppend = new ArrayBuffer[Tree]() // order matters. parents should go first + queue.clear() + + var visited = 0 + queue += entryPoint + while (visited < queue.size) { + val owningLabelDefSym = queue(visited).symbol + val owningLabelDef = labelDefs(owningLabelDefSym) + for (call <- labelCalls(owningLabelDefSym)) + if (disallowed.contains(call.symbol)) { + val oldCall = disallowed(call.symbol) + ctx.error(s"Multiple return locations for Label $oldCall and $call", call.symbol.pos) + } else { + if ((!visitedNow.contains(call.symbol)) && labelDefs.contains(call.symbol)) { + val df = labelDefs(call.symbol) + visitedNow.put(call.symbol, labelDefs(call.symbol)) + queue += call + } + } + if(!treesToAppend.contains(owningLabelDef)) + treesToAppend += owningLabelDef + visited += 1 + } + disallowed ++= visitedNow + + treesToAppend.toList + } else Nil + } + + cpy.Block(tree)(entryPoints.flatMap(moveLabels).toList ++ newStats, newExpr) + + } + + val collectLabelDefs = new TreeMap() { + + // label calls from this DefDef + var parentLabelCalls: mutable.Set[Tree] = new mutable.HashSet[Tree]() + var isInsideLabel = false + var isInsideBlock = false + + def shouldMoveLabel = !isInsideBlock + + // 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]]() + val labelCallCounts = new mutable.HashMap[Symbol, Int]() + + 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 tmp = isInsideBlock + isInsideBlock = true + val r = super.transform(t) + isInsideBlock = tmp + r + case t: DefDef => + assert(t.symbol is Flags.Label) + val st = parentLabelCalls + parentLabelCalls = new mutable.HashSet[Tree]() + val tmp = isInsideLabel + isInsideLabel = true + val r = super.transform(tree) + isInsideLabel = tmp + labelCalls(r.symbol) = parentLabelCalls + parentLabelCalls = st + if(shouldMoveLabel) { + labelDefs(r.symbol) = r + EmptyTree + } else r + case t: Apply if t.symbol is Flags.Label => + parentLabelCalls = parentLabelCalls + t + labelCallCounts.get(t.symbol) + super.transform(tree) + case _ => + super.transform(tree) + + } + } +} diff --git a/src/dotty/tools/backend/jvm/scalaPrimitives.scala b/src/dotty/tools/backend/jvm/scalaPrimitives.scala new file mode 100755 index 000000000..857a92d21 --- /dev/null +++ b/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.ArrayType(el) => el + case JavaArrayType(el) => el + case _ => + ctx.error(s"expected Array $tpe") + ErrorType + } + + code match { + + case APPLY => + elementType.classSymbol match { + case defn.BooleanClass => ZARRAY_GET + case defn.ByteClass => BARRAY_GET + case defn.ShortClass => SARRAY_GET + case defn.CharClass => CARRAY_GET + case defn.IntClass => IARRAY_GET + case defn.LongClass => LARRAY_GET + case defn.FloatClass => FARRAY_GET + case defn.DoubleClass => DARRAY_GET + case _ => OARRAY_GET + } + + case UPDATE => + elementType.classSymbol match { + case defn.BooleanClass => ZARRAY_SET + case defn.ByteClass => BARRAY_SET + case defn.ShortClass => SARRAY_SET + case defn.CharClass => CARRAY_SET + case defn.IntClass => IARRAY_SET + case defn.LongClass => LARRAY_SET + case defn.FloatClass => FARRAY_SET + case defn.DoubleClass => DARRAY_SET + case _ => OARRAY_SET + } + + case LENGTH => + elementType.classSymbol match { + case defn.BooleanClass => ZARRAY_LENGTH + case defn.ByteClass => BARRAY_LENGTH + case defn.ShortClass => SARRAY_LENGTH + case defn.CharClass => CARRAY_LENGTH + case defn.IntClass => IARRAY_LENGTH + case defn.LongClass => LARRAY_LENGTH + case defn.FloatClass => FARRAY_LENGTH + case defn.DoubleClass => DARRAY_LENGTH + case _ => OARRAY_LENGTH + } + + case _ => + code + } + } + + /** Initialize the primitive map */ + private def init: immutable.Map[Symbol, Int] = { + + implicit val ctx = 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 + })) + } + +} + diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index c414de130..943b54d7f 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -14,6 +14,9 @@ import dotty.tools.dotc.transform.TreeTransforms.{TreeTransform, TreeTransformer import dotty.tools.dotc.core.DenotTransformers.DenotTransformer import dotty.tools.dotc.core.Denotations.SingleDenotation + +import dotty.tools.backend.jvm.{LabelDefs, GenBCode} + class Compiler { /** Meta-ordering constraint: @@ -61,7 +64,8 @@ class Compiler { List(new LambdaLift, new Flatten, new RestoreScopes), - List(new PrivateToStatic) + List(/*new PrivateToStatic,*/ new CollectEntryPoints, new LabelDefs), + List(new GenBCode) ) var runId = 1 diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 462c223fb..8fdc4a9db 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -361,7 +361,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { val constrSym = modcls.primaryConstructor orElse ctx.newDefaultConstructor(modcls).entered val constr = DefDef(constrSym.asTerm, EmptyTree) val clsdef = ClassDef(modcls, constr, body) - val valdef = ValDef(sym, New(modcls.typeRef)) + val valdef = ValDef(sym, New(modcls.typeRef).select(constrSym).appliedToNone) Thicket(valdef, clsdef) } diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala index 9afe96c96..35c3ac16a 100644 --- a/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -26,8 +26,8 @@ class ScalaSettings extends Settings.SettingGroup { val nowarn = BooleanSetting("-nowarn", "Generate no warnings.") val print = BooleanSetting("-print", "Print program with Scala-specific features removed.") val target = ChoiceSetting("-target", "target", "Target platform for object files. All JVM 1.5 targets are deprecated.", - List("jvm-1.5", "jvm-1.5-fjbg", "jvm-1.5-asm", "jvm-1.6", "jvm-1.7", "msil"), - "jvm-1.6") + List("jvm-1.5", "jvm-1.5-fjbg", "jvm-1.5-asm", "jvm-1.6", "jvm-1.7", "jvm-1.8", "msil"), + "jvm-1.8") val unchecked = BooleanSetting("-unchecked", "Enable additional warnings where generated code depends on assumptions.") val uniqid = BooleanSetting("-uniqid", "Uniquely tag all identifiers in debugging output.") val usejavacp = BooleanSetting("-usejavacp", "Utilize the java.class.path in classpath resolution.") diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 19d175088..a7c7ac3c6 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -269,11 +269,11 @@ class Definitions { // fundamental classes lazy val StringClass = ctx.requiredClass("java.lang.String") - lazy val StringModule = StringClass.moduleClass + lazy val StringModule = StringClass.linkedClass lazy val String_+ = newMethod(StringClass, nme.raw.PLUS, methOfAny(StringType), Final) lazy val String_valueOf_Object = StringModule.info.member(nme.valueOf).suchThat(_.info.firstParamTypes match { - case List(pt) => pt isRef ObjectClass + case List(pt) => (pt isRef AnyClass) || (pt isRef ObjectClass) case _ => false }).symbol @@ -398,7 +398,7 @@ class Definitions { def apply(elem: Type)(implicit ctx: Context) = if (ctx.erasedTypes) JavaArrayType(elem) else ArrayClass.typeRef.appliedTo(elem :: Nil) - def unapply(tp: Type)(implicit ctx: Context) = tp.dealias match { + def unapply(tp: Type)(implicit ctx: Context): Option[Type] = tp.dealias match { case at: RefinedType if (at isRef ArrayClass) && at.argInfos.length == 1 => Some(at.argInfos.head) case _ => None } diff --git a/src/dotty/tools/dotc/core/Names.scala b/src/dotty/tools/dotc/core/Names.scala index cf53d0445..f0d3f02eb 100644 --- a/src/dotty/tools/dotc/core/Names.scala +++ b/src/dotty/tools/dotc/core/Names.scala @@ -200,7 +200,7 @@ object Names { private final val fillFactor = 0.7 /** Memory to store all names sequentially. */ - private var chrs: Array[Char] = new Array[Char](InitialNameSize) + private[dotty] var chrs: Array[Char] = new Array[Char](InitialNameSize) /** The number of characters filled. */ private var nc = 0 diff --git a/src/dotty/tools/dotc/core/Phases.scala b/src/dotty/tools/dotc/core/Phases.scala index 5777c0f5b..70dd4296a 100644 --- a/src/dotty/tools/dotc/core/Phases.scala +++ b/src/dotty/tools/dotc/core/Phases.scala @@ -3,6 +3,7 @@ package core import Periods._ import Contexts._ +import dotty.tools.backend.jvm.GenBCode import util.DotClass import DenotTransformers._ import Denotations._ @@ -171,6 +172,7 @@ object Phases { private val flattenCache = new PhaseCache(classOf[Flatten]) private val explicitOuterCache = new PhaseCache(classOf[ExplicitOuter]) private val gettersCache = new PhaseCache(classOf[Getters]) + private val genBCodeCache = new PhaseCache(classOf[GenBCode]) def typerPhase = typerCache.phase def refchecksPhase = refChecksCache.phase @@ -179,6 +181,7 @@ object Phases { def flattenPhase = flattenCache.phase def explicitOuterPhase = explicitOuterCache.phase def gettersPhase = gettersCache.phase + def genBCodePhase = genBCodeCache.phase def isAfterTyper(phase: Phase): Boolean = phase.id > typerPhase.id } diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala index 24e948fa9..04bcf616d 100644 --- a/src/dotty/tools/dotc/core/StdNames.scala +++ b/src/dotty/tools/dotc/core/StdNames.scala @@ -510,6 +510,9 @@ object StdNames { val wrap: N = "wrap" val zero: N = "zero" val zip: N = "zip" + val nothingRuntimeClass: N = "scala.runtime.Nothing$" + val nullRuntimeClass: N = "scala.runtime.Null$" + val synthSwitch: N = "$synthSwitch" diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index a05296802..eccdcbfb9 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -324,6 +324,9 @@ object SymDenotations { final def isAnonymousClass(implicit ctx: Context): Boolean = isClass && (initial.asSymDenotation.name startsWith tpnme.ANON_CLASS) + final def isAnonymousFunction(implicit ctx: Context) = + this.symbol.is(Method) && (initial.asSymDenotation.name startsWith nme.ANON_FUN) + /** Is symbol a primitive value class? */ def isPrimitiveValueClass(implicit ctx: Context) = defn.ScalaValueClasses contains symbol diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 473b23f7b..a1ca7796f 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -29,6 +29,7 @@ import collection.{mutable, Seq, breakOut} import config.Config import config.Printers._ import annotation.tailrec +import Flags.FlagSet import language.implicitConversions object Types { @@ -523,6 +524,18 @@ object Types { .toList.map(d => TermRef.withSig(this, d.symbol.asTerm)) } + /** The set of member classes of this type */ + final def memberClasses(implicit ctx: Context): Seq[SingleDenotation] = track("implicitMembers") { + memberDenots(typeNameFilter, + (name, buf) => buf ++= member(name).altsWith(x => x.isClass)) + } + + /** The set of members of this type having at least one of `requiredFlags` but none of `excludedFlags` set */ + final def membersBasedOnFlags(requiredFlags: FlagSet, excludedFlags: FlagSet)(implicit ctx: Context): Seq[SingleDenotation] = track("implicitMembers") { + memberDenots(takeAllFilter, + (name, buf) => buf ++= member(name).altsWith(x => x.is(requiredFlags, butNot = excludedFlags))) + } + /** The info of `sym`, seen as a member of this type. */ final def memberInfo(sym: Symbol)(implicit ctx: Context): Type = sym.info.asSeenFrom(this, sym.owner) diff --git a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala index 8231c25af..cc3d3eb7f 100644 --- a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala +++ b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala @@ -876,7 +876,9 @@ class ClassfileParser( val start = starts(index) if (in.buf(start).toInt != CONSTANT_CLASS) errorBadTag(start) val name = getExternalName(in.getChar(start + 1)) - if (name.isModuleClassName) c = ctx.requiredModule(name.sourceModuleName) + if (name.isModuleClassName && (name ne nme.nothingRuntimeClass) && (name ne nme.nullRuntimeClass)) + // Null$ and Nothing$ ARE classes + c = ctx.requiredModule(name.sourceModuleName) else c = classNameToSymbol(name) values(index) = c } diff --git a/src/dotty/tools/dotc/transform/LambdaLift.scala b/src/dotty/tools/dotc/transform/LambdaLift.scala index 95c5cd529..c8dacd1d7 100644 --- a/src/dotty/tools/dotc/transform/LambdaLift.scala +++ b/src/dotty/tools/dotc/transform/LambdaLift.scala @@ -257,8 +257,8 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform case mt @ MethodType(pnames, ptypes) => val ps = proxies(local.skipConstructor) MethodType( - pnames ++ ps.map(_.name.asTermName), - ptypes ++ ps.map(_.info), + ps.map(_.name.asTermName) ++ pnames, + ps.map(_.info) ++ ptypes, mt.resultType) case info => info } @@ -340,7 +340,7 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform private def addFreeArgs(sym: Symbol, args: List[Tree])(implicit ctx: Context, info: TransformerInfo) = free get sym match { - case Some(fvs) => args ++ fvs.toList.map(proxyRef(_)) + case Some(fvs) => fvs.toList.map(proxyRef(_)) ++ args case _ => args } @@ -354,9 +354,9 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform transformFollowingDeep(ValDef(proxy.asTerm).withPos(tree.pos)).asInstanceOf[ValDef]) tree match { case tree: DefDef => - cpy.DefDef(tree)(vparamss = tree.vparamss.map(_ ++ freeParamDefs)) + cpy.DefDef(tree)(vparamss = tree.vparamss.map(freeParamDefs ++ _)) case tree: Template => - cpy.Template(tree)(body = tree.body ++ freeParamDefs) + cpy.Template(tree)(body = freeParamDefs ++ tree.body) } } diff --git a/src/dotty/tools/dotc/transform/MixinOps.scala b/src/dotty/tools/dotc/transform/MixinOps.scala index de15b045f..e6074323a 100644 --- a/src/dotty/tools/dotc/transform/MixinOps.scala +++ b/src/dotty/tools/dotc/transform/MixinOps.scala @@ -4,15 +4,14 @@ package transform import core._ import Symbols._, Types._, Contexts._, SymDenotations._, DenotTransformers._, Flags._ import util.Positions._ +import SymUtils._ import StdNames._, NameOps._ class MixinOps(cls: ClassSymbol, thisTransform: DenotTransformer)(implicit ctx: Context) { import ast.tpd._ - val superCls: Symbol = cls.classInfo.parents.head.symbol - val mixins: List[ClassSymbol] = - if (cls is Trait) Nil - else cls.baseClasses.tail.takeWhile(_ ne superCls).reverse + val superCls: Symbol = cls.superClass + val mixins: List[ClassSymbol] = cls.mixins def implementation(member: TermSymbol): TermSymbol = member.copy( diff --git a/src/dotty/tools/dotc/transform/SymUtils.scala b/src/dotty/tools/dotc/transform/SymUtils.scala index 7d485f64c..9274150b1 100644 --- a/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/src/dotty/tools/dotc/transform/SymUtils.scala @@ -24,6 +24,32 @@ object SymUtils { class SymUtils(val self: Symbol) extends AnyVal { import SymUtils._ + def superClass(implicit ctx: Context) = { + val parents = self.asClass.classInfo.parents + if (parents.isEmpty) NoSymbol + else parents.head.symbol + } + + + /** + * For a class: All interfaces implemented by a class except for those inherited through the superclass. + * For a trait: all parent traits + */ + + def superInterfaces(implicit ctx: Context) = { + val superCls = self.superClass + val baseClasses = self.asClass.baseClasses + if (baseClasses.isEmpty) Nil + else baseClasses.tail.takeWhile(_ ne superCls).reverse + + } + + /** All interfaces implemented by a class, except for those inherited through the superclass. */ + def mixins(implicit ctx: Context) = { + if (self is Trait) Nil + else superInterfaces + } + def isTypeTestOrCast(implicit ctx: Context): Boolean = self == defn.Any_asInstanceOf || self == defn.Any_isInstanceOf @@ -36,9 +62,6 @@ class SymUtils(val self: Symbol) extends AnyVal { final def skipConstructor(implicit ctx: Context): Symbol = if (self.isConstructor) self.owner else self - final def isAnonymousFunction(implicit ctx: Context): Boolean = - self.is(Method) && (self.denot.initial.asSymDenotation.name startsWith nme.ANON_FUN) - /** The logically enclosing method or class for this symbol. * Instead of constructors one always picks the enclosing class. */ |