summaryrefslogblamecommitdiff
path: root/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/ClassInfos.scala
blob: 026d66415c0ebfeccd8bc85bb4e09fa0ccb3d8d3 (plain) (tree)














































































































































                                                                                    
/* Scala.js compiler
 * Copyright 2013 LAMP/EPFL
 * @author  Sébastien Doeraene
 */

package scala.scalajs.compiler

import scala.language.implicitConversions

import scala.collection.mutable
import scala.tools.nsc._

import java.io.{ File, PrintWriter, BufferedOutputStream, FileOutputStream }

import scala.scalajs.ir
import ir.{Trees => js, Types => jstpe, ClassKind}
import ir.Infos._

trait ClassInfos extends SubComponent { self: GenJSCode =>
  import global._
  import jsAddons._

  /** Class data that are never eliminated by dce, so we don't need to
   *  record them.
   */
  private val AlwaysPresentClassData = {
    import ir.Definitions._
    Set("V", "Z", "C", "B", "S", "I", "J", "F", "D",
        ObjectClass, StringClass)
  }

  class ClassInfoBuilder(val symbol: ClassSymbol) {
    val name = classNameOf(symbol)
    val encodedName = encodeClassFullName(symbol)
    var isExported: Boolean = false
    val ancestorCount = symbol.ancestors.count(!_.isInterface)
    val kind = {
      if (isStaticModule(symbol))            ClassKind.ModuleClass
      else if (symbol.isInterface)           ClassKind.Interface
      else if (isRawJSType(symbol.tpe))      ClassKind.RawJSType
      else if (isHijackedBoxedClass(symbol)) ClassKind.HijackedClass
      else if (symbol.isImplClass)           ClassKind.TraitImpl
      else                                   ClassKind.Class
    }
    val superClass =
      if (kind.isClass || kind == ClassKind.HijackedClass)
        encodeClassFullName(symbol.superClass)
      else
        ""
    val ancestors = (symbol :: symbol.ancestors) map encodeClassFullName

    var optimizerHints: OptimizerHints = OptimizerHints.empty

    val methodInfos = mutable.ListBuffer.empty[MethodInfoBuilder]

    def addMethod(encodedName: String, isAbstract: Boolean = false,
        isExported: Boolean = false): MethodInfoBuilder = {
      val b = new MethodInfoBuilder(encodedName, isAbstract, isExported)
      methodInfos += b
      b
    }

    def result(): ClassInfo = {
      ClassInfo(name, encodedName, isExported, ancestorCount, kind,
          superClass, ancestors, optimizerHints,
          methodInfos.map(_.result()).result())
    }
  }

  class MethodInfoBuilder(val encodedName: String,
      val isAbstract: Boolean = false,
      val isExported: Boolean = false) {

    val calledMethods = mutable.Set.empty[(String, String)] // (tpe, method)
    val calledMethodsStatic = mutable.Set.empty[(String, String)] // (class, method)
    val instantiatedClasses = mutable.Set.empty[String]
    val accessedModules = mutable.Set.empty[String]
    val accessedClassData = mutable.Set.empty[String]
    var optimizerHints: OptimizerHints = OptimizerHints.empty

    def callsMethod(ownerIdent: js.Ident, method: js.Ident): Unit =
      calledMethods += ((patchClassName(ownerIdent.name), method.name))

    def callsMethod(owner: Symbol, method: js.Ident): Unit =
      calledMethods += ((patchClassName(encodeClassFullName(owner)), method.name))

    def callsMethodStatic(ownerIdent: js.Ident, method: js.Ident): Unit =
      calledMethodsStatic += ((patchClassName(ownerIdent.name), method.name))

    def instantiatesClass(classSym: Symbol): Unit =
      instantiatedClasses += patchClassName(encodeClassFullName(classSym))

    def accessesModule(moduleClassSym: Symbol): Unit =
      accessedModules += patchModuleName(encodeModuleFullName(moduleClassSym))

    def accessesClassData(refType: jstpe.ReferenceType): Unit = {
      val className = refType match {
        case jstpe.ClassType(name)    => name
        case jstpe.ArrayType(base, _) => base
      }
      if (!AlwaysPresentClassData.contains(className))
        accessedClassData += className
    }

    def createsAnonFunction(funInfo: ClassInfoBuilder): Unit = {
      for (methodInfo <- funInfo.methodInfos) {
        calledMethods ++= methodInfo.calledMethods
        calledMethodsStatic ++= methodInfo.calledMethodsStatic
        instantiatedClasses ++= methodInfo.instantiatedClasses
        accessedModules ++= methodInfo.accessedModules
        accessedClassData ++= methodInfo.accessedClassData
      }
    }

    private def patchClassName(name: String): String = name match {
      case "jl_String$" => "sjsr_RuntimeString$"
      case _ => name
    }

    private def patchModuleName(name: String): String = name match {
      case "jl_String" => "sjsr_RuntimeString"
      case _ => name
    }

    def result(): MethodInfo = {
      MethodInfo(
          encodedName,
          isAbstract,
          isExported,
          calledMethods.toList.groupBy(_._1).mapValues(_.map(_._2)),
          calledMethodsStatic.toList.groupBy(_._1).mapValues(_.map(_._2)),
          instantiatedClasses.toList,
          accessedModules.result.toList,
          accessedClassData.result.toList,
          optimizerHints
      )
    }
  }

  private def classNameOf(sym: Symbol): String =
    if (needsModuleClassSuffix(sym)) sym.fullName + nme.MODULE_SUFFIX_STRING
    else sym.fullName
}