Skip to content

Commit e6fd38d

Browse files
committed
Use JSObjectConstr more often for js.Dynamic.literal.
Now that `JSObjectConstr` admits `ComputedName`s for keys, there are more patterns of calls of `js.Dynamic.literal` that can be emitted as `JSObjectConstr`. This commit leverages that.
1 parent a9e98b9 commit e6fd38d

File tree

2 files changed

+57
-60
lines changed

2 files changed

+57
-60
lines changed

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

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3908,9 +3908,10 @@ abstract class GenJSCode extends plugins.PluginComponent
39083908
* {name1: arg1, name2: arg2, ... }
39093909
*/
39103910

3911-
def warnIfDuplicatedKey(pairs: List[(js.StringLiteral, js.Tree)]): Unit = {
3912-
val allKeys = pairs.collect { case (js.StringLiteral(keyName), _) => keyName }
3913-
val keyCounts = allKeys.distinct.map(key => key -> allKeys.count(_ == key))
3911+
def warnIfDuplicatedKey(keys: List[js.StringLiteral]): Unit = {
3912+
val keyNames = keys.map(_.value)
3913+
val keyCounts =
3914+
keyNames.distinct.map(key => key -> keyNames.count(_ == key))
39143915
val duplicateKeyCounts = keyCounts.filter(1 < _._2)
39153916
if (duplicateKeyCounts.nonEmpty) {
39163917
reporter.warning(pos,
@@ -3923,12 +3924,21 @@ abstract class GenJSCode extends plugins.PluginComponent
39233924
}
39243925
}
39253926

3927+
def keyToPropName(key: js.Tree, index: Int): js.PropertyName = key match {
3928+
case key: js.StringLiteral => key
3929+
case _ => js.ComputedName(key, "local" + index)
3930+
}
3931+
39263932
// Extract first arg to future proof against varargs
39273933
extractFirstArg(genArgs) match {
3928-
// case js.Dynamic.literal("name1" -> ..., "name2" -> ...)
3929-
case (js.StringLiteral("apply"), jse.LitNamed(pairs)) =>
3930-
warnIfDuplicatedKey(pairs)
3931-
js.JSObjectConstr(pairs)
3934+
// case js.Dynamic.literal("name1" -> ..., nameExpr2 -> ...)
3935+
case (js.StringLiteral("apply"), jse.Tuple2List(pairs)) =>
3936+
warnIfDuplicatedKey(pairs.collect {
3937+
case (key: js.StringLiteral, _) => key
3938+
})
3939+
js.JSObjectConstr(pairs.zipWithIndex.map {
3940+
case ((key, value), index) => (keyToPropName(key, index), value)
3941+
})
39323942

39333943
/* case js.Dynamic.literal(x: _*)
39343944
* Even though scalac does not support this notation, it is still
@@ -3950,31 +3960,27 @@ abstract class GenJSCode extends plugins.PluginComponent
39503960
// case js.Dynamic.literal(x, y)
39513961
case (js.StringLiteral("apply"), tups) =>
39523962
// Check for duplicated explicit keys
3953-
val pairs = jse.LitNamedExtractor.extractFrom(tups)
3954-
warnIfDuplicatedKey(pairs)
3955-
3956-
// Create tmp variable
3957-
val resIdent = freshLocalIdent("obj")
3958-
val resVarDef = js.VarDef(resIdent, jstpe.AnyType, mutable = false,
3959-
js.JSObjectConstr(Nil))
3960-
val res = resVarDef.ref
3963+
warnIfDuplicatedKey(jse.extractLiteralKeysFrom(tups))
39613964

3962-
// Assign fields
3965+
// Evaluate all tuples first
39633966
val tuple2Type = encodeClassType(TupleClass(2))
3964-
val assigns = tups flatMap {
3965-
// special case for literals
3966-
case jse.Tuple2(name, value) =>
3967-
js.Assign(js.JSBracketSelect(res, name), value) :: Nil
3968-
case tupExpr =>
3969-
val tupIdent = freshLocalIdent("tup")
3970-
val tup = js.VarRef(tupIdent)(tuple2Type)
3971-
js.VarDef(tupIdent, tuple2Type, mutable = false, tupExpr) ::
3972-
js.Assign(js.JSBracketSelect(res,
3973-
genApplyMethod(tup, js.Ident("$$und1__O"), Nil, jstpe.AnyType)),
3974-
genApplyMethod(tup, js.Ident("$$und2__O"), Nil, jstpe.AnyType)) :: Nil
3967+
val evalTuples = tups.map { tup =>
3968+
js.VarDef(freshLocalIdent("tup"), tuple2Type, mutable = false,
3969+
tup)(tup.pos)
39753970
}
39763971

3977-
js.Block(resVarDef +: assigns :+ res: _*)
3972+
// Build the resulting object
3973+
val result = js.JSObjectConstr(evalTuples.zipWithIndex.map {
3974+
case (evalTuple, index) =>
3975+
val tupRef = evalTuple.ref
3976+
val key = genApplyMethod(tupRef, js.Ident("$$und1__O"), Nil,
3977+
jstpe.AnyType)
3978+
val value = genApplyMethod(tupRef, js.Ident("$$und2__O"), Nil,
3979+
jstpe.AnyType)
3980+
keyToPropName(key, index) -> value
3981+
})
3982+
3983+
js.Block(evalTuples :+ result)
39783984

39793985
// case where another method is called
39803986
case (js.StringLiteral(name), _) if name != "apply" =>

compiler/src/main/scala/org/scalajs/core/compiler/JSTreeExtractors.scala

Lines changed: 23 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -22,45 +22,36 @@ object JSTreeExtractors {
2222
})
2323
}
2424

25-
/**
26-
* A partially literally named sequence (like in a call to applyDynamicNamed)
27-
* Where some parameters are expected to be literally named.
25+
/** Extracts the literal strings in "key" position of a sequence of Tuple2.
2826
*
29-
* Example (Scala): method(("name1", x), (a, y), z)
27+
* Non-Tuple2 constructors are silently ignored, as well as non-literal
28+
* keys.
3029
*/
31-
object LitNamedExtractor {
32-
def extractFrom(exprs: List[Tree]): List[(StringLiteral, Tree)] = {
33-
// Note that with 'failIfNonLit = false'
34-
// genNameLitExtract will never return None
35-
genNamedLitExtract(exprs, Nil, false).getOrElse(Nil)
36-
}
37-
38-
@tailrec
39-
private[jse] final def genNamedLitExtract(
40-
exprs: List[Tree],
41-
acc: List[(StringLiteral, Tree)],
42-
failIfNonLit: Boolean
43-
): Option[List[(StringLiteral, Tree)]] = exprs match {
44-
case Tuple2(name: StringLiteral, value) :: xs =>
45-
genNamedLitExtract(xs, (name, value) :: acc, failIfNonLit)
46-
case _ :: xs =>
47-
if (failIfNonLit)
48-
None
49-
else
50-
genNamedLitExtract(xs, acc, failIfNonLit)
51-
case Nil => Some(acc.reverse)
30+
def extractLiteralKeysFrom(exprs: List[Tree]): List[StringLiteral] = {
31+
exprs.collect {
32+
case Tuple2(key: StringLiteral, _) => key
5233
}
5334
}
5435

55-
/**
56-
* A literally named sequence (like in a call to applyDynamicNamed)
57-
* Where all parameters are expected to be literally named.
36+
/** A list of Tuple2, for example used as a list of key/value pairs
37+
* (like in a call to applyDynamicNamed).
5838
*
59-
* Example (Scala): method(("name1", x), ("name2", y))
39+
* Examples (Scala):
40+
* {{{
41+
* method(("name1", x), ("name2", y))
42+
* method("name1" -> x, "name2" -> y)
43+
* method(nameExpr1 -> x, (nameExpr2, y))
44+
* }}}
6045
*/
61-
object LitNamed {
62-
def unapply(exprs: List[Tree]): Option[List[(StringLiteral, Tree)]] = {
63-
LitNamedExtractor.genNamedLitExtract(exprs, Nil, true)
46+
object Tuple2List {
47+
def unapply(exprs: List[Tree]): Option[List[(Tree, Tree)]] = {
48+
val tuples = exprs.collect {
49+
case Tuple2(key, value) => (key, value)
50+
}
51+
if (tuples.size == exprs.size)
52+
Some(tuples)
53+
else
54+
None
6455
}
6556
}
6657

0 commit comments

Comments
 (0)