[ESnext] Implement Object Rest - Implementing Object Rest Destructuring
https://bugs.webkit.org/show_bug.cgi?id=167962

Patch by Caio Lima <ticaiolima@gmail.com> on 2017-03-09
Reviewed by Keith Miller.

JSTests:

* stress/object-rest-deconstruct.js: Added.
(let.assert):
(let.assertPropDescriptor):
(catch):
(get 3):
(foo):
(let.src.get y):
(let.src.set y):
(let.gen):

Source/JavaScriptCore:

Object Rest/Spread Destructing proposal is in stage 3[1] and this
Patch is a prototype implementation of it. A simple change over the
parser was necessary to support the new '...' token on Object Pattern
destruction rule. In the bytecode generator side, We changed the
bytecode generated on ObjectPatternNode::bindValue to store in an
array identifiers of already destructed properties, following spec draft
section[2], and then pass it as excludedNames to CopyDataProperties.
The rest destruction the calls copyDataProperties to perform the
copy of rest properties in rhs.

We also implemented CopyDataProperties as private JS global operation
on builtins/GlobalOperations.js following it's specification on [3].
It is implemented using Set object to verify if a property is on
excludedNames to keep this algorithm with O(n + m) complexity, where n
= number of source's own properties and m = excludedNames.length.

As a requirement to use JSSets as constants, a change in
CodeBlock::create API was necessary, because JSSet creation can throws OOM
exception. Now, CodeBlock::finishCreation returns ```false``` if an
execption is throwed by
CodeBlock::setConstantIdentifierSetRegisters and then we return
nullptr to ScriptExecutable::newCodeBlockFor. It is responsible to
check if CodeBlock was constructed properly and then, throw OOM
exception to the correct scope.

[1] - https://github.com/sebmarkbage/ecmascript-rest-spread
[2] - http://sebmarkbage.github.io/ecmascript-rest-spread/#Rest-RuntimeSemantics-PropertyDestructuringAssignmentEvaluation
[3] - http://sebmarkbage.github.io/ecmascript-rest-spread/#AbstractOperations-CopyDataProperties

* builtins/BuiltinNames.h:
* builtins/GlobalOperations.js:
(globalPrivate.copyDataProperties):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::finishCreation):
(JSC::CodeBlock::setConstantIdentifierSetRegisters):
* bytecode/CodeBlock.h:
* bytecode/EvalCodeBlock.h:
(JSC::EvalCodeBlock::create):
* bytecode/FunctionCodeBlock.h:
(JSC::FunctionCodeBlock::create):
* bytecode/ModuleProgramCodeBlock.h:
(JSC::ModuleProgramCodeBlock::create):
* bytecode/ProgramCodeBlock.h:
(JSC::ProgramCodeBlock::create):
* bytecode/UnlinkedCodeBlock.h:
(JSC::UnlinkedCodeBlock::addSetConstant):
(JSC::UnlinkedCodeBlock::constantIdentifierSets):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitLoad):
* bytecompiler/BytecodeGenerator.h:
* bytecompiler/NodesCodegen.cpp:
(JSC::ObjectPatternNode::bindValue):
* parser/ASTBuilder.h:
(JSC::ASTBuilder::appendObjectPatternEntry):
(JSC::ASTBuilder::appendObjectPatternRestEntry):
(JSC::ASTBuilder::setContainsObjectRestElement):
* parser/Nodes.h:
(JSC::ObjectPatternNode::appendEntry):
(JSC::ObjectPatternNode::setContainsRestElement):
* parser/Parser.cpp:
(JSC::Parser<LexerType>::parseDestructuringPattern):
(JSC::Parser<LexerType>::parseProperty):
* parser/SyntaxChecker.h:
(JSC::SyntaxChecker::operatorStackPop):
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
* runtime/JSGlobalObjectFunctions.cpp:
(JSC::privateToObject):
* runtime/JSGlobalObjectFunctions.h:
* runtime/ScriptExecutable.cpp:
(JSC::ScriptExecutable::newCodeBlockFor):

Source/WTF:

* wtf/HashSet.h:
(WTF::=):

LayoutTests:

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

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@213697 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
index f8f08b8..9d31e13 100644
--- a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
+++ b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
@@ -41,8 +41,10 @@
 #include "Parser.h"
 #include "StackAlignment.h"
 #include <wtf/Assertions.h>
+#include <wtf/HashSet.h>
 #include <wtf/Threading.h>
 #include <wtf/text/StringBuilder.h>
+#include <wtf/text/UniquedStringImpl.h>
 
 using namespace WTF;
 
@@ -4027,25 +4029,56 @@
 void ObjectPatternNode::bindValue(BytecodeGenerator& generator, RegisterID* rhs) const
 {
     generator.emitRequireObjectCoercible(rhs, ASCIILiteral("Right side of assignment cannot be destructured"));
-    for (const auto& target : m_targetPatterns) {
-        RefPtr<RegisterID> temp = generator.newTemporary();
-        if (!target.propertyExpression) {
-            // Should not emit get_by_id for indexed ones.
-            std::optional<uint32_t> optionalIndex = parseIndex(target.propertyName);
-            if (!optionalIndex)
-                generator.emitGetById(temp.get(), rhs, target.propertyName);
-            else {
-                RefPtr<RegisterID> index = generator.emitLoad(nullptr, jsNumber(optionalIndex.value()));
-                generator.emitGetByVal(temp.get(), rhs, index.get());
-            }
-        } else {
-            RefPtr<RegisterID> propertyName = generator.emitNode(target.propertyExpression);
-            generator.emitGetByVal(temp.get(), rhs, propertyName.get());
-        }
+    
+    HashSet<UniquedStringImpl*> excludedSet;
 
-        if (target.defaultValue)
-            assignDefaultValueIfUndefined(generator, temp.get(), target.defaultValue);
-        target.pattern->bindValue(generator, temp.get());
+    for (const auto& target : m_targetPatterns) {
+        if (target.bindingType == BindingType::Element) {
+            RefPtr<RegisterID> temp = generator.newTemporary();
+            if (!target.propertyExpression) {
+                
+                // Should not emit get_by_id for indexed ones.
+                std::optional<uint32_t> optionalIndex = parseIndex(target.propertyName);
+                if (!optionalIndex)
+                    generator.emitGetById(temp.get(), rhs, target.propertyName);
+                else {
+                    RefPtr<RegisterID> pIndex = generator.emitLoad(nullptr, jsNumber(optionalIndex.value()));
+                    generator.emitGetByVal(temp.get(), rhs, pIndex.get());
+                }
+            } else {
+                RefPtr<RegisterID> propertyName = generator.emitNode(target.propertyExpression);
+                generator.emitGetByVal(temp.get(), rhs, propertyName.get());
+            }
+            
+            if (UNLIKELY(m_containsRestElement))
+                excludedSet.add(target.propertyName.impl());
+            
+            if (target.defaultValue)
+                assignDefaultValueIfUndefined(generator, temp.get(), target.defaultValue);
+            target.pattern->bindValue(generator, temp.get());
+        } else {
+            RefPtr<RegisterID> excludedSetReg = generator.emitLoad(generator.newTemporary(), excludedSet);
+        
+            RefPtr<RegisterID> newObject = generator.emitNewObject(generator.newTemporary());
+            
+            // 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++), newObject.get());
+            generator.emitMove(args.argumentRegister(argumentCount++), rhs);
+            generator.emitMove(args.argumentRegister(argumentCount++), excludedSetReg.get());
+
+            RefPtr<RegisterID> result = generator.newTemporary();
+            generator.emitCall(result.get(), copyDataProperties.get(), NoExpectedFunction, args, divot(), divotStart(), divotEnd(), DebuggableCall::No);
+            target.pattern->bindValue(generator, result.get());
+        }
     }
 }