aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/backend/jvm/CollectEntryPoints.scala
blob: abcbbbb83059b9fb40f3453ed0873369ca0bc6a5 (plain) (blame)
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
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.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.isJavaEntryPoint(tree.symbol)) {
      ctx.genBCodePhase.asInstanceOf[GenBCode].registerEntryPoint(tree.symbol)
    }
    tree
  }
}

object CollectEntryPoints{
  def isJavaMainMethod(sym: Symbol)(implicit ctx: Context) = {
    (sym.name == nme.main) && (sym.info match {
      case r@MethodTpe(_, List(defn.ArrayOf(t)), _) =>
        (t.widenDealias =:= defn.StringType) && (
        r.resultType.widenDealias =:= defn.UnitType)
      case _ => false
    })
  }

  def isJavaEntryPoint(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 MethodTpe(_, 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 succeed, 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 MethodTpe(paramNames, paramTypes, resultType) =>
                if (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)
            }
          }
        }
      }
    }
  }
}