diff options
author | Aleksandar Pokopec <aleksandar.prokopec@epfl.ch> | 2009-11-20 17:25:08 +0000 |
---|---|---|
committer | Aleksandar Pokopec <aleksandar.prokopec@epfl.ch> | 2009-11-20 17:25:08 +0000 |
commit | 346aff23bf186ab911e12c856ba837e50aa218b3 (patch) | |
tree | 016297b9ab41bfee6956386d4d2ff73d99fda249 /src | |
parent | 88b60e35e60304b6e31cec8c53542855c30197bb (diff) | |
download | scala-346aff23bf186ab911e12c856ba837e50aa218b3.tar.gz scala-346aff23bf186ab911e12c856ba837e50aa218b3.tar.bz2 scala-346aff23bf186ab911e12c856ba837e50aa218b3.zip |
Changes made in the clean up phase - now the sy...
Changes made in the clean up phase - now the symbols get interned during classload for each symbol literal - references to them reside in static fields. These static fields get initialized in static constructors - the java backend will now identify ctors with static flags and generate a static initializer containing the necessary code.
Diffstat (limited to 'src')
7 files changed, 215 insertions, 11 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index 373859b836..dbbb306130 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -135,7 +135,7 @@ trait Trees { */ def setType(tp: Type): this.type = { /*assert(kindingIrrelevant(tp) || !kindStar || !tp.isHigherKinded, - tp+" should not be higher-kinded");*/ + tp+" should not be higher-kinded"); */ tpe = tp this } diff --git a/src/compiler/scala/tools/nsc/backend/icode/Members.scala b/src/compiler/scala/tools/nsc/backend/icode/Members.scala index 35eeec316e..65616c9a1f 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Members.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Members.scala @@ -113,6 +113,11 @@ trait Members { self: ICodes => def lookupField(s: Symbol) = fields find (_.symbol == s) def lookupMethod(s: Symbol) = methods find (_.symbol == s) def lookupMethod(s: Name) = methods find (_.symbol.name == s) + + /* determines whether or not this class contains a static ctor. */ + def containsStaticCtor: Boolean = methods.exists(_.isStaticCtor) // alex + /* returns this methods static ctor if it has one. */ + def lookupStaticCtor: Option[IMethod] = methods.find(_.isStaticCtor) // alex } /** Represent a field in ICode */ @@ -193,6 +198,9 @@ trait Members { self: ICodes => def isStatic: Boolean = symbol.isStaticMember + /* determines whether or not this method is the class static constructor. */ + def isStaticCtor: Boolean = isStatic && symbol.rawname == nme.CONSTRUCTOR + override def toString() = symbol.fullNameString import opcodes._ diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala index 9abd29cd0a..15852b61d3 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -203,7 +203,7 @@ abstract class GenJVM extends SubComponent { if (isStaticModule(c.symbol) || serialVUID != None || clasz.bootstrapClass.isDefined) { if (isStaticModule(c.symbol)) addModuleInstanceField; - addStaticInit(jclass) + addStaticInit(jclass, c.lookupStaticCtor) if (isTopLevelModule(c.symbol)) { if (c.symbol.linkedClassOfModule == NoSymbol) @@ -214,6 +214,8 @@ abstract class GenJVM extends SubComponent { } } else { + if (c.containsStaticCtor) addStaticInit(jclass, c.lookupStaticCtor) // alex - adds static ctors to class + // it must be a top level class (name contains no $s) def isCandidateForForwarders(sym: Symbol): Boolean = atPhase (currentRun.picklerPhase.next) { @@ -557,6 +559,8 @@ abstract class GenJVM extends SubComponent { } def genMethod(m: IMethod) { + if (m.isStaticCtor) return // alex - skip constructors marked as static, they were handled earlier + log("Generating method " + m.symbol.fullNameString) method = m endPC.clear @@ -668,7 +672,7 @@ abstract class GenJVM extends SubComponent { jclass.getType()) } - def addStaticInit(cls: JClass) { + def addStaticInit(cls: JClass, mopt: Option[IMethod]) { // alex import JAccessFlags._ val clinitMethod = cls.addNewMethod(ACC_PUBLIC | ACC_STATIC, "<clinit>", @@ -676,6 +680,53 @@ abstract class GenJVM extends SubComponent { JType.EMPTY_ARRAY, new Array[String](0)) val clinit = clinitMethod.getCode().asInstanceOf[JExtendedCode] + + mopt match { + case Some(m) => + if (clasz.bootstrapClass.isDefined) legacyEmitBootstrapMethodInstall(clinit) + + val oldLastBlock = m.code.blocks.last + val lastBlock = m.code.newBlock + oldLastBlock.replaceInstruction(oldLastBlock.length - 1, JUMP(lastBlock)) + + if (isStaticModule(clasz.symbol)) { + // call object's private ctor from static ctor + lastBlock.emit(NEW(REFERENCE(m.symbol.enclClass))) + lastBlock.emit(CALL_METHOD(m.symbol.enclClass.primaryConstructor, Static(true))) + } + + // add serialVUID code + serialVUID match { + case Some(value) => + import Flags._ + import definitions._ + val fieldName = "serialVersionUID" + val fieldSymbol = clasz.symbol.newValue(NoPosition, newTermName(fieldName)) + .setFlag(STATIC | FINAL) + .setInfo(longType) + clasz.addField(new IField(fieldSymbol)) + lastBlock.emit(CONSTANT(Constant(value))) + lastBlock.emit(STORE_FIELD(fieldSymbol, true)) + case None => () + } + + if (clasz.bootstrapClass.isDefined) { + // emit bootstrap method install + //emitBootstrapMethodInstall(block) + } + + lastBlock.emit(RETURN(UNIT)) + lastBlock.close + + method = m + jmethod = clinitMethod + genCode(m) + case None => + legacyStaticInitializer(cls, clinit) + } + } + + private def legacyStaticInitializer(cls: JClass, clinit: JExtendedCode) { if (isStaticModule(clasz.symbol)) { clinit.emitNEW(cls.getName()) clinit.emitINVOKESPECIAL(cls.getName(), @@ -694,7 +745,7 @@ abstract class GenJVM extends SubComponent { case None => () } - if (clasz.bootstrapClass.isDefined) emitBootstrapMethodInstall(clinit) + if (clasz.bootstrapClass.isDefined) legacyEmitBootstrapMethodInstall(clinit) clinit.emitRETURN() } @@ -702,7 +753,7 @@ abstract class GenJVM extends SubComponent { /** Emit code that installs a boostrap method for invoke dynamic. It installs the default * method, found in scala.runtime.DynamicDispatch. */ - def emitBootstrapMethodInstall(jcode: JExtendedCode) { + def legacyEmitBootstrapMethodInstall(jcode: JExtendedCode) { jcode.emitPUSH(jclass.getType.asInstanceOf[JReferenceType]) jcode.emitPUSH(new JObjectType("scala.runtime.DynamicDispatch")) jcode.emitPUSH("bootstrapInvokeDynamic") diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala index 2ba0ee68b9..c8e85edd76 100644 --- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala +++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala @@ -128,6 +128,7 @@ trait Definitions { lazy val StringClass = getClass(sn.String) lazy val ClassClass = getClass(sn.Class) def Class_getMethod = getMember(ClassClass, nme.getMethod_) + lazy val SymbolClass = getClass("scala.Symbol") // alex // fundamental modules lazy val PredefModule: Symbol = getModule("scala.Predef") @@ -141,6 +142,7 @@ trait Definitions { def Predef_conforms = getMember(PredefModule, nme.conforms) lazy val ConsoleModule: Symbol = getModule("scala.Console") lazy val ScalaRunTimeModule: Symbol = getModule("scala.runtime.ScalaRunTime") + lazy val SymbolModule: Symbol = getModule("scala.Symbol") // alex def SeqFactory = getMember(ScalaRunTimeModule, nme.Seq) def checkDefinedMethod = getMember(ScalaRunTimeModule, "checkDefined") def isArrayMethod = getMember(ScalaRunTimeModule, "isArray") @@ -246,6 +248,8 @@ trait Definitions { def optionType(tp: Type) = typeRef(OptionClass.typeConstructor.prefix, OptionClass, List(tp)) def someType(tp: Type) = typeRef(SomeClass.typeConstructor.prefix, SomeClass, List(tp)) + def symbolType = typeRef(SymbolClass.typeConstructor.prefix, SymbolClass, List()) // alex + def longType = typeRef(LongClass.typeConstructor.prefix, LongClass, List()) // alex // Product, Tuple, Function private def mkArityArray(name: String, arity: Int, countFrom: Int = 1) = { diff --git a/src/compiler/scala/tools/nsc/symtab/Symbols.scala b/src/compiler/scala/tools/nsc/symtab/Symbols.scala index dcb2097c5f..1634e04025 100644 --- a/src/compiler/scala/tools/nsc/symtab/Symbols.scala +++ b/src/compiler/scala/tools/nsc/symtab/Symbols.scala @@ -563,14 +563,14 @@ trait Symbols { final def isStaticOwner: Boolean = isPackageClass || isModuleClass && isStatic - /** Is this symbol final?*/ + /** Is this symbol final? */ final def isFinal: Boolean = ( hasFlag(FINAL) || isTerm && ( hasFlag(PRIVATE) || isLocal || owner.isClass && owner.hasFlag(FINAL | MODULE)) ) - /** Is this symbol a sealed class?*/ + /** Is this symbol a sealed class? */ final def isSealed: Boolean = isClass && (hasFlag(SEALED) || isValueClass(this)) diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala index 444dc42299..556f4d6de4 100644 --- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala +++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala @@ -1,5 +1,5 @@ /* NSC -- new Scala compiler - * Copyright 2005-2009 LAMP/EPFL + * Copyrights 2005-2009 LAMP/EPFL * @author Martin Odersky */ // $Id$ @@ -28,6 +28,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL { private val newInits = new ListBuffer[Tree] private val classConstantMeth = new HashMap[String, Symbol] + private val symbolStaticFields = new HashMap[String, (Symbol, Tree, Tree)] // alex private var localTyper: analyzer.Typer = null @@ -501,7 +502,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL { * constructor. */ case Template(parents, self, body) => localTyper = typer.atOwner(tree, currentClass) - if (!forMSIL) { + val transformedTemplate = if (!forMSIL) { // alex - assigned this to a val classConstantMeth.clear newDefs.clear newInits.clear @@ -525,6 +526,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL { treeCopy.Template(tree, parents, self, newBody) } else super.transform(tree) + applySymbolFieldInitsToStaticCtor(transformedTemplate.asInstanceOf[Template]) // alex - postprocess to include static ctors case Literal(c) if (c.tag == ClassTag) && !forMSIL=> val tpe = c.typeValue @@ -569,9 +571,148 @@ abstract class CleanUp extends Transform with ast.TreeDSL { } super.transform(tree) + /* + * This transformation should identify Scala symbol invocations in the tree and replace them + * with references to a static member. Also, whenever a class has at least a single symbol invocation + * somewhere in its methods, a new static member should be created and initialized for that symbol. + * For instance, say we have a Scala class: + * + * class Cls { + * // ... + * def someSymbol = `symbolic + * // ... + * } + * + * After transformation, this class looks like this: + * + * class Cls { + * private "static" val <some_name>$symbolic = Symbol("symbolic") + * // ... + * def someSymbol = <some_name>$symbolic + * // ... + * } + * + * The reasoning behind this transformation is the following. Symbols get interned - they are stored + * in a global map which is protected with a lock. The reason for this is making equality checks + * quicker. But calling Symbol.apply, although it does return a unique symbol, accesses a locked object, + * making symbol access slow. To solve this, the unique symbol from the global symbol map in Symbol + * is accessed only once during class loading, and after that, the unique symbol is in the static + * member. Hence, it is cheap to both reach the unique symbol and do equality checks on it. + * + * And, finally, be advised - scala symbol literal and the Symbol class of the compiler + * have little in common. + */ + case symapp @ Apply(Select(Select(a @ Ident(nme.scala_), b @ nme.Symbol), nme.apply), + List(Literal(Constant(symname)))) => // alex + // add the symbol name to a map if it's not there already + val rhs = treeGen.mkCast(Apply(treeGen.scalaDot(nme.Symbol), List(Literal(Constant(symname)))), symbolType) + val (staticFieldSym, sfdef, sfinit) = getSymbolStaticField(symapp.pos, symname.asInstanceOf[String], rhs, symapp) + + // create a reference to a static field + val ntree = typedWithPos(symapp.pos)(REF(staticFieldSym)) + + super.transform(ntree) case _ => super.transform(tree) } + + /* serves as a tree generator */ + object treeGen extends scala.tools.nsc.ast.TreeGen { + val global: CleanUp.this.global.type = CleanUp.this.global + } + + /* Returns the symbol and the tree for the symbol field interning a reference to a symbol 'synmname'. + * If it doesn't exist, i.e. the symbol is encountered the first time, + * it creates a new static field definition and initalization and returns it. + */ + private def getSymbolStaticField(pos: Position, symname: String, rhs: Tree, tree: Tree): (Symbol, Tree, Tree) = { // alex + if (symbolStaticFields.contains(symname)) symbolStaticFields(symname) + else { + val freshname = unit.fresh.newName(pos, "symbol$") + val theTyper = typer.atOwner(tree, currentClass) + + // create a symbol for the static field + val stfieldSym = currentClass.newVariable(pos, freshname) + .setFlag(PRIVATE | STATIC | SYNTHETIC | FINAL) + .setInfo(symbolType) + currentClass.info.decls enter stfieldSym + + // create field definition and initialization + val stfieldDef = theTyper.typed { atPos(pos)(VAL(stfieldSym) === rhs) } + val stfieldInit = theTyper.typed { atPos(pos)(REF(stfieldSym) === rhs) } + + // add field definition to new defs + newDefs append stfieldDef + + symbolStaticFields.put(symname, (stfieldSym, stfieldDef, stfieldInit)) + + symbolStaticFields(symname) + } + } + + /* returns a list of all trees for symbol static fields, and clear the list */ + private def flushSymbolFieldsInitializations: List[Tree] = { + var fieldlst: List[Tree] = Nil + + for ((symbolname, (symbol, deftree, inittree)) <- symbolStaticFields) { + fieldlst ::= inittree + } + symbolStaticFields.clear + + fieldlst + } + + /* finds the static ctor DefDef tree within the template if it exists. */ + def findStaticCtor(template: Template): Option[Tree] = { + template.body.find(_ match { + case defdef @ DefDef(mods, name, tparam, vparam, tp, rhs) => name == nme.CONSTRUCTOR && defdef.symbol.hasFlag(STATIC) + case _ => false + }) + } + + /* changes the template for the class so that it contains a static constructor with symbol fields inits, + * augments an existing static ctor if one already existed. + */ + def applySymbolFieldInitsToStaticCtor(template: Template): Template = { + val symbolInitTrees = flushSymbolFieldsInitializations + if (symbolInitTrees.isEmpty) template + else { + val theTyper = typer.atOwner(template, currentClass) + val newCtor = findStaticCtor(template) match { + // in case there already were static ctors - augment existing ones + // currently, however, static ctors aren't being generated anywhere else + case Some(ctorTree) => + val ctor = ctorTree.asInstanceOf[DefDef] + // modify existing static ctor + val newBlock = ctor.rhs match { + case block @ Block(stats, expr) => + // need to add inits to existing block + treeCopy.Block(block, symbolInitTrees ::: stats, expr) + case term @ _ if term.isInstanceOf[TermTree] => + // need to create a new block with inits and the old term + treeCopy.Block(term, symbolInitTrees, term) + } + treeCopy.DefDef(ctor, ctor.mods, ctor.name, ctor.tparams, ctor.vparamss, ctor.tpt, newBlock) + case None => + // create new static ctor + val staticCtorSym = currentClass.newConstructor(template.pos) + .setFlag(STATIC) + .setInfo(UnitClass.tpe) + val rhs = Block(symbolInitTrees, Literal(())) + val staticCtorTree = DefDef(staticCtorSym, rhs) + theTyper.typed{ atPos(template.pos)(staticCtorTree) } + } + val newTemplate = treeCopy.Template(template, template.parents, template.self, newCtor :: template.body) + //println(newTemplate) + newTemplate + } + } + } // CleanUpTransformer } + + + + + diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index a6d4230969..6347251a5e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1733,7 +1733,7 @@ trait Typers { self: Analyzer => ddef.tpt.setType(tpt1.tpe) val typedMods = removeAnnotations(ddef.mods) var rhs1 = - if (ddef.name == nme.CONSTRUCTOR) { + if (ddef.name == nme.CONSTRUCTOR && !ddef.symbol.hasFlag(STATIC)) { // alex - need this to make it possible to generate static ctors if (!meth.isPrimaryConstructor && (!meth.owner.isClass || meth.owner.isModuleClass || @@ -2861,7 +2861,7 @@ trait Typers { self: Analyzer => * class NPE[T <: NPE[T] @peer] * * (Note: -Yself-in-annots must be on to see the problem) - **/ + * */ val sym = context.owner.newLocalDummy(ann.pos) .newValue(ann.pos, nme.self) |