Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit bddd5b7

Browse files
authored
Merge pull request scala-js#5215 from sjrd/wasm-friendly-varargs
Wasm-friendly implementation of varargs.
2 parents 1b0e7a6 + 24750cf commit bddd5b7

File tree

13 files changed

+385
-81
lines changed

13 files changed

+385
-81
lines changed

compiler/src/main/scala/org/scalajs/nscplugin/GenJSCode.scala

Lines changed: 54 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5953,10 +5953,26 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
59535953
*/
59545954
for ((arg, wasRepeated) <- args.zipAll(wereRepeated, EmptyTree, false)) yield {
59555955
if (wasRepeated) {
5956-
tryGenRepeatedParamAsJSArray(arg, handleNil = false).fold {
5957-
genExpr(arg)
5958-
} { genArgs =>
5959-
genJSArrayToVarArgs(js.JSArrayConstr(genArgs))
5956+
/* If the argument is a call to the compiler's chosen `wrapArray`
5957+
* method with an array literal as argument, we know it actually
5958+
* came from expanded varargs. In that case, rewrite to calling our
5959+
* custom `scala.scalajs.runtime.to*VarArgs` method. These methods
5960+
* choose the best implementation of varargs depending on the
5961+
* target platform.
5962+
*/
5963+
arg match {
5964+
case MaybeAsInstanceOf(wrapArray @ WrapArray(
5965+
MaybeAsInstanceOf(arrayValue: ArrayValue))) =>
5966+
implicit val pos = wrapArray.pos
5967+
js.Apply(
5968+
js.ApplyFlags.empty,
5969+
genLoadModule(RuntimePackageModule),
5970+
js.MethodIdent(WrapArray.wrapArraySymToToVarArgsName(wrapArray.symbol)),
5971+
List(genExpr(arrayValue))
5972+
)(jstpe.ClassType(encodeClassName(SeqClass), nullable = true))
5973+
5974+
case _ =>
5975+
genExpr(arg)
59605976
}
59615977
} else {
59625978
genExpr(arg)
@@ -6108,27 +6124,6 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
61086124
* Otherwise, it returns a JSSpread with the Seq converted to a js.Array.
61096125
*/
61106126
private def genPrimitiveJSRepeatedParam(arg: Tree): List[js.TreeOrJSSpread] = {
6111-
tryGenRepeatedParamAsJSArray(arg, handleNil = true) getOrElse {
6112-
/* Fall back to calling runtime.toJSVarArgs to perform the conversion
6113-
* to js.Array, then wrap in a Spread operator.
6114-
*/
6115-
implicit val pos = arg.pos
6116-
val jsArrayArg = genApplyMethod(
6117-
genLoadModule(RuntimePackageModule),
6118-
Runtime_toJSVarArgs,
6119-
List(genExpr(arg)))
6120-
List(js.JSSpread(jsArrayArg))
6121-
}
6122-
}
6123-
6124-
/** Try and expand a repeated param (xs: T*) at compile-time.
6125-
* This method recognizes the shapes of tree generated by the desugaring
6126-
* of repeated params in Scala, and expands them.
6127-
* If `arg` does not have the shape of a generated repeated param, this
6128-
* method returns `None`.
6129-
*/
6130-
private def tryGenRepeatedParamAsJSArray(arg: Tree,
6131-
handleNil: Boolean): Option[List[js.Tree]] = {
61326127
implicit val pos = arg.pos
61336128

61346129
// Given a method `def foo(args: T*)`
@@ -6140,15 +6135,22 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
61406135
* the type before erasure.
61416136
*/
61426137
val elemTpe = tpt.tpe
6143-
Some(elems.map(e => ensureBoxed(genExpr(e), elemTpe)))
6138+
elems.map(e => ensureBoxed(genExpr(e), elemTpe))
61446139

61456140
// foo()
6146-
case Select(_, _) if handleNil && arg.symbol == NilModule =>
6147-
Some(Nil)
6141+
case Select(_, _) if arg.symbol == NilModule =>
6142+
Nil
61486143

61496144
// foo(argSeq:_*) - cannot be optimized
61506145
case _ =>
6151-
None
6146+
/* Fall back to calling runtime.toJSVarArgs to perform the conversion
6147+
* to js.Array, then wrap in a Spread operator.
6148+
*/
6149+
val jsArrayArg = genApplyMethod(
6150+
genLoadModule(RuntimePackageModule),
6151+
Runtime_toJSVarArgs,
6152+
List(genExpr(arg)))
6153+
List(js.JSSpread(jsArrayArg))
61526154
}
61536155
}
61546156

@@ -6176,25 +6178,33 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
61766178
def isClassTagBasedWrapArrayMethod(sym: Symbol): Boolean =
61776179
sym == wrapRefArrayMethod || sym == genericWrapArrayMethod
61786180

6179-
private val isWrapArray: Set[Symbol] = {
6180-
Seq(
6181-
nme.wrapRefArray,
6182-
nme.wrapByteArray,
6183-
nme.wrapShortArray,
6184-
nme.wrapCharArray,
6185-
nme.wrapIntArray,
6186-
nme.wrapLongArray,
6187-
nme.wrapFloatArray,
6188-
nme.wrapDoubleArray,
6189-
nme.wrapBooleanArray,
6190-
nme.wrapUnitArray,
6191-
nme.genericWrapArray
6192-
).map(getMemberMethod(wrapArrayModule, _)).toSet
6181+
val wrapArraySymToToVarArgsName: Map[Symbol, MethodName] = {
6182+
val SeqClassRef = jstpe.ClassRef(encodeClassName(SeqClass))
6183+
6184+
val items: Seq[(Name, String, jstpe.TypeRef)] = Seq(
6185+
(nme.genericWrapArray, "toGenericVarArgs", jswkn.ObjectRef),
6186+
(nme.wrapRefArray, "toRefVarArgs", jstpe.ArrayTypeRef(jswkn.ObjectRef, 1)),
6187+
(nme.wrapUnitArray, "toUnitVarArgs", jstpe.ArrayTypeRef(jstpe.ClassRef(jswkn.BoxedUnitClass), 1)),
6188+
(nme.wrapBooleanArray, "toBooleanVarArgs", jstpe.ArrayTypeRef(jstpe.BooleanRef, 1)),
6189+
(nme.wrapCharArray, "toCharVarArgs", jstpe.ArrayTypeRef(jstpe.CharRef, 1)),
6190+
(nme.wrapByteArray, "toByteVarArgs", jstpe.ArrayTypeRef(jstpe.ByteRef, 1)),
6191+
(nme.wrapShortArray, "toShortVarArgs", jstpe.ArrayTypeRef(jstpe.ShortRef, 1)),
6192+
(nme.wrapIntArray, "toIntVarArgs", jstpe.ArrayTypeRef(jstpe.IntRef, 1)),
6193+
(nme.wrapLongArray, "toLongVarArgs", jstpe.ArrayTypeRef(jstpe.LongRef, 1)),
6194+
(nme.wrapFloatArray, "toFloatVarArgs", jstpe.ArrayTypeRef(jstpe.FloatRef, 1)),
6195+
(nme.wrapDoubleArray, "toDoubleVarArgs", jstpe.ArrayTypeRef(jstpe.DoubleRef, 1))
6196+
)
6197+
6198+
items.map { case (wrapArrayName, simpleName, argTypeRef) =>
6199+
val wrapArraySym = getMemberMethod(wrapArrayModule, wrapArrayName)
6200+
val toVarArgsName = MethodName(simpleName, argTypeRef :: Nil, SeqClassRef)
6201+
wrapArraySym -> toVarArgsName
6202+
}.toMap
61936203
}
61946204

61956205
def unapply(tree: Apply): Option[Tree] = tree match {
61966206
case Apply(wrapArray_?, List(wrapped))
6197-
if isWrapArray(wrapArray_?.symbol) =>
6207+
if wrapArraySymToToVarArgsName.contains(wrapArray_?.symbol) =>
61986208
Some(wrapped)
61996209
case _ =>
62006210
None

compiler/src/test/scala/org/scalajs/nscplugin/test/OptimizationTest.scala

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,10 @@ class OptimizationTest extends JSASTTest {
8585
val d = js.Array(Nil)
8686
val e = js.Array(new VC(151189))
8787
}
88-
""".
89-
hasNot("any of the wrapArray methods") {
88+
""".hasNot("any of the wrapArray methods") {
9089
case WrapArrayCall() =>
90+
}.hasNot("any toVarArgs calls") {
91+
case ToVarArgsCall() =>
9192
}
9293
}
9394

@@ -108,9 +109,10 @@ class OptimizationTest extends JSASTTest {
108109
val d = List(Nil)
109110
val e = List(new VC(151189))
110111
}
111-
""".
112-
hasNot("any of the wrapArray methods") {
112+
""".hasNot("any of the wrapArray methods") {
113113
case WrapArrayCall() =>
114+
}.hasExactly(5, "toVarArgs calls") {
115+
case ToVarArgsCall() =>
114116
}
115117

116118
/* #2265 and #2741:
@@ -136,9 +138,10 @@ class OptimizationTest extends JSASTTest {
136138
def single(x: Int, ys: Int*): Int = x + ys.size
137139
def multiple(x: Int)(ys: Int*): Int = x + ys.size
138140
}
139-
""".
140-
hasNot("any of the wrapArray methods") {
141+
""".hasNot("any of the wrapArray methods") {
141142
case WrapArrayCall() =>
143+
}.hasExactly(3, "toVarArgs calls") {
144+
case ToVarArgsCall() =>
142145
}
143146

144147
/* Make sure our wrapper matcher has the right name.
@@ -162,6 +165,8 @@ class OptimizationTest extends JSASTTest {
162165
}
163166
sanityCheckCode.has("one of the wrapArray methods") {
164167
case WrapArrayCall() =>
168+
}.hasNot("any toVarArgs calls") {
169+
case ToVarArgsCall() =>
165170
}
166171
}
167172

@@ -697,6 +702,7 @@ class OptimizationTest extends JSASTTest {
697702
object OptimizationTest {
698703

699704
private val ArrayModuleClass = ClassName("scala.Array$")
705+
private val ScalaJSRunTimeModuleClass = ClassName("scala.scalajs.runtime.package$")
700706

701707
private val applySimpleMethodName = SimpleMethodName("apply")
702708

@@ -718,4 +724,15 @@ object OptimizationTest {
718724
}
719725
}
720726

727+
private object ToVarArgsCall {
728+
def unapply(tree: js.Apply): Boolean = {
729+
tree.method.name.simpleName.nameString.endsWith("VarArgs") && {
730+
tree.receiver match {
731+
case js.LoadModule(ScalaJSRunTimeModuleClass) => true
732+
case _ => false
733+
}
734+
}
735+
}
736+
}
737+
721738
}

library/src/main/scala-new-collections/scala/scalajs/runtime/Compat.scala

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
package scala.scalajs.runtime
1414

1515
import scala.collection.IterableOnce
16+
import scala.collection.immutable.ArraySeq
1617

1718
import scala.scalajs.js
1819

@@ -32,4 +33,37 @@ private[runtime] object Compat {
3233
}
3334
}
3435

36+
@inline def toGenericVarArgsWasmImpl[T](xs: Array[T]): Seq[T] =
37+
ArraySeq.unsafeWrapArray(xs)
38+
39+
@inline def toRefVarArgsWasmImpl[T <: AnyRef](xs: Array[T]): Seq[T] =
40+
new ArraySeq.ofRef[T](xs)
41+
42+
@inline def toUnitVarArgsWasmImpl(xs: Array[Unit]): Seq[Unit] =
43+
new ArraySeq.ofUnit(xs)
44+
45+
@inline def toBooleanVarArgsWasmImpl(xs: Array[Boolean]): Seq[Boolean] =
46+
new ArraySeq.ofBoolean(xs)
47+
48+
@inline def toCharVarArgsWasmImpl(xs: Array[Char]): Seq[Char] =
49+
new ArraySeq.ofChar(xs)
50+
51+
@inline def toByteVarArgsWasmImpl(xs: Array[Byte]): Seq[Byte] =
52+
new ArraySeq.ofByte(xs)
53+
54+
@inline def toShortVarArgsWasmImpl(xs: Array[Short]): Seq[Short] =
55+
new ArraySeq.ofShort(xs)
56+
57+
@inline def toIntVarArgsWasmImpl(xs: Array[Int]): Seq[Int] =
58+
new ArraySeq.ofInt(xs)
59+
60+
@inline def toLongVarArgsWasmImpl(xs: Array[Long]): Seq[Long] =
61+
new ArraySeq.ofLong(xs)
62+
63+
@inline def toFloatVarArgsWasmImpl(xs: Array[Float]): Seq[Float] =
64+
new ArraySeq.ofFloat(xs)
65+
66+
@inline def toDoubleVarArgsWasmImpl(xs: Array[Double]): Seq[Double] =
67+
new ArraySeq.ofDouble(xs)
68+
3569
}

library/src/main/scala-old-collections/scala/scalajs/runtime/Compat.scala

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
package scala.scalajs.runtime
1414

1515
import scala.collection.GenTraversableOnce
16+
import scala.collection.mutable.WrappedArray
17+
1618
import scala.scalajs.js
1719

1820
private[runtime] object Compat {
@@ -32,4 +34,37 @@ private[runtime] object Compat {
3234
}
3335
}
3436

37+
@inline def toGenericVarArgsWasmImpl[T](xs: Array[T]): Seq[T] =
38+
WrappedArray.make(xs)
39+
40+
@inline def toRefVarArgsWasmImpl[T <: AnyRef](xs: Array[T]): Seq[T] =
41+
new WrappedArray.ofRef[T](xs)
42+
43+
@inline def toUnitVarArgsWasmImpl(xs: Array[Unit]): Seq[Unit] =
44+
new WrappedArray.ofUnit(xs)
45+
46+
@inline def toBooleanVarArgsWasmImpl(xs: Array[Boolean]): Seq[Boolean] =
47+
new WrappedArray.ofBoolean(xs)
48+
49+
@inline def toCharVarArgsWasmImpl(xs: Array[Char]): Seq[Char] =
50+
new WrappedArray.ofChar(xs)
51+
52+
@inline def toByteVarArgsWasmImpl(xs: Array[Byte]): Seq[Byte] =
53+
new WrappedArray.ofByte(xs)
54+
55+
@inline def toShortVarArgsWasmImpl(xs: Array[Short]): Seq[Short] =
56+
new WrappedArray.ofShort(xs)
57+
58+
@inline def toIntVarArgsWasmImpl(xs: Array[Int]): Seq[Int] =
59+
new WrappedArray.ofInt(xs)
60+
61+
@inline def toLongVarArgsWasmImpl(xs: Array[Long]): Seq[Long] =
62+
new WrappedArray.ofLong(xs)
63+
64+
@inline def toFloatVarArgsWasmImpl(xs: Array[Float]): Seq[Float] =
65+
new WrappedArray.ofFloat(xs)
66+
67+
@inline def toDoubleVarArgsWasmImpl(xs: Array[Double]): Seq[Double] =
68+
new WrappedArray.ofDouble(xs)
69+
3570
}

0 commit comments

Comments
 (0)