Skip to content

Instantly share code, notes, and snippets.

@gzm0
Last active January 21, 2020 19:01
Show Gist options
  • Save gzm0/400eca19489165821a6f5dd4f8ea4d74 to your computer and use it in GitHub Desktop.
Save gzm0/400eca19489165821a6f5dd4f8ea4d74 to your computer and use it in GitHub Desktop.
diff --cc compiler/src/main/scala/org/scalajs/nscplugin/GenJSExports.scala
index bf38d993e,fab6f8801..000000000
--- a/compiler/src/main/scala/org/scalajs/nscplugin/GenJSExports.scala
+++ b/compiler/src/main/scala/org/scalajs/nscplugin/GenJSExports.scala
@@@ -372,10 -399,8 +372,10 @@@ trait GenJSExports[G <: Global with Sin
}
}
- val getterBody = getter.headOption.map(
- genApplyForSym(new FormalArgsRegistry(0, false), _, static))
+ val getterBody = getter.headOption.map { getterSym =>
- genApplyForSym(minArgc = 0, hasRestParam = false,
++ genApplyForSym(new FormalArgsRegistry(0, false),
+ ExportedSymbol(getterSym), static)
+ }
val setterArgAndBody = {
if (setters.isEmpty) {
@@@ -511,8 -536,11 +512,9 @@@
else {
assert(needsRestParam,
"Trying to read rest param length but needsRestParam is false")
++ val restArgRef = formalArgsRegistry.genRestArgRef()
js.Match(
- js.AsInstanceOf(js.JSSelect(genRestArgRef(), js.StringLiteral("length")), jstpe.IntType),
- js.Unbox(js.JSBracketSelect(
- formalArgsRegistry.genRestArgRef(),
- js.StringLiteral("length")),
- 'I'),
++ js.AsInstanceOf(js.JSSelect(restArgRef, js.StringLiteral("length")), jstpe.IntType),
cases.toList, defaultCase)(jstpe.AnyType)
}
}
@@@ -567,14 -600,16 +570,14 @@@
val (typeTest, subAlts) = elem
implicit val pos = subAlts.head.pos
- val paramRef = genFormalArgRef(paramIndex+1, minArgc)
- val genSubAlts = genExportSameArgc(jsName, minArgc, hasRestParam,
- subAlts, paramIndex+1, static, maxArgc)
+ val paramRef = formalArgsRegistry.genArgRef(paramIndex)
+ val genSubAlts = genExportSameArgc(jsName, formalArgsRegistry,
+ subAlts, paramIndex + 1, static, maxArgc)
- def hasDefaultParam = subAlts.exists {
- case ExportedSymbol(p) =>
- val params = p.tpe.params
- params.size > paramIndex &&
- params(paramIndex).hasFlag(Flags.DEFAULTPARAM)
- case _: ExportedBody => false
+ def hasDefaultParam = subAlts.exists { exported =>
+ val params = exported.params
+ params.size > paramIndex &&
+ params(paramIndex).hasDefault
}
val optCond = typeTest match {
@@@ -636,43 -722,24 +639,38 @@@
* and potentially the argument array. Also inserts default parameters if
* required.
*/
- private def genApplyForSym(minArgc: Int, hasRestParam: Boolean,
+ private def genApplyForSym(formalArgsRegistry: FormalArgsRegistry,
- sym: Symbol, static: Boolean): js.Tree = {
- if (isScalaJSDefinedJSClass(currentClassSym) &&
- sym.owner != currentClassSym.get) {
- assert(!static, s"nonsensical JS super call in static export of $sym")
- genApplyForSymJSSuperCall(formalArgsRegistry, sym)
+ exported: Exported, static: Boolean): js.Tree = {
+ if (isNonNativeJSClass(currentClassSym) &&
+ exported.sym.owner != currentClassSym.get) {
+ assert(!static,
+ s"nonsensical JS super call in static export of ${exported.sym}")
- genApplyForSymJSSuperCall(minArgc, hasRestParam, exported)
++ genApplyForSymJSSuperCall(formalArgsRegistry, exported)
} else {
- genApplyForSymNonJSSuperCall(minArgc, exported, static)
- genApplyForSymNonJSSuperCall(formalArgsRegistry, sym, static)
++ genApplyForSymNonJSSuperCall(formalArgsRegistry, exported, static)
}
}
- private def genApplyForSymJSSuperCall(minArgc: Int, hasRestParam: Boolean,
- exported: Exported): js.Tree = {
+ private def genApplyForSymJSSuperCall(
- formalArgsRegistry: FormalArgsRegistry, sym: Symbol): js.Tree = {
- implicit val pos = sym.pos
++ formalArgsRegistry: FormalArgsRegistry, exported: Exported): js.Tree = {
+ implicit val pos = exported.pos
+
+ val sym = exported.sym
+ assert(!sym.isClassConstructor,
+ "Trying to genApplyForSymJSSuperCall for the constructor " +
+ sym.fullName)
- val restArg =
- if (hasRestParam) js.JSSpread(genRestArgRef()) :: Nil
- else Nil
-
- val allArgs =
- (1 to minArgc).map(genFormalArgRef(_, minArgc)) ++: restArg
+ val allArgs = formalArgsRegistry.genAllArgsRefsForForwarder()
- val cls = jstpe.ClassType(encodeClassFullName(currentClassSym))
+ val superClass = {
+ val superClassSym = currentClassSym.superClass
+ if (isNestedJSClass(superClassSym)) {
+ js.VarRef(js.LocalIdent(JSSuperClassParamName))(jstpe.AnyType)
+ } else {
+ js.LoadJSConstructor(encodeClassName(superClassSym))
+ }
+ }
+
val receiver = js.This()(jstpe.AnyType)
val nameString = genExpr(jsNameOf(sym))
@@@ -690,42 -757,35 +688,43 @@@
}
}
- private def genApplyForSymNonJSSuperCall(minArgc: Int,
- exported: Exported, static: Boolean): js.Tree = {
+ private def genApplyForSymNonJSSuperCall(
- formalArgsRegistry: FormalArgsRegistry, sym: Symbol,
++ formalArgsRegistry: FormalArgsRegistry, exported: Exported,
+ static: Boolean): js.Tree = {
- implicit val pos = sym.pos
+ implicit val pos = exported.pos
// the (single) type of the repeated parameter if any
- val repeatedTpe = enteringPhase(currentRun.uncurryPhase) {
- for {
- param <- sym.paramss.flatten.lastOption
- if isRepeated(param)
- } yield repeatedToSingle(param.tpe)
- }
+ val repeatedTpe =
+ exported.params.lastOption.withFilter(_.isRepeated).map(_.tpe)
- val normalArgc = sym.tpe.params.size -
+ val normalArgc = exported.params.size -
(if (repeatedTpe.isDefined) 1 else 0)
// optional repeated parameter list
val jsVarArgPrep = repeatedTpe map { tpe =>
- val rhs = genJSArrayToVarArgs(genVarargRef(normalArgc, minArgc))
+ val rhs = genJSArrayToVarArgs(formalArgsRegistry.genVarargRef(normalArgc))
- js.VarDef(freshLocalIdent("prep"), rhs.tpe, mutable = false, rhs)
+ val ident = freshLocalIdent("prep" + normalArgc)
+ js.VarDef(ident, NoOriginalName, rhs.tpe, mutable = false, rhs)
}
// normal arguments
- val jsArgRefs = (1 to normalArgc).toList.map(
- i => genFormalArgRef(i, minArgc))
+ val jsArgRefs =
+ (0 until normalArgc).toList.map(formalArgsRegistry.genArgRef(_))
// Generate JS code to prepare arguments (default getters and unboxes)
- val jsArgPrep = genPrepareArgs(jsArgRefs, sym) ++ jsVarArgPrep
- val jsResult = genResult(sym, jsArgPrep.map(_.ref), static)
+ val jsArgPrep = genPrepareArgs(jsArgRefs, exported) ++ jsVarArgPrep
+ val jsArgPrepRefs = jsArgPrep.map(_.ref)
+
+ // Combine prep'ed formal arguments with captures
+ def varRefForCaptureParam(param: ParamSpec): js.Tree =
+ js.VarRef(encodeLocalSym(param.sym))(toIRType(param.sym.tpe))
+ val allJSArgs = {
+ exported.captureParamsFront.map(varRefForCaptureParam) :::
+ jsArgPrepRefs :::
+ exported.captureParamsBack.map(varRefForCaptureParam)
+ }
+
+ val jsResult = genResult(exported, allJSArgs, static)
js.Block(jsArgPrep :+ jsResult)
}
@@@ -863,118 -906,34 +862,118 @@@
}
private sealed abstract class Exported {
+ def sym: Symbol
def pos: Position
- def params: List[Type]
+ def isLiftedJSConstructor: Boolean
+ def params: immutable.IndexedSeq[ParamSpec]
+ def captureParamsFront: List[ParamSpec]
+ def captureParamsBack: List[ParamSpec]
+ def exportArgTypeAt(paramIndex: Int): Type
- def genBody(minArgc: Int, hasRestParam: Boolean, static: Boolean): js.Tree
+ def genBody(formalArgsRegistry: FormalArgsRegistry, static: Boolean): js.Tree
def typeInfo: String
def hasRepeatedParam: Boolean
}
private case class ExportedSymbol(sym: Symbol) extends Exported {
- def pos: Position = sym.pos
- def params: List[Type] = sym.tpe.params.map(_.tpe)
+ private val isAnonJSClassConstructor =
+ sym.isClassConstructor && sym.owner.isAnonymousClass && isJSType(sym.owner)
+
+ val isLiftedJSConstructor =
+ sym.isClassConstructor && isNestedJSClass(sym.owner)
+
+ val (params, captureParamsFront, captureParamsBack) = {
+ val allParamsUncurry =
+ enteringPhase(currentRun.uncurryPhase)(sym.paramss.flatten.map(ParamSpec))
+ val allParamsPosterasure =
+ enteringPhase(currentRun.posterasurePhase)(sym.paramss.flatten.map(ParamSpec))
+ val allParamsNow = sym.paramss.flatten.map(ParamSpec)
+
+ def mergeUncurryPosterasure(paramsUncurry: List[ParamSpec],
+ paramsPosterasure: List[ParamSpec]): List[ParamSpec] = {
+ for {
+ (paramUncurry, paramPosterasure) <- paramsUncurry.zip(paramsPosterasure)
+ } yield {
+ if (paramUncurry.isRepeated) paramUncurry
+ else paramPosterasure
+ }
+ }
- def genBody(formalArgsRegistry: FormalArgsRegistry, static: Boolean): js.Tree =
- genApplyForSym(formalArgsRegistry, sym, static)
+ if (!isLiftedJSConstructor && !isAnonJSClassConstructor) {
+ /* Easy case: all params are formal params, and we only need to
+ * travel back before uncurry to handle repeated params, or before
+ * posterasure for other params.
+ */
+ assert(allParamsUncurry.size == allParamsPosterasure.size,
+ s"Found ${allParamsUncurry.size} params entering uncurry but " +
+ s"${allParamsPosterasure.size} params entering posterasure for " +
+ s"non-lifted symbol ${sym.fullName}")
+ val formalParams =
+ mergeUncurryPosterasure(allParamsUncurry, allParamsPosterasure)
+ (formalParams.toIndexedSeq, Nil, Nil)
+ } else {
+ /* The `arg$outer` param is added by explicitouter (between uncurry
+ * and posterasure) while the other capture params are added by
+ * lambdalift (between posterasure and now).
+ *
+ * Note that lambdalift creates new symbols even for parameters that
+ * are not the result of lambda lifting, but it preserves their
+ * `name`s.
+ */
+
+ val hasOuterParam = {
+ allParamsPosterasure.size == allParamsUncurry.size + 1 &&
+ allParamsPosterasure.head.sym.name == jsnme.arg_outer
+ }
+ assert(
+ hasOuterParam ||
+ allParamsPosterasure.size == allParamsUncurry.size,
+ s"Found ${allParamsUncurry.size} params entering uncurry but " +
+ s"${allParamsPosterasure.size} params entering posterasure for " +
+ s"lifted constructor symbol ${sym.fullName}")
+
+ val nonOuterParamsPosterasure =
+ if (hasOuterParam) allParamsPosterasure.tail
+ else allParamsPosterasure
+ val formalParams =
+ mergeUncurryPosterasure(allParamsUncurry, nonOuterParamsPosterasure)
+
+ val startOfRealParams =
+ allParamsNow.map(_.sym.name).indexOfSlice(allParamsUncurry.map(_.sym.name))
+ val (captureParamsFront, restOfParamsNow) =
+ allParamsNow.splitAt(startOfRealParams)
+ val captureParamsBack = restOfParamsNow.drop(formalParams.size)
+
+ if (isAnonJSClassConstructor) {
+ /* For an anonymous JS class constructor, we put the capture
+ * parameters back as formal parameters.
+ */
+ val allFormalParams =
+ captureParamsFront ::: formalParams ::: captureParamsBack
+ (allFormalParams.toIndexedSeq, Nil, Nil)
+ } else {
+ (formalParams.toIndexedSeq, captureParamsFront, captureParamsBack)
+ }
+ }
+ }
- def typeInfo: String = sym.tpe.toString
- def hasRepeatedParam: Boolean = GenJSExports.this.hasRepeatedParam(sym)
- }
+ val hasRepeatedParam = params.nonEmpty && params.last.isRepeated
+
+ def pos: Position = sym.pos
- private class ExportedBody(val params: List[Type],
- genBodyFun: FormalArgsRegistry => js.Tree, name: String,
- val pos: Position)
- extends Exported {
+ def exportArgTypeAt(paramIndex: Int): Type = {
+ if (paramIndex < params.length) {
+ params(paramIndex).tpe
+ } else {
+ assert(hasRepeatedParam,
+ s"$sym does not have varargs nor enough params for $paramIndex")
+ params.last.tpe
+ }
+ }
- def genBody(minArgc: Int, hasRestParam: Boolean, static: Boolean): js.Tree =
- genApplyForSym(minArgc, hasRestParam, this, static)
+ def genBody(formalArgsRegistry: FormalArgsRegistry, static: Boolean): js.Tree =
- genBodyFun(formalArgsRegistry)
++ genApplyForSym(formalArgsRegistry, this, static)
- def typeInfo: String = params.mkString("(", ", ", ")")
- val hasRepeatedParam: Boolean = false
+ def typeInfo: String = sym.tpe.toString
}
}
@@@ -1098,50 -1061,71 +1097,76 @@@
js.Throw(js.StringLiteral(msg))
}
- private def genFormalArgs(minArgc: Int, needsRestParam: Boolean)(
- implicit pos: Position): List[js.ParamDef] = {
- val fixedParams = (1 to minArgc map genFormalArg).toList
- if (needsRestParam) fixedParams :+ genRestFormalArg()
- else fixedParams
- }
+ private class FormalArgsRegistry(minArgc: Int, needsRestParam: Boolean) {
- private val fixedParamNames: scala.collection.immutable.IndexedSeq[String] =
++ private val fixedParamNames: scala.collection.immutable.IndexedSeq[LocalName] =
+ (0 until minArgc).toIndexedSeq.map(_ => freshLocalIdent("arg")(NoPosition).name)
- private def genFormalArg(index: Int)(implicit pos: Position): js.ParamDef = {
- js.ParamDef(js.LocalIdent(LocalName("arg$" + index)), NoOriginalName,
- jstpe.AnyType, mutable = false, rest = false)
- }
- private val restParamName: String =
- if (needsRestParam) freshLocalIdent("rest")(NoPosition).name
- else ""
++ private lazy val restParamName: LocalName = {
++ require(needsRestParam)
++ freshLocalIdent("rest")(NoPosition).name
++ }
- private def genRestFormalArg()(implicit pos: Position): js.ParamDef = {
- js.ParamDef(js.LocalIdent(LocalName("arg$rest")), NoOriginalName,
- jstpe.AnyType, mutable = false, rest = true)
- }
+ def genFormalArgs()(implicit pos: Position): List[js.ParamDef] = {
+ val fixedParamDefs = fixedParamNames.toList.map { paramName =>
- js.ParamDef(js.Ident(paramName), jstpe.AnyType,
++ js.ParamDef(js.LocalIdent(paramName), NoOriginalName, jstpe.AnyType,
+ mutable = false, rest = false)
+ }
- private def genFormalArgRef(index: Int, minArgc: Int)(
- implicit pos: Position): js.Tree = {
- if (index <= minArgc)
- js.VarRef(js.LocalIdent(LocalName("arg$" + index)))(jstpe.AnyType)
- else
- js.JSSelect(genRestArgRef(), js.IntLiteral(index - 1 - minArgc))
- }
+ if (needsRestParam) {
- val restParamDef = js.ParamDef(js.Ident(restParamName), jstpe.AnyType,
- mutable = false, rest = true)
++ val restParamDef = {
++ js.ParamDef(js.LocalIdent(restParamName),
++ NoOriginalName, jstpe.AnyType,
++ mutable = false, rest = true)
++ }
+ fixedParamDefs :+ restParamDef
+ } else {
+ fixedParamDefs
+ }
+ }
- private def genVarargRef(fixedParamCount: Int, minArgc: Int)(
- implicit pos: Position): js.Tree = {
- val restParam = genRestArgRef()
- assert(fixedParamCount >= minArgc,
- s"genVarargRef($fixedParamCount, $minArgc) at $pos")
- if (fixedParamCount == minArgc) restParam
- else {
- js.JSMethodApply(restParam, js.StringLiteral("slice"),
- List(js.IntLiteral(fixedParamCount - minArgc)))
+ def genArgRef(index: Int)(implicit pos: Position): js.Tree = {
+ if (index < minArgc)
- js.VarRef(js.Ident(fixedParamNames(index)))(jstpe.AnyType)
++ js.VarRef(js.LocalIdent(fixedParamNames(index)))(jstpe.AnyType)
+ else
- js.JSBracketSelect(genRestArgRef(), js.IntLiteral(index - minArgc))
++ js.JSSelect(genRestArgRef(), js.IntLiteral(index - minArgc))
+ }
+
+ def genVarargRef(fixedParamCount: Int)(implicit pos: Position): js.Tree = {
+ val restParam = genRestArgRef()
+ assert(fixedParamCount >= minArgc,
+ s"genVarargRef($fixedParamCount) with minArgc = $minArgc at $pos")
+ if (fixedParamCount == minArgc) {
+ restParam
+ } else {
- js.JSBracketMethodApply(restParam, js.StringLiteral("slice"), List(
- js.IntLiteral(fixedParamCount - minArgc)))
++ js.JSMethodApply(restParam, js.StringLiteral("slice"),
++ List(js.IntLiteral(fixedParamCount - minArgc)))
+ }
}
- }
- private def genRestArgRef()(implicit pos: Position): js.Tree =
- js.VarRef(js.LocalIdent(LocalName("arg$rest")))(jstpe.AnyType)
+ def genRestArgRef()(implicit pos: Position): js.Tree = {
+ assert(needsRestParam,
+ s"trying to generate a reference to non-existent rest param at $pos")
- js.VarRef(js.Ident(restParamName))(jstpe.AnyType)
++ js.VarRef(js.LocalIdent(restParamName))(jstpe.AnyType)
+ }
+
+ def genAllArgsRefsForForwarder()(implicit pos: Position): List[js.Tree] = {
+ val fixedArgRefs = fixedParamNames.toList.map { paramName =>
- js.VarRef(js.Ident(paramName))(jstpe.AnyType)
++ js.VarRef(js.LocalIdent(paramName))(jstpe.AnyType)
+ }
+
+ if (needsRestParam) {
- val restArgRef = js.VarRef(js.Ident(restParamName))(jstpe.AnyType)
++ val restArgRef = js.VarRef(js.LocalIdent(restParamName))(jstpe.AnyType)
+ fixedArgRefs :+ restArgRef
+ } else {
+ fixedArgRefs
+ }
+ }
+ }
- private def hasRepeatedParam(sym: Symbol) =
+ private def hasRepeatedParam(sym: Symbol) = {
enteringPhase(currentRun.uncurryPhase) {
sym.paramss.flatten.lastOption.exists(isRepeated _)
+ }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment