[ESnext] Implement Object Spread
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 16 Mar 2017 12:58:14 +0000 (12:58 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 16 Mar 2017 12:58:14 +0000 (12:58 +0000)
https://bugs.webkit.org/show_bug.cgi?id=167963

Patch by Caio Lima <ticaiolima@gmail.com> on 2017-03-16
Reviewed by Yusuke Suzuki.

JSTests:

* stress/object-spread.js: Added.
(let.assert):
(assert.sameValue):
(let.o.get a):
(let.obj.get c):
(cthulhu.get x):
(let.obj.set c):
(calls.o.get z):
(calls.o.get a):
(try.let.obj.get foo):
(get calls):

Source/JavaScriptCore:

This patch implements ECMA262 stage 3 Object Spread proposal [1].
It's implemented using CopyDataProperties to copy all enumerable keys
from object being spreaded.

It's also fixing CopyDataProperties that was using
Object.getOwnPropertyNames to list all keys to be copied, and now is
using Relect.ownKeys.

[1] - https://github.com/sebmarkbage/ecmascript-rest-spread

* builtins/GlobalOperations.js:
(globalPrivate.copyDataProperties):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::setConstantIdentifierSetRegisters):
* bytecode/UnlinkedCodeBlock.h:
(JSC::UnlinkedCodeBlock::addSetConstant):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitLoad):
* bytecompiler/BytecodeGenerator.h:
* bytecompiler/NodesCodegen.cpp:
(JSC::PropertyListNode::emitBytecode):
(JSC::ObjectPatternNode::bindValue):
(JSC::ObjectSpreadExpressionNode::emitBytecode):
* parser/ASTBuilder.h:
(JSC::ASTBuilder::createObjectSpreadExpression):
(JSC::ASTBuilder::createProperty):
* parser/NodeConstructors.h:
(JSC::PropertyNode::PropertyNode):
(JSC::ObjectSpreadExpressionNode::ObjectSpreadExpressionNode):
* parser/Nodes.h:
(JSC::ObjectSpreadExpressionNode::expression):
* parser/Parser.cpp:
(JSC::Parser<LexerType>::parseProperty):
* parser/SyntaxChecker.h:
(JSC::SyntaxChecker::createObjectSpreadExpression):
(JSC::SyntaxChecker::createProperty):
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
* runtime/JSGlobalObjectFunctions.cpp:
(JSC::privateToObject): Deleted.
* runtime/JSGlobalObjectFunctions.h:

LayoutTests:

* js/parser-syntax-check-expected.txt:
* js/script-tests/parser-syntax-check.js:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@214038 268f45cc-cd09-0410-ab3c-d52691b4dbfc

20 files changed:
JSTests/ChangeLog
JSTests/stress/object-spread.js [new file with mode: 0644]
LayoutTests/ChangeLog
LayoutTests/js/parser-syntax-check-expected.txt
LayoutTests/js/script-tests/parser-syntax-check.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/builtins/GlobalOperations.js
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
Source/JavaScriptCore/parser/ASTBuilder.h
Source/JavaScriptCore/parser/NodeConstructors.h
Source/JavaScriptCore/parser/Nodes.h
Source/JavaScriptCore/parser/Parser.cpp
Source/JavaScriptCore/parser/SyntaxChecker.h
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp
Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.h

index fafbd85..712b4dc 100644 (file)
@@ -1,3 +1,22 @@
+2017-03-16  Caio Lima  <ticaiolima@gmail.com>
+
+        [ESnext] Implement Object Spread
+        https://bugs.webkit.org/show_bug.cgi?id=167963
+
+        Reviewed by Yusuke Suzuki.
+
+        * stress/object-spread.js: Added.
+        (let.assert):
+        (assert.sameValue):
+        (let.o.get a):
+        (let.obj.get c):
+        (cthulhu.get x):
+        (let.obj.set c):
+        (calls.o.get z):
+        (calls.o.get a):
+        (try.let.obj.get foo):
+        (get calls):
+
 2017-03-15  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [JSC] Default parameter part should be retrieved by op_get_argument opcode instead of changing arity
diff --git a/JSTests/stress/object-spread.js b/JSTests/stress/object-spread.js
new file mode 100644 (file)
index 0000000..42af0b9
--- /dev/null
@@ -0,0 +1,297 @@
+let assert = (a) => {
+    if (!a)
+        throw new Error("Bad Assertion");
+}
+
+assert.sameValue = (a, b) =>  {
+    assert(a === b);
+}
+
+function validatePropertyDescriptor(o, p) {
+    let desc = Object.getOwnPropertyDescriptor(o, p);
+
+    assert(desc.enumerable);
+    assert(desc.configurable);
+    assert(desc.writable);
+}
+
+// Base cases
+
+(() => {
+    let obj = {a: 1, b: 2, ...{c: 3, d: 4}};
+
+    assert.sameValue(obj.a, 1);
+    assert(obj.b, 2);
+    assert(obj.c, 3);
+    assert(obj.d, 4);
+    validatePropertyDescriptor(obj, "c");
+    validatePropertyDescriptor(obj, "d");
+    assert(Object.keys(obj), 2);
+})();
+
+(() => {
+    let o = {c: 3, d: 4};
+    let obj = {a: 1, b: 2, ...o};
+
+    assert.sameValue(obj.a, 1);
+    assert.sameValue(obj.b, 2);
+    assert.sameValue(obj.c, 3);
+    assert.sameValue(obj.d, 4);
+    assert.sameValue(Object.keys(obj).length, 4);
+
+    validatePropertyDescriptor(obj, "a");
+    validatePropertyDescriptor(obj, "b");
+    validatePropertyDescriptor(obj, "c");
+    validatePropertyDescriptor(obj, "d");
+})();
+
+(() => {
+    let o = {a: 2, b: 3};
+    let o2 = {c: 4, d: 5};
+
+    let obj = {...o, ...o2};
+
+    assert.sameValue(obj.a, 2);
+    assert.sameValue(obj.b, 3);
+    assert.sameValue(obj.c, 4);
+    assert.sameValue(obj.d, 5);
+    assert.sameValue(Object.keys(obj).length, 4);
+})();
+
+// Empty case
+
+(() => {
+    let obj = {a: 1, b: 2, ...{}};
+
+    assert.sameValue(obj.a, 1);
+    assert.sameValue(obj.b, 2);
+    assert.sameValue(Object.keys(obj).length, 2);
+})();
+
+// Ignoring cases
+
+(() => {
+    let obj = {a: 1, ...null, b: 2, ...undefined, c: 3, ...{}, ...{...{}}, d: 4};
+
+    assert.sameValue(obj.a, 1);
+    assert.sameValue(obj.b, 2);
+    assert.sameValue(obj.c, 3);
+    assert.sameValue(obj.d, 4);
+
+    let keys = Object.keys(obj);
+    assert.sameValue(keys[0], "a");
+    assert.sameValue(keys[1], "b");
+    assert.sameValue(keys[2], "c");
+    assert.sameValue(keys[3], "d");
+})();
+
+// Null case
+
+(() => {
+    let obj = {a: 1, b: 2, ...null};
+
+    assert.sameValue(obj.a, 1);
+    assert.sameValue(obj.b, 2);
+    assert.sameValue(Object.keys(obj).length, 2);
+})();
+
+(() => {
+    let obj = {...null};
+
+    assert.sameValue(Object.keys(obj).length, 0);
+})();
+
+// Undefined case
+
+(() => {
+    let obj = {a: 1, b: 2, ...undefined};
+
+    assert.sameValue(obj.a, 1);
+    assert.sameValue(obj.b, 2);
+    assert.sameValue(Object.keys(obj).length, 2);
+})();
+
+(() => {
+    let obj = {...undefined};
+
+    assert.sameValue(Object.keys(obj).length, 0);
+})();
+
+// Getter case
+
+(() => {
+    let o = {
+        get a() {
+            return 42;
+        }
+    };
+
+    let obj = {...o, c: 4, d: 5};
+
+    assert.sameValue(Object.getOwnPropertyDescriptor(obj, "a").value, 42);
+    assert.sameValue(obj.c, 4);
+    assert.sameValue(obj.d, 5);
+    assert.sameValue(Object.keys(obj).length, 3);
+
+    validatePropertyDescriptor(obj, "a");
+})();
+
+(() => {
+    let o = {a: 2, b: 3}
+    let executedGetter = false;
+
+    let obj = {...o, get c() { executedGetter = true; }};
+
+    assert.sameValue(obj.a, 2);
+    assert.sameValue(obj.b, 3);
+    assert.sameValue(executedGetter, false)
+    assert.sameValue(Object.keys(obj).length, 3);
+})();
+
+(() => {
+    let getterCallCount = 0;
+    let o = {
+        get a() {
+            return ++getterCallCount;
+        }
+    };
+
+    let obj = {...o, c: 4, d: 5, a: 42, ...o};
+
+    assert.sameValue(obj.a, 2);
+    assert.sameValue(obj.c, 4);
+    assert.sameValue(obj.d, 5);
+    assert.sameValue(Object.keys(obj).length, 3);
+})();
+
+// Manipulate Object case
+
+(() => {
+    var o = { a: 0, b: 1 };
+    var cthulhu = { get x() {
+      delete o.a;
+      o.b = 42;
+      o.c = "ni";
+    }};
+
+    let obj = {...cthulhu, ...o};
+
+    assert.sameValue(obj.hasOwnProperty("a"), false);
+    assert.sameValue(obj.b, 42);
+    assert.sameValue(obj.c, "ni");
+    assert(obj.hasOwnProperty("x"));
+    assert.sameValue(Object.keys(obj).length, 3);
+})();
+
+// Override
+
+(() => {
+    let o = {a: 2, b: 3};
+
+    let obj = {a: 1, b: 7, ...o};
+
+    assert.sameValue(obj.a, 2);
+    assert.sameValue(obj.b, 3);
+    assert.sameValue(Object.keys(obj).length, 2);
+    assert.sameValue(o.a, 2);
+    assert.sameValue(o.b, 3);
+})();
+
+(() => {
+    let o = {a: 2, b: 3, c: 4, e: undefined, f: null, g: false};
+
+    let obj = {...o, a: 1, b: 7, d: 5, h: -0, i: Symbol("foo"), j: o};
+
+    assert.sameValue(obj.a, 1);
+    assert.sameValue(obj.b, 7);
+    assert.sameValue(obj.c, 4);
+    assert.sameValue(obj.d, 5);
+    assert(obj.hasOwnProperty("e"));
+    assert.sameValue(obj.f, null);
+    assert.sameValue(obj.g, false);
+    assert.sameValue(obj.h, -0);
+    assert.sameValue(obj.i.toString(), "Symbol(foo)");
+    assert(Object.is(obj.j, o));
+    assert.sameValue(Object.keys(obj).length, 10);
+})();
+
+// Override Immutable
+
+(() => {
+    let o = {b: 2};
+    Object.defineProperty(o, "a", {value: 1, enumerable: true, writable: false, configurable: true});
+
+    let obj = {...o, a: 3};
+
+    assert.sameValue(obj.a, 3)
+    assert.sameValue(obj.b, 2);
+    validatePropertyDescriptor(obj, "a");
+    validatePropertyDescriptor(obj, "b");
+})();
+
+// Setter
+
+(() => {
+    let executedSetter = false;
+
+    let obj = {set c(v) { executedSetter = true; }, ...{c: 1}};
+
+    assert.sameValue(obj.c, 1);
+    assert.sameValue(executedSetter, false);
+    assert.sameValue(Object.keys(obj).length, 1);
+})();
+
+// Skip non-enumerble
+
+(() => {
+    let o = {};
+    Object.defineProperty(o, "b", {value: 3, enumerable: false});
+
+    let obj = {...o};
+
+    assert.sameValue(obj.hasOwnProperty("b"), false)
+    assert.sameValue(Object.keys(obj).length, 0);
+})();
+
+// Spread order
+
+(() => {
+    var calls = []
+    var o = { get z() { calls.push('z') }, get a() { calls.push('a') } };
+    Object.defineProperty(o, 1, { get: () => { calls.push(1) }, enumerable: true });
+    Object.defineProperty(o, Symbol('foo'), { get: () => { calls.push("Symbol(foo)") }, enumerable: true });
+
+    let obj = {...o};
+
+    assert.sameValue(calls[0], 1);
+    assert.sameValue(calls[1], "z");
+    assert.sameValue(calls[2], "a");
+    assert.sameValue(calls[3], "Symbol(foo)");
+    assert.sameValue(Object.keys(obj).length, 3);
+})();
+
+// Symbol property
+(() => {
+    let symbol = Symbol('foo');
+    let o = {};
+    o[symbol] = 1;
+
+    let obj = {...o, c: 4, d: 5};
+
+    assert.sameValue(obj[symbol], 1);
+    assert.sameValue(obj.c, 4);
+    assert.sameValue(obj.d, 5);
+    assert.sameValue(Object.keys(obj).length, 2);
+})();
+
+// Getter throw
+
+(() => {
+    try {
+        let obj = {...{ get foo() { throw new Error("Getter Exception"); } }};
+        assert(false);
+    } catch(e) {
+        assert.sameValue(e.message, "Getter Exception");
+    }
+})();
+
index af5b22f..e9abb0c 100644 (file)
@@ -1,3 +1,13 @@
+2017-03-16  Caio Lima  <ticaiolima@gmail.com>
+
+        [ESnext] Implement Object Spread
+        https://bugs.webkit.org/show_bug.cgi?id=167963
+
+        Reviewed by Yusuke Suzuki.
+
+        * js/parser-syntax-check-expected.txt:
+        * js/script-tests/parser-syntax-check.js:
+
 2017-03-16  Carlos Alberto Lopez Perez  <clopez@igalia.com>
 
         [WebRTC] SDP sess-id in the "o=" line should be a value between 0 and LLONG_MAX.
index 4a9b5d3..1a60d80 100644 (file)
@@ -1185,6 +1185,20 @@ PASS Valid:   "var a, b, c; ({a, b, ...r} = {a: 1, b: 2, c: 3, d: 4});"
 PASS Valid:   "function f() { var a, b, c; ({a, b, ...r} = {a: 1, b: 2, c: 3, d: 4}); }"
 PASS Valid:   "function * foo(o) { ({...{ x = yield }} = o); }"
 PASS Valid:   "function f() { function * foo(o) { ({...{ x = yield }} = o); } }"
+PASS Valid:   "let c = {}; let o = {a: 1, b: 2, ...c};"
+PASS Valid:   "function f() { let c = {}; let o = {a: 1, b: 2, ...c}; }"
+PASS Valid:   "let o = {a: 1, b: 3, ...{}};"
+PASS Valid:   "function f() { let o = {a: 1, b: 3, ...{}}; }"
+PASS Valid:   "let o = {a: 1, b: 2, ...null, c: 3};"
+PASS Valid:   "function f() { let o = {a: 1, b: 2, ...null, c: 3}; }"
+PASS Valid:   "let o = {a: 1, b: 2, ...undefined, c: 3};"
+PASS Valid:   "function f() { let o = {a: 1, b: 2, ...undefined, c: 3}; }"
+PASS Valid:   "let o = {a: 1, b: 2, ...{...{}}, c: 3};"
+PASS Valid:   "function f() { let o = {a: 1, b: 2, ...{...{}}, c: 3}; }"
+PASS Valid:   "let c = {}; let o = {a: 1, b: 2, ...c, d: 3, ...c, e: 5};"
+PASS Valid:   "function f() { let c = {}; let o = {a: 1, b: 2, ...c, d: 3, ...c, e: 5}; }"
+PASS Valid:   "function* gen() { yield {a: 1, b: 2, ...yield yield, d: 3, ...yield, e: 5}; }"
+PASS Valid:   "function f() { function* gen() { yield {a: 1, b: 2, ...yield yield, d: 3, ...yield, e: 5}; } }"
 PASS Invalid: "var {...r = {a: 2}} = {a: 1, b: 2};". Produced the following syntax error: "SyntaxError: Unexpected token '='. Expected a closing '}' following a rest element destructuring pattern."
 PASS Invalid: "function f() { var {...r = {a: 2}} = {a: 1, b: 2}; }". Produced the following syntax error: "SyntaxError: Unexpected token '='. Expected a closing '}' following a rest element destructuring pattern."
 PASS Invalid: "var {...r, b} = {a: 1, b: 2};". Produced the following syntax error: "SyntaxError: Unexpected token ','. Expected a closing '}' following a rest element destructuring pattern."
index a4193a9..bb98bb8 100644 (file)
@@ -700,6 +700,13 @@ valid("(({a, b, ...r}) => {})({a: 1, b: 2, c: 3, d: 4});");
 valid("(function ({a, b, ...r}) {})({a: 1, b: 2, c: 3, d: 4});");
 valid("var a, b, c; ({a, b, ...r} = {a: 1, b: 2, c: 3, d: 4});");
 valid("function * foo(o) { ({...{ x = yield }} = o); }");
+valid("let c = {}; let o = {a: 1, b: 2, ...c};");
+valid("let o = {a: 1, b: 3, ...{}};");
+valid("let o = {a: 1, b: 2, ...null, c: 3};");
+valid("let o = {a: 1, b: 2, ...undefined, c: 3};");
+valid("let o = {a: 1, b: 2, ...{...{}}, c: 3};");
+valid("let c = {}; let o = {a: 1, b: 2, ...c, d: 3, ...c, e: 5};");
+valid("function* gen() { yield {a: 1, b: 2, ...yield yield, d: 3, ...yield, e: 5}; }");
 invalid("var {...r = {a: 2}} = {a: 1, b: 2};");
 invalid("var {...r, b} = {a: 1, b: 2};");
 invalid("var {...r, ...e} = {a: 1, b: 2};");
index c9c677a..c79aea9 100644 (file)
@@ -1,3 +1,52 @@
+2017-03-16  Caio Lima  <ticaiolima@gmail.com>
+
+        [ESnext] Implement Object Spread
+        https://bugs.webkit.org/show_bug.cgi?id=167963
+
+        Reviewed by Yusuke Suzuki.
+
+        This patch implements ECMA262 stage 3 Object Spread proposal [1].
+        It's implemented using CopyDataProperties to copy all enumerable keys
+        from object being spreaded.
+
+        It's also fixing CopyDataProperties that was using
+        Object.getOwnPropertyNames to list all keys to be copied, and now is
+        using Relect.ownKeys.
+
+        [1] - https://github.com/sebmarkbage/ecmascript-rest-spread
+
+        * builtins/GlobalOperations.js:
+        (globalPrivate.copyDataProperties):
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::setConstantIdentifierSetRegisters):
+        * bytecode/UnlinkedCodeBlock.h:
+        (JSC::UnlinkedCodeBlock::addSetConstant):
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::emitLoad):
+        * bytecompiler/BytecodeGenerator.h:
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::PropertyListNode::emitBytecode):
+        (JSC::ObjectPatternNode::bindValue):
+        (JSC::ObjectSpreadExpressionNode::emitBytecode):
+        * parser/ASTBuilder.h:
+        (JSC::ASTBuilder::createObjectSpreadExpression):
+        (JSC::ASTBuilder::createProperty):
+        * parser/NodeConstructors.h:
+        (JSC::PropertyNode::PropertyNode):
+        (JSC::ObjectSpreadExpressionNode::ObjectSpreadExpressionNode):
+        * parser/Nodes.h:
+        (JSC::ObjectSpreadExpressionNode::expression):
+        * parser/Parser.cpp:
+        (JSC::Parser<LexerType>::parseProperty):
+        * parser/SyntaxChecker.h:
+        (JSC::SyntaxChecker::createObjectSpreadExpression):
+        (JSC::SyntaxChecker::createProperty):
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::init):
+        * runtime/JSGlobalObjectFunctions.cpp:
+        (JSC::privateToObject): Deleted.
+        * runtime/JSGlobalObjectFunctions.h:
+
 2017-03-15  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [JSC] Default parameter part should be retrieved by op_get_argument opcode instead of changing arity
index 2044068..e5cb3c2 100644 (file)
@@ -86,11 +86,11 @@ function copyDataProperties(target, source, excludedSet)
     if (!@isObject(target))
         @throwTypeError("target needs to be an object");
     
-    if (source === @undefined || source === null)
+    if (source == null)
         return target;
     
-    let from = @toObject(source);
-    let keys = @Object.@getOwnPropertyNames(from);
+    let from = @Object(source);
+    let keys = @Reflect.@ownKeys(from);
     let keysLength = keys.length;
     for (let i = 0; i < keysLength; i++) {
         let nextKey = keys[i];
index 5283399..5b9e396 100644 (file)
@@ -875,9 +875,9 @@ bool CodeBlock::setConstantIdentifierSetRegisters(VM& vm, const Vector<ConstantI
         JSSet* jsSet = JSSet::create(exec, vm, setStructure);
         RETURN_IF_EXCEPTION(scope, false);
 
-        const HashSet<UniquedStringImpl*>& set = entry.first;
-        for (auto setEntry : set) {
-            JSString* jsString = jsOwnedString(&vm, setEntry);
+        const IdentifierSet& set = entry.first;
+        for (auto& setEntry : set) {
+            JSString* jsString = jsOwnedString(&vm, setEntry.get());
             jsSet->add(exec, JSValue(jsString));
             RETURN_IF_EXCEPTION(scope, false);
         }
index 669cc64..a2fb118 100644 (file)
@@ -68,7 +68,7 @@ typedef unsigned UnlinkedArrayProfile;
 typedef unsigned UnlinkedArrayAllocationProfile;
 typedef unsigned UnlinkedObjectAllocationProfile;
 typedef unsigned UnlinkedLLIntCallLinkInfo;
-typedef std::pair<HashSet<UniquedStringImpl*>, unsigned> ConstantIndentifierSetEntry;
+typedef std::pair<IdentifierSet, unsigned> ConstantIndentifierSetEntry;
 
 struct UnlinkedStringJumpTable {
     struct OffsetLocation {
@@ -187,7 +187,7 @@ public:
         return m_bitVectors.size() - 1;
     }
     
-    void addSetConstant(HashSet<UniquedStringImpl*>& set)
+    void addSetConstant(IdentifierSet& set)
     {
         VM& vm = *this->vm();
         auto locker = lockDuringMarking(vm.heap, *this);
index b056116..e2f1c98 100644 (file)
@@ -1934,9 +1934,9 @@ RegisterID* BytecodeGenerator::emitLoad(RegisterID* dst, JSValue v, SourceCodeRe
     return constantID;
 }
 
-RegisterID* BytecodeGenerator::emitLoad(RegisterID* dst, HashSet<UniquedStringImpl*>& set)
+RegisterID* BytecodeGenerator::emitLoad(RegisterID* dst, IdentifierSet& set)
 {
-    for (ConstantIndentifierSetEntry entry : m_codeBlock->constantIdentifierSets()) {
+    for (const auto& entry : m_codeBlock->constantIdentifierSets()) {
         if (entry.first != set)
             continue;
         
index 78dcb4a..0329213 100644 (file)
@@ -587,7 +587,7 @@ namespace JSC {
         RegisterID* emitLoad(RegisterID* dst, bool);
         RegisterID* emitLoad(RegisterID* dst, const Identifier&);
         RegisterID* emitLoad(RegisterID* dst, JSValue, SourceCodeRepresentation = SourceCodeRepresentation::Other);
-        RegisterID* emitLoad(RegisterID* dst, HashSet<UniquedStringImpl*>& excludedList);
+        RegisterID* emitLoad(RegisterID* dst, IdentifierSet& excludedList);
         RegisterID* emitLoadGlobalObject(RegisterID* dst);
 
         RegisterID* emitUnaryOp(OpcodeID, RegisterID* dst, RegisterID* src);
index bab452b..dd8e575 100644 (file)
@@ -516,7 +516,7 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe
                 hasComputedProperty = true;
                 break;
             }
-            if (node->m_type & PropertyNode::Constant)
+            if (node->m_type & PropertyNode::Constant || node->m_type & PropertyNode::Spread)
                 continue;
 
             // Duplicates are possible.
@@ -538,6 +538,9 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe
             if (node->m_type & PropertyNode::Constant) {
                 emitPutConstantProperty(generator, dst, *node);
                 continue;
+            } else if (node->m_type & PropertyNode::Spread) {
+                generator.emitNode(dst, node->m_assign);
+                continue;
             }
 
             RefPtr<RegisterID> value = generator.emitNode(node->m_assign);
@@ -4030,7 +4033,7 @@ void ObjectPatternNode::bindValue(BytecodeGenerator& generator, RegisterID* rhs)
 {
     generator.emitRequireObjectCoercible(rhs, ASCIILiteral("Right side of assignment cannot be destructured"));
     
-    HashSet<UniquedStringImpl*> excludedSet;
+    IdentifierSet excludedSet;
 
     for (const auto& target : m_targetPatterns) {
         if (target.bindingType == BindingType::Element) {
@@ -4228,4 +4231,31 @@ RegisterID* SpreadExpressionNode::emitBytecode(BytecodeGenerator&, RegisterID*)
     return 0;
 }
 
+RegisterID* ObjectSpreadExpressionNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
+{
+    RefPtr<RegisterID> src = generator.newTemporary();
+    generator.emitNode(src.get(), m_expression);
+    IdentifierSet excludedSet;
+    
+    RefPtr<RegisterID> excludedSetReg = generator.emitLoad(generator.newTemporary(), excludedSet);
+        
+    // load and call @copyDataProperties
+    auto var = generator.variable(generator.propertyNames().builtinNames().copyDataPropertiesPrivateName());
+    
+    RefPtr<RegisterID> scope = generator.newTemporary();
+    generator.moveToDestinationIfNeeded(scope.get(), generator.emitResolveScope(scope.get(), var));
+    RefPtr<RegisterID> copyDataProperties = generator.emitGetFromScope(generator.newTemporary(), scope.get(), var, ThrowIfNotFound);
+    
+    CallArguments args(generator, nullptr, 3);
+    unsigned argumentCount = 0;
+    generator.emitLoad(args.thisRegister(), jsUndefined());
+    generator.emitMove(args.argumentRegister(argumentCount++), dst);
+    generator.emitMove(args.argumentRegister(argumentCount++), src.get());
+    generator.emitMove(args.argumentRegister(argumentCount++), excludedSetReg.get());
+    
+    generator.emitCall(generator.newTemporary(), copyDataProperties.get(), NoExpectedFunction, args, divot(), divotStart(), divotEnd(), DebuggableCall::No);
+    
+    return 0;
+}
+
 } // namespace JSC
index 68246a8..457ad08 100644 (file)
@@ -275,6 +275,13 @@ public:
         return node;
     }
 
+    ExpressionNode* createObjectSpreadExpression(const JSTokenLocation& location, ExpressionNode* expression, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end)
+    {
+        auto node = new (m_parserArena) ObjectSpreadExpressionNode(location, expression);
+        setExceptionLocation(node, start, divot, end);
+        return node;
+    }
+
     TemplateStringNode* createTemplateString(const JSTokenLocation& location, const Identifier* cooked, const Identifier* raw)
     {
         return new (m_parserArena) TemplateStringNode(location, cooked, raw);
@@ -493,6 +500,10 @@ public:
             static_cast<ClassExprNode*>(node)->setEcmaName(*propertyName);
         return new (m_parserArena) PropertyNode(*propertyName, node, type, putType, superBinding, isClassProperty);
     }
+    PropertyNode* createProperty(ExpressionNode* node, PropertyNode::Type type, PropertyNode::PutType putType, bool, SuperBinding superBinding, bool isClassProperty)
+    {
+        return new (m_parserArena) PropertyNode(node, type, putType, superBinding, isClassProperty);
+    }
     PropertyNode* createProperty(VM* vm, ParserArena& parserArena, double propertyName, ExpressionNode* node, PropertyNode::Type type, PropertyNode::PutType putType, bool, SuperBinding superBinding, bool isClassProperty)
     {
         return new (m_parserArena) PropertyNode(parserArena.identifierArena().makeNumericIdentifier(vm, propertyName), node, type, putType, superBinding, isClassProperty);
index da71e8a..7ab63c4 100644 (file)
@@ -234,9 +234,19 @@ namespace JSC {
         , m_isClassProperty(isClassProperty)
     {
     }
+    
+    inline PropertyNode::PropertyNode(ExpressionNode* assign, Type type, PutType putType, SuperBinding superBinding, bool isClassProperty)
+        : m_name(nullptr)
+        , m_assign(assign)
+        , m_type(type)
+        , m_needsSuperBinding(superBinding == SuperBinding::Needed)
+        , m_putType(putType)
+        , m_isClassProperty(isClassProperty)
+    {
+    }
 
     inline PropertyNode::PropertyNode(ExpressionNode* name, ExpressionNode* assign, Type type, PutType putType, SuperBinding superBinding, bool isClassProperty)
-        : m_name(0)
+        : m_name(nullptr)
         , m_expression(name)
         , m_assign(assign)
         , m_type(type)
@@ -294,6 +304,12 @@ namespace JSC {
         , m_expression(expression)
     {
     }
+    
+    inline ObjectSpreadExpressionNode::ObjectSpreadExpressionNode(const JSTokenLocation& location, ExpressionNode* expression)
+        : ExpressionNode(location)
+        , m_expression(expression)
+    {
+    }
 
     inline ArgumentListNode::ArgumentListNode(const JSTokenLocation& location, ExpressionNode* expr)
         : ExpressionNode(location)
index 792e43c..f5288e4 100644 (file)
@@ -646,10 +646,11 @@ namespace JSC {
 
     class PropertyNode : public ParserArenaFreeable {
     public:
-        enum Type { Constant = 1, Getter = 2, Setter = 4, Computed = 8, Shorthand = 16 };
+        enum Type { Constant = 1, Getter = 2, Setter = 4, Computed = 8, Shorthand = 16, Spread = 32 };
         enum PutType { Unknown, KnownDirect };
 
         PropertyNode(const Identifier&, ExpressionNode*, Type, PutType, SuperBinding, bool isClassProperty);
+        PropertyNode(ExpressionNode*, Type, PutType, SuperBinding, bool isClassProperty);
         PropertyNode(ExpressionNode* propertyName, ExpressionNode*, Type, PutType, SuperBinding, bool isClassProperty);
 
         ExpressionNode* expressionName() const { return m_expression; }
@@ -665,7 +666,7 @@ namespace JSC {
         const Identifier* m_name;
         ExpressionNode* m_expression;
         ExpressionNode* m_assign;
-        unsigned m_type : 5;
+        unsigned m_type : 6;
         unsigned m_needsSuperBinding : 1;
         unsigned m_putType : 1;
         unsigned m_isClassProperty: 1;
@@ -747,6 +748,18 @@ namespace JSC {
         bool isSpreadExpression() const override { return true; }
         ExpressionNode* m_expression;
     };
+    
+    class ObjectSpreadExpressionNode : public ExpressionNode, public ThrowableExpressionData {
+    public:
+        ObjectSpreadExpressionNode(const JSTokenLocation&, ExpressionNode*);
+        
+        ExpressionNode* expression() const { return m_expression; }
+        
+    private:
+        RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
+        
+        ExpressionNode* m_expression;
+    };
 
     class ArgumentListNode : public ExpressionNode {
     public:
index caf4f4b..7ac24bb 100644 (file)
@@ -3779,8 +3779,14 @@ namedProperty:
         return context.createProperty(propertyName, node, static_cast<PropertyNode::Type>(PropertyNode::Constant | PropertyNode::Computed), PropertyNode::Unknown, complete, SuperBinding::NotNeeded, isClassProperty);
     }
     case DOTDOTDOT: {
-        classifyExpressionError(ErrorIndicatesPattern);
-        FALLTHROUGH;
+        auto spreadLocation = m_token.m_location;
+        auto start = m_token.m_startPosition;
+        auto divot = m_token.m_endPosition;
+        next();
+        TreeExpression elem = parseAssignmentExpressionOrPropagateErrorClass(context);
+        failIfFalse(elem, "Cannot parse subject of a spread operation");
+        auto node = context.createObjectSpreadExpression(spreadLocation, elem, start, divot, m_lastTokenEndPosition);
+        return context.createProperty(node, PropertyNode::Spread, PropertyNode::Unknown, complete, SuperBinding::NotNeeded, isClassProperty);
     }
     default:
         failIfFalse(m_token.m_type & KeywordTokenFlag, "Expected a property name");
index e44a1df..a1bb293 100644 (file)
@@ -76,7 +76,7 @@ public:
         ConditionalExpr, AssignmentExpr, TypeofExpr, NewTargetExpr,
         DeleteExpr, ArrayLiteralExpr, BindingDestructuring, RestParameter,
         ArrayDestructuring, ObjectDestructuring, SourceElementsResult,
-        FunctionBodyResult, SpreadExpr, ArgumentsResult,
+        FunctionBodyResult, SpreadExpr, ObjectSpreadExpr, ArgumentsResult,
         PropertyListResult, ArgumentsListResult, ElementsListResult,
         StatementResult, FormalParameterListResult, ClauseResult,
         ClauseListResult, CommaExpr, DestructuringAssignment,
@@ -194,6 +194,7 @@ public:
     int createArguments() { return ArgumentsResult; }
     int createArguments(int) { return ArgumentsResult; }
     ExpressionType createSpreadExpression(const JSTokenLocation&, ExpressionType, int, int, int) { return SpreadExpr; }
+    ExpressionType createObjectSpreadExpression(const JSTokenLocation&, ExpressionType, int, int, int) { return ObjectSpreadExpr; }
     TemplateString createTemplateString(const JSTokenLocation&, const Identifier*, const Identifier*) { return TemplateStringResult; }
     TemplateStringList createTemplateStringList(TemplateString) { return TemplateStringListResult; }
     TemplateStringList createTemplateStringList(TemplateStringList, TemplateString) { return TemplateStringListResult; }
@@ -212,6 +213,10 @@ public:
         ASSERT(name);
         return Property(name, type);
     }
+    Property createProperty(int, PropertyNode::Type type, PropertyNode::PutType, bool, SuperBinding, bool)
+    {
+        return Property(type);
+    }
     Property createProperty(VM* vm, ParserArena& parserArena, double name, int, PropertyNode::Type type, PropertyNode::PutType, bool complete, SuperBinding, bool)
     {
         if (!complete)
index eed92a8..8e6ef58 100644 (file)
@@ -735,7 +735,6 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
 
     JSFunction* privateFuncGetTemplateObject = JSFunction::create(vm, this, 0, String(), getTemplateObject);
     JSFunction* privateFuncImportModule = JSFunction::create(vm, this, 0, String(), globalFuncImportModule);
-    JSFunction* privateFuncToObject = JSFunction::create(vm, this, 0, String(), privateToObject);
     JSFunction* privateFuncTypedArrayLength = JSFunction::create(vm, this, 0, String(), typedArrayViewPrivateFuncLength);
     JSFunction* privateFuncTypedArrayGetOriginalConstructor = JSFunction::create(vm, this, 0, String(), typedArrayViewPrivateFuncGetOriginalConstructor);
     JSFunction* privateFuncTypedArraySort = JSFunction::create(vm, this, 0, String(), typedArrayViewPrivateFuncSort);
@@ -790,7 +789,6 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
         GlobalPropertyInfo(vm.propertyNames->builtinNames().ownEnumerablePropertyKeysPrivateName(), JSFunction::create(vm, this, 0, String(), ownEnumerablePropertyKeys), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().getTemplateObjectPrivateName(), privateFuncGetTemplateObject, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().importModulePrivateName(), privateFuncImportModule, DontEnum | DontDelete | ReadOnly),
-        GlobalPropertyInfo(vm.propertyNames->builtinNames().toObjectPrivateName(), privateFuncToObject, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().enqueueJobPrivateName(), JSFunction::create(vm, this, 0, String(), enqueueJob), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().ErrorPrivateName(), m_errorConstructor.get(), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().RangeErrorPrivateName(), m_rangeErrorConstructor.get(), DontEnum | DontDelete | ReadOnly),
index 3a47aea..f437204 100644 (file)
@@ -736,11 +736,6 @@ EncodedJSValue JSC_HOST_CALL globalFuncProtoSetter(ExecState* exec)
     return JSValue::encode(jsUndefined());
 }
 
-EncodedJSValue JSC_HOST_CALL privateToObject(ExecState* exec)
-{
-    return JSValue::encode(JSValue(exec->argument(0).toObject(exec)));
-}
-    
 EncodedJSValue JSC_HOST_CALL globalFuncBuiltinLog(ExecState* exec)
 {
     dataLog(exec->argument(0).toWTFString(exec), "\n");
index c9a6d53..d69b4d7 100644 (file)
@@ -50,7 +50,6 @@ EncodedJSValue JSC_HOST_CALL globalFuncThrowTypeErrorArgumentsCalleeAndCaller(Ex
 EncodedJSValue JSC_HOST_CALL globalFuncProtoGetter(ExecState*);
 EncodedJSValue JSC_HOST_CALL globalFuncProtoSetter(ExecState*);
 EncodedJSValue JSC_HOST_CALL globalFuncBuiltinLog(ExecState*);
-EncodedJSValue JSC_HOST_CALL privateToObject(ExecState*);
 EncodedJSValue JSC_HOST_CALL globalFuncImportModule(ExecState*);
 
 double jsToNumber(StringView);