[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