diff options
author | Dmitry Petrashko <dmitry.petrashko@gmail.com> | 2016-04-18 21:29:41 +0200 |
---|---|---|
committer | Dmitry Petrashko <dmitry.petrashko@gmail.com> | 2016-06-07 14:18:27 +0200 |
commit | 27846bb2ba8519decbfe4a152460805e591ec98b (patch) | |
tree | cc47f2808d14453dcecc157e1c560024b34e163b /src/dotty/tools/dotc | |
parent | 4ce8ab06470db959aed3527f013a261bc4c3c9a0 (diff) | |
download | dotty-27846bb2ba8519decbfe4a152460805e591ec98b.tar.gz dotty-27846bb2ba8519decbfe4a152460805e591ec98b.tar.bz2 dotty-27846bb2ba8519decbfe4a152460805e591ec98b.zip |
MoveStatic: Move static methods & fields into companion class
As a funny side-effect this allows to execute arbitrary code in static
initialisers:
@static val a: Unit = {println("loaded")}
Diffstat (limited to 'src/dotty/tools/dotc')
-rw-r--r-- | src/dotty/tools/dotc/Compiler.scala | 1 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/MoveStatics.scala | 64 |
2 files changed, 65 insertions, 0 deletions
diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 2ddd81718..3844f42a7 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -91,6 +91,7 @@ class Compiler { new RestoreScopes), // Repair scopes rendered invalid by moving definitions in prior phases of the group List(new ExpandPrivate, // Widen private definitions accessed from nested classes new CollectEntryPoints, // Find classes with main methods + new MoveStatics, // Move static methods to companion classes new LabelDefs), // Converts calls to labels to jumps List(new GenSJSIR), // Generate .js code List(new GenBCode) // Generate JVM bytecode diff --git a/src/dotty/tools/dotc/transform/MoveStatics.scala b/src/dotty/tools/dotc/transform/MoveStatics.scala new file mode 100644 index 000000000..c7cc947aa --- /dev/null +++ b/src/dotty/tools/dotc/transform/MoveStatics.scala @@ -0,0 +1,64 @@ +package dotty.tools.dotc.transform + +import dotty.tools.dotc.ast.{Trees, tpd} +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.DenotTransformers.{InfoTransformer, SymTransformer} +import dotty.tools.dotc.core.SymDenotations.SymDenotation +import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.NameOps._ +import dotty.tools.dotc.core.{Flags, Names} +import dotty.tools.dotc.core.Names.Name +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.core.Types.MethodType +import dotty.tools.dotc.transform.TreeTransforms.{MiniPhaseTransform, TransformerInfo} + +/** Move static methods from companion to the class itself */ +class MoveStatics extends MiniPhaseTransform with SymTransformer { thisTransformer => + + import tpd._ + override def phaseName = "moveStatic" + + + def transformSym(sym: SymDenotation)(implicit ctx: Context): SymDenotation = { + if (sym.hasAnnotation(defn.ScalaStaticAnnot) && sym.owner.is(Flags.Module)) { + sym.owner.companionClass.asClass.enter(sym.symbol) + val flags = if (sym.is(Flags.Method)) sym.flags else sym.flags | Flags.Mutable + sym.copySymDenotation(owner = sym.owner.companionClass, initFlags = flags) + } + else sym + } + + override def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[Tree] = { + if (ctx.owner.is(Flags.Package)) { + val (classes, others) = trees.partition(x => x.isInstanceOf[TypeDef] && x.symbol.isClass) + val pairs = classes.groupBy(_.symbol.name.stripModuleClassSuffix).asInstanceOf[Map[Name, List[TypeDef]]] + + def move(companion: TypeDef, module: TypeDef): Thicket = { + if (companion.symbol.is(Flags.Module)) move(module, companion) + else { + val allMembers = companion.rhs.asInstanceOf[Template].body ++ module.rhs.asInstanceOf[Template].body + val (newCompanionBody, newModuleBody) = allMembers.partition(x => {assert(x.symbol.exists); x.symbol.owner == companion.symbol}) + def rebuild(orig: TypeDef, newBody: List[Tree]) = { + val oldTemplate = orig.rhs.asInstanceOf[Template] + val statics = newBody.filter(x => x.isInstanceOf[ValDef] && x.symbol.hasAnnotation(defn.ScalaStaticAnnot)).asInstanceOf[List[ValDef]] + val newBodyWithStaticConstr = + if (statics.nonEmpty) { + val staticCostructor = ctx.newSymbol(orig.symbol, Names.STATIC_CONSTRUCTOR, Flags.Synthetic | Flags.JavaStatic | Flags.Method, MethodType(Nil, defn.UnitType)) + val staticAssigns = statics.map(x => Assign(ref(x.symbol), x.rhs.changeOwner(x.symbol, staticCostructor))) + tpd.DefDef(staticCostructor, Block(staticAssigns, tpd.unitLiteral)) :: newBody + } else newBody + + cpy.TypeDef(orig)(rhs = cpy.Template(orig.rhs)(oldTemplate.constr, oldTemplate.parents, oldTemplate.self, newBodyWithStaticConstr)) + } + Thicket(rebuild(companion, newCompanionBody), rebuild(module, newModuleBody)) + } + } + val newPairs = + for ((name, classes) <- pairs) + yield + if (classes.tail.isEmpty) classes.head + else move(classes.head, classes.tail.head) + Trees.flatten(newPairs.toList) + } else trees + } +} |