1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
package dotty.tools.backend.jvm
import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.core.Contexts.Context
import dotty.tools.dotc.core.Types
import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, MiniPhase, MiniPhaseTransform}
import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc
import dotty.tools.dotc.backend.jvm.DottyPrimitives
import dotty.tools.dotc.core.Flags.FlagSet
import dotty.tools.dotc.transform.Erasure
import dotty.tools.dotc.transform.SymUtils._
import java.io.{File => JFile}
import scala.collection.generic.Clearable
import scala.collection.mutable
import scala.reflect.ClassTag
import scala.reflect.internal.util.WeakHashSet
import scala.reflect.io.{Directory, PlainDirectory, AbstractFile}
import scala.tools.asm.{ClassVisitor, FieldVisitor, MethodVisitor}
import scala.tools.nsc.backend.jvm.{BCodeHelpers, BackendInterface}
import dotty.tools.dotc.core._
import Periods._
import SymDenotations._
import Contexts._
import Types._
import Symbols._
import Denotations._
import Phases._
import java.lang.AssertionError
import dotty.tools.dotc.util.Positions.Position
import Decorators._
import tpd._
import StdNames.nme
/**
* Created by dark on 26/11/14.
*/
class CollectEntryPoints extends MiniPhaseTransform {
def phaseName: String = "Collect entry points"
override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
if ((tree.symbol ne NoSymbol) && CollectEntryPoints.isJavaEntyPoint(tree.symbol)) {
ctx.genBCodePhase.asInstanceOf[GenBCode].registerEntryPoint(tree.symbol)
}
tree
}
}
object CollectEntryPoints{
def isJavaMainMethod(sym: Symbol)(implicit ctx: Context) = {
val d = ctx.definitions
val StringType = d.StringType
(sym.name == nme.main) && (sym.info match {
case r@MethodType(_, List(d.ArrayType(t))) =>
(t.widenDealias =:= StringType) && (
r.resultType.widenDealias =:= d.UnitType)
case _ => false
})
}
def isJavaEntyPoint(sym: Symbol)(implicit ctx: Context): Boolean = {
import Types.MethodType
val d = ctx.definitions
val StringType = d.StringType
// The given class has a main method.
def hasJavaMainMethod(sym: Symbol): Boolean =
(toDenot(sym).info member nme.main).alternatives exists(x => isJavaMainMethod(x.symbol))
def fail(msg: String, pos: Position = sym.pos) = {
ctx.warning( sym.name +
s" has a main method with parameter type Array[String], but ${toDenot(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) (toDenot(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
}
}
// 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.
{
// implicit val c = ctx.withPhase(ctx.erasurePhase)
val companion = sym.asClass.moduleClass
if (hasJavaMainMethod(companion))
failNoForwarder("companion contains its own main method")
else if (toDenot(companion).info.member(nme.main) != NoDenotation)
// 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=> isJavaMainMethod(x.symbol))) || {
possibles exists { m =>
toDenot(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
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)
}
}
}
}
}
}
}
|