/* 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 }