aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/transform/CollectEntryPoints.scala
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/src/dotty/tools/dotc/transform/CollectEntryPoints.scala')
-rw-r--r--compiler/src/dotty/tools/dotc/transform/CollectEntryPoints.scala116
1 files changed, 116 insertions, 0 deletions
diff --git a/compiler/src/dotty/tools/dotc/transform/CollectEntryPoints.scala b/compiler/src/dotty/tools/dotc/transform/CollectEntryPoints.scala
new file mode 100644
index 000000000..714255962
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/transform/CollectEntryPoints.scala
@@ -0,0 +1,116 @@
+package dotty.tools.dotc.transform
+
+import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer, MiniPhaseTransform}
+import dotty.tools.dotc.ast.tpd
+import dotty.tools.dotc.core.Contexts.Context
+import scala.collection.mutable.ListBuffer
+import dotty.tools.dotc.core.{Scopes, Flags}
+import dotty.tools.dotc.core.Symbols.NoSymbol
+import scala.annotation.tailrec
+import dotty.tools.dotc.core._
+import Symbols._
+import scala.Some
+import dotty.tools.dotc.transform.TreeTransforms.{NXTransformations, TransformerInfo, TreeTransform, TreeTransformer}
+import dotty.tools.dotc.ast.tpd
+import dotty.tools.dotc.core.Contexts.Context
+import scala.collection.mutable
+import dotty.tools.dotc.core.Names.Name
+import NameOps._
+import Types._
+import scala.collection.SortedSet
+import Decorators._
+import StdNames._
+import dotty.tools.dotc.util.Positions.Position
+import dotty.tools.dotc.config.JavaPlatform
+
+class CollectEntryPoints extends MiniPhaseTransform {
+
+ /** perform context-dependant initialization */
+ override def prepareForUnit(tree: tpd.Tree)(implicit ctx: Context) = {
+ entryPoints = collection.immutable.TreeSet.empty[Symbol](new SymbolOrdering())
+ assert(ctx.platform.isInstanceOf[JavaPlatform], "Java platform specific phase")
+ this
+ }
+
+ private var entryPoints: Set[Symbol] = _
+
+ def getEntryPoints = entryPoints.toList
+
+ override def phaseName: String = "collectEntryPoints"
+ override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
+ if (tree.symbol.owner.isClass && isJavaEntryPoint(tree.symbol)) {
+ // collecting symbols for entry points here (as opposed to GenBCode where they are used)
+ // has the advantage of saving an additional pass over all ClassDefs.
+ entryPoints += tree.symbol
+ }
+ tree
+ }
+
+ def isJavaEntryPoint(sym: Symbol)(implicit ctx: Context): Boolean = {
+ def fail(msg: String, pos: Position = sym.pos) = {
+ ctx.warning(sym.name +
+ s" has a main method with parameter type Array[String], but ${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) (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
+ }
+ }
+ def precise(implicit ctx: Context) = {
+ val companion = sym.companionClass //sym.asClass.linkedClassOfClass
+ val javaPlatform = ctx.platform.asInstanceOf[JavaPlatform]
+ if (javaPlatform.hasJavaMainMethod(companion))
+ failNoForwarder("companion contains its own main method")
+ else if (companion.exists && companion.info.member(nme.main).exists)
+ // this is only because forwarders aren't smart enough yet
+ failNoForwarder("companion contains its own main method (implementation restriction: no main is allowed, regardless of signature)")
+ else if (companion.flags is Flags.Trait)
+ failNoForwarder("companion is a trait")
+ // Now either succeed, or issue some additional warnings for things which look like
+ // attempts to be java main methods.
+ else (possibles exists (x => javaPlatform.isJavaMainMethod(x.symbol))) || {
+ possibles exists {
+ m =>
+ 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
+ javaPlatform.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)
+ }
+ }
+ }
+ }
+
+ // At this point it's a module with a main-looking method, so either succeed or warn that it isn't.
+ hasApproximate && precise(ctx.withPhase(ctx.erasurePhase))
+ // Before erasure so we can identify generic mains.
+
+
+}
+
+}
+
+class SymbolOrdering(implicit ctx: Context) extends Ordering[Symbol] {
+ override def compare(x: Symbol, y: Symbol): Int = {
+ x.fullName.toString.compareTo(y.fullName.toString)
+ }
+}