Skip to content

entry point finding #22

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 29 additions & 16 deletions src/dotty/tools/dotc/backend/jvm/BCodeHelpers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -202,7 +216,6 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
}
}
}

}

/*
Expand Down
48 changes: 48 additions & 0 deletions src/dotty/tools/dotc/backend/jvm/CollectEntryPoints.scala
Original file line number Diff line number Diff line change
@@ -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.
}

}
5 changes: 2 additions & 3 deletions src/dotty/tools/dotc/backend/jvm/GenBCode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down