From 0b585c662a1422914e549c142521cab46ccd2cb9 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Fri, 11 Apr 2014 15:13:14 +0200 Subject: CollectEntryPoints. Collect entry points for backend. Previously this was done by cleanup. --- src/dotty/tools/dotc/Compiler.scala | 2 +- src/dotty/tools/dotc/config/JavaPlatform.scala | 20 +++- .../tools/dotc/transform/CollectEntryPoints.scala | 115 +++++++++++++++++++++ 3 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 src/dotty/tools/dotc/transform/CollectEntryPoints.scala (limited to 'src/dotty/tools/dotc') 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 -- cgit v1.2.3