Last active
January 21, 2020 19:01
-
-
Save gzm0/400eca19489165821a6f5dd4f8ea4d74 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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