Skip to content

Commit 0db32d2

Browse files
committed
New phase: collectEntryPoints
collects entry points and registers them in backend
1 parent c742e95 commit 0db32d2

File tree

2 files changed

+121
-69
lines changed

2 files changed

+121
-69
lines changed
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package dotty.tools.backend.jvm
2+
3+
import dotty.tools.dotc.ast.tpd
4+
import dotty.tools.dotc.core.Contexts.Context
5+
import dotty.tools.dotc.core.Types
6+
import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, MiniPhase, MiniPhaseTransform}
7+
import dotty.tools.dotc.ast.tpd
8+
import dotty.tools.dotc
9+
import dotty.tools.dotc.backend.jvm.DottyPrimitives
10+
import dotty.tools.dotc.core.Flags.FlagSet
11+
import dotty.tools.dotc.transform.Erasure
12+
import dotty.tools.dotc.transform.SymUtils._
13+
import java.io.{File => JFile}
14+
15+
import scala.collection.generic.Clearable
16+
import scala.collection.mutable
17+
import scala.reflect.ClassTag
18+
import scala.reflect.internal.util.WeakHashSet
19+
import scala.reflect.io.{Directory, PlainDirectory, AbstractFile}
20+
import scala.tools.asm.{ClassVisitor, FieldVisitor, MethodVisitor}
21+
import scala.tools.nsc.backend.jvm.{BCodeHelpers, BackendInterface}
22+
import dotty.tools.dotc.core._
23+
import Periods._
24+
import SymDenotations._
25+
import Contexts._
26+
import Types._
27+
import Symbols._
28+
import Denotations._
29+
import Phases._
30+
import java.lang.AssertionError
31+
import dotty.tools.dotc.util.Positions.Position
32+
import Decorators._
33+
import tpd._
34+
import StdNames.nme
35+
36+
/**
37+
* Created by dark on 26/11/14.
38+
*/
39+
class CollectEntryPoints extends MiniPhaseTransform {
40+
def phaseName: String = "Collect entry points"
41+
42+
override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
43+
if((tree.symbol ne NoSymbol) && CollectEntryPoints.isJavaEntyPoint(tree.symbol)) {
44+
ctx.genBCodePhase.asInstanceOf[GenBCode].registerEntryPoint(tree.symbol)
45+
}
46+
tree
47+
}
48+
}
49+
50+
object CollectEntryPoints{
51+
def isJavaEntyPoint(sym: Symbol)(implicit ctx: Context): Boolean = {
52+
import Types.MethodType
53+
val d = ctx.definitions
54+
val StringType = d.StringType
55+
def isJavaMainMethod(sym: Symbol) = (sym.name == nme.main) && (toDenot(sym).info match {
56+
case r@ MethodType(_, List(d.ArrayType(StringType))) => r.resultType eq d.UnitType
57+
case _ => false
58+
})
59+
// The given class has a main method.
60+
def hasJavaMainMethod(sym: Symbol): Boolean =
61+
(toDenot(sym).info member nme.main).alternatives exists(x => isJavaMainMethod(x.symbol))
62+
63+
def fail(msg: String, pos: Position = sym.pos) = {
64+
ctx.warning( sym.name +
65+
s" has a main method with parameter type Array[String], but ${toDenot(sym).fullName} will not be a runnable program.\n Reason: $msg",
66+
sourcePos(sym.pos)
67+
// TODO: make this next claim true, if possible
68+
// by generating valid main methods as static in module classes
69+
// not sure what the jvm allows here
70+
// + " You can still run the program by calling it as " + javaName(sym) + " instead."
71+
)
72+
false
73+
}
74+
def failNoForwarder(msg: String) = {
75+
fail(s"$msg, which means no static forwarder can be generated.\n")
76+
}
77+
val possibles = if (sym.flags is Flags.Module) (toDenot(sym).info nonPrivateMember nme.main).alternatives else Nil
78+
val hasApproximate = possibles exists { m =>
79+
m.info match {
80+
case MethodType(_, p :: Nil) =>
81+
p.typeSymbol == defn.ArrayClass
82+
case _ => false
83+
}
84+
}
85+
// At this point it's a module with a main-looking method, so either succeed or warn that it isn't.
86+
hasApproximate && {
87+
// Before erasure so we can identify generic mains.
88+
{
89+
// implicit val c = ctx.withPhase(ctx.erasurePhase)
90+
91+
val companion = sym.asClass.moduleClass
92+
93+
if (hasJavaMainMethod(companion))
94+
failNoForwarder("companion contains its own main method")
95+
else if (toDenot(companion).info.member(nme.main) != NoDenotation)
96+
// this is only because forwarders aren't smart enough yet
97+
failNoForwarder("companion contains its own main method (implementation restriction: no main is allowed, regardless of signature)")
98+
else if (companion.flags is Flags.Trait)
99+
failNoForwarder("companion is a trait")
100+
// Now either succeeed, or issue some additional warnings for things which look like
101+
// attempts to be java main methods.
102+
else (possibles exists(x=> isJavaMainMethod(x.symbol))) || {
103+
possibles exists { m =>
104+
toDenot(m.symbol).info match {
105+
case t:PolyType =>
106+
fail("main methods cannot be generic.")
107+
case t@MethodType(paramNames, paramTypes) =>
108+
if (t.resultType :: paramTypes exists (_.typeSymbol.isAbstractType))
109+
fail("main methods cannot refer to type parameters or abstract types.", m.symbol.pos)
110+
else
111+
isJavaMainMethod(m.symbol) || fail("main method must have exact signature (Array[String])Unit", m.symbol.pos)
112+
case tp =>
113+
fail(s"don't know what this is: $tp", m.symbol.pos)
114+
}
115+
}
116+
}
117+
}
118+
}
119+
}
120+
}

src/dotty/tools/backend/jvm/DottyBackendInterface.scala

Lines changed: 1 addition & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -395,75 +395,7 @@ abstract class DottyBackendInterface()(implicit ctx: Context) extends BackendInt
395395
def hasAnnotation(sym: Symbol): Boolean = false
396396
def shouldEmitForwarders: Boolean = //exitingPickler { !(sym.name.toString contains '$')
397397
(sym is Flags.Module) && !(sym is Flags.ImplClass) /// !sym.isNestedClass
398-
def isJavaEntryPoint: Boolean = {
399-
import Types.MethodType
400-
val d = ctx.definitions
401-
val StringType = d.StringType
402-
def isJavaMainMethod(sym: Symbol) = (sym.name == nme.main) && (toDenot(sym).info match {
403-
case r@ MethodType(_, List(d.ArrayType(StringType))) => r.resultType eq d.UnitType
404-
case _ => false
405-
})
406-
// The given class has a main method.
407-
def hasJavaMainMethod(sym: Symbol): Boolean =
408-
(toDenot(sym).info member nme.main).alternatives exists(x => isJavaMainMethod(x.symbol))
409-
410-
def fail(msg: String, pos: Position = sym.pos) = {
411-
ctx.warning( sym.name +
412-
s" has a main method with parameter type Array[String], but ${toDenot(sym).fullName} will not be a runnable program.\n Reason: $msg",
413-
sourcePos(sym.pos)
414-
// TODO: make this next claim true, if possible
415-
// by generating valid main methods as static in module classes
416-
// not sure what the jvm allows here
417-
// + " You can still run the program by calling it as " + javaName(sym) + " instead."
418-
)
419-
false
420-
}
421-
def failNoForwarder(msg: String) = {
422-
fail(s"$msg, which means no static forwarder can be generated.\n")
423-
}
424-
val possibles = if (sym.flags is Flags.Module) (toDenot(sym).info nonPrivateMember nme.main).alternatives else Nil
425-
val hasApproximate = possibles exists { m =>
426-
m.info match {
427-
case MethodType(_, p :: Nil) =>
428-
p.typeSymbol == defn.ArrayClass
429-
case _ => false
430-
}
431-
}
432-
// At this point it's a module with a main-looking method, so either succeed or warn that it isn't.
433-
hasApproximate && {
434-
// Before erasure so we can identify generic mains.
435-
{
436-
// implicit val c = ctx.withPhase(ctx.erasurePhase)
437-
438-
val companion = sym.asClass.moduleClass
439-
440-
if (hasJavaMainMethod(companion))
441-
failNoForwarder("companion contains its own main method")
442-
else if (toDenot(companion).info.member(nme.main) != NoSymbol)
443-
// this is only because forwarders aren't smart enough yet
444-
failNoForwarder("companion contains its own main method (implementation restriction: no main is allowed, regardless of signature)")
445-
else if (companion.flags is Flags.Trait)
446-
failNoForwarder("companion is a trait")
447-
// Now either succeeed, or issue some additional warnings for things which look like
448-
// attempts to be java main methods.
449-
else (possibles exists(x=> isJavaMainMethod(x.symbol))) || {
450-
possibles exists { m =>
451-
toDenot(m.symbol).info match {
452-
case t:PolyType =>
453-
fail("main methods cannot be generic.")
454-
case t@MethodType(paramNames, paramTypes) =>
455-
if (t.resultType :: paramTypes exists (_.typeSymbol.isAbstractType))
456-
fail("main methods cannot refer to type parameters or abstract types.", m.symbol.pos)
457-
else
458-
isJavaMainMethod(m.symbol) || fail("main method must have exact signature (Array[String])Unit", m.symbol.pos)
459-
case tp =>
460-
fail(s"don't know what this is: $tp", m.symbol.pos)
461-
}
462-
}
463-
}
464-
}
465-
}
466-
}
398+
def isJavaEntryPoint: Boolean = CollectEntryPoints.isJavaEntyPoint(sym)
467399

468400
def isClassConstructor: Boolean = sym.name == nme.CONSTRUCTOR
469401

0 commit comments

Comments
 (0)