diff options
Diffstat (limited to 'compiler/src/dotty/tools/dotc/transform/CollectEntryPoints.scala')
-rw-r--r-- | compiler/src/dotty/tools/dotc/transform/CollectEntryPoints.scala | 116 |
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) + } +} |