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

dotty.tools.dotc.transform.sjs.AddLocalJSFakeNews.scala Maven / Gradle / Ivy

There is a newer version: 3.6.4-RC1-bin-20241220-0bfa1af-NIGHTLY
Show newest version
package dotty.tools
package dotc
package transform
package sjs

import MegaPhase.*
import core.Constants
import core.Contexts.*
import core.Decorators.*
import core.StdNames.nme
import core.Symbols.*

import dotty.tools.backend.sjs.JSDefinitions.jsdefn

/** Adds fake calls to the constructors of local JS classes in calls to
 *  `createLocalJSClass`.
 *
 *  Given a call of the form
 *  {{{
 *  scala.scalajs.runtime.createLocalJSClass(classOf[C], jsClassValue, ???)
 *  }}}
 *  this phase fills in the `???` with an array of calls to the constructors
 *  of `C`, like
 *  {{{
 *  [ new C(), new C(???, ???) : Object ]
 *  }}}
 *
 *  If the class `C` has an outer pointer, as determined by the `ExplicitOuter`
 *  phase, the calls to the constructor insert a reference to the outer
 *  instance:
 *  {{{
 *  [ new C(Enclosing.this), new C(Enclosing.this, ???, ???) : Object ]
 *  }}}
 *
 *  The `LambdaLift` phase will further expand those constructor calls with
 *  values for captures. The back-end will extract the values of the outer
 *  pointer and/or the captures to introduce them as JS class captures.
 *
 *  Since we need to insert fake `new C()` calls, this scheme does not work for
 *  abstract local classes. We therefore reject them as implementation
 *  restriction in `PrepJSInterop`.
 *
 *  This phase complements `ExplicitJSClasses`. The latter cannot create the
 *  fake new invocations because that would require inventing sound type
 *  arguments for the class' type parameters in order not to break Ycheck.
 */
class AddLocalJSFakeNews extends MiniPhase { thisPhase =>
  import ExplicitOuter.outer
  import ast.tpd.*

  override def phaseName: String = AddLocalJSFakeNews.name

  override def description: String = AddLocalJSFakeNews.description

  override def isEnabled(using Context): Boolean =
    ctx.settings.scalajs.value

  override def runsAfter: Set[String] = Set(Erasure.name)

  override def transformApply(tree: Apply)(using Context): Tree = {
    if (tree.symbol == jsdefn.Runtime_createLocalJSClass) {
      val classValueArg :: superClassValueArg :: _ :: Nil = tree.args: @unchecked
      val cls = classValueArg match {
        case Literal(constant) if constant.tag == Constants.ClazzTag =>
          constant.typeValue.typeSymbol.asClass
        case _ =>
          // this shouldn't happen
          report.error(em"unexpected $classValueArg for the first argument to `createLocalJSClass`", classValueArg)
          jsdefn.JSObjectClass
      }

      val fakeNews = {
        val ctors = cls.info.decls.lookupAll(nme.CONSTRUCTOR).toList.reverse
        val elems = ctors.map(ctor => fakeNew(cls, ctor.asTerm))
        JavaSeqLiteral(elems, TypeTree(defn.ObjectType))
      }

      cpy.Apply(tree)(tree.fun, classValueArg :: superClassValueArg :: fakeNews :: Nil)
    } else {
      tree
    }
  }

  /** Creates a fake invocation of the given class with the given constructor. */
  private def fakeNew(cls: ClassSymbol, ctor: TermSymbol)(using Context): Tree = {
    val tycon = cls.typeRef
    val outerArgs = outer.argsForNew(cls, tycon)
    val nonOuterArgCount = ctor.info.firstParamTypes.size - outerArgs.size
    val nonOuterArgs = List.fill(nonOuterArgCount)(ref(defn.Predef_undefined).appliedToNone)

    New(tycon, ctor, outerArgs ::: nonOuterArgs)
  }
}

object AddLocalJSFakeNews {
  val name: String = "addLocalJSFakeNews"
  val description: String = "adds fake new invocations to local JS classes in calls to `createLocalJSClass`"
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy