[K/JS]: KT-21626: Support spread properties in object literals
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/jsexport/ExportModelToJsStatements.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/jsexport/ExportModelToJsStatements.kt
index 85090a5..01df052 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/jsexport/ExportModelToJsStatements.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/jsexport/ExportModelToJsStatements.kt
@@ -220,9 +220,9 @@
JsLoweredDeclarationOrigin.OBJECT_GET_INSTANCE_FUNCTION -> "getInstance"
else -> "get"
}
- propertyInitializers += JsPropertyInitializer(JsStringLiteral(fieldName), it.makeRef())
+ propertyInitializers += JsPropertyInitializer.KeyValue(JsStringLiteral(fieldName), it.makeRef())
}
- setter?.let { propertyInitializers += JsPropertyInitializer(JsStringLiteral("set"), it.makeRef()) }
+ setter?.let { propertyInitializers += JsPropertyInitializer.KeyValue(JsStringLiteral("set"), it.makeRef()) }
}
)
}
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt
index 76af809..662b848 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt
@@ -542,7 +542,7 @@
JsObjectLiteral(
associatedObjects
.map { (key, objectGetInstanceFunction) ->
- JsPropertyInitializer(JsIntLiteral(key.associatedObjectKey!!), objectGetInstanceFunction)
+ JsPropertyInitializer.KeyValue(JsIntLiteral(key.associatedObjectKey!!), objectGetInstanceFunction)
}
.toSmartList()
)
@@ -551,7 +551,7 @@
JsObjectLiteral(
associatedObjects
.map { (key, objectGetInstanceFunction) ->
- JsPropertyInitializer(
+ JsPropertyInitializer.KeyValue(
JsInvocation(
context.staticContext.getNameForStaticFunction(backendContext.symbols.getAssociatedObjectId.owner).makeRef(),
key.getClassRef(context.staticContext),
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt
index 262b0cbe..cc1d15b 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt
@@ -275,7 +275,7 @@
addAll(sharedVariableBoxConstructors) { call, context ->
val arg = translateCallArguments(call, context).single()
- JsObjectLiteral(listOf(JsPropertyInitializer(JsStringLiteral(Namer.SHARED_BOX_V), arg)))
+ JsObjectLiteral(listOf(JsPropertyInitializer.KeyValue(JsStringLiteral(Namer.SHARED_BOX_V), arg)))
}
add(symbols.genericSharedVariableBox.load) { call, context: JsGenerationContext ->
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/serialization/Constants.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/serialization/Constants.kt
index 217a4eb..567c449 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/serialization/Constants.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/serialization/Constants.kt
@@ -78,4 +78,9 @@
const val TEMPLATE_STRING_LITERAL = 27
const val TEMPLATE_ELEMENT_STRING = 28
const val TEMPLATE_ELEMENT_INTERPOLATION = 29
+}
+
+object PropertyInitializerKinds {
+ const val KEY_VALUE = 0
+ const val SPREAD = 1
}
\ No newline at end of file
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/serialization/JsIrAstDeserializer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/serialization/JsIrAstDeserializer.kt
index 4dbde14..c6cebf3 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/serialization/JsIrAstDeserializer.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/serialization/JsIrAstDeserializer.kt
@@ -358,7 +358,17 @@
}
OBJECT_LITERAL -> {
JsObjectLiteral(
- readList { JsPropertyInitializer(readExpression(), readExpression()) },
+ readList {
+ when (readInt()) {
+ PropertyInitializerKinds.KEY_VALUE -> {
+ JsPropertyInitializer.KeyValue(readExpression(), readExpression())
+ }
+ PropertyInitializerKinds.SPREAD -> {
+ JsPropertyInitializer.Spread(readExpression())
+ }
+ else -> error("Unknown property initializer kind: $id")
+ }
+ },
readBoolean()
)
}
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/serialization/JsIrAstSerializer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/serialization/JsIrAstSerializer.kt
index 412c36a..c609c18 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/serialization/JsIrAstSerializer.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/serialization/JsIrAstSerializer.kt
@@ -457,8 +457,17 @@
override fun visitObjectLiteral(x: JsObjectLiteral) {
writeByte(ExpressionIds.OBJECT_LITERAL)
writeCollection(x.propertyInitializers) {
- writeExpression(it.labelExpr)
- writeExpression(it.valueExpr)
+ when (it) {
+ is JsPropertyInitializer.KeyValue -> {
+ writeInt(PropertyInitializerKinds.KEY_VALUE)
+ writeExpression(it.labelExpr)
+ writeExpression(it.valueExpr)
+ }
+ is JsPropertyInitializer.Spread -> {
+ writeInt(PropertyInitializerKinds.SPREAD)
+ writeExpression(it.expression)
+ }
+ }
}
writeBoolean(x.isMultiline)
}
diff --git a/js/js.ast/src/org/jetbrains/kotlin/js/backend/JsToStringGenerationVisitor.java b/js/js.ast/src/org/jetbrains/kotlin/js/backend/JsToStringGenerationVisitor.java
index 1b3cb6a..c487bd4 100644
--- a/js/js.ast/src/org/jetbrains/kotlin/js/backend/JsToStringGenerationVisitor.java
+++ b/js/js.ast/src/org/jetbrains/kotlin/js/backend/JsToStringGenerationVisitor.java
@@ -58,6 +58,7 @@
private static final char[] CHARS_LET = "let".toCharArray();
private static final char[] CHARS_CONST = "const".toCharArray();
private static final char[] CHARS_WHILE = "while".toCharArray();
+ private static final char[] CHARS_ELLIPSIS = "...".toCharArray();
private static final char[] HEX_DIGITS = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
private static final Map<Character, Integer> COMMON_ESCAPE_MAPPING = createCommonEscapeMapping();
@@ -1097,30 +1098,37 @@
pushSourceInfo(item.getSource());
- JsExpression labelExpr = item.getLabelExpr();
+ if (item instanceof JsPropertyInitializer.Spread) {
+ JsExpression expression = ((JsPropertyInitializer.Spread)item).getExpression();
+ ellipsis();
+ accept(expression);
+ } else if (item instanceof JsPropertyInitializer.KeyValue) {
+ JsPropertyInitializer.KeyValue keyValue = (JsPropertyInitializer.KeyValue)item;
+ JsExpression labelExpr = keyValue.getLabelExpr();
- if (labelExpr instanceof JsStringLiteral) {
- JsStringLiteral stringLiteral = (JsStringLiteral) labelExpr;
- String value = stringLiteral.getValue();
- if (IdentifierPolicyKt.isValidES5Identifier(value)) {
- String escaped = IdentifierPolicyKt.getRESERVED_KEYWORDS().contains(value) ? "'" + value + "'" : value;
- labelExpr = new JsNameRef(escaped).withMetadataFrom(stringLiteral);
+ if (labelExpr instanceof JsStringLiteral) {
+ JsStringLiteral stringLiteral = (JsStringLiteral) labelExpr;
+ String value = stringLiteral.getValue();
+ if (IdentifierPolicyKt.isValidES5Identifier(value)) {
+ String escaped = IdentifierPolicyKt.getRESERVED_KEYWORDS().contains(value) ? "'" + value + "'" : value;
+ labelExpr = new JsNameRef(escaped).withMetadataFrom(stringLiteral);
+ }
+ accept(labelExpr);
+ } else if (labelExpr instanceof JsNumberLiteral || labelExpr instanceof JsBigIntLiteral) {
+ accept(labelExpr);
+ } else {
+ leftSquare();
+ accept(labelExpr);
+ rightSquare();
}
- accept(labelExpr);
- } else if (labelExpr instanceof JsNumberLiteral || labelExpr instanceof JsBigIntLiteral) {
- accept(labelExpr);
- } else {
- leftSquare();
- accept(labelExpr);
- rightSquare();
- }
- _colon();
- space();
- JsExpression valueExpr = item.getValueExpr();
- boolean wasEnclosed = parenPushIfCommaExpression(valueExpr);
- accept(valueExpr);
- if (wasEnclosed) {
- rightParen();
+ _colon();
+ space();
+ JsExpression valueExpr = keyValue.getValueExpr();
+ boolean wasEnclosed = parenPushIfCommaExpression(valueExpr);
+ accept(valueExpr);
+ if (wasEnclosed) {
+ rightParen();
+ }
}
popSourceInfo();
@@ -1920,4 +1928,6 @@
private void _while() {
p.print(CHARS_WHILE);
}
+
+ private void ellipsis() { p.print(CHARS_ELLIPSIS); }
}
diff --git a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsPropertyInitializer.kt b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsPropertyInitializer.kt
index 6321c40..f161eff 100644
--- a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsPropertyInitializer.kt
+++ b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsPropertyInitializer.kt
@@ -6,38 +6,68 @@
/**
* Used in object literals to specify properties.
*/
-open class JsPropertyInitializer(
- labelExpr: JsExpression,
- valueExpr: JsExpression,
-) : SourceInfoAwareJsNode() {
- var labelExpr: JsExpression = labelExpr
- private set
- var valueExpr: JsExpression = valueExpr
- private set
+sealed class JsPropertyInitializer : SourceInfoAwareJsNode() {
+ class KeyValue(
+ labelExpr: JsExpression,
+ valueExpr: JsExpression,
+ ) : JsPropertyInitializer() {
+ var labelExpr: JsExpression = labelExpr
+ private set
+ var valueExpr: JsExpression = valueExpr
+ private set
- override fun accept(v: JsVisitor) {
- v.visitPropertyInitializer(this)
- }
-
- override fun acceptChildren(visitor: JsVisitor) {
- visitor.accept(labelExpr)
- visitor.accept(valueExpr)
- }
-
- override fun traverse(v: JsVisitorWithContext, ctx: JsContext<*>) {
- if (v.visit(this, ctx)) {
- labelExpr = v.accept(labelExpr)
- valueExpr = v.accept(valueExpr)
+ override fun accept(v: JsVisitor) {
+ v.visitKeyValuePropertyInitializer(this)
}
- v.endVisit(this, ctx)
+
+ override fun acceptChildren(visitor: JsVisitor) {
+ visitor.accept(labelExpr)
+ visitor.accept(valueExpr)
+ }
+
+ override fun traverse(v: JsVisitorWithContext, ctx: JsContext<*>) {
+ if (v.visit(this, ctx)) {
+ labelExpr = v.accept(labelExpr)
+ valueExpr = v.accept(valueExpr)
+ }
+ v.endVisit(this, ctx)
+ }
+
+ override fun deepCopy(): KeyValue {
+ return KeyValue(
+ labelExpr.deepCopy(),
+ valueExpr.deepCopy()
+ ).withMetadataFrom<KeyValue>(this)
+ }
+
+ override fun toString() = "$labelExpr: $valueExpr"
}
- override fun deepCopy(): JsPropertyInitializer {
- return JsPropertyInitializer(
- labelExpr.deepCopy(),
- valueExpr.deepCopy()
- ).withMetadataFrom<JsPropertyInitializer>(this)
- }
+ class Spread(expression: JsExpression) : JsPropertyInitializer() {
+ var expression: JsExpression = expression
+ private set
- override fun toString() = "$labelExpr: $valueExpr"
+ override fun accept(v: JsVisitor) {
+ v.visitSpreadPropertyInitializer(this)
+ }
+
+ override fun acceptChildren(visitor: JsVisitor) {
+ visitor.accept(expression)
+ }
+
+ override fun traverse(v: JsVisitorWithContext, ctx: JsContext<*>) {
+ if (v.visit(this, ctx)) {
+ expression = v.accept(expression)
+ }
+ v.endVisit(this, ctx)
+ }
+
+ override fun deepCopy(): Spread {
+ return Spread(
+ expression.deepCopy(),
+ ).withMetadataFrom<Spread>(this)
+ }
+
+ override fun toString() = "...$expression"
+ }
}
diff --git a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsVisitor.kt b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsVisitor.kt
index d0c1f1b..a398b94 100644
--- a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsVisitor.kt
+++ b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsVisitor.kt
@@ -144,6 +144,12 @@
open fun visitPropertyInitializer(x: JsPropertyInitializer): Unit =
visitElement(x)
+ open fun visitKeyValuePropertyInitializer(x: JsPropertyInitializer.KeyValue): Unit =
+ visitPropertyInitializer(x)
+
+ open fun visitSpreadPropertyInitializer(x: JsPropertyInitializer.Spread): Unit =
+ visitPropertyInitializer(x)
+
open fun visitRegExp(x: JsRegExp): Unit =
visitElement(x)
diff --git a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsVisitorWithContext.java b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsVisitorWithContext.java
index 02a1926..7afbac6 100644
--- a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsVisitorWithContext.java
+++ b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsVisitorWithContext.java
@@ -191,6 +191,14 @@
public void endVisit(@NotNull JsPropertyInitializer x, @NotNull JsContext ctx) {
}
+ public void endVisit(@NotNull JsPropertyInitializer.KeyValue x, @NotNull JsContext ctx) {
+ endVisit((JsPropertyInitializer) x, ctx);
+ }
+
+ public void endVisit(@NotNull JsPropertyInitializer.Spread x, @NotNull JsContext ctx) {
+ endVisit((JsPropertyInitializer) x, ctx);
+ }
+
public void endVisit(@NotNull JsRegExp x, @NotNull JsContext ctx) {
endVisit((JsExpression) x, ctx);
}
@@ -392,6 +400,14 @@
return true;
}
+ public boolean visit(@NotNull JsPropertyInitializer.KeyValue x, @NotNull JsContext ctx) {
+ return visit((JsPropertyInitializer) x, ctx);
+ }
+
+ public boolean visit(@NotNull JsPropertyInitializer.Spread x, @NotNull JsContext ctx) {
+ return visit((JsPropertyInitializer) x, ctx);
+ }
+
public boolean visit(@NotNull JsRegExp x, @NotNull JsContext ctx) {
return true;
}
diff --git a/js/js.parser/src/org/jetbrains/kotlin/js/parser/antlr/JsAstMapperVisitor.kt b/js/js.parser/src/org/jetbrains/kotlin/js/parser/antlr/JsAstMapperVisitor.kt
index fe00151..fdf8121 100644
--- a/js/js.parser/src/org/jetbrains/kotlin/js/parser/antlr/JsAstMapperVisitor.kt
+++ b/js/js.parser/src/org/jetbrains/kotlin/js/parser/antlr/JsAstMapperVisitor.kt
@@ -490,14 +490,14 @@
}
override fun visitPropertyExpressionAssignment(ctx: JavaScriptParser.PropertyExpressionAssignmentContext): JsPropertyInitializer {
- return JsPropertyInitializer(
+ return JsPropertyInitializer.KeyValue(
visitNode<JsExpression>(ctx.propertyName()),
visitNode<JsExpression>(ctx.singleExpression())
).applyLocation(ctx)
}
override fun visitComputedPropertyExpressionAssignment(ctx: JavaScriptParser.ComputedPropertyExpressionAssignmentContext): JsNode? {
- return JsPropertyInitializer(
+ return JsPropertyInitializer.KeyValue(
visitNode<JsExpression>(ctx.label),
visitNode<JsExpression>(ctx.value)
).applyLocation(ctx)
@@ -516,11 +516,13 @@
}
override fun visitSpreadProperty(ctx: JavaScriptParser.SpreadPropertyContext): JsNode? {
- reportError("Spread properties are not supported yet", ctx)
+ return JsPropertyInitializer.Spread(
+ visitNode<JsExpression>(ctx.singleExpression())
+ ).applyLocation(ctx)
}
override fun visitPropertyShorthand(ctx: JavaScriptParser.PropertyShorthandContext): JsPropertyInitializer {
- return JsPropertyInitializer(
+ return JsPropertyInitializer.KeyValue(
JsStringLiteral(ctx.text),
makeRefNode(ctx.text)
).applyLocation(ctx)
diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/inline/clean/RedundantStatementElimination.kt b/js/js.translator/src/org/jetbrains/kotlin/js/inline/clean/RedundantStatementElimination.kt
index 2650583..eb2e61e 100644
--- a/js/js.translator/src/org/jetbrains/kotlin/js/inline/clean/RedundantStatementElimination.kt
+++ b/js/js.translator/src/org/jetbrains/kotlin/js/inline/clean/RedundantStatementElimination.kt
@@ -205,7 +205,13 @@
is JsArrayLiteral -> replaceMany(expression.expressions)
- is JsObjectLiteral -> expression.propertyInitializers.flatMap { replace(it.labelExpr) + replace(it.valueExpr) }
+ is JsObjectLiteral -> expression.propertyInitializers
+ .flatMap {
+ when (it) {
+ is JsPropertyInitializer.KeyValue -> replace(it.labelExpr) + replace(it.valueExpr)
+ is JsPropertyInitializer.Spread -> replace(it.expression)
+ }
+ }
is JsFunction -> if (expression.name == null) listOf() else listOf(expression)
diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/inline/clean/TemporaryVariableElimination.kt b/js/js.translator/src/org/jetbrains/kotlin/js/inline/clean/TemporaryVariableElimination.kt
index 6a70d4b..3d4db27 100644
--- a/js/js.translator/src/org/jetbrains/kotlin/js/inline/clean/TemporaryVariableElimination.kt
+++ b/js/js.translator/src/org/jetbrains/kotlin/js/inline/clean/TemporaryVariableElimination.kt
@@ -185,10 +185,12 @@
override fun visitContinue(x: JsContinue) { }
- override fun visitObjectLiteral(x: JsObjectLiteral) {
- for (initializer in x.propertyInitializers) {
- accept(initializer.valueExpr)
- }
+ override fun visitKeyValuePropertyInitializer(x: JsPropertyInitializer.KeyValue) {
+ accept(x.valueExpr)
+ }
+
+ override fun visitSpreadPropertyInitializer(x: JsPropertyInitializer.Spread) {
+ accept(x.expression)
}
override fun visitLoop(x: JsLoop) = withNewScope { super.visitLoop(x) }
@@ -303,10 +305,12 @@
x.cases.forEach { accept(it); invalidateTemporaries() }
}
- override fun visitObjectLiteral(x: JsObjectLiteral) {
- for (initializer in x.propertyInitializers) {
- accept(initializer.valueExpr)
- }
+ override fun visitKeyValuePropertyInitializer(x: JsPropertyInitializer.KeyValue) {
+ accept(x.valueExpr)
+ }
+
+ override fun visitSpreadPropertyInitializer(x: JsPropertyInitializer.Spread) {
+ accept(x.expression)
}
override fun visitWhile(x: JsWhile) {
@@ -425,12 +429,18 @@
}
override fun visitObjectLiteral(x: JsObjectLiteral) {
- for (initializer in x.propertyInitializers) {
- accept(initializer.valueExpr)
- }
+ super.visitObjectLiteral(x)
sideEffectOccurred = true
}
+ override fun visitKeyValuePropertyInitializer(x: JsPropertyInitializer.KeyValue) {
+ accept(x.valueExpr)
+ }
+
+ override fun visitSpreadPropertyInitializer(x: JsPropertyInitializer.Spread) {
+ accept(x.expression)
+ }
+
override fun visitNew(x: JsNew) {
super.visitNew(x)
if (x.sideEffects == SideEffectKind.AFFECTS_STATE) {
@@ -574,11 +584,21 @@
override fun visit(x: JsObjectLiteral, ctx: JsContext<*>): Boolean {
for (initializer in x.propertyInitializers) {
- accept(initializer.valueExpr)
+ accept(initializer)
}
return super.visit(x, ctx)
}
+ override fun visit(x: JsPropertyInitializer.KeyValue, ctx: JsContext<*>): Boolean {
+ accept(x.valueExpr)
+ return super.visit(x, ctx)
+ }
+
+ override fun visit(x: JsPropertyInitializer.Spread, ctx: JsContext<*>): Boolean {
+ accept(x.expression)
+ return super.visit(x, ctx)
+ }
+
override fun visit(x: JsNameRef, ctx: JsContext<JsNode>): Boolean {
val name = x.name
if (name != null && x.qualifier == null && name in namesToSubstitute) {
diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/inline/clean/VoidPropertiesElimination.kt b/js/js.translator/src/org/jetbrains/kotlin/js/inline/clean/VoidPropertiesElimination.kt
index c6013d2..36883a8 100644
--- a/js/js.translator/src/org/jetbrains/kotlin/js/inline/clean/VoidPropertiesElimination.kt
+++ b/js/js.translator/src/org/jetbrains/kotlin/js/inline/clean/VoidPropertiesElimination.kt
@@ -24,7 +24,7 @@
fun apply(): Boolean {
val visitor = object : JsVisitorWithContextImpl() {
- override fun endVisit(x: JsPropertyInitializer, ctx: JsContext<JsNode>) {
+ override fun endVisit(x: JsPropertyInitializer.KeyValue, ctx: JsContext<JsNode>) {
super.endVisit(x, ctx)
if ((x.valueExpr as? JsNameRef)?.name === voidName) {
ctx.removeMe()
diff --git a/js/js.translator/testData/box/jsCode/objectExpression.kt b/js/js.translator/testData/box/jsCode/objectExpression.kt
index 9a4fb6a..6a8a469 100644
--- a/js/js.translator/testData/box/jsCode/objectExpression.kt
+++ b/js/js.translator/testData/box/jsCode/objectExpression.kt
@@ -33,5 +33,12 @@
if (d.b != b) return "fail: d.b == ${d.b}"
if (d.c != c) return "fail: d.c == ${d.c}"
+ val e = js("{ ...d }")
+ if (!isOrdinaryObject(e)) return "fail: e is not an object"
+ if (Object.keys(e).size != 3) return "fail: e should have three properties"
+ if (e.a != a) return "fail: e.a == ${e.a}"
+ if (e.b != b) return "fail: e.b == ${e.b}"
+ if (e.c != c) return "fail: e.c == ${e.c}"
+
return "OK"
}
\ No newline at end of file