All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.scalajs.linker.backend.emitter.SJSGen.scala Maven / Gradle / Ivy

The newest version!
/*
 * Scala.js (https://www.scala-js.org/)
 *
 * Copyright EPFL.
 *
 * Licensed under Apache License 2.0
 * (https://www.apache.org/licenses/LICENSE-2.0).
 *
 * See the NOTICE file distributed with this work for
 * additional information regarding copyright ownership.
 */

package org.scalajs.linker.backend.emitter

import org.scalajs.ir._
import org.scalajs.ir.Names._
import org.scalajs.ir.Types._
import org.scalajs.ir.{Trees => irt}

import org.scalajs.linker.backend.javascript.Trees._
import org.scalajs.linker.interface._

import EmitterNames._
import PolyfillableBuiltin._

/** Scala.js specific tree generators that are used across the board.
 *
 *  This class is fully stateless.
 *
 *  Also carries around lower-level generators.
 */
private[emitter] final class SJSGen(
    val jsGen: JSGen,
    val nameGen: NameGen,
    val varGen: VarGen,
    val nameCompressor: Option[NameCompressor]
) {

  import jsGen._
  import config._
  import nameGen._
  import varGen._

  val useBigIntForLongs = esFeatures.allowBigIntsForLongs

  /** Core Property Names. */
  object cpn {
    // --- Scala.js objects ---

    /** The class-wide classData field of Scala.js objects, which references their TypeData. */
    val classData = "$classData" // always in full; it is used as identification of Scala.js objects

    // --- Class ---

    /** `Char.c`: the int value of the character. */
    val c = "c"

    // --- TypeData fields ---

    /** `TypeData.constr`: the run-time constructor of the class. */
    val constr = if (minify) "C" else "constr"

    /** `TypeData.parentData`: the super class data. */
    val parentData = if (minify) "P" else "parentData"

    /** `TypeData.ancestors`: dictionary where keys are the ancestor names of all ancestors. */
    val ancestors = if (minify) "n" else "ancestors"

    /** `TypeData.componentData`: the `TypeData` of the component type of an array type. */
    val componentData = if (minify) "O" else "componentData"

    /** `TypeData.arrayBase`: the `TypeData` of the base type of an array type. */
    val arrayBase = if (minify) "B" else "arrayBase"

    /** `TypeData.arrayDepth`: the depth of an array type. */
    val arrayDepth = if (minify) "D" else "arrayDepth"

    /** `TypeData.zero`: the zero value of the type. */
    val zero = if (minify) "z" else "zero"

    /** `TypeData.arrayEncodedName`: the name of the type as it appears in its array type's name. */
    val arrayEncodedName = if (minify) "E" else "arrayEncodedName"

    /** `TypeData._classOf`: the field storing the `jl.Class` instance for that type. */
    val _classOf = if (minify) "L" else "_classOf"

    /** `TypeData._arrayOf`: the field storing the `TypeData` for that type's array type. */
    val _arrayOf = if (minify) "A" else "_arrayOf"

    /** `TypeData.isAssignableFromFun`: the implementation of `jl.Class.isAssignableFrom` without fast path. */
    val isAssignableFromFun = if (minify) "F" else "isAssignableFromFun"

    /** `TypeData.wrapArray`: the function to create an ArrayClass instance from a JS array of its elements. */
    val wrapArray = if (minify) "w" else "wrapArray"

    /** `TypeData.isJSType`: whether it is a JS type. */
    val isJSType = if (minify) "J" else "isJSType"

    /** `TypeData.name`: the user name of the class (the result of `jl.Class.getName()`). */
    val name = if (minify) "N" else "name"

    /** `TypeData.isPrimitive`: whether it is a primitive type. */
    val isPrimitive = if (minify) "X" else "isPrimitive"

    /** `TypeData.isInterface`: whether it is an interface type. */
    val isInterface = if (minify) "Y" else "isInterface"

    /** `TypeData.isArrayClass`: whether it is an array type. */
    val isArrayClass = if (minify) "Z" else "isArrayClass"

    // --- TypeData constructors ---

    val initPrim = if (minify) "p" else "initPrim"

    val initClass = if (minify) "i" else "initClass"

    val initSpecializedArray = if (minify) "y" else "initSpecializedArray"

    val initArray = if (minify) "a" else "initArray"

    // --- TypeData methods ---

    /** `TypeData.getArrayOf()`: the `Type` instance for that type's array type. */
    val getArrayOf = if (minify) "r" else "getArrayOf"

    /** `TypeData.getClassOf()`: the `jl.Class` instance for that type. */
    val getClassOf = if (minify) "l" else "getClassOf"

    /** `TypeData.getSuperclass()`: implementation of `Class_superClass`. */
    val getSuperclass = if (minify) "S" else "getSuperclass"

    /** `TypeData.getComponentType()`: implementation of `Class_componentType`. */
    val getComponentType = if (minify) "Q" else "getComponentType"

    /** `TypeData.isInstance()`: implementation of `Class_isInstance`. */
    val isInstance = if (minify) "I" else "isInstance"

    /** `TypeData.isAssignableFrom()`: implementation of `Class_isAssignableFrom`. */
    val isAssignableFrom = if (minify) "R" else "isAssignableFrom"

    /** `TypeData.cast()`: implementation of `Class_cast`. */
    val cast = if (minify) "T" else "cast"

    /** `TypeData.newArray()`: implementation of `Class_newArray`. */
    val newArray = if (minify) "U" else "newArray"
  }

  /* This is a `val` because it is used at the top of every file, outside of
   * any cache. Fortunately it does not depend on any dynamic content.
   */
  val declarePrototypeVar: List[Tree] = {
    implicit val pos = Position.NoPosition
    if (minify) VarDef(fileLevelVarIdent(VarField.p), None) :: Nil
    else Nil
  }

  def prototypeFor(classRef: Tree)(implicit pos: Position): Tree = {
    import TreeDSL._
    if (minify) fileLevelVar(VarField.p)
    else classRef.prototype
  }

  def genAssignPrototype(classRef: Tree, value: Tree, localDecl: Boolean = false)(implicit pos: Position): Tree = {
    import TreeDSL._
    val assign = classRef.prototype := value
    if (!minify)
      assign
    else if (localDecl)
      VarDef(fileLevelVarIdent(VarField.p), Some(assign))
    else
      fileLevelVar(VarField.p) := assign
  }

  /** Under `minify`, set `$p` to `classRef.prototype`. */
  def setPrototypeVar(classRef: Tree)(implicit pos: Position): List[Tree] = {
    import TreeDSL._
    if (minify) (fileLevelVar(VarField.p) := classRef.prototype) :: Nil
    else Nil
  }

  def genZeroOf(tpe: Type)(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      pos: Position): Tree = {
    tpe match {
      case tpe: PrimType => genZeroOfPrim(tpe)
      case _             => Null()
    }
  }

  def genZeroOf(typeRef: TypeRef)(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      pos: Position): Tree = {
    typeRef match {
      case PrimRef(tpe) => genZeroOfPrim(tpe)
      case _            => Null()
    }
  }

  private def genZeroOfPrim(tpe: PrimType)(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      pos: Position): Tree = {
    tpe match {
      case BooleanType => BooleanLiteral(false)
      case CharType    => IntLiteral(0)
      case ByteType    => IntLiteral(0)
      case ShortType   => IntLiteral(0)
      case IntType     => IntLiteral(0)
      case LongType    => genLongZero()
      case FloatType   => DoubleLiteral(0.0)
      case DoubleType  => DoubleLiteral(0.0)
      case StringType  => StringLiteral("")
      case UndefType   => Undefined()
      case NullType    => Null()

      case NoType | NothingType =>
        throw new IllegalArgumentException(s"cannot generate a zero for $tpe")
    }
  }

  def genLongZero()(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      pos: Position): Tree = {
    if (useBigIntForLongs)
      BigIntLiteral(0L)
    else
      globalVar(VarField.L0, CoreVar)
  }

  def genBoxedZeroOf(tpe: Type)(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      pos: Position): Tree = {
    if (tpe == CharType) genBoxedCharZero()
    else genZeroOf(tpe)
  }

  def genBoxedCharZero()(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      pos: Position): Tree = {
    globalVar(VarField.bC0, CoreVar)
  }

  def genLongModuleApply(methodName: MethodName, args: Tree*)(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      pos: Position): Tree = {
    import TreeDSL._
    genApply(
        genLoadModule(LongImpl.RuntimeLongModuleClass), methodName,
        args.toList)
  }

  def usesUnderlyingTypedArray(elemTypeRef: NonArrayTypeRef): Boolean = {
    /* We are only interested in whether `getArrayUnderlyingTypedArrayClassRef`
     * returns a `Some` or not. We do not keep the result, so the `Position`
     * and the `GlobalRefTracking` are irrelevant.
     */
    implicit val dontCareGlobalRefTracking = GlobalRefTracking.Dangerous
    implicit val dontCarePosition = Position.NoPosition
    getArrayUnderlyingTypedArrayClassRef(elemTypeRef).nonEmpty
  }

  def getArrayUnderlyingTypedArrayClassRef(elemTypeRef: NonArrayTypeRef)(
      implicit tracking: GlobalRefTracking, pos: Position): Option[WithGlobals[VarRef]] = {
    elemTypeRef match {
      case _ if esFeatures.esVersion < ESVersion.ES2015 => None
      case primRef: PrimRef                             => typedArrayRef(primRef)
      case _                                            => None
    }
  }

  def typedArrayRef(primRef: PrimRef)(
      implicit tracking: GlobalRefTracking, pos: Position): Option[WithGlobals[VarRef]] = {
    def some(name: String) = Some(globalRef(name))

    primRef match {
      case CharRef   => some("Uint16Array")
      case ByteRef   => some("Int8Array")
      case ShortRef  => some("Int16Array")
      case IntRef    => some("Int32Array")
      case FloatRef  => some("Float32Array")
      case DoubleRef => some("Float64Array")

      case LongRef if useBigIntForLongs => some("BigInt64Array")

      case _ => None
    }
  }

  def genSelect(receiver: Tree, field: irt.FieldIdent)(
      implicit pos: Position): Tree = {
    DotSelect(receiver, genFieldIdent(field.name)(field.pos))
  }

  def genSelectForDef(receiver: Tree, field: irt.FieldIdent,
      originalName: OriginalName)(
      implicit pos: Position): Tree = {
    DotSelect(receiver, genFieldIdentForDef(field.name, originalName)(field.pos))
  }

  private def genFieldIdent(fieldName: FieldName)(
      implicit pos: Position): MaybeDelayedIdent = {
    nameCompressor match {
      case None =>
        Ident(genName(fieldName))
      case Some(compressor) =>
        DelayedIdent(compressor.genResolverFor(fieldName))
    }
  }

  private def genFieldIdentForDef(fieldName: FieldName,
      originalName: OriginalName)(
      implicit pos: Position): MaybeDelayedIdent = {
    nameCompressor match {
      case None =>
        val jsName = genName(fieldName)
        val jsOrigName = genOriginalName(fieldName, originalName, jsName)
        Ident(jsName, jsOrigName)
      case Some(compressor) =>
        DelayedIdent(compressor.genResolverFor(fieldName), originalName.orElse(fieldName))
    }
  }

  def genApply(receiver: Tree, methodName: MethodName, args: List[Tree])(
      implicit pos: Position): Tree = {
    Apply(DotSelect(receiver, genMethodIdent(methodName)), args)
  }

  def genApply(receiver: Tree, methodName: MethodName, args: Tree*)(
      implicit pos: Position): Tree = {
    genApply(receiver, methodName, args.toList)
  }

  def genMethodIdent(methodIdent: irt.MethodIdent): MaybeDelayedIdent =
    genMethodIdent(methodIdent.name)(methodIdent.pos)

  def genMethodIdentForDef(methodIdent: irt.MethodIdent,
      originalName: OriginalName): MaybeDelayedIdent = {
    genMethodIdentForDef(methodIdent.name, originalName)(methodIdent.pos)
  }

  def genMethodIdent(methodName: MethodName)(implicit pos: Position): MaybeDelayedIdent = {
    nameCompressor match {
      case None             => Ident(genName(methodName))
      case Some(compressor) => DelayedIdent(compressor.genResolverFor(methodName))
    }
  }

  def genMethodIdentForDef(methodName: MethodName, originalName: OriginalName)(
      implicit pos: Position): MaybeDelayedIdent = {
    nameCompressor match {
      case None =>
        val jsName = genName(methodName)
        val jsOrigName = genOriginalName(methodName, originalName, jsName)
        Ident(jsName, jsOrigName)
      case Some(compressor) =>
        DelayedIdent(compressor.genResolverFor(methodName), originalName.orElse(methodName))
    }
  }

  def genSyntheticPropApply(receiver: Tree, prop: SyntheticProperty, args: Tree*)(
      implicit pos: Position): Tree = {
    genSyntheticPropApply(receiver, prop, args.toList)
  }

  def genSyntheticPropApply(receiver: Tree, prop: SyntheticProperty, args: List[Tree])(
      implicit pos: Position): Tree = {
    Apply(genSyntheticPropSelect(receiver, prop), args)
  }

  def genSyntheticPropSelect(qualifier: Tree, prop: SyntheticProperty)(
      implicit pos: Position): Tree = {
    DotSelect(qualifier, genSyntheticProperty(prop))
  }

  def genSyntheticProperty(prop: SyntheticProperty)(implicit pos: Position): MaybeDelayedIdent = {
    nameCompressor match {
      case None             => Ident(prop.nonMinifiedName)
      case Some(compressor) => DelayedIdent(compressor.genResolverFor(prop))
    }
  }

  def genSyntheticPropertyForDef(prop: SyntheticProperty)(implicit pos: Position): MaybeDelayedIdent = {
    nameCompressor match {
      case None             => Ident(prop.nonMinifiedName)
      case Some(compressor) => DelayedIdent(compressor.genResolverFor(prop), prop.originalName)
    }
  }

  def genAncestorIdent(ancestor: ClassName)(implicit pos: Position): MaybeDelayedIdent = {
    nameCompressor match {
      case None             => Ident(genName(ancestor))
      case Some(compressor) => DelayedIdent(compressor.genResolverForAncestor(ancestor))
    }
  }

  def genJSPrivateSelect(receiver: Tree, field: irt.FieldIdent)(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      pos: Position): Tree = {
    val fieldName = {
      implicit val pos = field.pos
      globalVar(VarField.r, field.name)
    }

    BracketSelect(receiver, fieldName)
  }

  def genIsInstanceOf(expr: Tree, tpe: Type)(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      pos: Position): Tree = {
    import TreeDSL._

    tpe match {
      case ClassType(className, false) =>
        if (HijackedClasses.contains(className)) {
          genIsInstanceOfHijackedClass(expr, className)
        } else if (className == ObjectClass) {
          expr === Null()
        } else if (!globalKnowledge.isAncestorOfHijackedClass(className) &&
            !globalKnowledge.isInterface(className)) {
          genIsInstanceOfClass(expr, className)
        } else {
          Apply(globalVar(VarField.is, className), List(expr))
        }

      case ArrayType(arrayTypeRef, false) =>
        arrayTypeRef match {
          case ArrayTypeRef(_:PrimRef | ClassRef(ObjectClass), 1) =>
            expr instanceof genArrayConstrOf(arrayTypeRef)
          case ArrayTypeRef(base, depth) =>
            Apply(typeRefVar(VarField.isArrayOf, base), List(expr, IntLiteral(depth)))
        }

      case UndefType   => expr === Undefined()
      case BooleanType => typeof(expr) === "boolean"
      case CharType    => expr instanceof globalVar(VarField.Char, CoreVar)
      case ByteType    => genCallHelper(VarField.isByte, expr)
      case ShortType   => genCallHelper(VarField.isShort, expr)
      case IntType     => genCallHelper(VarField.isInt, expr)
      case LongType    => genIsLong(expr)
      case FloatType   => genIsFloat(expr)
      case DoubleType  => typeof(expr) === "number"
      case StringType  => typeof(expr) === "string"

      case AnyNotNullType => expr !== Null()

      case NoType | NullType | NothingType | AnyType |
          ClassType(_, true) | ArrayType(_, true) | _:RecordType =>
        throw new AssertionError(s"Unexpected type $tpe in genIsInstanceOf")
    }
  }

  def genIsInstanceOfClass(expr: Tree, className: ClassName)(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      pos: Position): Tree = {
    import TreeDSL._

    if (!globalKnowledge.hasInstances(className)) {
      /* We need to constant-fold the instance test, to avoid emitting
       * `x instanceof $c_TheClass`, because `$c_TheClass` won't be
       * declared at all. Otherwise, we'd get a `ReferenceError`.
       */
      BooleanLiteral(false)
    } else {
      expr instanceof globalVar(VarField.c, className)
    }
  }

  def genIsInstanceOfHijackedClass(expr: Tree, className: ClassName)(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      pos: Position): Tree = {
    import TreeDSL._

    className match {
      case BoxedUnitClass      => expr === Undefined()
      case BoxedBooleanClass   => typeof(expr) === "boolean"
      case BoxedCharacterClass => expr instanceof globalVar(VarField.Char, CoreVar)
      case BoxedByteClass      => genCallHelper(VarField.isByte, expr)
      case BoxedShortClass     => genCallHelper(VarField.isShort, expr)
      case BoxedIntegerClass   => genCallHelper(VarField.isInt, expr)
      case BoxedLongClass      => genIsLong(expr)
      case BoxedFloatClass     => genIsFloat(expr)
      case BoxedDoubleClass    => typeof(expr) === "number"
      case BoxedStringClass    => typeof(expr) === "string"
    }
  }

  private def genIsLong(expr: Tree)(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      pos: Position): Tree = {
    import TreeDSL._

    if (useBigIntForLongs) genCallHelper(VarField.isLong, expr)
    else expr instanceof globalVar(VarField.c, LongImpl.RuntimeLongClass)
  }

  private def genIsFloat(expr: Tree)(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      pos: Position): Tree = {
    import TreeDSL._

    if (semantics.strictFloats) genCallHelper(VarField.isFloat, expr)
    else typeof(expr) === "number"
  }

  def genAsInstanceOf(expr: Tree, tpe: Type)(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      tracking: GlobalRefTracking, pos: Position): WithGlobals[Tree] = {
    import TreeDSL._

    // Local short-hand of WithGlobals(...)
    def wg(tree: Tree): WithGlobals[Tree] = WithGlobals(tree)

    if (semantics.asInstanceOfs == CheckedBehavior.Unchecked) {
      tpe match {
        case ClassType(_, true) | ArrayType(_, true) | AnyType =>
          wg(expr)

        case UndefType                     => wg(Block(expr, Undefined()))
        case BooleanType                   => wg(!(!expr))
        case CharType                      => wg(genCallHelper(VarField.uC, expr))
        case ByteType | ShortType| IntType => wg(expr | 0)
        case LongType                      => wg(genCallHelper(VarField.uJ, expr))
        case DoubleType                    => wg(UnaryOp(irt.JSUnaryOp.+, expr))
        case StringType                    => wg(expr || StringLiteral(""))

        case FloatType =>
          if (semantics.strictFloats) genCallPolyfillableBuiltin(FroundBuiltin, expr)
          else wg(UnaryOp(irt.JSUnaryOp.+, expr))

        case NoType | NullType | NothingType | AnyNotNullType |
            ClassType(_, false) | ArrayType(_, false) | _:RecordType =>
          throw new AssertionError(s"Unexpected type $tpe in genAsInstanceOf")
      }
    } else {
      val resultTree = tpe match {
        case ClassType(ObjectClass, true) =>
          expr
        case ClassType(className, true) =>
          Apply(globalVar(VarField.as, className), List(expr))

        case ArrayType(ArrayTypeRef(base, depth), true) =>
          Apply(typeRefVar(VarField.asArrayOf, base), List(expr, IntLiteral(depth)))

        case UndefType   => genCallHelper(VarField.uV, expr)
        case BooleanType => genCallHelper(VarField.uZ, expr)
        case CharType    => genCallHelper(VarField.uC, expr)
        case ByteType    => genCallHelper(VarField.uB, expr)
        case ShortType   => genCallHelper(VarField.uS, expr)
        case IntType     => genCallHelper(VarField.uI, expr)
        case LongType    => genCallHelper(VarField.uJ, expr)
        case FloatType   => genCallHelper(VarField.uF, expr)
        case DoubleType  => genCallHelper(VarField.uD, expr)
        case StringType  => genCallHelper(VarField.uT, expr)
        case AnyType     => expr

        case NoType | NullType | NothingType | AnyNotNullType |
            ClassType(_, false) | ArrayType(_, false) | _:RecordType =>
          throw new AssertionError(s"Unexpected type $tpe in genAsInstanceOf")
      }

      wg(resultTree)
    }
  }

  /** Orders a subset of hijacked classes by priority for a series of type
   *  tests.
   *
   *  If `j.l.Double` is in the list, then run-time subclasses of `Double` are
   *  excluded (i.e., `Byte`, `Short`, `Integer` and `Float`).
   *
   *  If we do not use bigints to implement Longs, `j.l.Long` is excluded.
   *
   *  The result is ordered in an "efficient" way, putting `typeof`-based tests
   *  first when possible, and otherwise ordering by a gut-feeling of
   *  "likelihood".
   */
  def subsetOfHijackedClassesOrderedForTypeTests(
      hijackedClasses: Set[ClassName]): List[ClassName] = {
    val baseList = {
      if (hijackedClasses.contains(BoxedDoubleClass))
        nonSmallNumberHijackedClassesOrderedForTypeTests
      else
        allHijackedClassesOrderedForTypeTests
    }

    baseList.filter(hijackedClasses)
  }

  /** List of hijacked classes ordered by priority for a series of type tests,
   *  excluding run-time subclasses of Double.
   *
   *  Those with `typeof`-based tests come first because they are cheaper.
   */
  private val nonSmallNumberHijackedClassesOrderedForTypeTests = List(
    BoxedStringClass,
    BoxedDoubleClass,
    BoxedBooleanClass,
    BoxedUnitClass,
    BoxedLongClass,
    BoxedCharacterClass
  )

  /** List of all the hijacked classes ordered by priority for a series of type
   *  tests.
   */
  private val allHijackedClassesOrderedForTypeTests = List(
    BoxedByteClass,
    BoxedShortClass,
    BoxedIntegerClass,
    BoxedFloatClass
  ) ::: nonSmallNumberHijackedClassesOrderedForTypeTests

  def genCallHelper(helperName: VarField, args: Tree*)(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      pos: Position): Tree = {
    Apply(globalVar(helperName, CoreVar), args.toList)
  }

  def genCallPolyfillableBuiltin(builtin: PolyfillableBuiltin, args: Tree*)(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      tracking: GlobalRefTracking, pos: Position): WithGlobals[Tree] = {
    if (esFeatures.esVersion >= builtin.availableInESVersion) {
      builtin match {
        case builtin: GlobalVarBuiltin =>
          for (global <- globalRef(builtin.globalVar)) yield
            Apply(global, args.toList)
        case builtin: NamespacedBuiltin =>
          for (namespace <- globalRef(builtin.namespaceGlobalVar)) yield
            Apply(genIdentBracketSelect(namespace, builtin.builtinName), args.toList)
      }
    } else {
      WithGlobals(genCallHelper(builtin.polyfillField, args: _*))
    }
  }

  def genLoadModule(moduleClass: ClassName)(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      pos: Position): Tree = {
    import TreeDSL._
    Apply(globalVar(VarField.m, moduleClass), Nil)
  }

  def genScalaClassNew(className: ClassName, ctor: MethodName, args: Tree*)(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      pos: Position): Tree = {
    val encodedClassVar = globalVar(VarField.c, className)
    val argsList = args.toList
    if (globalKnowledge.hasInlineableInit(className)) {
      New(encodedClassVar, argsList)
    } else {
      Apply(globalVar(VarField.ct, (className, ctor)), New(encodedClassVar, Nil) :: argsList)
    }
  }

  def genJSClassConstructor(className: ClassName)(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      tracking: GlobalRefTracking, pos: Position): WithGlobals[Tree] = {

    genJSClassConstructor(className,
        globalKnowledge.getJSNativeLoadSpec(className))
  }

  def genJSClassConstructor(className: ClassName,
      spec: Option[irt.JSNativeLoadSpec])(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      tracking: GlobalRefTracking, pos: Position): WithGlobals[Tree] = {
    spec match {
      case None =>
        // This is a non-native JS class
        WithGlobals(genNonNativeJSClassConstructor(className))

      case Some(spec) =>
        genLoadJSFromSpec(spec)
    }
  }

  def genNonNativeJSClassConstructor(className: ClassName)(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      pos: Position): Tree = {
    Apply(globalVar(VarField.a, className), Nil)
  }

  def genLoadJSFromSpec(spec: irt.JSNativeLoadSpec)(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      tracking: GlobalRefTracking, pos: Position): WithGlobals[Tree] = {

    def pathSelection(from: Tree, path: List[String]): Tree = {
      path.foldLeft(from) {
        (prev, part) => genBracketSelect(prev, StringLiteral(part))
      }
    }

    spec match {
      case irt.JSNativeLoadSpec.Global(globalRefName, path) =>
        for (globalVarRef <- globalRef(globalRefName)) yield
          pathSelection(globalVarRef, path)

      case irt.JSNativeLoadSpec.Import(module, path) =>
        val moduleValue = VarRef(externalModuleFieldIdent(module))
        path match {
          case "default" :: rest if moduleKind == ModuleKind.CommonJSModule =>
            val defaultField = genCallHelper(VarField.moduleDefault, moduleValue)
            WithGlobals(pathSelection(defaultField, rest))
          case _ =>
            WithGlobals(pathSelection(moduleValue, path))
        }

      case irt.JSNativeLoadSpec.ImportWithGlobalFallback(importSpec, globalSpec) =>
        moduleKind match {
          case ModuleKind.NoModule =>
            genLoadJSFromSpec(globalSpec)
          case ModuleKind.ESModule | ModuleKind.CommonJSModule =>
            genLoadJSFromSpec(importSpec)
        }
    }
  }

  def genArrayValue(arrayTypeRef: ArrayTypeRef, elems: List[Tree])(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      tracking: GlobalRefTracking, pos: Position): WithGlobals[Tree] = {
    genNativeArrayWrapper(arrayTypeRef, ArrayConstr(elems))
  }

  def genNativeArrayWrapper(arrayTypeRef: ArrayTypeRef, nativeArray: Tree)(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      tracking: GlobalRefTracking, pos: Position): WithGlobals[Tree] = {
    val argWithGlobals = arrayTypeRef match {
      case ArrayTypeRef(elemTypeRef, 1) =>
        getArrayUnderlyingTypedArrayClassRef(elemTypeRef) match {
          case Some(typedArrayWithGlobals) =>
            for (typedArray <- typedArrayWithGlobals) yield
              New(typedArray, nativeArray :: Nil)
          case _ =>
            WithGlobals(nativeArray)
        }
      case _ =>
        WithGlobals(nativeArray)
    }

    for (arg <- argWithGlobals) yield
      New(genArrayConstrOf(arrayTypeRef), arg :: Nil)
  }

  def genArrayConstrOf(arrayTypeRef: ArrayTypeRef)(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      pos: Position): Tree = {
    import TreeDSL._

    arrayTypeRef match {
      case ArrayTypeRef(primRef: PrimRef, 1) =>
        globalVar(VarField.ac, primRef)
      case ArrayTypeRef(ClassRef(ObjectClass), 1) =>
        globalVar(VarField.ac, ObjectClass)
      case _ =>
        genClassDataOf(arrayTypeRef) DOT cpn.constr
    }
  }

  def genClassOf(typeRef: TypeRef)(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      pos: Position): Tree = {
    Apply(DotSelect(genClassDataOf(typeRef), Ident(cpn.getClassOf)), Nil)
  }

  def genClassOf(className: ClassName)(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      pos: Position): Tree = {
    genClassOf(ClassRef(className))
  }

  def genClassDataOf(typeRef: TypeRef)(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      pos: Position): Tree = {
    typeRef match {
      case typeRef: NonArrayTypeRef =>
        typeRefVar(VarField.d, typeRef)

      case ArrayTypeRef(base, dims) =>
        val baseData = genClassDataOf(base)
        (1 to dims).foldLeft[Tree](baseData) { (prev, _) =>
          Apply(DotSelect(prev, Ident(cpn.getArrayOf)), Nil)
        }
    }
  }

  def genClassDataOf(className: ClassName)(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      pos: Position): Tree = {
    genClassDataOf(ClassRef(className))
  }

  def genCheckNotNull(obj: Tree)(
      implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
      pos: Position): Tree = {
    if (semantics.nullPointers == CheckedBehavior.Unchecked)
      obj
    else
      genCallHelper(VarField.n, obj)
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy