aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc
diff options
context:
space:
mode:
authorDmitry Petrashko <dmitry.petrashko@gmail.com>2014-04-11 15:13:14 +0200
committerDmitry Petrashko <dmitry.petrashko@gmail.com>2014-05-06 16:18:40 +0200
commit0b585c662a1422914e549c142521cab46ccd2cb9 (patch)
treebc762837feb2020002c29d8ef06ba1ab75fbb5e5 /src/dotty/tools/dotc
parentafb21953f75beda26a14b447e2caa0a2cb21904a (diff)
downloaddotty-0b585c662a1422914e549c142521cab46ccd2cb9.tar.gz
dotty-0b585c662a1422914e549c142521cab46ccd2cb9.tar.bz2
dotty-0b585c662a1422914e549c142521cab46ccd2cb9.zip
CollectEntryPoints.
Collect entry points for backend. Previously this was done by cleanup.
Diffstat (limited to 'src/dotty/tools/dotc')
-rw-r--r--src/dotty/tools/dotc/Compiler.scala2
-rw-r--r--src/dotty/tools/dotc/config/JavaPlatform.scala20
-rw-r--r--src/dotty/tools/dotc/transform/CollectEntryPoints.scala115
3 files changed, 135 insertions, 2 deletions
diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala
index 5f2f4f7b0..36af6a438 100644
--- a/src/dotty/tools/dotc/Compiler.scala
+++ b/src/dotty/tools/dotc/Compiler.scala
@@ -30,7 +30,7 @@ class Compiler {
new TypeTestsCasts,
new InterceptedMethods),
List(new Erasure),
- List(new UncurryTreeTransform)
+ List(new UncurryTreeTransform, new CollectEntryPoints)
)
var runId = 1
diff --git a/src/dotty/tools/dotc/config/JavaPlatform.scala b/src/dotty/tools/dotc/config/JavaPlatform.scala
index c4421d16b..043f41a44 100644
--- a/src/dotty/tools/dotc/config/JavaPlatform.scala
+++ b/src/dotty/tools/dotc/config/JavaPlatform.scala
@@ -5,7 +5,9 @@ package config
import io.{AbstractFile,ClassPath,JavaClassPath,MergedClassPath,DeltaClassPath}
import ClassPath.{ JavaContext, DefaultJavaContext }
import core.Contexts._
-import core.SymDenotations._, core.Symbols._, core.{SymbolLoader, ClassfileLoader}
+import core.SymDenotations._, core.Symbols._, dotty.tools.dotc.core._
+import Types._, Contexts._, Symbols._, Denotations._, SymDenotations._, StdNames._, Names._
+import Flags._, Scopes._, Decorators._, NameOps._, util.Positions._
class JavaPlatform extends Platform {
@@ -17,6 +19,22 @@ class JavaPlatform extends Platform {
currentClassPath.get
}
+ // The given symbol is a method with the right name and signature to be a runnable java program.
+ def isJavaMainMethod(sym: SymDenotation)(implicit ctx: Context) = {
+ val dn = defn
+ (sym.name == nme.main) && (sym.info match {
+ case t@MethodType(_, dn.ArrayType(el) :: Nil) => el =:= defn.StringType && (t.resultType isRef defn.UnitClass)
+ case _ => false
+ })
+ }
+
+ // The given class has a main method.
+ def hasJavaMainMethod(sym: Symbol)(implicit ctx: Context): Boolean =
+ (sym.info member nme.main).hasAltWith {
+ case x: SymDenotation => isJavaMainMethod(x)
+ case _ => false
+ }
+
/** Update classpath with a substituted subentry */
def updateClassPath(subst: Map[ClassPath, ClassPath]) =
currentClassPath = Some(new DeltaClassPath(currentClassPath.get, subst))
diff --git a/src/dotty/tools/dotc/transform/CollectEntryPoints.scala b/src/dotty/tools/dotc/transform/CollectEntryPoints.scala
new file mode 100644
index 000000000..0e9f98e79
--- /dev/null
+++ b/src/dotty/tools/dotc/transform/CollectEntryPoints.scala
@@ -0,0 +1,115 @@
+package dotty.tools.dotc.transform
+
+import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer}
+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 TreeTransform {
+
+ /** perform context-dependant initialization */
+ override def init(implicit ctx: Context, info: TransformerInfo): Unit = {
+ entryPoints = collection.immutable.TreeSet.empty[Symbol](new SymbolOrdering())
+ assert(ctx.platform.isInstanceOf[JavaPlatform], "Java platform specific phase")
+ }
+
+ private var entryPoints: Set[Symbol] = _
+
+ def getEntryPoints = entryPoints.toList
+
+ override def name: 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 != NoSymbol && companion.info.member(nme.main) != NoSymbol)
+ // 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 => 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)
+ }
+} \ No newline at end of file