aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Petrashko <dmitry.petrashko@gmail.com>2016-04-18 21:29:41 +0200
committerDmitry Petrashko <dmitry.petrashko@gmail.com>2016-06-07 14:18:27 +0200
commit27846bb2ba8519decbfe4a152460805e591ec98b (patch)
treecc47f2808d14453dcecc157e1c560024b34e163b
parent4ce8ab06470db959aed3527f013a261bc4c3c9a0 (diff)
downloaddotty-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")}
-rw-r--r--src/dotty/tools/backend/jvm/DottyBackendInterface.scala9
-rw-r--r--src/dotty/tools/dotc/Compiler.scala1
-rw-r--r--src/dotty/tools/dotc/transform/MoveStatics.scala64
3 files changed, 71 insertions, 3 deletions
diff --git a/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala
index 002a6bf27..295a419f8 100644
--- a/src/dotty/tools/backend/jvm/DottyBackendInterface.scala
+++ b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala
@@ -12,7 +12,7 @@ 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.reflect.io.{AbstractFile, Directory, PlainDirectory}
import scala.tools.asm.{AnnotationVisitor, ClassVisitor, FieldVisitor, MethodVisitor}
import scala.tools.nsc.backend.jvm.{BCodeHelpers, BackendInterface}
import dotty.tools.dotc.core._
@@ -24,13 +24,16 @@ import Symbols._
import Denotations._
import Phases._
import java.lang.AssertionError
-import dotty.tools.dotc.util.{Positions, DotClass}
+
+import dotty.tools.dotc.util.{DotClass, Positions}
import Decorators._
import tpd._
+
import scala.tools.asm
import NameOps._
import StdNames.nme
import NameOps._
+import dotty.tools.dotc.core
class DottyBackendInterface(outputDirectory: AbstractFile)(implicit ctx: Context) extends BackendInterface{
type Symbol = Symbols.Symbol
@@ -633,7 +636,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile)(implicit ctx: Context
toDenot(sym)(shiftedContext).isStatic(shiftedContext)
}
- def isStaticConstructor: Boolean = isStaticMember && isClassConstructor
+ def isStaticConstructor: Boolean = (isStaticMember && isClassConstructor) || (sym.name eq core.Names.STATIC_CONSTRUCTOR)
// navigation
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
+ }
+}