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

org.scalajs.linker.backend.emitter.JSGen.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.Position

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

/** Collection of tree generators that are used across the board.
 *  This class is fully stateless.
 *
 *  Also carries around config (semantics and esFeatures).
 */
private[emitter] final class JSGen(val config: Emitter.Config) {

  import config._

  /** Should we use ECMAScript classes for JavaScript classes and Throwable
   *  classes?
   *
   *  This is true iff `useECMAScript2015Semantics` is true, independently of
   *  [[org.scalajs.linker.interface.ESFeatures.avoidClasses ESFeatures.avoidClasses]].
   *
   *  We must emit classes for JavaScript classes for semantics reasons:
   *  inheritance of static properties and ability to extend native JavaScript
   *  ES classes.
   *
   *  We must emit classes Throwable classes so that they are recognized as
   *  proper JavaScript error classes, which gives them better support in
   *  debuggers.
   */
  val useClassesForJSClassesAndThrowables = esFeatures.useECMAScript2015Semantics

  /** Should we use ECMAScript classes for non-Throwable Scala classes?
   *
   *  If [[org.scalajs.linker.interface.ESFeatures.avoidClasses ESFeatures.avoidClasses]]
   *  is true, we do not use classes for non-Throwable classes. We can do that
   *  because whether regular classes are compiled as classes or functions and
   *  prototypes has no impact on observable semantics.
   *
   *  `useClassesForRegularClasses` is always false when
   *  `useClassesForJSClassesAndThrowables` is false.
   */
  val useClassesForRegularClasses =
    useClassesForJSClassesAndThrowables && !esFeatures.avoidClasses

  /** Should we emit `let`s and `const`s for all internal variables?
   *
   *  See [[org.scalajs.linker.interface.ESFeatures.avoidLetsAndConsts ESFeatures.avoidLetsAndConsts]]
   *  for a rationale.
   *
   *  Note: top-level exports in Script (`NoModule`) mode are always
   *  emitted as `let`s under ECMAScript 2015 semantics, irrespective of this
   *  value.
   */
  val useLets = esFeatures.esVersion >= ESVersion.ES2015 && !esFeatures.avoidLetsAndConsts

  def genConst(name: MaybeDelayedIdent, rhs: Tree)(implicit pos: Position): LocalDef =
    genLet(name, mutable = false, rhs)

  def genLet(name: MaybeDelayedIdent, mutable: Boolean, rhs: Tree)(
      implicit pos: Position): LocalDef = {
    if (useLets)
      Let(name, mutable, Some(rhs))
    else
      VarDef(name, Some(rhs))
  }

  def genEmptyMutableLet(name: Ident)(implicit pos: Position): LocalDef =
    genEmptyLet(name, mutable = true)

  def genEmptyImmutableLet(name: Ident)(implicit pos: Position): LocalDef =
    genEmptyLet(name, mutable = false)

  private def genEmptyLet(name: Ident, mutable: Boolean)(
      implicit pos: Position): LocalDef = {
    if (useLets)
      Let(name, mutable, rhs = None)
    else
      VarDef(name, rhs = None)
  }

  def genBracketSelect(qual: Tree, item: Tree)(implicit pos: Position): Tree = {
    if (optimizeBracketSelects)
      BracketSelect.makeOptimized(qual, item)
    else
      BracketSelect(qual, item)
  }

  def genIdentBracketSelect(qual: Tree, item: String)(
      implicit pos: Position): Tree = {
    require(item != "eval")
    if (optimizeBracketSelects)
      DotSelect(qual, Ident(item))
    else
      BracketSelect(qual, StringLiteral(item))
  }

  /** Generates an arrow function if supported by the ES version.
   *
   *  This is independent of the ECMAScript 2015 *semantics*. This method must
   *  not be used for closures that are *specified* to be arrow functions in
   *  ES 2015 but `function`s in ES 5.1 semantics. In other words, it must not
   *  be used to compile `ir.Trees.Closure`s.
   */
  def genArrowFunction(args: List[ParamDef], restParam: Option[ParamDef], body: Tree)(
      implicit pos: Position): Function = {
    Function(esFeatures.esVersion >= ESVersion.ES2015, args, restParam, body)
  }

  def genDefineProperty(obj: Tree, prop: Tree, descriptor: List[(String, Tree)])(
      implicit tracking: GlobalRefTracking, pos: Position): WithGlobals[Tree] = {
    val descriptorTree =
        ObjectConstr(descriptor.map(x => StringLiteral(x._1) -> x._2))

    globalRef("Object").map { objRef =>
      Apply(genIdentBracketSelect(objRef, "defineProperty"),
          List(obj, prop, descriptorTree))
    }
  }

  def globalRef(name: String)(
      implicit tracking: GlobalRefTracking, pos: Position): WithGlobals[VarRef] = {
    val trackedSet: Set[String] =
      if (tracking.shouldTrack(name)) Set(name)
      else Set.empty
    WithGlobals(VarRef(Ident(name)), trackedSet)
  }

  def genPropSelect(qual: Tree, item: PropertyName)(
      implicit pos: Position): Tree = {
    item match {
      case item: MaybeDelayedIdent => DotSelect(qual, item)
      case item: StringLiteral     => genBracketSelect(qual, item)
      case ComputedName(tree)      => genBracketSelect(qual, tree)
    }
  }

  def genIIFE(captures: List[(ParamDef, Tree)], body: Tree)(
      implicit pos: Position): Tree = {
    val (params, args) = captures.unzip
    Apply(genArrowFunction(params, None, body), args)
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy