@@ -1609,10 +1609,10 @@ class JSCodeGen()(using genCtx: Context) {
1609
1609
optimizerHints, Unversioned )
1610
1610
} else {
1611
1611
val namespace = if (isMethodStaticInIR(sym)) {
1612
- if (sym.isPrivate ) js.MemberNamespace .PrivateStatic
1612
+ if (sym.is( Private ) ) js.MemberNamespace .PrivateStatic
1613
1613
else js.MemberNamespace .PublicStatic
1614
1614
} else {
1615
- if (sym.isPrivate ) js.MemberNamespace .Private
1615
+ if (sym.is( Private ) ) js.MemberNamespace .Private
1616
1616
else js.MemberNamespace .Public
1617
1617
}
1618
1618
val resultIRType = toIRType(patchedResultType(sym))
@@ -3770,7 +3770,7 @@ class JSCodeGen()(using genCtx: Context) {
3770
3770
/** Gen a statically linked call to an instance method. */
3771
3771
def genApplyMethodMaybeStatically (receiver : js.Tree , method : Symbol ,
3772
3772
arguments : List [js.Tree ])(implicit pos : Position ): js.Tree = {
3773
- if (method.isPrivate || method.isClassConstructor)
3773
+ if (method.is( Private ) || method.isClassConstructor)
3774
3774
genApplyMethodStatically(receiver, method, arguments)
3775
3775
else
3776
3776
genApplyMethod(receiver, method, arguments)
@@ -3779,7 +3779,7 @@ class JSCodeGen()(using genCtx: Context) {
3779
3779
/** Gen a dynamically linked call to a Scala method. */
3780
3780
def genApplyMethod (receiver : js.Tree , method : Symbol , arguments : List [js.Tree ])(
3781
3781
implicit pos : Position ): js.Tree = {
3782
- assert(! method.isPrivate ,
3782
+ assert(! method.is( Private ) ,
3783
3783
s " Cannot generate a dynamic call to private method $method at $pos" )
3784
3784
js.Apply (js.ApplyFlags .empty, receiver, encodeMethodSym(method), arguments)(
3785
3785
toIRType(patchedResultType(method)))
@@ -3789,7 +3789,7 @@ class JSCodeGen()(using genCtx: Context) {
3789
3789
def genApplyMethodStatically (receiver : js.Tree , method : Symbol , arguments : List [js.Tree ])(
3790
3790
implicit pos : Position ): js.Tree = {
3791
3791
val flags = js.ApplyFlags .empty
3792
- .withPrivate(method.isPrivate && ! method.isClassConstructor)
3792
+ .withPrivate(method.is( Private ) && ! method.isClassConstructor)
3793
3793
.withConstructor(method.isClassConstructor)
3794
3794
js.ApplyStatically (flags, receiver, encodeClassName(method.owner),
3795
3795
encodeMethodSym(method), arguments)(
@@ -3799,7 +3799,7 @@ class JSCodeGen()(using genCtx: Context) {
3799
3799
/** Gen a call to a static method. */
3800
3800
private def genApplyStatic (method : Symbol , arguments : List [js.Tree ])(
3801
3801
implicit pos : Position ): js.Tree = {
3802
- js.ApplyStatic (js.ApplyFlags .empty.withPrivate(method.isPrivate ),
3802
+ js.ApplyStatic (js.ApplyFlags .empty.withPrivate(method.is( Private ) ),
3803
3803
encodeClassName(method.owner), encodeMethodSym(method), arguments)(
3804
3804
toIRType(patchedResultType(method)))
3805
3805
}
@@ -4400,34 +4400,53 @@ class JSCodeGen()(using genCtx: Context) {
4400
4400
* This tries to optimize repeated arguments (varargs) by turning them
4401
4401
* into js.WrappedArray instead of Scala wrapped arrays.
4402
4402
*/
4403
- private def genActualArgs (sym : Symbol , args : List [Tree ])(
4404
- implicit pos : Position ): List [js.Tree ] = {
4405
- args.map(genExpr)
4406
- /* val wereRepeated = exitingPhase(currentRun.typerPhase) {
4407
- sym.tpe.params.map(p => isScalaRepeatedParamType(p.tpe))
4403
+ private def genActualArgs (sym : Symbol , args : List [Tree ]): List [js.Tree ] = {
4404
+ // Fast path for methods that do not have any repeated parameter
4405
+ val isAnyParamRepeated : Boolean = atPhase(elimRepeatedPhase) {
4406
+ sym.info.paramInfoss.flatten.exists(_.isRepeatedParam)
4408
4407
}
4409
4408
4410
- if (wereRepeated.size > args.size) {
4411
- // Should not happen, but let's not crash
4409
+ if (! isAnyParamRepeated) {
4412
4410
args.map(genExpr)
4413
4411
} else {
4414
- /* Arguments that are in excess compared to the type signature after
4415
- * erasure are lambda-lifted arguments. They cannot be repeated, hence
4416
- * the extension to `false`.
4412
+ /* Erasure and lambdalift both insert capture params in dotc, at
4413
+ * unpredictable positions. Therefore, we use the *names* of parameters
4414
+ * as the only reliable link to whether they were initially repeated.
4415
+ * We also do this in JSSymUtils.jsParamInfos for JS methods.
4417
4416
*/
4418
- for ((arg, wasRepeated) <- args.zipAll(wereRepeated, EmptyTree, false)) yield {
4419
- if (wasRepeated) {
4420
- tryGenRepeatedParamAsJSArray(arg, handleNil = false).fold {
4421
- genExpr(arg)
4422
- } { genArgs =>
4423
- genNew(WrappedArrayClass, WrappedArray_ctor,
4424
- List(js.JSArrayConstr(genArgs)))
4417
+ val namesOfRepeatedParams : Set [TermName ] = atPhase(elimRepeatedPhase) {
4418
+ sym.info.paramNamess.flatten.zip(sym.info.paramInfoss.flatten).collect {
4419
+ case (paramName, paramInfo) if paramInfo.isRepeatedParam => paramName
4420
+ }.toSet
4421
+ }
4422
+
4423
+ for ((arg, paramName) <- args.zip(sym.info.paramNamess.flatten)) yield {
4424
+ if (namesOfRepeatedParams.contains(paramName)) {
4425
+ /* If the argument is a call to the compiler's chosen `wrapArray`
4426
+ * method with an array literal as argument, we know it actually
4427
+ * came from expanded varargs. In that case, rewrite to calling our
4428
+ * custom `scala.scalajs.runtime.to*VarArgs` method. These methods
4429
+ * choose the best implementation of varargs depending on the
4430
+ * target platform.
4431
+ */
4432
+ arg match {
4433
+ case MaybeAsInstanceOf (wrapArray @ WrapArray (MaybeAsInstanceOf (arrayValue : JavaSeqLiteral ))) =>
4434
+ implicit val pos : SourcePosition = wrapArray.sourcePos
4435
+ js.Apply (
4436
+ js.ApplyFlags .empty,
4437
+ js.LoadModule (ScalaJSRuntimeModClassName ),
4438
+ js.MethodIdent (WrapArray .wrapArraySymToToVarArgsName(wrapArray.symbol)),
4439
+ List (genExpr(arrayValue))
4440
+ )(jstpe.ClassType (encodeClassName(defn.SeqClass ), nullable = true ))
4441
+
4442
+ case _ =>
4443
+ genExpr(arg)
4425
4444
}
4426
4445
} else {
4427
4446
genExpr(arg)
4428
4447
}
4429
4448
}
4430
- }*/
4449
+ }
4431
4450
}
4432
4451
4433
4452
/** Gen actual actual arguments to a JS method call.
@@ -4496,47 +4515,28 @@ class JSCodeGen()(using genCtx: Context) {
4496
4515
* `js.Array`.
4497
4516
*/
4498
4517
private def genJSRepeatedParam (arg : Tree ): List [js.TreeOrJSSpread ] = {
4499
- tryGenRepeatedParamAsJSArray(arg, handleNil = true ).getOrElse {
4500
- /* Fall back to calling runtime.genTraversableOnce2jsArray
4501
- * to perform the conversion to js.Array, then wrap in a Spread
4502
- * operator.
4503
- */
4504
- implicit val pos : SourcePosition = arg.sourcePos
4505
- val jsArrayArg = genModuleApplyMethod(
4506
- jsdefn.Runtime_toJSVarArgs ,
4507
- List (genExpr(arg)))
4508
- List (js.JSSpread (jsArrayArg))
4509
- }
4510
- }
4511
-
4512
- /** Try and expand an actual argument to a repeated param `(xs: T*)`.
4513
- *
4514
- * This method recognizes the shapes of tree generated by the desugaring
4515
- * of repeated params in Scala, and expands them.
4516
- * If `arg` does not have the shape of a generated repeated param, this
4517
- * method returns `None`.
4518
- */
4519
- private def tryGenRepeatedParamAsJSArray (arg : Tree ,
4520
- handleNil : Boolean ): Option [List [js.Tree ]] = {
4521
- implicit val pos = arg.span
4518
+ implicit val pos : SourcePosition = arg.sourcePos
4522
4519
4523
4520
// Given a method `def foo(args: T*)`
4524
4521
arg match {
4525
4522
// foo(arg1, arg2, ..., argN) where N > 0
4526
4523
case MaybeAsInstanceOf (WrapArray (MaybeAsInstanceOf (array : JavaSeqLiteral ))) =>
4527
4524
/* Value classes in arrays are already boxed, so no need to use
4528
4525
* the type before erasure.
4529
- * TODO Is this true in dotty?
4530
4526
*/
4531
- Some ( array.elems.map(e => box(genExpr(e), e.tpe) ))
4527
+ array.elems.map(e => box(genExpr(e), e.tpe))
4532
4528
4533
4529
// foo()
4534
- case Ident (_) if handleNil && arg.symbol == defn.NilModule =>
4535
- Some ( Nil )
4530
+ case Ident (_) if arg.symbol == defn.NilModule =>
4531
+ Nil
4536
4532
4537
4533
// foo(argSeq: _*) - cannot be optimized
4538
4534
case _ =>
4539
- None
4535
+ /* Fall back to calling runtime.toJSVarArgs to perform the conversion
4536
+ * to js.Array, then wrap in a Spread operator.
4537
+ */
4538
+ val jsArrayArg = genModuleApplyMethod(jsdefn.Runtime_toJSVarArgs , List (genExpr(arg)))
4539
+ List (js.JSSpread (jsArrayArg))
4540
4540
}
4541
4541
}
4542
4542
@@ -4551,16 +4551,32 @@ class JSCodeGen()(using genCtx: Context) {
4551
4551
}
4552
4552
4553
4553
private object WrapArray {
4554
- lazy val isWrapArray : Set [Symbol ] = {
4555
- val names0 = defn.ScalaValueClasses ().map(sym => nme.wrapXArray(sym.name))
4556
- val names1 = names0 ++ Set (nme.wrapRefArray, nme.genericWrapArray)
4557
- val symsInPredef = names1.map(defn.ScalaPredefModule .requiredMethod(_))
4558
- val symsInScalaRunTime = names1.map(defn.ScalaRuntimeModule .requiredMethod(_))
4559
- (symsInPredef ++ symsInScalaRunTime).toSet
4554
+ lazy val wrapArraySymToToVarArgsName : Map [Symbol , MethodName ] = {
4555
+ val SeqClassRef = jstpe.ClassRef (encodeClassName(defn.SeqClass ))
4556
+
4557
+ val items : Seq [(Name , String , jstpe.TypeRef )] = Seq (
4558
+ (nme.genericWrapArray, " toGenericVarArgs" , jswkn.ObjectRef ),
4559
+ (nme.wrapRefArray, " toRefVarArgs" , jstpe.ArrayTypeRef (jswkn.ObjectRef , 1 )),
4560
+ (tpd.wrapArrayMethodName(defn.UnitType ), " toUnitVarArgs" , jstpe.ArrayTypeRef (jstpe.ClassRef (jswkn.BoxedUnitClass ), 1 )),
4561
+ (tpd.wrapArrayMethodName(defn.BooleanType ), " toBooleanVarArgs" , jstpe.ArrayTypeRef (jstpe.BooleanRef , 1 )),
4562
+ (tpd.wrapArrayMethodName(defn.CharType ), " toCharVarArgs" , jstpe.ArrayTypeRef (jstpe.CharRef , 1 )),
4563
+ (tpd.wrapArrayMethodName(defn.ByteType ), " toByteVarArgs" , jstpe.ArrayTypeRef (jstpe.ByteRef , 1 )),
4564
+ (tpd.wrapArrayMethodName(defn.ShortType ), " toShortVarArgs" , jstpe.ArrayTypeRef (jstpe.ShortRef , 1 )),
4565
+ (tpd.wrapArrayMethodName(defn.IntType ), " toIntVarArgs" , jstpe.ArrayTypeRef (jstpe.IntRef , 1 )),
4566
+ (tpd.wrapArrayMethodName(defn.LongType ), " toLongVarArgs" , jstpe.ArrayTypeRef (jstpe.LongRef , 1 )),
4567
+ (tpd.wrapArrayMethodName(defn.FloatType ), " toFloatVarArgs" , jstpe.ArrayTypeRef (jstpe.FloatRef , 1 )),
4568
+ (tpd.wrapArrayMethodName(defn.DoubleType ), " toDoubleVarArgs" , jstpe.ArrayTypeRef (jstpe.DoubleRef , 1 ))
4569
+ )
4570
+
4571
+ items.map { case (wrapArrayName, simpleName, argTypeRef) =>
4572
+ val wrapArraySym = defn.getWrapVarargsArrayModule.requiredMethod(wrapArrayName)
4573
+ val toVarArgsName = MethodName (simpleName, argTypeRef :: Nil , SeqClassRef )
4574
+ wrapArraySym -> toVarArgsName
4575
+ }.toMap
4560
4576
}
4561
4577
4562
4578
def unapply (tree : Apply ): Option [Tree ] = tree match {
4563
- case Apply (wrapArray_?, List (wrapped)) if isWrapArray (wrapArray_?.symbol) =>
4579
+ case Apply (wrapArray_?, List (wrapped)) if wrapArraySymToToVarArgsName.contains (wrapArray_?.symbol) =>
4564
4580
Some (wrapped)
4565
4581
case _ =>
4566
4582
None
@@ -5002,6 +5018,7 @@ object JSCodeGen {
5002
5018
private val JLRArrayClassName = ClassName (" java.lang.reflect.Array" )
5003
5019
private val JSObjectClassName = ClassName (" scala.scalajs.js.Object" )
5004
5020
private val JavaScriptExceptionClassName = ClassName (" scala.scalajs.js.JavaScriptException" )
5021
+ private val ScalaJSRuntimeModClassName = ClassName (" scala.scalajs.runtime.package$" )
5005
5022
5006
5023
private val ObjectArrayTypeRef = jstpe.ArrayTypeRef (jswkn.ObjectRef , 1 )
5007
5024
0 commit comments