diff options
Diffstat (limited to 'src/dotty/tools/backend/sjs/JSEncoding.scala')
-rw-r--r-- | src/dotty/tools/backend/sjs/JSEncoding.scala | 389 |
1 files changed, 0 insertions, 389 deletions
diff --git a/src/dotty/tools/backend/sjs/JSEncoding.scala b/src/dotty/tools/backend/sjs/JSEncoding.scala deleted file mode 100644 index e8ea3258b..000000000 --- a/src/dotty/tools/backend/sjs/JSEncoding.scala +++ /dev/null @@ -1,389 +0,0 @@ -package dotty.tools.backend.sjs - -import scala.collection.mutable - -import dotty.tools.FatalError - -import dotty.tools.dotc.core._ -import Periods._ -import SymDenotations._ -import Contexts._ -import Types._ -import Symbols._ -import Denotations._ -import NameOps._ -import StdNames._ - -import org.scalajs.core.ir -import ir.{Trees => js, Types => jstpe} - -import ScopedVar.withScopedVars -import JSDefinitions._ -import JSInterop._ - -/** Encoding of symbol names for JavaScript - * - * Some issues that this encoding solves: - * * Overloading: encode the full signature in the JS name - * * Same scope for fields and methods of a class - * * Global access to classes and modules (by their full name) - * - * @author Sébastien Doeraene - */ -object JSEncoding { - - /** Signature separator string (between parameter types) */ - private final val SignatureSep = "__" - - /** Name given to the local Scala.js environment variable */ - private final val ScalaJSEnvironmentName = "ScalaJS" - - implicit class SymOps(val self: Symbol) extends AnyVal { - def unexpandedName(implicit ctx: Context): Names.Name = - self.name.unexpandedName - } - - implicit class MyNameOps(val self: Names.Name) extends AnyVal { - def decoded: String = self.decode.toString - } - - // Fresh local name generator ---------------------------------------------- - - class LocalNameGenerator { - import LocalNameGenerator._ - - private val usedLocalNames = mutable.Set.empty[String] - private val localSymbolNames = mutable.Map.empty[Symbol, String] - - def localSymbolName(sym: Symbol)(implicit ctx: Context): String = - localSymbolNames.getOrElseUpdate(sym, freshName(sym.name.toString)) - - def freshLocalIdent()(implicit pos: ir.Position): js.Ident = - js.Ident(freshName(), None) - - def freshLocalIdent(base: String)(implicit pos: ir.Position): js.Ident = - js.Ident(freshName(base), Some(base)) - - private def freshName(base: String = "x"): String = { - var suffix = 1 - var longName = base - while (usedLocalNames(longName) || isReserved(longName)) { - suffix += 1 - longName = base+"$"+suffix - } - usedLocalNames += longName - mangleJSName(longName) - } - } - - private object LocalNameGenerator { - private val isReserved = - Set("arguments", "eval", ScalaJSEnvironmentName) - } - - // Encoding methods ---------------------------------------------------------- - - def encodeLabelSym(sym: Symbol)( - implicit ctx: Context, pos: ir.Position, localNames: LocalNameGenerator): js.Ident = { - require(sym.is(Flags.Label), "encodeLabelSym called with non-label symbol: " + sym) - js.Ident(localNames.localSymbolName(sym), Some(sym.unexpandedName.decoded)) - } - - private def allRefClasses(implicit ctx: Context): Set[Symbol] = { - //TODO - /*(Set(ObjectRefClass, VolatileObjectRefClass) ++ - refClass.values ++ volatileRefClass.values)*/ - Set() - } - - def encodeFieldSym(sym: Symbol)( - implicit ctx: Context, pos: ir.Position): js.Ident = { - require(sym.owner.isClass && sym.isTerm && !sym.is(Flags.Method) && !sym.is(Flags.Module), - "encodeFieldSym called with non-field symbol: " + sym) - - val name0 = encodeMemberNameInternal(sym) - val name = - if (name0.charAt(name0.length()-1) != ' ') name0 - else name0.substring(0, name0.length()-1) - - /* We have to special-case fields of Ref types (IntRef, ObjectRef, etc.) - * because they are emitted as private by our .scala source files, but - * they are considered public at use site since their symbols come from - * Java-emitted .class files. - */ - val idSuffix = - if (sym.is(Flags.Private) || allRefClasses.contains(sym.owner)) - sym.owner.asClass.baseClasses.size.toString - else - "f" - - val encodedName = name + "$" + idSuffix - js.Ident(mangleJSName(encodedName), Some(sym.unexpandedName.decoded)) - } - - def encodeMethodSym(sym: Symbol, reflProxy: Boolean = false)( - implicit ctx: Context, pos: ir.Position): js.Ident = { - val (encodedName, paramsString) = encodeMethodNameInternal(sym, reflProxy) - js.Ident(encodedName + paramsString, - Some(sym.unexpandedName.decoded + paramsString)) - } - - def encodeMethodName(sym: Symbol, reflProxy: Boolean = false)( - implicit ctx: Context): String = { - val (encodedName, paramsString) = encodeMethodNameInternal(sym, reflProxy) - encodedName + paramsString - } - - /** Encodes a method symbol of java.lang.String for use in RuntimeString. - * - * This basically means adding an initial parameter of type - * java.lang.String, which is the `this` parameter. - */ - def encodeRTStringMethodSym(sym: Symbol)( - implicit ctx: Context, pos: ir.Position): js.Ident = { - require(sym.owner == defn.StringClass) - require(!sym.isClassConstructor && !sym.is(Flags.Private)) - - val (encodedName, paramsString) = - encodeMethodNameInternal(sym, inRTClass = true) - js.Ident(encodedName + paramsString, - Some(sym.unexpandedName.decoded + paramsString)) - } - - /** Encodes a constructor symbol of java.lang.String for use in RuntimeString. - * - * - The name is rerouted to `newString` - * - The result type is set to `java.lang.String` - */ - def encodeRTStringCtorSym(sym: Symbol)( - implicit ctx: Context, pos: ir.Position): js.Ident = { - require(sym.owner == defn.StringClass) - require(sym.isClassConstructor && !sym.is(Flags.Private)) - - val paramTypeNames = sym.info.firstParamTypes.map(internalName(_)) - val paramAndResultTypeNames = paramTypeNames :+ ir.Definitions.StringClass - val paramsString = makeParamsString(paramAndResultTypeNames) - - js.Ident("newString" + paramsString, - Some(sym.unexpandedName.decoded + paramsString)) - } - - private def encodeMethodNameInternal(sym: Symbol, - reflProxy: Boolean = false, inRTClass: Boolean = false)( - implicit ctx: Context): (String, String) = { - require(sym.is(Flags.Method), "encodeMethodSym called with non-method symbol: " + sym) - - def name = encodeMemberNameInternal(sym) - - val encodedName = { - if (sym.isClassConstructor) { - "init_" - } else if (sym.is(Flags.Private)) { - (mangleJSName(name) + SignatureSep + "p" + - sym.owner.asClass.baseClasses.size.toString) - } else { - mangleJSName(name) - } - } - - val paramsString = makeParamsString(sym, reflProxy, inRTClass) - - (encodedName, paramsString) - } - - def encodeStaticMemberSym(sym: Symbol)( - implicit ctx: Context, pos: ir.Position): js.Ident = { - require(sym.is(Flags.JavaStaticTerm), - "encodeStaticMemberSym called with non-static symbol: " + sym) - js.Ident( - mangleJSName(encodeMemberNameInternal(sym)) + - makeParamsString(List(internalName(sym.info))), - Some(sym.unexpandedName.decoded)) - } - - def encodeLocalSym(sym: Symbol)( - implicit ctx: Context, pos: ir.Position, localNames: LocalNameGenerator): js.Ident = { - require(!sym.owner.isClass && sym.isTerm && !sym.is(Flags.Method) && !sym.is(Flags.Module), - "encodeLocalSym called with non-local symbol: " + sym) - js.Ident(localNames.localSymbolName(sym), Some(sym.unexpandedName.decoded)) - } - - def foreignIsImplClass(sym: Symbol)(implicit ctx: Context): Boolean = - sym.name.isImplClassName - - def encodeClassType(sym: Symbol)(implicit ctx: Context): jstpe.Type = { - if (sym == defn.ObjectClass) jstpe.AnyType - else if (isJSType(sym)) jstpe.AnyType - else { - assert(sym != defn.ArrayClass, - "encodeClassType() cannot be called with ArrayClass") - jstpe.ClassType(encodeClassFullName(sym)) - } - } - - def encodeClassFullNameIdent(sym: Symbol)( - implicit ctx: Context, pos: ir.Position): js.Ident = { - js.Ident(encodeClassFullName(sym), Some(sym.fullName.toString)) - } - - def encodeClassFullName(sym: Symbol)(implicit ctx: Context): String = { - if (sym == defn.NothingClass) ir.Definitions.RuntimeNothingClass - else if (sym == defn.NullClass) ir.Definitions.RuntimeNullClass - else ir.Definitions.encodeClassName(sym.fullName.toString) - } - - private def encodeMemberNameInternal(sym: Symbol)( - implicit ctx: Context): String = { - sym.name.toString.replace("_", "$und").replace("~", "$tilde") - } - - def toIRType(tp: Type)(implicit ctx: Context): jstpe.Type = { - val refType = toReferenceTypeInternal(tp) - refType._1 match { - case tpe: jstpe.ClassType => - val sym = refType._2 - if (sym.asClass.isPrimitiveValueClass) { - if (sym == defn.BooleanClass) - jstpe.BooleanType - else if (sym == defn.FloatClass) - jstpe.FloatType - else if (sym == defn.DoubleClass) - jstpe.DoubleType - else if (sym == defn.LongClass) - jstpe.LongType - else if (sym == defn.UnitClass) - jstpe.NoType - else - jstpe.IntType - } else { - if (sym == defn.ObjectClass || isJSType(sym)) - jstpe.AnyType - else if (sym == defn.NothingClass) - jstpe.NothingType - else if (sym == defn.NullClass) - jstpe.NullType - else - tpe - } - - case tpe: jstpe.ArrayType => - tpe - } - } - - def toReferenceType(tp: Type)(implicit ctx: Context): jstpe.ReferenceType = - toReferenceTypeInternal(tp)._1 - - private def toReferenceTypeInternal(tp: Type)( - implicit ctx: Context): (jstpe.ReferenceType, Symbol) = { - - /** - * Primitive types are represented as TypeRefs to the class symbol of, for example, scala.Int. - * The `primitiveTypeMap` maps those class symbols to the corresponding PrimitiveBType. - */ - def primitiveOrClassToRefType(sym: Symbol): (jstpe.ReferenceType, Symbol) = { - assert(sym.isClass, sym) - //assert(sym != defn.ArrayClass || isCompilingArray, sym) - (jstpe.ClassType(encodeClassFullName(sym)), sym) - } - - /** - * When compiling Array.scala, the type parameter T is not erased and shows up in method - * signatures, e.g. `def apply(i: Int): T`. A TyperRef to T is replaced by ObjectReference. - */ - def nonClassTypeRefToRefType(sym: Symbol): (jstpe.ReferenceType, Symbol) = { - //assert(sym.isType && isCompilingArray, sym) - (jstpe.ClassType(ir.Definitions.ObjectClass), defn.ObjectClass) - } - - tp.widenDealias match { - // Array type such as Array[Int] (kept by erasure) - case JavaArrayType(el) => - val elRefType = toReferenceTypeInternal(el) - (jstpe.ArrayType(elRefType._1), elRefType._2) - - case t: TypeRef => - if (!t.symbol.isClass) nonClassTypeRefToRefType(t.symbol) // See comment on nonClassTypeRefToBType - else primitiveOrClassToRefType(t.symbol) // Common reference to a type such as scala.Int or java.lang.String - - case Types.ClassInfo(_, sym, _, _, _) => - /* We get here, for example, for genLoadModule, which invokes - * toTypeKind(moduleClassSymbol.info) - */ - primitiveOrClassToRefType(sym) - - case t: MethodType => // triggers for LabelDefs - toReferenceTypeInternal(t.resultType) - - /* AnnotatedType should (probably) be eliminated by erasure. However we know it happens for - * meta-annotated annotations (@(ann @getter) val x = 0), so we don't emit a warning. - * The type in the AnnotationInfo is an AnnotatedTpe. Tested in jvm/annotations.scala. - */ - case a @ AnnotatedType(t, _) => - //debuglog(s"typeKind of annotated type $a") - toReferenceTypeInternal(t) - } - } - - /** Patches the result type of a method symbol to sanitize it. - * - * For some reason, dotc thinks that the `info.resultType`of an - * `isConstructor` method (for classes or traits) is the enclosing class - * or trait, but the bodies and usages act as if the result type was `Unit`. - * - * This method returns `UnitType` for constructor methods, and otherwise - * `sym.info.resultType`. - */ - def patchedResultType(sym: Symbol)(implicit ctx: Context): Type = - if (sym.isConstructor) defn.UnitType - else sym.info.resultType - - // Encoding of method signatures - - private def makeParamsString(sym: Symbol, reflProxy: Boolean, - inRTClass: Boolean)( - implicit ctx: Context): String = { - val tpe = sym.info - - val paramTypeNames0 = tpe.firstParamTypes.map(internalName(_)) - - val hasExplicitThisParameter = - inRTClass || isScalaJSDefinedJSClass(sym.owner) - val paramTypeNames = - if (!hasExplicitThisParameter) paramTypeNames0 - else encodeClassFullName(sym.owner) :: paramTypeNames0 - - val paramAndResultTypeNames = { - if (sym.isClassConstructor) - paramTypeNames - else if (reflProxy) - paramTypeNames :+ "" - else - paramTypeNames :+ internalName(patchedResultType(sym)) - } - makeParamsString(paramAndResultTypeNames) - } - - private def makeParamsString(paramAndResultTypeNames: List[String]) = - paramAndResultTypeNames.mkString(SignatureSep, SignatureSep, "") - - /** Computes the internal name for a type. */ - private def internalName(tpe: Type)(implicit ctx: Context): String = - encodeReferenceType(toReferenceType(tpe)) - - /** Encodes a [[Types.ReferenceType]], such as in an encoded method signature. - */ - private def encodeReferenceType(refType: jstpe.ReferenceType): String = { - refType match { - case jstpe.ClassType(encodedName) => encodedName - case jstpe.ArrayType(base, depth) => "A" * depth + base - } - } - - /** Mangles names that are illegal in JavaScript by prepending a `$`. - * Also mangles names that would collide with these mangled names. - */ - private def mangleJSName(name: String) = - if (js.isKeyword(name) || name(0).isDigit || name(0) == '$') "$" + name - else name -} |