[ES6] Add support for rest parameters
https://bugs.webkit.org/show_bug.cgi?id=38408
Reviewed by Geoffrey Garen.
Source/JavaScriptCore:
This patch implements rest parameters from the ES6 spec.
http://www.ecma-international.org/ecma-262/6.0/index.html#sec-function-definitions
We implement the rest parameter as a new AST node. This AST node
lowers to "op_new_array X, op_copy_rest X". Note
that the op_copy_rest opcode does not have a result.
The bulk of this patch is implementing op_copy_rest.
This patch implements this in all four tiers in a straight forward way.
The opcode is implemented as a C call that will read the pertinent
arguments from the call frame and fill them into the array.
* bytecode/BytecodeList.json:
* bytecode/BytecodeUseDef.h:
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
* bytecode/Instruction.h:
(JSC::Instruction::Instruction):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::generate):
(JSC::BytecodeGenerator::BytecodeGenerator):
(JSC::BytecodeGenerator::initializeDefaultParameterValuesAndSetupFunctionScopeStack):
(JSC::BytecodeGenerator::invalidateForInContextForLocal):
(JSC::BytecodeGenerator::emitRestParameter):
* bytecompiler/BytecodeGenerator.h:
* bytecompiler/NodesCodegen.cpp:
(JSC::AssignmentElementNode::toString):
(JSC::RestParameterNode::collectBoundIdentifiers):
(JSC::RestParameterNode::toString):
(JSC::RestParameterNode::bindValue):
(JSC::RestParameterNode::emit):
(JSC::SpreadExpressionNode::emitBytecode):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNode.h:
(JSC::DFG::Node::setEpoch):
(JSC::DFG::Node::numberOfArgumentsToSkip):
(JSC::DFG::Node::dumpChildren):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileCreateClonedArguments):
(JSC::DFG::SpeculativeJIT::compileCopyRest):
(JSC::DFG::SpeculativeJIT::compileNotifyWrite):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLIntrinsicRepository.h:
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::DFG::LowerDFGToLLVM::compileNode):
(JSC::FTL::DFG::LowerDFGToLLVM::compileCreateClonedArguments):
(JSC::FTL::DFG::LowerDFGToLLVM::compileCopyRest):
(JSC::FTL::DFG::LowerDFGToLLVM::compileNewObject):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
* jit/JIT.h:
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_create_out_of_band_arguments):
(JSC::JIT::emit_op_copy_rest):
* jit/JITOperations.h:
* llint/LowLevelInterpreter.asm:
* parser/ASTBuilder.h:
(JSC::ASTBuilder::createBindingLocation):
(JSC::ASTBuilder::createRestParameter):
(JSC::ASTBuilder::createAssignmentElement):
* parser/NodeConstructors.h:
(JSC::AssignmentElementNode::AssignmentElementNode):
(JSC::RestParameterNode::RestParameterNode):
(JSC::DestructuringAssignmentNode::DestructuringAssignmentNode):
* parser/Nodes.h:
(JSC::DestructuringPatternNode::isBindingNode):
(JSC::DestructuringPatternNode::isRestParameter):
(JSC::DestructuringPatternNode::emitDirectBinding):
(JSC::RestParameterNode::name):
* parser/Parser.cpp:
(JSC::Parser<LexerType>::parseVariableDeclarationList):
(JSC::Parser<LexerType>::declareRestOrNormalParameter):
(JSC::Parser<LexerType>::createBindingPattern):
(JSC::Parser<LexerType>::parseFormalParameters):
* parser/Parser.h:
(JSC::Parser::strictMode):
(JSC::Parser::isValidStrictMode):
(JSC::Parser::declareParameter):
(JSC::Parser::breakIsValid):
* parser/SyntaxChecker.h:
(JSC::SyntaxChecker::operatorStackPop):
* runtime/CommonSlowPaths.cpp:
(JSC::SLOW_PATH_DECL):
* runtime/CommonSlowPaths.h:
* tests/es6.yaml:
* tests/stress/rest-parameter-and-default-arguments.js: Added.
(assert):
(shouldThrowTDZ):
(foo):
(baz):
(i.shouldThrowTDZ):
* tests/stress/rest-parameter-basics.js: Added.
(assert):
(foo):
(bar):
(capture):
(baz):
(jaz):
(kaz):
(raz):
(restLength):
(testArgumentsObject):
(strictModeLikeArgumentsObject):
* tests/stress/rest-parameter-inlined.js: Added.
(assert):
(bar):
(foo):
(baz):
(jaz):
LayoutTests:
* js/parser-syntax-check-expected.txt:
* js/script-tests/parser-syntax-check.js:
(catch):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@192671 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 3ae550d..54458b3 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,146 @@
+2015-11-19 Saam barati <sbarati@apple.com>
+
+ [ES6] Add support for rest parameters
+ https://bugs.webkit.org/show_bug.cgi?id=38408
+
+ Reviewed by Geoffrey Garen.
+
+ This patch implements rest parameters from the ES6 spec.
+ http://www.ecma-international.org/ecma-262/6.0/index.html#sec-function-definitions
+
+ We implement the rest parameter as a new AST node. This AST node
+ lowers to "op_new_array X, op_copy_rest X". Note
+ that the op_copy_rest opcode does not have a result.
+ The bulk of this patch is implementing op_copy_rest.
+ This patch implements this in all four tiers in a straight forward way.
+ The opcode is implemented as a C call that will read the pertinent
+ arguments from the call frame and fill them into the array.
+
+ * bytecode/BytecodeList.json:
+ * bytecode/BytecodeUseDef.h:
+ (JSC::computeUsesForBytecodeOffset):
+ (JSC::computeDefsForBytecodeOffset):
+ * bytecode/CodeBlock.cpp:
+ (JSC::CodeBlock::dumpBytecode):
+ * bytecode/Instruction.h:
+ (JSC::Instruction::Instruction):
+ * bytecompiler/BytecodeGenerator.cpp:
+ (JSC::BytecodeGenerator::generate):
+ (JSC::BytecodeGenerator::BytecodeGenerator):
+ (JSC::BytecodeGenerator::initializeDefaultParameterValuesAndSetupFunctionScopeStack):
+ (JSC::BytecodeGenerator::invalidateForInContextForLocal):
+ (JSC::BytecodeGenerator::emitRestParameter):
+ * bytecompiler/BytecodeGenerator.h:
+ * bytecompiler/NodesCodegen.cpp:
+ (JSC::AssignmentElementNode::toString):
+ (JSC::RestParameterNode::collectBoundIdentifiers):
+ (JSC::RestParameterNode::toString):
+ (JSC::RestParameterNode::bindValue):
+ (JSC::RestParameterNode::emit):
+ (JSC::SpreadExpressionNode::emitBytecode):
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::parseBlock):
+ * dfg/DFGCapabilities.cpp:
+ (JSC::DFG::capabilityLevel):
+ * dfg/DFGClobberize.h:
+ (JSC::DFG::clobberize):
+ * dfg/DFGDoesGC.cpp:
+ (JSC::DFG::doesGC):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ * dfg/DFGNode.h:
+ (JSC::DFG::Node::setEpoch):
+ (JSC::DFG::Node::numberOfArgumentsToSkip):
+ (JSC::DFG::Node::dumpChildren):
+ * dfg/DFGNodeType.h:
+ * dfg/DFGOperations.cpp:
+ * dfg/DFGOperations.h:
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ (JSC::DFG::PredictionPropagationPhase::propagate):
+ * dfg/DFGSafeToExecute.h:
+ (JSC::DFG::safeToExecute):
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::compileCreateClonedArguments):
+ (JSC::DFG::SpeculativeJIT::compileCopyRest):
+ (JSC::DFG::SpeculativeJIT::compileNotifyWrite):
+ * dfg/DFGSpeculativeJIT.h:
+ (JSC::DFG::SpeculativeJIT::callOperation):
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * ftl/FTLCapabilities.cpp:
+ (JSC::FTL::canCompile):
+ * ftl/FTLIntrinsicRepository.h:
+ * ftl/FTLLowerDFGToLLVM.cpp:
+ (JSC::FTL::DFG::LowerDFGToLLVM::compileNode):
+ (JSC::FTL::DFG::LowerDFGToLLVM::compileCreateClonedArguments):
+ (JSC::FTL::DFG::LowerDFGToLLVM::compileCopyRest):
+ (JSC::FTL::DFG::LowerDFGToLLVM::compileNewObject):
+ * jit/JIT.cpp:
+ (JSC::JIT::privateCompileMainPass):
+ * jit/JIT.h:
+ * jit/JITOpcodes.cpp:
+ (JSC::JIT::emit_op_create_out_of_band_arguments):
+ (JSC::JIT::emit_op_copy_rest):
+ * jit/JITOperations.h:
+ * llint/LowLevelInterpreter.asm:
+ * parser/ASTBuilder.h:
+ (JSC::ASTBuilder::createBindingLocation):
+ (JSC::ASTBuilder::createRestParameter):
+ (JSC::ASTBuilder::createAssignmentElement):
+ * parser/NodeConstructors.h:
+ (JSC::AssignmentElementNode::AssignmentElementNode):
+ (JSC::RestParameterNode::RestParameterNode):
+ (JSC::DestructuringAssignmentNode::DestructuringAssignmentNode):
+ * parser/Nodes.h:
+ (JSC::DestructuringPatternNode::isBindingNode):
+ (JSC::DestructuringPatternNode::isRestParameter):
+ (JSC::DestructuringPatternNode::emitDirectBinding):
+ (JSC::RestParameterNode::name):
+ * parser/Parser.cpp:
+ (JSC::Parser<LexerType>::parseVariableDeclarationList):
+ (JSC::Parser<LexerType>::declareRestOrNormalParameter):
+ (JSC::Parser<LexerType>::createBindingPattern):
+ (JSC::Parser<LexerType>::parseFormalParameters):
+ * parser/Parser.h:
+ (JSC::Parser::strictMode):
+ (JSC::Parser::isValidStrictMode):
+ (JSC::Parser::declareParameter):
+ (JSC::Parser::breakIsValid):
+ * parser/SyntaxChecker.h:
+ (JSC::SyntaxChecker::operatorStackPop):
+ * runtime/CommonSlowPaths.cpp:
+ (JSC::SLOW_PATH_DECL):
+ * runtime/CommonSlowPaths.h:
+ * tests/es6.yaml:
+ * tests/stress/rest-parameter-and-default-arguments.js: Added.
+ (assert):
+ (shouldThrowTDZ):
+ (foo):
+ (baz):
+ (i.shouldThrowTDZ):
+ * tests/stress/rest-parameter-basics.js: Added.
+ (assert):
+ (foo):
+ (bar):
+ (capture):
+ (baz):
+ (jaz):
+ (kaz):
+ (raz):
+ (restLength):
+ (testArgumentsObject):
+ (strictModeLikeArgumentsObject):
+ * tests/stress/rest-parameter-inlined.js: Added.
+ (assert):
+ (bar):
+ (foo):
+ (baz):
+ (jaz):
+
2015-11-19 Filip Pizlo <fpizlo@apple.com>
B3 should have a story for Ext/Trunc strength reduction
diff --git a/Source/JavaScriptCore/bytecode/BytecodeList.json b/Source/JavaScriptCore/bytecode/BytecodeList.json
index bd29c55..2990fae 100644
--- a/Source/JavaScriptCore/bytecode/BytecodeList.json
+++ b/Source/JavaScriptCore/bytecode/BytecodeList.json
@@ -128,7 +128,8 @@
{ "name" : "op_enumerator_generic_pname", "length" : 4 },
{ "name" : "op_to_index_string", "length" : 3 },
{ "name" : "op_load_arrowfunction_this", "length" : 2 },
- { "name" : "op_assert", "length" : 3 }
+ { "name" : "op_assert", "length" : 3 },
+ { "name" : "op_copy_rest", "length": 3 }
]
},
{
diff --git a/Source/JavaScriptCore/bytecode/BytecodeUseDef.h b/Source/JavaScriptCore/bytecode/BytecodeUseDef.h
index 12cc191..a6132c3 100644
--- a/Source/JavaScriptCore/bytecode/BytecodeUseDef.h
+++ b/Source/JavaScriptCore/bytecode/BytecodeUseDef.h
@@ -70,7 +70,8 @@
case op_jeq_null:
case op_jneq_null:
case op_dec:
- case op_inc: {
+ case op_inc:
+ case op_copy_rest: {
functor(codeBlock, instruction, opcodeID, instruction[1].u.operand);
return;
}
@@ -247,6 +248,7 @@
OpcodeID opcodeID = interpreter->getOpcodeID(instruction->u.opcode);
switch (opcodeID) {
// These don't define anything.
+ case op_copy_rest:
case op_put_to_scope:
case op_end:
case op_profile_will_call:
diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.cpp b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
index 18ffa07..fca6301 100644
--- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp
+++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
@@ -781,6 +781,14 @@
out.printf("%s", registerName(r0).data());
break;
}
+ case op_copy_rest: {
+ int r0 = (++it)->u.operand;
+ printLocationAndOp(out, exec, location, it, "copy_rest");
+ out.printf("%s, ", registerName(r0).data());
+ unsigned argumentOffset = (++it)->u.unsignedValue;
+ out.printf("ArgumentsOffset: %u", argumentOffset);
+ break;
+ }
case op_create_this: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
diff --git a/Source/JavaScriptCore/bytecode/Instruction.h b/Source/JavaScriptCore/bytecode/Instruction.h
index 173ee97..494b000 100644
--- a/Source/JavaScriptCore/bytecode/Instruction.h
+++ b/Source/JavaScriptCore/bytecode/Instruction.h
@@ -75,6 +75,13 @@
u.jsCell.clear();
u.operand = operand;
}
+ Instruction(unsigned unsignedValue)
+ {
+ // We have to initialize one of the pointer members to ensure that
+ // the entire struct is initialized in 64-bit.
+ u.jsCell.clear();
+ u.unsignedValue = unsignedValue;
+ }
Instruction(PutByIdFlags flags)
{
@@ -112,6 +119,7 @@
union {
Opcode opcode;
int operand;
+ unsigned unsignedValue;
WriteBarrierBase<Structure> structure;
StructureID structureID;
WriteBarrierBase<SymbolTable> symbolTable;
diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
index 9ee6111..b9370c8 100644
--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
@@ -70,6 +70,9 @@
if (m_needToInitializeArguments)
initializeVariable(variable(propertyNames().arguments), m_argumentsRegister);
+ if (m_restParameter)
+ m_restParameter->emit(*this);
+
{
RefPtr<RegisterID> temp = newTemporary();
RefPtr<RegisterID> globalScope;
@@ -272,8 +275,14 @@
// needing destructuring are noted.
m_parameters.grow(parameters.size() + 1); // reserve space for "this"
m_thisRegister.setIndex(initializeNextParameter()->index()); // this
- for (unsigned i = 0; i < parameters.size(); ++i)
- initializeNextParameter();
+ for (unsigned i = 0; i < parameters.size(); ++i) {
+ auto pattern = parameters.at(i).first;
+ if (pattern->isRestParameter()) {
+ RELEASE_ASSERT(!m_restParameter);
+ m_restParameter = static_cast<RestParameterNode*>(pattern);
+ } else
+ initializeNextParameter();
+ }
// Figure out some interesting facts about our arguments.
bool capturesAnyArgumentByName = false;
@@ -307,7 +316,14 @@
m_argumentsRegister->ref();
}
- if (needsArguments && !codeBlock->isStrictMode() && !parameters.hasDefaultParameterValues()) {
+ // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-functiondeclarationinstantiation
+ // This implements IsSimpleParameterList in the Ecma 2015 spec.
+ // If IsSimpleParameterList is false, we will create a strict-mode like arguments object.
+ // IsSimpleParameterList is false if the argument list contains any default parameter values,
+ // a rest parameter, or any destructuring patterns.
+ // FIXME: Take into account destructuring to make isSimpleParameterList false. https://bugs.webkit.org/show_bug.cgi?id=151450
+ bool isSimpleParameterList = !parameters.hasDefaultParameterValues() && !m_restParameter;
+ if (needsArguments && !codeBlock->isStrictMode() && isSimpleParameterList) {
// If we captured any formal parameter by name, then we use ScopedArguments. Otherwise we
// use DirectArguments. With ScopedArguments, we lift all of our arguments into the
// activation.
@@ -389,7 +405,7 @@
}
}
- if (needsArguments && (codeBlock->isStrictMode() || parameters.hasDefaultParameterValues())) {
+ if (needsArguments && (codeBlock->isStrictMode() || !isSimpleParameterList)) {
// Allocate an out-of-bands arguments object.
emitOpcode(op_create_out_of_band_arguments);
instructions().append(m_argumentsRegister->index());
@@ -707,6 +723,8 @@
RefPtr<RegisterID> temp = newTemporary();
for (unsigned i = 0; i < parameters.size(); i++) {
std::pair<DestructuringPatternNode*, ExpressionNode*> parameter = parameters.at(i);
+ if (parameter.first->isRestParameter())
+ continue;
RefPtr<RegisterID> parameterValue = ®isterFor(virtualRegisterForArgument(1 + i));
emitMove(temp.get(), parameterValue.get());
if (parameter.second) {
@@ -764,7 +782,7 @@
// If we have default parameter values, we handle this case above.
for (unsigned i = 0; i < parameters.size(); i++) {
DestructuringPatternNode* pattern = parameters.at(i).first;
- if (!pattern->isBindingNode()) {
+ if (!pattern->isBindingNode() && !pattern->isRestParameter()) {
RefPtr<RegisterID> parameterValue = ®isterFor(virtualRegisterForArgument(1 + i));
pattern->bindValue(*this, parameterValue.get());
}
@@ -3813,4 +3831,15 @@
}
}
+RegisterID* BytecodeGenerator::emitRestParameter(RegisterID* result, unsigned numParametersToSkip)
+{
+ emitNewArray(result, nullptr, 0);
+
+ emitOpcode(op_copy_rest);
+ instructions().append(result->index());
+ instructions().append(numParametersToSkip);
+
+ return result;
+}
+
} // namespace JSC
diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
index d191f99..38b659a 100644
--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
@@ -598,6 +598,8 @@
RegisterID* emitIteratorNext(RegisterID* dst, RegisterID* iterator, const ThrowableExpressionData* node);
void emitIteratorClose(RegisterID* iterator, const ThrowableExpressionData* node);
+ RegisterID* emitRestParameter(RegisterID* result, unsigned numParametersToSkip);
+
bool emitReadOnlyExceptionIfNeeded(const Variable&);
// Start a try block. 'start' must have been emitted.
@@ -828,6 +830,7 @@
enum FunctionVariableType : uint8_t { NormalFunctionVariable, GlobalFunctionVariable };
Vector<std::pair<FunctionMetadataNode*, FunctionVariableType>> m_functionsToInitialize;
bool m_needToInitializeArguments { false };
+ RestParameterNode* m_restParameter { nullptr };
Vector<TryRange> m_tryRanges;
SegmentedVector<TryData, 8> m_tryData;
diff --git a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
index 4166671..312a8c7 100644
--- a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
+++ b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
@@ -3456,6 +3456,35 @@
builder.append(static_cast<ResolveNode*>(m_assignmentTarget)->identifier().string());
}
+void RestParameterNode::collectBoundIdentifiers(Vector<Identifier>& identifiers) const
+{
+ identifiers.append(m_name);
+}
+void RestParameterNode::toString(StringBuilder& builder) const
+{
+ builder.append(m_name.string());
+}
+void RestParameterNode::bindValue(BytecodeGenerator&, RegisterID*) const
+{
+ RELEASE_ASSERT_NOT_REACHED();
+}
+void RestParameterNode::emit(BytecodeGenerator& generator)
+{
+ Variable var = generator.variable(m_name);
+ if (RegisterID* local = var.local()) {
+ generator.emitRestParameter(local, m_numParametersToSkip);
+ generator.emitProfileType(local, var, m_divotStart, m_divotEnd);
+ return;
+ }
+
+ RefPtr<RegisterID> restParameterArray = generator.emitRestParameter(generator.newTemporary(), m_numParametersToSkip);
+ generator.emitProfileType(restParameterArray.get(), var, m_divotStart, m_divotEnd);
+ RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var);
+ generator.emitExpressionInfo(m_divotEnd, m_divotStart, m_divotEnd);
+ generator.emitPutToScope(scope.get(), var, restParameterArray.get(), generator.isStrictMode() ? ThrowIfNotFound : DoNotThrowIfNotFound, Initialization);
+}
+
+
RegisterID* SpreadExpressionNode::emitBytecode(BytecodeGenerator&, RegisterID*)
{
RELEASE_ASSERT_NOT_REACHED();
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
index a4e6923..b95ef8f 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
+++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
@@ -2508,6 +2508,9 @@
case CheckTierUpAtReturn:
break;
+ case CopyRest:
+ break;
+
case Check: {
// Simplify out checks that don't actually do checking.
for (unsigned i = 0; i < AdjacencyList::Size; ++i) {
diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
index f092ef6..24dbd7b 100644
--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
@@ -3330,6 +3330,13 @@
set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(NewRegexp, OpInfo(currentInstruction[2].u.operand)));
NEXT_OPCODE(op_new_regexp);
}
+
+ case op_copy_rest: {
+ noticeArgumentsUse();
+ addToGraph(CopyRest,
+ OpInfo(currentInstruction[2].u.unsignedValue), get(VirtualRegister(currentInstruction[1].u.operand)));
+ NEXT_OPCODE(op_copy_rest);
+ }
// === Bitwise operations ===
diff --git a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp
index cda14c3..ae06593 100644
--- a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp
+++ b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp
@@ -219,6 +219,7 @@
case op_create_lexical_environment:
case op_get_parent_scope:
case op_catch:
+ case op_copy_rest:
return CanCompileAndInline;
case op_put_to_scope: {
diff --git a/Source/JavaScriptCore/dfg/DFGClobberize.h b/Source/JavaScriptCore/dfg/DFGClobberize.h
index 6ff4456..9d7969d 100644
--- a/Source/JavaScriptCore/dfg/DFGClobberize.h
+++ b/Source/JavaScriptCore/dfg/DFGClobberize.h
@@ -983,6 +983,12 @@
return;
}
+ case CopyRest: {
+ read(Stack);
+ write(Heap);
+ return;
+ }
+
case NewObject:
case NewRegexp:
case NewStringObject:
diff --git a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp
index 0e9e1d8..4269088 100644
--- a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp
+++ b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp
@@ -224,6 +224,7 @@
case GetStack:
case GetFromArguments:
case PutToArguments:
+ case CopyRest:
return false;
case CreateActivation:
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index e0a89ea..97c3c1e 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -1368,6 +1368,11 @@
break;
}
+ case CopyRest: {
+ fixEdge<KnownCellUse>(node->child1());
+ break;
+ }
+
#if !ASSERT_DISABLED
// Have these no-op cases here to ensure that nobody forgets to add handlers for new opcodes.
case SetArgument:
diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h
index eedeb88..532e689 100644
--- a/Source/JavaScriptCore/dfg/DFGNode.h
+++ b/Source/JavaScriptCore/dfg/DFGNode.h
@@ -2158,7 +2158,13 @@
{
m_misc.epoch = epoch.toUnsigned();
}
-
+
+ unsigned numberOfArgumentsToSkip()
+ {
+ ASSERT(op() == CopyRest);
+ return static_cast<unsigned>(m_opInfo);
+ }
+
void dumpChildren(PrintStream& out)
{
if (!child1())
diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h
index c0498ae..9a321f9 100644
--- a/Source/JavaScriptCore/dfg/DFGNodeType.h
+++ b/Source/JavaScriptCore/dfg/DFGNodeType.h
@@ -262,6 +262,7 @@
macro(NewArrayBuffer, NodeResultJS) \
macro(NewTypedArray, NodeResultJS | NodeMustGenerate) \
macro(NewRegexp, NodeResultJS) \
+ macro(CopyRest, NodeMustGenerate) \
\
/* Support for allocation sinking. */\
macro(PhantomNewObject, NodeResultJS | NodeMustGenerate) \
diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp
index 716c1ea..afc4cd3 100644
--- a/Source/JavaScriptCore/dfg/DFGOperations.cpp
+++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp
@@ -818,6 +818,16 @@
return result;
}
+void JIT_OPERATION operationCopyRest(ExecState* exec, JSCell* arrayAsCell, Register* argumentStart, unsigned numberOfParamsToSkip, unsigned numberOfArguments)
+{
+ RELEASE_ASSERT(numberOfArguments > numberOfParamsToSkip); // We should only call this from JIT code when this condition is true.
+ JSArray* array = jsCast<JSArray*>(arrayAsCell);
+ unsigned arraySize = numberOfArguments - numberOfParamsToSkip;
+ array->setLength(exec, arraySize);
+ for (unsigned i = 0; i < arraySize; i++)
+ array->putDirectIndex(exec, i, argumentStart[i + numberOfParamsToSkip].jsValue());
+}
+
size_t JIT_OPERATION operationObjectIsObject(ExecState* exec, JSGlobalObject* globalObject, JSCell* object)
{
VM& vm = exec->vm();
diff --git a/Source/JavaScriptCore/dfg/DFGOperations.h b/Source/JavaScriptCore/dfg/DFGOperations.h
index bbafc0b..d6c2b74 100644
--- a/Source/JavaScriptCore/dfg/DFGOperations.h
+++ b/Source/JavaScriptCore/dfg/DFGOperations.h
@@ -103,6 +103,7 @@
JSCell* JIT_OPERATION operationCreateScopedArguments(ExecState*, Structure*, Register* argumentStart, int32_t length, JSFunction* callee, JSLexicalEnvironment*);
JSCell* JIT_OPERATION operationCreateClonedArgumentsDuringExit(ExecState*, InlineCallFrame*, JSFunction*, int32_t argumentCount);
JSCell* JIT_OPERATION operationCreateClonedArguments(ExecState*, Structure*, Register* argumentStart, int32_t length, JSFunction* callee);
+void JIT_OPERATION operationCopyRest(ExecState*, JSCell*, Register* argumentStart, unsigned numberOfParamsToSkip, unsigned argumentsCount);
double JIT_OPERATION operationFModOnInts(int32_t, int32_t) WTF_INTERNAL;
size_t JIT_OPERATION operationObjectIsObject(ExecState*, JSGlobalObject*, JSCell*) WTF_INTERNAL;
size_t JIT_OPERATION operationObjectIsFunction(ExecState*, JSGlobalObject*, JSCell*) WTF_INTERNAL;
diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
index e65f6e4..5c09c22 100644
--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
@@ -676,6 +676,7 @@
case ZombieHint:
case ExitOK:
case LoadVarargs:
+ case CopyRest:
break;
// This gets ignored because it only pretends to produce a value.
diff --git a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
index 42abad0..b15b0b4 100644
--- a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
+++ b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
@@ -322,6 +322,7 @@
case PhantomClonedArguments:
case GetMyArgumentByVal:
case ForwardVarargs:
+ case CopyRest:
return true;
case BottomValue:
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
index 5b4ca34..81b87ff 100755
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
@@ -5344,6 +5344,34 @@
cellResult(resultGPR, node);
}
+void SpeculativeJIT::compileCopyRest(Node* node)
+{
+ ASSERT(node->op() == CopyRest);
+
+ SpeculateCellOperand array(this, node->child1());
+ GPRReg arrayGPR = array.gpr();
+
+ GPRTemporary argumentsLength(this);
+ GPRReg argumentsLengthGPR = argumentsLength.gpr();
+
+ GPRTemporary argumentsStart(this);
+ GPRReg argumentsStartGPR = argumentsStart.gpr();
+
+ emitGetLength(node->origin.semantic, argumentsLengthGPR);
+ CCallHelpers::Jump done = m_jit.branch32(MacroAssembler::LessThanOrEqual, argumentsLengthGPR, TrustedImm32(node->numberOfArgumentsToSkip()));
+
+ emitGetArgumentStart(node->origin.semantic, argumentsStartGPR);
+ silentSpillAllRegisters(argumentsLengthGPR, argumentsStartGPR);
+ // Arguments: 0:exec, 1:JSCell* array, 2:arguments start, 3:number of arguments to skip, 4:number of arguments
+ callOperation(operationCopyRest, arrayGPR, argumentsStartGPR, TrustedImm32(node->numberOfArgumentsToSkip()), argumentsLengthGPR);
+ silentFillAllRegisters(argumentsLengthGPR);
+ m_jit.exceptionCheck();
+
+ done.link(&m_jit);
+
+ noResult(node);
+}
+
void SpeculativeJIT::compileNotifyWrite(Node* node)
{
WatchpointSet* set = node->watchpointSet();
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
index ba7270c..cb11a44 100755
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
@@ -1221,6 +1221,12 @@
return appendCall(operation);
}
+ JITCompiler::Call callOperation(V_JITOperation_ECRUiUi operation, GPRReg arg1, GPRReg arg2, TrustedImm32 arg3, GPRReg arg4)
+ {
+ m_jit.setupArgumentsWithExecState(arg1, arg2, arg3, arg4);
+ return appendCall(operation);
+ }
+
#if USE(JSVALUE64)
JITCompiler::Call callOperation(J_JITOperation_E operation, GPRReg result)
{
@@ -2238,6 +2244,7 @@
void compilePutToArguments(Node*);
void compileCreateScopedArguments(Node*);
void compileCreateClonedArguments(Node*);
+ void compileCopyRest(Node*);
void compileNotifyWrite(Node*);
bool compileRegExpExec(Node*);
void compileIsObjectOrNull(Node*);
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
index 5bd6ebb..2a3c2f8 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
@@ -4438,6 +4438,11 @@
break;
}
+ case CopyRest: {
+ compileCopyRest(node);
+ break;
+ }
+
case NewFunction:
case NewArrowFunction:
compileNewFunction(node);
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
index 67b1cf8..d38c0f2 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
@@ -4427,6 +4427,10 @@
compileCreateClonedArguments(node);
break;
}
+ case CopyRest: {
+ compileCopyRest(node);
+ break;
+ }
case NewFunction:
case NewArrowFunction:
diff --git a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
index e327f76..f4bb10b 100644
--- a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
+++ b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
@@ -210,6 +210,7 @@
case PutGetterSetterById:
case PutGetterByVal:
case PutSetterByVal:
+ case CopyRest:
// These are OK.
break;
diff --git a/Source/JavaScriptCore/ftl/FTLIntrinsicRepository.h b/Source/JavaScriptCore/ftl/FTLIntrinsicRepository.h
index b0735c3..f5f4ea7 100644
--- a/Source/JavaScriptCore/ftl/FTLIntrinsicRepository.h
+++ b/Source/JavaScriptCore/ftl/FTLIntrinsicRepository.h
@@ -130,6 +130,7 @@
macro(Z_JITOperation_EGC, functionType(int32, intPtr, intPtr, intPtr)) \
macro(Z_JITOperation_EJZ, functionType(int32, intPtr, int64, int32)) \
macro(Z_JITOperation_ESJss, functionType(int32, intPtr, intPtr, int64)) \
+ macro(V_JITOperation_ECRUiUi, functionType(voidType, intPtr, intPtr, intPtr, int32, int32))
class IntrinsicRepository : public CommonValues {
public:
diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
index 203d129..9b1face 100644
--- a/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
+++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
@@ -967,6 +967,9 @@
case CheckWatchdogTimer:
compileCheckWatchdogTimer();
break;
+ case CopyRest:
+ compileCopyRest();
+ break;
case PhantomLocal:
case LoopHint:
@@ -3636,6 +3639,28 @@
setJSValue(result);
}
+
+ void compileCopyRest()
+ {
+ LBasicBlock doCopyRest = FTL_NEW_BLOCK(m_out, ("CopyRest C call"));
+ LBasicBlock continuation = FTL_NEW_BLOCK(m_out, ("FillRestParameter continuation"));
+
+ LValue numberOfArgumentsToSkip = m_out.constInt32(m_node->numberOfArgumentsToSkip());
+ LValue numberOfArguments = getArgumentsLength().value;
+
+ m_out.branch(
+ m_out.above(numberOfArguments, numberOfArgumentsToSkip),
+ unsure(doCopyRest), unsure(continuation));
+
+ LBasicBlock lastNext = m_out.appendTo(doCopyRest, continuation);
+ // Arguments: 0:exec, 1:JSCell* array, 2:arguments start, 3:number of arguments to skip, 4:number of arguments
+ vmCall(
+ m_out.voidType,m_out.operation(operationCopyRest), m_callFrame, lowCell(m_node->child1()),
+ getArgumentsStart(), numberOfArgumentsToSkip, numberOfArguments);
+ m_out.jump(continuation);
+
+ m_out.appendTo(continuation, lastNext);
+ }
void compileNewObject()
{
diff --git a/Source/JavaScriptCore/jit/JIT.cpp b/Source/JavaScriptCore/jit/JIT.cpp
index cd686b7..a61b57d 100644
--- a/Source/JavaScriptCore/jit/JIT.cpp
+++ b/Source/JavaScriptCore/jit/JIT.cpp
@@ -211,6 +211,7 @@
DEFINE_OP(op_create_direct_arguments)
DEFINE_OP(op_create_scoped_arguments)
DEFINE_OP(op_create_out_of_band_arguments)
+ DEFINE_OP(op_copy_rest)
DEFINE_OP(op_check_tdz)
DEFINE_OP(op_assert)
DEFINE_OP(op_debug)
diff --git a/Source/JavaScriptCore/jit/JIT.h b/Source/JavaScriptCore/jit/JIT.h
index 6bf488c..d225823 100755
--- a/Source/JavaScriptCore/jit/JIT.h
+++ b/Source/JavaScriptCore/jit/JIT.h
@@ -490,6 +490,7 @@
void emit_op_create_direct_arguments(Instruction*);
void emit_op_create_scoped_arguments(Instruction*);
void emit_op_create_out_of_band_arguments(Instruction*);
+ void emit_op_copy_rest(Instruction*);
void emit_op_check_tdz(Instruction*);
void emit_op_assert(Instruction*);
void emit_op_debug(Instruction*);
diff --git a/Source/JavaScriptCore/jit/JITOpcodes.cpp b/Source/JavaScriptCore/jit/JITOpcodes.cpp
index c1bb743..d494896 100755
--- a/Source/JavaScriptCore/jit/JITOpcodes.cpp
+++ b/Source/JavaScriptCore/jit/JITOpcodes.cpp
@@ -1399,6 +1399,12 @@
slowPathCall.call();
}
+void JIT::emit_op_copy_rest(Instruction* currentInstruction)
+{
+ JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_copy_rest);
+ slowPathCall.call();
+}
+
} // namespace JSC
#endif // ENABLE(JIT)
diff --git a/Source/JavaScriptCore/jit/JITOperations.h b/Source/JavaScriptCore/jit/JITOperations.h
index f6c57f3..9638f14 100644
--- a/Source/JavaScriptCore/jit/JITOperations.h
+++ b/Source/JavaScriptCore/jit/JITOperations.h
@@ -92,6 +92,7 @@
Vm: VM*
Ws: WatchpointSet*
Z: int32_t
+ Ui: uint32_t
*/
typedef CallFrame* JIT_OPERATION (*F_JITOperation_EFJZZ)(ExecState*, CallFrame*, EncodedJSValue, int32_t, int32_t);
@@ -219,6 +220,7 @@
typedef void JIT_OPERATION (*V_JITOperation_EVm)(ExecState*, VM*);
typedef void JIT_OPERATION (*V_JITOperation_J)(EncodedJSValue);
typedef void JIT_OPERATION (*V_JITOperation_Z)(int32_t);
+typedef void JIT_OPERATION (*V_JITOperation_ECRUiUi)(ExecState*, JSCell*, Register*, uint32_t, uint32_t);
typedef char* JIT_OPERATION (*P_JITOperation_E)(ExecState*);
typedef char* JIT_OPERATION (*P_JITOperation_EC)(ExecState*, JSCell*);
typedef char* JIT_OPERATION (*P_JITOperation_ECli)(ExecState*, CallLinkInfo*);
diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter.asm
index 849b2d7..f8e128c 100644
--- a/Source/JavaScriptCore/llint/LowLevelInterpreter.asm
+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter.asm
@@ -1675,6 +1675,12 @@
callSlowPath(_slow_path_to_index_string)
dispatch(3)
+_llint_op_copy_rest:
+ traceExecution()
+ callSlowPath(_slow_path_copy_rest)
+ dispatch(3)
+
+
# Lastly, make sure that we can link even though we don't support all opcodes.
# These opcodes should never arise when using LLInt or either JIT. We assert
# as much.
diff --git a/Source/JavaScriptCore/parser/ASTBuilder.h b/Source/JavaScriptCore/parser/ASTBuilder.h
index 6d75d60..acc2162 100644
--- a/Source/JavaScriptCore/parser/ASTBuilder.h
+++ b/Source/JavaScriptCore/parser/ASTBuilder.h
@@ -857,6 +857,11 @@
return new (m_parserArena) BindingNode(boundProperty, start, end, context);
}
+ RestParameterNode* createRestParameter(const Identifier& name, size_t numParametersToSkip, const JSTextPosition& start, const JSTextPosition& end)
+ {
+ return new (m_parserArena) RestParameterNode(name, numParametersToSkip, start, end);
+ }
+
AssignmentElement createAssignmentElement(const Expression& assignmentTarget, const JSTextPosition& start, const JSTextPosition& end)
{
return new (m_parserArena) AssignmentElementNode(assignmentTarget, start, end);
diff --git a/Source/JavaScriptCore/parser/NodeConstructors.h b/Source/JavaScriptCore/parser/NodeConstructors.h
index bf512ab..2ab5370 100644
--- a/Source/JavaScriptCore/parser/NodeConstructors.h
+++ b/Source/JavaScriptCore/parser/NodeConstructors.h
@@ -1029,6 +1029,15 @@
{
}
+ inline RestParameterNode::RestParameterNode(const Identifier& name, unsigned numParametersToSkip, const JSTextPosition& start, const JSTextPosition& end)
+ : DestructuringPatternNode()
+ , m_name(name)
+ , m_numParametersToSkip(numParametersToSkip)
+ , m_divotStart(start)
+ , m_divotEnd(end)
+ {
+ }
+
inline DestructuringAssignmentNode::DestructuringAssignmentNode(const JSTokenLocation& location, DestructuringPatternNode* bindings, ExpressionNode* initializer)
: ExpressionNode(location)
, m_bindings(bindings)
diff --git a/Source/JavaScriptCore/parser/Nodes.h b/Source/JavaScriptCore/parser/Nodes.h
index 13982c5..0aa311d 100644
--- a/Source/JavaScriptCore/parser/Nodes.h
+++ b/Source/JavaScriptCore/parser/Nodes.h
@@ -1973,6 +1973,7 @@
virtual void toString(StringBuilder&) const = 0;
virtual bool isBindingNode() const { return false; }
+ virtual bool isRestParameter() const { return false; }
virtual RegisterID* emitDirectBinding(BytecodeGenerator&, RegisterID*, ExpressionNode*) { return 0; }
protected:
@@ -2053,6 +2054,27 @@
AssignmentContext m_bindingContext;
};
+ class RestParameterNode : public DestructuringPatternNode {
+ public:
+ RestParameterNode(const Identifier& boundProperty, unsigned numParametersToSkip, const JSTextPosition& start, const JSTextPosition& end);
+
+ bool isRestParameter() const override { return true; }
+
+ void emit(BytecodeGenerator&);
+
+ const Identifier& name() const { return m_name; }
+
+ private:
+ virtual void collectBoundIdentifiers(Vector<Identifier>&) const override;
+ virtual void bindValue(BytecodeGenerator&, RegisterID*) const override;
+ virtual void toString(StringBuilder&) const override;
+
+ const Identifier& m_name;
+ unsigned m_numParametersToSkip;
+ JSTextPosition m_divotStart; // "f" in "...foo"
+ JSTextPosition m_divotEnd;
+ };
+
class AssignmentElementNode : public DestructuringPatternNode {
public:
AssignmentElementNode(ExpressionNode* assignmentTarget, const JSTextPosition& start, const JSTextPosition& end);
diff --git a/Source/JavaScriptCore/parser/Parser.cpp b/Source/JavaScriptCore/parser/Parser.cpp
index 017495b..4eb855a 100644
--- a/Source/JavaScriptCore/parser/Parser.cpp
+++ b/Source/JavaScriptCore/parser/Parser.cpp
@@ -669,6 +669,30 @@
}
template <typename LexerType>
+bool Parser<LexerType>::declareRestOrNormalParameter(const Identifier& name, const Identifier** duplicateIdentifier)
+{
+ DeclarationResultMask declarationResult = declareParameter(&name);
+ if ((declarationResult & DeclarationResult::InvalidStrictMode) && strictMode()) {
+ semanticFailIfTrue(isEvalOrArguments(&name), "Cannot destructure to a parameter name '", name.impl(), "' in strict mode");
+ if (m_lastFunctionName && name == *m_lastFunctionName)
+ semanticFail("Cannot declare a parameter named '", name.impl(), "' as it shadows the name of a strict mode function");
+ semanticFailureDueToKeyword("parameter name");
+ if (hasDeclaredParameter(name))
+ semanticFail("Cannot declare a parameter named '", name.impl(), "' in strict mode as it has already been declared");
+ semanticFail("Cannot declare a parameter named '", name.impl(), "' in strict mode");
+ }
+ if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration) {
+ // It's not always an error to define a duplicate parameter.
+ // It's only an error when there are default parameter values or destructuring parameters.
+ // We note this value now so we can check it later.
+ if (duplicateIdentifier)
+ *duplicateIdentifier = &name;
+ }
+
+ return true;
+}
+
+template <typename LexerType>
template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::createBindingPattern(TreeBuilder& context, DestructuringKind kind, ExportType exportType, const Identifier& name, JSToken token, AssignmentContext bindingContext, const Identifier** duplicateIdentifier)
{
ASSERT(!name.isNull());
@@ -687,23 +711,8 @@
failIfTrue(declarationResult & DeclarationResult::InvalidDuplicateDeclaration, "Cannot declare a lexical variable twice: '", name.impl(), "'");
}
} else if (kind == DestructureToParameters) {
- DeclarationResultMask declarationResult = declareParameter(&name);
- if ((declarationResult & DeclarationResult::InvalidStrictMode) && strictMode()) {
- semanticFailIfTrue(isEvalOrArguments(&name), "Cannot destructure to a parameter name '", name.impl(), "' in strict mode");
- if (m_lastFunctionName && name == *m_lastFunctionName)
- semanticFail("Cannot declare a parameter named '", name.impl(), "' as it shadows the name of a strict mode function");
- semanticFailureDueToKeyword("parameter name");
- if (hasDeclaredParameter(name))
- semanticFail("Cannot declare a parameter named '", name.impl(), "' in strict mode as it has already been declared");
- semanticFail("Cannot declare a parameter named '", name.impl(), "' in strict mode");
- }
- if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration) {
- // It's not always an error to define a duplicate parameter.
- // It's only an error when there are default parameter values or destructuring parameters.
- // We note this value now so we can check it later.
- if (duplicateIdentifier)
- *duplicateIdentifier = &name;
- }
+ declareRestOrNormalParameter(name, duplicateIdentifier);
+ propagateError();
}
if (exportType == ExportType::Exported) {
@@ -1536,32 +1545,45 @@
template <typename LexerType>
template <class TreeBuilder> bool Parser<LexerType>::parseFormalParameters(TreeBuilder& context, TreeFormalParameterList list, unsigned& parameterCount)
{
-#define failFromDuplicate() \
+#define failIfDuplicateIfViolation() \
if (duplicateParameter) {\
semanticFailIfTrue(defaultValue, "Duplicate parameter '", duplicateParameter->impl(), "' not allowed in function with default parameter values");\
semanticFailIfTrue(hasDestructuringPattern, "Duplicate parameter '", duplicateParameter->impl(), "' not allowed in function with destructuring parameters");\
+ semanticFailIfTrue(isRestParameter, "Duplicate parameter '", duplicateParameter->impl(), "' not allowed in function with a rest parameter");\
}
- const Identifier* duplicateParameter = nullptr;
bool hasDestructuringPattern = false;
- auto parameter = parseDestructuringPattern(context, DestructureToParameters, ExportType::NotExported, &duplicateParameter, &hasDestructuringPattern);
- failIfFalse(parameter, "Cannot parse parameter pattern");
- auto defaultValue = parseDefaultValueForDestructuringPattern(context);
- propagateError();
- failFromDuplicate();
- context.appendParameter(list, parameter, defaultValue);
- parameterCount++;
- while (consume(COMMA)) {
- parameter = parseDestructuringPattern(context, DestructureToParameters, ExportType::NotExported, &duplicateParameter, &hasDestructuringPattern);
+ bool isRestParameter = false;
+ const Identifier* duplicateParameter = nullptr;
+ do {
+ TreeDestructuringPattern parameter = 0;
+ TreeExpression defaultValue = 0;
+
+ if (match(DOTDOTDOT)) {
+ next();
+ failIfFalse(matchSpecIdentifier(), "Rest parameter '...' should be followed by a variable identifier");
+ declareRestOrNormalParameter(*m_token.m_data.ident, &duplicateParameter);
+ propagateError();
+ JSTextPosition identifierStart = tokenStartPosition();
+ JSTextPosition identifierEnd = tokenEndPosition();
+ parameter = context.createRestParameter(*m_token.m_data.ident, parameterCount, identifierStart, identifierEnd);
+ next();
+ failIfTrue(match(COMMA), "Rest parameter should be the last parameter in a function declaration"); // Let's have a good error message for this common case.
+ isRestParameter = true;
+ } else
+ parameter = parseDestructuringPattern(context, DestructureToParameters, ExportType::NotExported, &duplicateParameter, &hasDestructuringPattern);
failIfFalse(parameter, "Cannot parse parameter pattern");
- defaultValue = parseDefaultValueForDestructuringPattern(context);
+ if (!isRestParameter)
+ defaultValue = parseDefaultValueForDestructuringPattern(context);
propagateError();
- failFromDuplicate();
+ failIfDuplicateIfViolation();
context.appendParameter(list, parameter, defaultValue);
- parameterCount++;
- }
+ if (!isRestParameter)
+ parameterCount++;
+ } while (!isRestParameter && consume(COMMA));
+
return true;
-#undef failFromDuplicate
+#undef failIfDuplicateIfViolation
}
template <typename LexerType>
diff --git a/Source/JavaScriptCore/parser/Parser.h b/Source/JavaScriptCore/parser/Parser.h
index f3307f1..6294f31 100644
--- a/Source/JavaScriptCore/parser/Parser.h
+++ b/Source/JavaScriptCore/parser/Parser.h
@@ -1073,6 +1073,8 @@
bool strictMode() { return currentScope()->strictMode(); }
bool isValidStrictMode() { return currentScope()->isValidStrictMode(); }
DeclarationResultMask declareParameter(const Identifier* ident) { return currentScope()->declareParameter(ident); }
+ bool declareRestOrNormalParameter(const Identifier&, const Identifier**);
+
bool breakIsValid()
{
ScopeRef current = currentScope();
diff --git a/Source/JavaScriptCore/parser/SyntaxChecker.h b/Source/JavaScriptCore/parser/SyntaxChecker.h
index e3a867e..95536c3 100644
--- a/Source/JavaScriptCore/parser/SyntaxChecker.h
+++ b/Source/JavaScriptCore/parser/SyntaxChecker.h
@@ -75,7 +75,7 @@
FunctionExpr, ClassExpr, SuperExpr, BracketExpr, DotExpr, CallExpr,
NewExpr, PreExpr, PostExpr, UnaryExpr, BinaryExpr,
ConditionalExpr, AssignmentExpr, TypeofExpr, NewTargetExpr,
- DeleteExpr, ArrayLiteralExpr, BindingDestructuring,
+ DeleteExpr, ArrayLiteralExpr, BindingDestructuring, RestParameter,
ArrayDestructuring, ObjectDestructuring, SourceElementsResult,
FunctionBodyResult, SpreadExpr, ArgumentsResult,
PropertyListResult, ArgumentsListResult, ElementsListResult,
@@ -135,6 +135,7 @@
typedef int DestructuringPattern;
typedef DestructuringPattern ArrayPattern;
typedef DestructuringPattern ObjectPattern;
+ typedef DestructuringPattern RestPattern;
static const bool CreatesAST = false;
static const bool NeedsFreeVariableInfo = false;
@@ -346,6 +347,10 @@
{
return BindingDestructuring;
}
+ RestPattern createRestParameter(const Identifier&, size_t, const JSTextPosition&, const JSTextPosition&)
+ {
+ return RestParameter;
+ }
DestructuringPattern createAssignmentElement(const Expression&, const JSTextPosition&, const JSTextPosition&)
{
return BindingDestructuring;
diff --git a/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp b/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp
index c4f6c22..90f65c2 100644
--- a/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp
+++ b/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp
@@ -701,4 +701,20 @@
RETURN(resolvedScope);
}
+SLOW_PATH_DECL(slow_path_copy_rest)
+{
+ BEGIN();
+ unsigned numParamsToSkip = pc[2].u.unsignedValue;
+ unsigned numArgumentsToFunction = exec->argumentCount();
+ if (numArgumentsToFunction <= numParamsToSkip)
+ END();
+
+ JSArray* array = jsCast<JSArray*>(OP(1).jsValue());
+ unsigned arraySize = numArgumentsToFunction - numParamsToSkip;
+ array->setLength(exec, arraySize);
+ for (unsigned i = 0; i < arraySize; i++)
+ array->putDirectIndex(exec, i, exec->uncheckedArgument(i + numParamsToSkip));
+ END();
+}
+
} // namespace JSC
diff --git a/Source/JavaScriptCore/runtime/CommonSlowPaths.h b/Source/JavaScriptCore/runtime/CommonSlowPaths.h
index b0ba9e8..226185c 100644
--- a/Source/JavaScriptCore/runtime/CommonSlowPaths.h
+++ b/Source/JavaScriptCore/runtime/CommonSlowPaths.h
@@ -293,6 +293,7 @@
SLOW_PATH_HIDDEN_DECL(slow_path_create_lexical_environment);
SLOW_PATH_HIDDEN_DECL(slow_path_push_with_scope);
SLOW_PATH_HIDDEN_DECL(slow_path_resolve_scope);
+SLOW_PATH_HIDDEN_DECL(slow_path_copy_rest);
} // namespace JSC
diff --git a/Source/JavaScriptCore/tests/es6.yaml b/Source/JavaScriptCore/tests/es6.yaml
index e05c742..e0bc3d5 100644
--- a/Source/JavaScriptCore/tests/es6.yaml
+++ b/Source/JavaScriptCore/tests/es6.yaml
@@ -1089,15 +1089,15 @@
- path: es6/RegExp_y_and_u_flags_y_flag_lastIndex.js
cmd: runES6 :fail
- path: es6/rest_parameters_arguments_object_interaction.js
- cmd: runES6 :fail
+ cmd: runES6 :normal
- path: es6/rest_parameters_basic_functionality.js
- cmd: runES6 :fail
+ cmd: runES6 :normal
- path: es6/rest_parameters_cant_be_used_in_setters.js
- cmd: runES6 :fail
+ cmd: runES6 :normal
- path: es6/rest_parameters_function_length_property.js
- cmd: runES6 :fail
+ cmd: runES6 :normal
- path: es6/rest_parameters_new_Function_support.js
- cmd: runES6 :fail
+ cmd: runES6 :normal
- path: es6/Set_iterator_closing.js
cmd: runES6 :fail
- path: es6/Set_Set[Symbol.species].js
diff --git a/Source/JavaScriptCore/tests/stress/rest-parameter-and-default-arguments.js b/Source/JavaScriptCore/tests/stress/rest-parameter-and-default-arguments.js
new file mode 100644
index 0000000..ee1b451
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/rest-parameter-and-default-arguments.js
@@ -0,0 +1,36 @@
+function assert(b) {
+ if (!b)
+ throw new Error("Bad assertion")
+}
+noInline(assert);
+
+function shouldThrowTDZ(func) {
+ var hasThrown = false;
+ try {
+ func();
+ } catch(e) {
+ if (e.name.indexOf("ReferenceError") !== -1)
+ hasThrown = true;
+ }
+ assert(hasThrown);
+}
+noInline(shouldThrowTDZ);
+
+function foo(a = function() { return c; }, b = a(), ...c) {
+ return a();
+}
+noInline(foo);
+
+function baz(a = function() { return b; }, ...b) {
+ return a();
+}
+
+for (let i = 0; i < 1000; i++) {
+ shouldThrowTDZ(function() { foo(undefined, undefined, 10, 20); });
+ let o = {x: 20};
+ let result = baz(undefined, 10, o, "baz");
+ assert(result.length === 3);
+ assert(result[0] === 10);
+ assert(result[1] === o);
+ assert(result[2] === "baz");
+}
diff --git a/Source/JavaScriptCore/tests/stress/rest-parameter-basics.js b/Source/JavaScriptCore/tests/stress/rest-parameter-basics.js
new file mode 100644
index 0000000..e8f1007
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/rest-parameter-basics.js
@@ -0,0 +1,103 @@
+function assert(b) {
+ if (!b)
+ throw new Error("Bad assertion")
+}
+noInline(assert);
+
+function foo(a, ...b) {
+ return b;
+}
+noInline(foo);
+
+function bar(a, ...b) {
+ return a + b[0];
+}
+noInline(bar);
+
+function baz(a, ...b) {
+ function capture() { return b; }
+ assert(b[0] === capture()[0]);
+ return a + b[0];
+}
+noInline(baz);
+
+function jaz(a, ...b) {
+ function capture() { return a + b[0]; }
+ assert(capture() === a + b[0]);
+ return a + b[0];
+}
+noInline(jaz);
+
+function kaz(a = 10, ...b) {
+ return a + b[0]
+}
+noInline(kaz);
+
+function raz(a = 10, ...b) {
+ function capture() { return a + b[0]; }
+ assert(capture() === a + b[0]);
+ return a + b[0];
+}
+noInline(raz);
+
+function restLength(a, ...b) {
+ return b.length;
+}
+noInline(restLength);
+
+function testArgumentsObject(...args) {
+ assert(args.length === arguments.length);
+ for (let i = 0; i < args.length; i++)
+ assert(args[i] === arguments[i]);
+}
+noInline(testArgumentsObject);
+
+function strictModeLikeArgumentsObject(a, ...args) {
+ assert(arguments[0] === a);
+ a = "a";
+ assert(arguments[0] !== a);
+ assert(arguments[0] === 20);
+ assert(arguments.length === 2);
+ assert(args.length === 1);
+ assert(arguments[1] === args[0]);
+ arguments[1] = "b";
+ assert(args[0] !== "b");
+}
+noInline(strictModeLikeArgumentsObject);
+
+for (let i = 0; i < 10000; i++) {
+ let a1 = foo(10, 20);
+ assert(a1 instanceof Array);
+ assert(a1.length === 1);
+ assert(a1[0] === 20);
+
+ let a2 = foo(10);
+ assert(a2 instanceof Array);
+ assert(a2.length === 0);
+
+ let a3 = bar(10, 20);
+ assert(a3 === 30);
+
+ let a4 = baz(10, 20);
+ assert(a4 === 30);
+
+ let a5 = jaz("hello", "world");
+ assert(a5 === "helloworld");
+
+ let a6 = kaz(undefined, 40);
+ assert(a6 === 50);
+
+ let a7 = kaz(undefined, 40);
+ assert(a7 === 50);
+
+ assert(restLength() === 0);
+ assert(restLength(1) === 0);
+ assert(restLength(1, 1) === 1);
+ assert(restLength(1, 1, 1) === 2);
+ assert(restLength(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) === 20);
+
+ let obj = {foo: 40};
+ testArgumentsObject("hello", obj, 100, 12.34, "world", obj, [1, 2, 3]);
+
+ strictModeLikeArgumentsObject(20, 30);
+}
diff --git a/Source/JavaScriptCore/tests/stress/rest-parameter-inlined.js b/Source/JavaScriptCore/tests/stress/rest-parameter-inlined.js
new file mode 100644
index 0000000..92f266b
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/rest-parameter-inlined.js
@@ -0,0 +1,38 @@
+function assert(b) {
+ if (!b)
+ throw new Error("Bad assertion")
+}
+noInline(assert);
+
+function bar(...rest) {
+ return rest;
+}
+
+function foo(a, b, c) {
+ return bar(a, b, c);
+}
+noInline(foo);
+
+for (let i = 0; i < 10000; i++) {
+ let result = foo(10, 20, 30);
+ assert(result.length === 3);
+ assert(result[0] === 10);
+ assert(result[1] === 20);
+ assert(result[2] === 30);
+}
+
+function baz(...rest) {
+ return rest;
+}
+function jaz(a, b, c) {
+ return baz.apply(null, Array.prototype.slice.call(arguments));
+}
+noInline(jaz);
+
+for (let i = 0; i < 50000; i++) {
+ let result = jaz(10, 20, 30);
+ assert(result.length === 3);
+ assert(result[0] === 10);
+ assert(result[1] === 20);
+ assert(result[2] === 30);
+}