aboutsummaryrefslogblamecommitdiff
path: root/compiler/src/dotty/tools/backend/jvm/CollectEntryPoints.scala
blob: abcbbbb83059b9fb40f3453ed0873369ca0bc6a5 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
















                                                                                                                























                                                                                                            
                                                                                        






                                                                              
                                                              
                                               
                                                      

                                                    



                     
                                                                       


                                 




















                                                                                                                                              

                                                                         
















                                                                                                                                             
                                                                                           



                                                                    
                                 
                                                       

                                                                                  











                                                                                                                               
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)
            }
          }
        }
      }
    }
  }
}