From 5afec10f2ddcc3768b523659cc158260cd2cd5ec Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Fri, 14 Feb 2014 20:13:47 +0100 Subject: [PATCH] entry point finding not quite done yet, need to figure out how to best write `definitions.isArrayOfSymbol` (see scalac). for that, need to understand the `Type` classes in dotty. --- .../tools/dotc/backend/jvm/BCodeHelpers.scala | 45 ++++++++++------- .../dotc/backend/jvm/CollectEntryPoints.scala | 48 +++++++++++++++++++ .../tools/dotc/backend/jvm/GenBCode.scala | 5 +- 3 files changed, 79 insertions(+), 19 deletions(-) create mode 100644 src/dotty/tools/dotc/backend/jvm/CollectEntryPoints.scala diff --git a/src/dotty/tools/dotc/backend/jvm/BCodeHelpers.scala b/src/dotty/tools/dotc/backend/jvm/BCodeHelpers.scala index ede04577b20b..e6301d1ca8a1 100755 --- a/src/dotty/tools/dotc/backend/jvm/BCodeHelpers.scala +++ b/src/dotty/tools/dotc/backend/jvm/BCodeHelpers.scala @@ -141,56 +141,70 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { * must-single-thread */ object isJavaEntryPoint { - import core.Types.{MethodType, PolyType} + // The given symbol is a method with the right name and signature to be a runnable java program. + def isJavaMainMethod(sym: Symbol)(implicit ctx: Context) = { + (sym.name == nme.main) && (sym.info match { + case mt @ MethodType(_, ptp :: Nil) => + isArrayOfSymbol(ptp, ctx.definitions.StringClass) && + mt.resultType.typeSymbol == ctx.definitions.UnitClass + case _ => false + }) + } + // The given class has a main method. + def hasJavaMainMethod(sym: Symbol)(implicit ctx: Context): Boolean = + (sym.info member nme.main).alternatives exists isJavaMainMethod + + /* * must-single-thread */ - def apply(sym: Symbol, ctx: Context): Boolean = { + def apply(sym: Symbol)(implicit ctx: Context): Boolean = { def fail(msg: String, pos: util.Positions.Position = sym.pos) = { - ctx.warning(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" + s" has a main method with parameter type Array[String], but ${sym.fullName} will not be a runnable program.\n Reason: $msg", // 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." + ctx.source.atPos(sym.pos) ) false } def failNoForwarder(msg: String) = { fail(s"$msg, which means no static forwarder can be generated.\n") } - val possibles = if (sym.hasModuleFlag) (sym.tpe nonPrivateMember nme.main).alternatives else Nil + val possibles = if (sym is Flags.Module) (sym.info nonPrivateMember nme.main).alternatives else Nil val hasApproximate = possibles exists { m => m.info match { - case MethodType(p :: Nil, _) => p.tpe.typeSymbol == definitions.ArrayClass + case MethodType(_, p :: Nil) => p.typeSymbol == ctx.definitions.ArrayClass case _ => false } } // At this point it's a module with a main-looking method, so either succeed or warn that it isn't. hasApproximate && { // Before erasure so we can identify generic mains. - enteringErasure { - val companion = sym.linkedClassOfClass + ctx.atPhase(ctx.typerPhase) { implicit ctx => + val companion = sym.linkedClass - if (definitions.hasJavaMainMethod(companion)) + if (hasJavaMainMethod(companion)) failNoForwarder("companion contains its own main method") - else if (companion.tpe.member(nme.main) != NoSymbol) + else if (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.isTrait) + else if (companion 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 definitions.isJavaMainMethod) || { + else (possibles exists isJavaMainMethod) || { possibles exists { m => m.info match { - case PolyType(_, _) => + case PolyType(_, _) => // TODO(lrytz) fix this fail("main methods cannot be generic.") - case MethodType(params, res) => - if (res.typeSymbol :: params exists (_.isAbstractType)) + case mt @ MethodType(_, paramTypes) => + if (mt.resultType.typeSymbol :: paramTypes.map(_.typeSymbol) exists (_.isAbstractType)) fail("main methods cannot refer to type parameters or abstract types.", m.pos) else definitions.isJavaMainMethod(m) || fail("main method must have exact signature (Array[String])Unit", m.pos) @@ -202,7 +216,6 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { } } } - } /* diff --git a/src/dotty/tools/dotc/backend/jvm/CollectEntryPoints.scala b/src/dotty/tools/dotc/backend/jvm/CollectEntryPoints.scala new file mode 100644 index 000000000000..0cf7ca4be6d5 --- /dev/null +++ b/src/dotty/tools/dotc/backend/jvm/CollectEntryPoints.scala @@ -0,0 +1,48 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2012 LAMP/EPFL + * @author Martin Odersky + */ + +package dotty.tools.dotc +package backend.jvm + +import ast.Trees._ +import core.Phases.Phase +import core.Contexts.Context +import core.Symbols.Symbol + +/** + * Collect entry points. + * + * When more dotty phases are implemented, this phase can be removed + * and its functionality moved to an earlier phase. In scalac, it's + * part of cleanup. + */ +object CollectEntryPoints extends Phase { + import ast.tpd._ + + def name = "entryPoints" + + def run(implicit ctx: Context): Unit = { + (new CollectTraverser).traverse(ctx.compilationUnit.tpdTree) + } + + class CollectTraverser(implicit ctx: Context) extends TreeTraverser { + def traverse(tree: Tree) = tree match { + case td: TypeDef if td.isClassDef => + if (GenBCode.isJavaEntryPoint(tree.symbol)) { + entryPoints ::= tree.symbol + } + // no need to traverse class members, nested classes can't hold main. + + case _ => foldOver((), tree) + } + } + + private var entryPoints: List[Symbol] = Nil + + def getEntryPoints(implicit ctx: Context): List[Symbol] = { + entryPoints sortBy ("" + _.fullName) // For predictably ordered error messages. + } + +} \ No newline at end of file diff --git a/src/dotty/tools/dotc/backend/jvm/GenBCode.scala b/src/dotty/tools/dotc/backend/jvm/GenBCode.scala index 3aa6cf11c32b..3f330bdab23e 100755 --- a/src/dotty/tools/dotc/backend/jvm/GenBCode.scala +++ b/src/dotty/tools/dotc/backend/jvm/GenBCode.scala @@ -47,7 +47,6 @@ import core.Symbols.{Symbol, NoSymbol} * */ object GenBCode extends BCodeSyncAndTry { - import ast.tpd._ final class PlainClassBuilder(cunit: CompilationUnit)(implicit protected val ctx: Context) @@ -265,7 +264,7 @@ object GenBCode extends BCodeSyncAndTry { * (c) tear down (closing the classfile-writer and clearing maps) * */ - def runOn(units: List[CompilationUnit])(implicit ctx: Context): Unit = { + override def runOn(units: List[CompilationUnit])(implicit ctx: Context): Unit = { arrivalPos = 0 // just in case scalaPrimitives.init @@ -304,7 +303,7 @@ object GenBCode extends BCodeSyncAndTry { beanInfoCodeGen = null } - override def run(implicit ctx: Context): Unit = unsupported("run()") + def run(implicit ctx: Context): Unit = unsupported("run()") /* * Sequentially: