Diff
Modified: trunk/LayoutTests/ChangeLog (187350 => 187351)
--- trunk/LayoutTests/ChangeLog 2015-07-24 18:39:41 UTC (rev 187350)
+++ trunk/LayoutTests/ChangeLog 2015-07-24 18:40:58 UTC (rev 187351)
@@ -1,3 +1,16 @@
+2015-07-24 Saam barati <[email protected]>
+
+ [ES6] Add support for default parameters
+ https://bugs.webkit.org/show_bug.cgi?id=38409
+
+ Reviewed by Filip Pizlo.
+
+ * js/destructuring-assignment-default-values-expected.txt:
+ * js/parser-syntax-check-expected.txt:
+ * js/script-tests/destructuring-assignment-default-values.js:
+ (shouldThrow): Deleted.
+ * js/script-tests/parser-syntax-check.js:
+
2015-07-24 Joseph Pecoraro <[email protected]>
CSS "content" property is missing in getComputedStyles
Modified: trunk/LayoutTests/js/destructuring-assignment-default-values-expected.txt (187350 => 187351)
--- trunk/LayoutTests/js/destructuring-assignment-default-values-expected.txt 2015-07-24 18:39:41 UTC (rev 187350)
+++ trunk/LayoutTests/js/destructuring-assignment-default-values-expected.txt 2015-07-24 18:40:58 UTC (rev 187351)
@@ -33,11 +33,6 @@
PASS 120,120
PASS 3628800,3628800
PASS 1,1
-PASS true,true
-PASS true,true
-PASS true,true
-PASS true,true
-PASS true,true
PASS successfullyParsed is true
TEST COMPLETE
Modified: trunk/LayoutTests/js/parser-syntax-check-expected.txt (187350 => 187351)
--- trunk/LayoutTests/js/parser-syntax-check-expected.txt 2015-07-24 18:39:41 UTC (rev 187350)
+++ trunk/LayoutTests/js/parser-syntax-check-expected.txt 2015-07-24 18:40:58 UTC (rev 187351)
@@ -867,6 +867,34 @@
PASS Invalid: "function f() { ({set [x](x){}}) }"
PASS Invalid: "({[...x]: 1})"
PASS Invalid: "function f() { ({[...x]: 1}) }"
+PASS Invalid: "function f({a, a}) {}"
+PASS Invalid: "function f() { function f({a, a}) {} }"
+PASS Invalid: "function f({a}, a) {}"
+PASS Invalid: "function f() { function f({a}, a) {} }"
+PASS Invalid: "function f([b, b]) {}"
+PASS Invalid: "function f() { function f([b, b]) {} }"
+PASS Invalid: "function f([b], b) {}"
+PASS Invalid: "function f() { function f([b], b) {} }"
+PASS Invalid: "function f({a: {b}}, b) {}"
+PASS Invalid: "function f() { function f({a: {b}}, b) {} }"
+PASS Valid: "function f(a, b = 20) {}"
+PASS Valid: "function f() { function f(a, b = 20) {} }"
+PASS Valid: "function f(a = 20, b = a) {}"
+PASS Valid: "function f() { function f(a = 20, b = a) {} }"
+PASS Valid: "function f({a = 20} = {a: 40}, b = a) {}"
+PASS Valid: "function f() { function f({a = 20} = {a: 40}, b = a) {} }"
+PASS Valid: "function f([a,b,c] = [1,2,3]) {}"
+PASS Valid: "function f() { function f([a,b,c] = [1,2,3]) {} }"
+PASS Invalid: "function f(a, a=20) {}"
+PASS Invalid: "function f() { function f(a, a=20) {} }"
+PASS Invalid: "function f({a} = 20, a=20) {}"
+PASS Invalid: "function f() { function f({a} = 20, a=20) {} }"
+PASS Invalid: "function f([a,b,a] = [1,2,3]) {}"
+PASS Invalid: "function f() { function f([a,b,a] = [1,2,3]) {} }"
+PASS Invalid: "function f([a,b,c] = [1,2,3], a) {}"
+PASS Invalid: "function f() { function f([a,b,c] = [1,2,3], a) {} }"
+PASS Invalid: "function f([a,b,c] = [1,2,3], {a}) {}"
+PASS Invalid: "function f() { function f([a,b,c] = [1,2,3], {a}) {} }"
PASS Valid: "( function(){ return this || eval('this'); }().x = 'y' )"
PASS Valid: "function f() { ( function(){ return this || eval('this'); }().x = 'y' ) }"
PASS Invalid: "function(){ return this || eval('this'); }().x = 'y'"
Modified: trunk/LayoutTests/js/script-tests/destructuring-assignment-default-values.js (187350 => 187351)
--- trunk/LayoutTests/js/script-tests/destructuring-assignment-default-values.js 2015-07-24 18:39:41 UTC (rev 187350)
+++ trunk/LayoutTests/js/script-tests/destructuring-assignment-default-values.js 2015-07-24 18:40:58 UTC (rev 187351)
@@ -105,21 +105,3 @@
assert(test8(5).p, 120);
assert(test8(10).p, 3628800);
assert(test8(0).p, 1);
-
-// FIXME: When we support default values in function parameters, we should remove this test.
-function shouldThrow(str) {
- var thrown = false;
- try {
- eval(str);
- } catch(e) {
- thrown = true;
- }
-
- assert(true, thrown);
-}
-
-shouldThrow("(function({x = 40}) {})");
-shouldThrow("(function({y}, {x = 40}) {}");
-shouldThrow("(function([y], [x = 40]) {})");
-shouldThrow("(function({y}, {x: {z = 50}}) {})");
-shouldThrow("(function({y}, {x: [z = 50]}) {})");
Modified: trunk/LayoutTests/js/script-tests/parser-syntax-check.js (187350 => 187351)
--- trunk/LayoutTests/js/script-tests/parser-syntax-check.js 2015-07-24 18:39:41 UTC (rev 187350)
+++ trunk/LayoutTests/js/script-tests/parser-syntax-check.js 2015-07-24 18:40:58 UTC (rev 187351)
@@ -526,6 +526,20 @@
invalid("({set [x](){}})")
invalid("({set [x](x){}})")
invalid("({[...x]: 1})")
+invalid("function f({a, a}) {}");
+invalid("function f({a}, a) {}");
+invalid("function f([b, b]) {}");
+invalid("function f([b], b) {}");
+invalid("function f({a: {b}}, b) {}");
+valid("function f(a, b = 20) {}");
+valid("function f(a = 20, b = a) {}");
+valid("function f({a = 20} = {a: 40}, b = a) {}");
+valid("function f([a,b,c] = [1,2,3]) {}");
+invalid("function f(a, a=20) {}");
+invalid("function f({a} = 20, a=20) {}");
+invalid("function f([a,b,a] = [1,2,3]) {}");
+invalid("function f([a,b,c] = [1,2,3], a) {}");
+invalid("function f([a,b,c] = [1,2,3], {a}) {}");
valid("( function(){ return this || eval('this'); }().x = 'y' )");
invalid("function(){ return this || eval('this'); }().x = 'y'");
invalid("1 % +");
Modified: trunk/Source/_javascript_Core/ChangeLog (187350 => 187351)
--- trunk/Source/_javascript_Core/ChangeLog 2015-07-24 18:39:41 UTC (rev 187350)
+++ trunk/Source/_javascript_Core/ChangeLog 2015-07-24 18:40:58 UTC (rev 187351)
@@ -1,3 +1,89 @@
+2015-07-24 Saam barati <[email protected]>
+
+ [ES6] Add support for default parameters
+ https://bugs.webkit.org/show_bug.cgi?id=38409
+
+ Reviewed by Filip Pizlo.
+
+ This patch implements ES6 default parameters according to the ES6
+ specification. This patch builds off the components introduced with
+ "let" scoping and parsing function parameters in the same parser
+ arena as the function itself. "let" scoping allows functions with default
+ parameter values to place their parameters under the TDZ. Parsing function
+ parameters in the same parser arena allows the FunctionParameters AST node
+ refer to ExpressionNodes.
+
+ The most subtle part of this patch is how we allocate lexical environments
+ when functions have default parameter values. If a function has default
+ parameter values then there must be a separate lexical environment for
+ its parameters. Then, the function's "var" lexical environment must have
+ the parameter lexical environment as its parent. The BytecodeGenerator
+ takes great care to not allocate the "var" lexical environment before its
+ really needed.
+
+ The "arguments" object for a function with default parameters will never be
+ a mapped arugments object. It will always be a cloned arugments object.
+
+ * bytecompiler/BytecodeGenerator.cpp:
+ (JSC::BytecodeGenerator::generate):
+ (JSC::BytecodeGenerator::BytecodeGenerator):
+ (JSC::BytecodeGenerator::~BytecodeGenerator):
+ (JSC::BytecodeGenerator::initializeDefaultParameterValuesAndSetupFunctionScopeStack):
+ (JSC::BytecodeGenerator::initializeNextParameter):
+ (JSC::BytecodeGenerator::initializeVarLexicalEnvironment):
+ (JSC::BytecodeGenerator::visibleNameForParameter):
+ (JSC::BytecodeGenerator::emitLoadGlobalObject):
+ (JSC::BytecodeGenerator::pushLexicalScopeInternal):
+ (JSC::BytecodeGenerator::pushLexicalScope):
+ (JSC::BytecodeGenerator::popLexicalScope):
+ * bytecompiler/BytecodeGenerator.h:
+ (JSC::BytecodeGenerator::lastOpcodeID):
+ * bytecompiler/NodesCodegen.cpp:
+ (JSC::FunctionNode::emitBytecode):
+ * jit/JITOperations.cpp:
+ * parser/ASTBuilder.h:
+ (JSC::ASTBuilder::createElementList):
+ (JSC::ASTBuilder::createFormalParameterList):
+ (JSC::ASTBuilder::appendParameter):
+ (JSC::ASTBuilder::createClause):
+ (JSC::ASTBuilder::createClauseList):
+ * parser/Nodes.h:
+ (JSC::FunctionParameters::size):
+ (JSC::FunctionParameters::at):
+ (JSC::FunctionParameters::hasDefaultParameterValues):
+ (JSC::FunctionParameters::append):
+ * parser/Parser.cpp:
+ (JSC::Parser<LexerType>::parseVariableDeclarationList):
+ (JSC::Parser<LexerType>::createBindingPattern):
+ (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression):
+ (JSC::Parser<LexerType>::parseDestructuringPattern):
+ (JSC::Parser<LexerType>::parseFormalParameters):
+ (JSC::Parser<LexerType>::parseFunctionParameters):
+ * parser/Parser.h:
+ (JSC::Scope::declareParameter):
+ * parser/SyntaxChecker.h:
+ (JSC::SyntaxChecker::createElementList):
+ (JSC::SyntaxChecker::createFormalParameterList):
+ (JSC::SyntaxChecker::appendParameter):
+ (JSC::SyntaxChecker::createClause):
+ (JSC::SyntaxChecker::createClauseList):
+ * tests/stress/es6-default-parameters.js: Added.
+ (assert):
+ (shouldThrow):
+ (shouldThrowSyntaxError):
+ (shouldThrowTDZ):
+ (basic):
+ (basicFunctionCaptureInDefault.basicFunctionCaptureInDefault.basicCaptured):
+ (basicCaptured.basicCaptured.tricky):
+ (strict):
+ (playground):
+ (scoping):
+ (augmentsArguments1):
+ (augmentsArguments2):
+ (augmentsArguments3):
+ (augmentsArguments4):
+ (augmentsArguments5):
+
2015-07-24 Xabier Rodriguez Calvar <[email protected]>
Remove JS Promise constructor unused piece of code
Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp (187350 => 187351)
--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp 2015-07-24 18:39:41 UTC (rev 187350)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp 2015-07-24 18:40:58 UTC (rev 187351)
@@ -63,7 +63,7 @@
ParserError BytecodeGenerator::generate()
{
SamplingRegion samplingRegion("Bytecode Generation");
-
+
m_codeBlock->setThisRegister(m_thisRegister.virtualRegister());
// If we have declared a variable named "arguments" and we are using arguments then we should
@@ -71,11 +71,6 @@
if (m_needToInitializeArguments)
initializeVariable(variable(propertyNames().arguments), m_argumentsRegister);
- for (size_t i = 0; i < m_destructuringParameters.size(); i++) {
- auto& entry = m_destructuringParameters[i];
- entry.second->bindValue(*this, entry.first.get());
- }
-
pushLexicalScope(m_scopeNode, true);
{
@@ -215,13 +210,15 @@
m_codeBlock->setSymbolTableConstantIndex(symbolTableConstantIndex);
Vector<Identifier> boundParameterProperties;
- FunctionParameters& parameters = *functionNode->parameters();
- for (size_t i = 0; i < parameters.size(); i++) {
- auto pattern = parameters.at(i);
- if (pattern->isBindingNode())
- continue;
- pattern->collectBoundIdentifiers(boundParameterProperties);
- continue;
+ FunctionParameters& parameters = *functionNode->parameters();
+ if (!parameters.hasDefaultParameterValues()) {
+ // If we do have default parameters, they will be allocated in a separate scope.
+ for (size_t i = 0; i < parameters.size(); i++) {
+ auto pattern = parameters.at(i).first;
+ if (pattern->isBindingNode())
+ continue;
+ pattern->collectBoundIdentifiers(boundParameterProperties);
+ }
}
bool shouldCaptureSomeOfTheThings = m_shouldEmitDebugHooks || m_codeBlock->needsFullScopeChain();
@@ -265,45 +262,36 @@
if (shouldCaptureSomeOfTheThings) {
m_lexicalEnvironmentRegister = addVar();
- m_codeBlock->setActivationRegister(m_lexicalEnvironmentRegister->virtualRegister());
- emitOpcode(op_create_lexical_environment);
- instructions().append(m_lexicalEnvironmentRegister->index());
- instructions().append(scopeRegister()->index());
- instructions().append(symbolTableConstantIndex);
- instructions().append(addConstantValue(jsUndefined())->index());
-
- emitOpcode(op_mov);
- instructions().append(scopeRegister()->index());
- instructions().append(m_lexicalEnvironmentRegister->index());
+ // We can allocate the "var" environment if we don't have default parameter expressions. If we have
+ // default parameter expressions, we have to hold off on allocating the "var" environment because
+ // the parent scope of the "var" environment is the parameter environment.
+ if (!parameters.hasDefaultParameterValues())
+ initializeVarLexicalEnvironment(symbolTableConstantIndex);
}
// Make sure the code block knows about all of our parameters, and make sure that parameters
// 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) {
- auto pattern = parameters.at(i);
- RegisterID* reg = initializeNextParameter();
- if (!pattern->isBindingNode())
- m_destructuringParameters.append(std::make_pair(reg, pattern));
- }
+ for (unsigned i = 0; i < parameters.size(); ++i)
+ initializeNextParameter();
// Figure out some interesting facts about our arguments.
bool capturesAnyArgumentByName = false;
if (functionNode->hasCapturedVariables()) {
FunctionParameters& parameters = *functionNode->parameters();
for (size_t i = 0; i < parameters.size(); ++i) {
- auto pattern = parameters.at(i);
+ auto pattern = parameters.at(i).first;
if (!pattern->isBindingNode())
continue;
const Identifier& ident = static_cast<const BindingNode*>(pattern)->boundProperty();
capturesAnyArgumentByName |= captures(ident.impl());
}
}
-
+
if (capturesAnyArgumentByName)
ASSERT(m_lexicalEnvironmentRegister);
-
+
// Need to know what our functions are called. Parameters have some goofy behaviors when it
// comes to functions of the same name.
for (FunctionBodyNode* function : functionNode->functionStack())
@@ -320,7 +308,7 @@
m_argumentsRegister->ref();
}
- if (needsArguments && !codeBlock->isStrictMode()) {
+ if (needsArguments && !codeBlock->isStrictMode() && !parameters.hasDefaultParameterValues()) {
// 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.
@@ -335,7 +323,7 @@
for (unsigned i = 0; i < parameters.size(); ++i) {
ScopeOffset offset = functionSymbolTable->takeNextScopeOffset();
functionSymbolTable->setArgumentOffset(vm, i, offset);
- if (UniquedStringImpl* name = visibleNameForParameter(parameters.at(i))) {
+ if (UniquedStringImpl* name = visibleNameForParameter(parameters.at(i).first)) {
VarOffset varOffset(offset);
SymbolTableEntry entry(varOffset);
// Stores to these variables via the ScopedArguments object will not do
@@ -363,18 +351,20 @@
// We're going to put all parameters into the DirectArguments object. First ensure
// that the symbol table knows that this is happening.
for (unsigned i = 0; i < parameters.size(); ++i) {
- if (UniquedStringImpl* name = visibleNameForParameter(parameters.at(i)))
+ if (UniquedStringImpl* name = visibleNameForParameter(parameters.at(i).first))
functionSymbolTable->set(name, SymbolTableEntry(VarOffset(DirectArgumentsOffset(i))));
}
emitOpcode(op_create_direct_arguments);
instructions().append(m_argumentsRegister->index());
}
- } else {
+ } else if (!parameters.hasDefaultParameterValues()) {
// Create the formal parameters the normal way. Any of them could be captured, or not. If
- // captured, lift them into the scope.
+ // captured, lift them into the scope. We can not do this if we have default parameter expressions
+ // because when default parameter expressions exist, they belong in their own lexical environment
+ // separate from the "var" lexical environment.
for (unsigned i = 0; i < parameters.size(); ++i) {
- UniquedStringImpl* name = visibleNameForParameter(parameters.at(i));
+ UniquedStringImpl* name = visibleNameForParameter(parameters.at(i).first);
if (!name)
continue;
@@ -387,7 +377,7 @@
ScopeOffset offset = functionSymbolTable->takeNextScopeOffset();
const Identifier& ident =
- static_cast<const BindingNode*>(parameters.at(i))->boundProperty();
+ static_cast<const BindingNode*>(parameters.at(i).first)->boundProperty();
functionSymbolTable->set(name, SymbolTableEntry(VarOffset(offset)));
emitOpcode(op_put_to_scope);
@@ -400,15 +390,17 @@
}
}
- if (needsArguments && codeBlock->isStrictMode()) {
+ if (needsArguments && (codeBlock->isStrictMode() || parameters.hasDefaultParameterValues())) {
// Allocate an out-of-bands arguments object.
emitOpcode(op_create_out_of_band_arguments);
instructions().append(m_argumentsRegister->index());
}
// Now declare all variables.
- for (const Identifier& ident : boundParameterProperties)
+ for (const Identifier& ident : boundParameterProperties) {
+ ASSERT(!parameters.hasDefaultParameterValues());
createVariable(ident, varKind(ident.impl()), functionSymbolTable);
+ }
for (FunctionBodyNode* function : functionNode->functionStack()) {
const Identifier& ident = function->ident();
createVariable(ident, varKind(ident.impl()), functionSymbolTable);
@@ -421,7 +413,7 @@
// Variables named "arguments" are never const.
createVariable(Identifier::fromUid(m_vm, entry.key.get()), varKind(entry.key.get()), functionSymbolTable, IgnoreExisting);
}
-
+
// There are some variables that need to be preinitialized to something other than Undefined:
//
// - "arguments": unless it's used as a function or parameter, this should refer to the
@@ -486,7 +478,7 @@
bool haveParameterNamedArguments = false;
for (unsigned i = 0; i < parameters.size(); ++i) {
- UniquedStringImpl* name = visibleNameForParameter(parameters.at(i));
+ UniquedStringImpl* name = visibleNameForParameter(parameters.at(i).first);
if (name == propertyNames().arguments.impl()) {
haveParameterNamedArguments = true;
break;
@@ -499,7 +491,7 @@
m_needToInitializeArguments = true;
}
}
-
+
m_newTargetRegister = addVar();
if (isConstructor()) {
emitMove(m_newTargetRegister, &m_thisRegister);
@@ -517,10 +509,8 @@
instructions().append(0);
}
- if (m_lexicalEnvironmentRegister)
- pushScopedControlFlowContext();
- m_symbolTableStack.append(SymbolTableStackEntry{ Strong<SymbolTable>(*m_vm, functionSymbolTable), m_lexicalEnvironmentRegister, false, symbolTableConstantIndex });
m_TDZStack.append(std::make_pair(*parentScopeTDZVariables, false));
+ initializeDefaultParameterValuesAndSetupFunctionScopeStack(parameters, functionNode, functionSymbolTable, symbolTableConstantIndex, captures);
}
BytecodeGenerator::BytecodeGenerator(VM& vm, EvalNode* evalNode, UnlinkedEvalCodeBlock* codeBlock, DebuggerMode debuggerMode, ProfilerMode profilerMode, const VariableEnvironment* parentScopeTDZVariables)
@@ -564,6 +554,98 @@
{
}
+void BytecodeGenerator::initializeDefaultParameterValuesAndSetupFunctionScopeStack(
+ FunctionParameters& parameters, FunctionNode* functionNode, SymbolTable* functionSymbolTable,
+ int symbolTableConstantIndex, const std::function<bool (UniquedStringImpl*)>& captures)
+{
+ Vector<std::pair<Identifier, RefPtr<RegisterID>>> valuesToMoveIntoVars;
+ if (parameters.hasDefaultParameterValues()) {
+ // Refer to the ES6 spec section 9.2.12: http://www.ecma-international.org/ecma-262/6.0/index.html#sec-functiondeclarationinstantiation
+ // This implements step 21.
+ VariableEnvironment environment;
+ Vector<Identifier> allParameterNames;
+ for (unsigned i = 0; i < parameters.size(); i++)
+ parameters.at(i).first->collectBoundIdentifiers(allParameterNames);
+ IdentifierSet parameterSet;
+ for (auto& ident : allParameterNames) {
+ parameterSet.add(ident.impl());
+ auto addResult = environment.add(ident);
+ addResult.iterator->value.setIsLet(); // When we have default parameter expressions, parameters act like "let" variables.
+ if (captures(ident.impl()))
+ addResult.iterator->value.setIsCaptured();
+ }
+
+ // This implements step 25 of section 9.2.12.
+ pushLexicalScopeInternal(environment, true, nullptr);
+
+ RefPtr<RegisterID> temp = newTemporary();
+ for (unsigned i = 0; i < parameters.size(); i++) {
+ std::pair<DestructuringPatternNode*, ExpressionNode*> parameter = parameters.at(i);
+ RefPtr<RegisterID> parameterValue = ®isterFor(virtualRegisterForArgument(1 + i));
+ emitMove(temp.get(), parameterValue.get());
+ if (parameter.second) {
+ RefPtr<RegisterID> condition = emitIsUndefined(newTemporary(), parameterValue.get());
+ RefPtr<Label> skipDefaultParameterBecauseNotUndefined = newLabel();
+ emitJumpIfFalse(condition.get(), skipDefaultParameterBecauseNotUndefined.get());
+ emitNode(temp.get(), parameter.second);
+ emitLabel(skipDefaultParameterBecauseNotUndefined.get());
+ }
+
+ parameter.first->bindValue(*this, temp.get());
+ }
+
+ // Final act of weirdness for default parameters. If a "var" also
+ // has the same name as a parameter, it should start out as the
+ // value of that parameter. Note, though, that they will be distinct
+ // bindings.
+ // This is step 28 of section 9.2.12.
+ for (auto& entry : functionNode->varDeclarations()) {
+ if (!entry.value.isVar()) // This is either a parameter or callee.
+ continue;
+
+ if (parameterSet.contains(entry.key)) {
+ Identifier ident = Identifier::fromUid(m_vm, entry.key.get());
+ Variable var = variable(ident);
+ RegisterID* scope = emitResolveScope(nullptr, var);
+ RefPtr<RegisterID> value = emitGetFromScope(newTemporary(), scope, var, DoNotThrowIfNotFound);
+ valuesToMoveIntoVars.append(std::make_pair(ident, value));
+ }
+ }
+
+ // Functions with default parameter expressions must have a separate environment
+ // record for parameters and "var"s. The "var" environment record must have the
+ // parameter environment record as its parent.
+ // See step 28 of section 9.2.12.
+ if (m_lexicalEnvironmentRegister)
+ initializeVarLexicalEnvironment(symbolTableConstantIndex);
+ }
+
+ if (m_lexicalEnvironmentRegister)
+ pushScopedControlFlowContext();
+ m_symbolTableStack.append(SymbolTableStackEntry{ Strong<SymbolTable>(*m_vm, functionSymbolTable), m_lexicalEnvironmentRegister, false, symbolTableConstantIndex });
+
+ // This completes step 28 of section 9.2.12.
+ for (unsigned i = 0; i < valuesToMoveIntoVars.size(); i++) {
+ ASSERT(parameters.hasDefaultParameterValues());
+ Variable var = variable(valuesToMoveIntoVars[i].first);
+ RegisterID* scope = emitResolveScope(nullptr, var);
+ emitPutToScope(scope, var, valuesToMoveIntoVars[i].second.get(), DoNotThrowIfNotFound);
+ }
+
+ if (!parameters.hasDefaultParameterValues()) {
+ ASSERT(!valuesToMoveIntoVars.size());
+ // Initialize destructuring parameters the old way as if we don't have any default parameter values.
+ // 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()) {
+ RefPtr<RegisterID> parameterValue = ®isterFor(virtualRegisterForArgument(1 + i));
+ pattern->bindValue(*this, parameterValue.get());
+ }
+ }
+ }
+}
+
RegisterID* BytecodeGenerator::initializeNextParameter()
{
VirtualRegister reg = virtualRegisterForArgument(m_codeBlock->numParameters());
@@ -573,6 +655,21 @@
return ¶meter;
}
+void BytecodeGenerator::initializeVarLexicalEnvironment(int symbolTableConstantIndex)
+{
+ RELEASE_ASSERT(m_lexicalEnvironmentRegister);
+ m_codeBlock->setActivationRegister(m_lexicalEnvironmentRegister->virtualRegister());
+ emitOpcode(op_create_lexical_environment);
+ instructions().append(m_lexicalEnvironmentRegister->index());
+ instructions().append(scopeRegister()->index());
+ instructions().append(symbolTableConstantIndex);
+ instructions().append(addConstantValue(jsUndefined())->index());
+
+ emitOpcode(op_mov);
+ instructions().append(scopeRegister()->index());
+ instructions().append(m_lexicalEnvironmentRegister->index());
+}
+
UniquedStringImpl* BytecodeGenerator::visibleNameForParameter(DestructuringPatternNode* pattern)
{
if (pattern->isBindingNode()) {
@@ -1258,9 +1355,8 @@
return m_globalObjectRegister;
}
-void BytecodeGenerator::pushLexicalScope(VariableEnvironmentNode* node, bool canOptimizeTDZChecks, RegisterID** constantSymbolTableResult)
+void BytecodeGenerator::pushLexicalScopeInternal(VariableEnvironment& environment, bool canOptimizeTDZChecks, RegisterID** constantSymbolTableResult)
{
- VariableEnvironment& environment = node->lexicalVariables();
if (!environment.size())
return;
@@ -1334,6 +1430,12 @@
}
}
+void BytecodeGenerator::pushLexicalScope(VariableEnvironmentNode* node, bool canOptimizeTDZChecks, RegisterID** constantSymbolTableResult)
+{
+ VariableEnvironment& environment = node->lexicalVariables();
+ pushLexicalScopeInternal(environment, canOptimizeTDZChecks, constantSymbolTableResult);
+}
+
void BytecodeGenerator::popLexicalScope(VariableEnvironmentNode* node)
{
VariableEnvironment& environment = node->lexicalVariables();
Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h (187350 => 187351)
--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h 2015-07-24 18:39:41 UTC (rev 187350)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h 2015-07-24 18:40:58 UTC (rev 187351)
@@ -621,6 +621,9 @@
OpcodeID lastOpcodeID() const { return m_lastOpcodeID; }
+ private:
+ void pushLexicalScopeInternal(VariableEnvironment&, bool canOptimizeTDZChecks, RegisterID** constantSymbolTableResult);
+ public:
void pushLexicalScope(VariableEnvironmentNode*, bool canOptimizeTDZChecks, RegisterID** constantSymbolTableResult = nullptr);
void popLexicalScope(VariableEnvironmentNode*);
void prepareLexicalScopeForNextForLoopIteration(VariableEnvironmentNode*, RegisterID* loopSymbolTable);
@@ -715,6 +718,9 @@
RegisterID* emitConstructVarargs(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, RegisterID* profileHookRegister, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd);
RegisterID* emitCallVarargs(OpcodeID, RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, RegisterID* profileHookRegister, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd);
+ void initializeVarLexicalEnvironment(int symbolTableConstantIndex);
+ void initializeDefaultParameterValuesAndSetupFunctionScopeStack(FunctionParameters&, FunctionNode*, SymbolTable*, int symbolTableConstantIndex, const std::function<bool (UniquedStringImpl*)>& captures);
+
public:
JSString* addStringConstant(const Identifier&);
JSTemplateRegistryKey* addTemplateRegistryKeyConstant(const TemplateRegistryKey&);
@@ -773,7 +779,6 @@
Vector<SwitchInfo> m_switchContextStack;
Vector<std::unique_ptr<ForInContext>> m_forInContextStack;
Vector<TryContext> m_tryContextStack;
- Vector<std::pair<RefPtr<RegisterID>, const DestructuringPatternNode*>> m_destructuringParameters;
enum FunctionVariableType : uint8_t { NormalFunctionVariable, GlobalFunctionVariable };
Vector<std::pair<FunctionBodyNode*, FunctionVariableType>> m_functionsToInitialize;
bool m_needToInitializeArguments { false };
Modified: trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp (187350 => 187351)
--- trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp 2015-07-24 18:39:41 UTC (rev 187350)
+++ trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp 2015-07-24 18:40:58 UTC (rev 187351)
@@ -3019,9 +3019,9 @@
if (generator.vm()->typeProfiler()) {
for (size_t i = 0; i < m_parameters->size(); i++) {
// FIXME: Handle Destructuring assignments into arguments.
- if (!m_parameters->at(i)->isBindingNode())
+ if (!m_parameters->at(i).first->isBindingNode())
continue;
- BindingNode* parameter = static_cast<BindingNode*>(m_parameters->at(i));
+ BindingNode* parameter = static_cast<BindingNode*>(m_parameters->at(i).first);
RegisterID reg(CallFrame::argumentOffset(i));
generator.emitProfileType(®, ProfileTypeBytecodeFunctionArgument, nullptr);
generator.emitTypeProfilerExpressionInfo(parameter->divotStart(), parameter->divotEnd());
Modified: trunk/Source/_javascript_Core/jit/JITOperations.cpp (187350 => 187351)
--- trunk/Source/_javascript_Core/jit/JITOperations.cpp 2015-07-24 18:39:41 UTC (rev 187350)
+++ trunk/Source/_javascript_Core/jit/JITOperations.cpp 2015-07-24 18:40:58 UTC (rev 187351)
@@ -687,11 +687,8 @@
EncodedJSValue JIT_OPERATION operationCallEval(ExecState* exec, ExecState* execCallee)
{
+ UNUSED_PARAM(exec);
- ASSERT_UNUSED(exec, exec->codeBlock()->codeType() != FunctionCode
- || !exec->codeBlock()->needsActivation()
- || exec->hasActivation());
-
execCallee->setCodeBlock(0);
if (!isHostFunction(execCallee->calleeAsValue(), globalFuncEval))
Modified: trunk/Source/_javascript_Core/parser/ASTBuilder.h (187350 => 187351)
--- trunk/Source/_javascript_Core/parser/ASTBuilder.h 2015-07-24 18:39:41 UTC (rev 187350)
+++ trunk/Source/_javascript_Core/parser/ASTBuilder.h 2015-07-24 18:40:58 UTC (rev 187351)
@@ -418,7 +418,10 @@
ElementNode* createElementList(ElementNode* elems, int elisions, ExpressionNode* expr) { return new (m_parserArena) ElementNode(elems, elisions, expr); }
FormalParameterList createFormalParameterList() { return new (m_parserArena) FunctionParameters(); }
- void appendParameter(FormalParameterList list, DestructuringPattern pattern) { list->append(pattern); }
+ void appendParameter(FormalParameterList list, DestructuringPattern pattern, ExpressionNode* defaultValue)
+ {
+ list->append(pattern, defaultValue);
+ }
CaseClauseNode* createClause(ExpressionNode* expr, JSC::SourceElements* statements) { return new (m_parserArena) CaseClauseNode(expr, statements); }
ClauseListNode* createClauseList(CaseClauseNode* clause) { return new (m_parserArena) ClauseListNode(clause); }
Modified: trunk/Source/_javascript_Core/parser/Nodes.h (187350 => 187351)
--- trunk/Source/_javascript_Core/parser/Nodes.h 2015-07-24 18:39:41 UTC (rev 187350)
+++ trunk/Source/_javascript_Core/parser/Nodes.h 2015-07-24 18:40:58 UTC (rev 187351)
@@ -1625,12 +1625,20 @@
public:
FunctionParameters();
ALWAYS_INLINE unsigned size() const { return m_patterns.size(); }
- ALWAYS_INLINE DestructuringPatternNode* at(unsigned index) { return m_patterns[index]; }
- ALWAYS_INLINE void append(DestructuringPatternNode* pattern) { ASSERT(pattern); m_patterns.append(pattern); }
+ ALWAYS_INLINE std::pair<DestructuringPatternNode*, ExpressionNode*> at(unsigned index) { return m_patterns[index]; }
+ bool hasDefaultParameterValues() const { return m_hasDefaultParameterValues; }
+ ALWAYS_INLINE void append(DestructuringPatternNode* pattern, ExpressionNode* defaultValue)
+ {
+ ASSERT(pattern);
+ m_patterns.append(std::make_pair(pattern, defaultValue));
+ if (defaultValue)
+ m_hasDefaultParameterValues = true;
+ }
private:
- Vector<DestructuringPatternNode*, 3> m_patterns;
+ Vector<std::pair<DestructuringPatternNode*, ExpressionNode*>, 3> m_patterns;
+ bool m_hasDefaultParameterValues { false };
};
class FunctionBodyNode final : public StatementNode, public ParserArenaDeletable {
Modified: trunk/Source/_javascript_Core/parser/Parser.cpp (187350 => 187351)
--- trunk/Source/_javascript_Core/parser/Parser.cpp 2015-07-24 18:39:41 UTC (rev 187350)
+++ trunk/Source/_javascript_Core/parser/Parser.cpp 2015-07-24 18:40:58 UTC (rev 187351)
@@ -576,7 +576,7 @@
}
} else {
lastIdent = 0;
- auto pattern = parseDestructuringPattern(context, destructuringKindFromDeclarationType(declarationType), assignmentContext);
+ auto pattern = parseDestructuringPattern(context, destructuringKindFromDeclarationType(declarationType), nullptr, nullptr, assignmentContext);
failIfFalse(pattern, "Cannot parse this destructuring pattern");
hasInitializer = match(EQUAL);
failIfTrue(declarationListContext == VarDeclarationContext && !hasInitializer, "Expected an initializer in destructuring variable declaration");
@@ -604,21 +604,22 @@
}
template <typename LexerType>
-template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::createBindingPattern(TreeBuilder& context, DestructuringKind kind, const Identifier& name, int depth, JSToken token, AssignmentContext bindingContext)
+template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::createBindingPattern(TreeBuilder& context, DestructuringKind kind, const Identifier& name, int depth, JSToken token, AssignmentContext bindingContext, const Identifier** duplicateIdentifier)
{
ASSERT(!name.isNull());
ASSERT(name.impl()->isAtomic() || name.impl()->isSymbol());
- if (depth) {
- if (kind == DestructureToVariables)
- failIfTrueIfStrict(declareVariable(&name) & DeclarationResult::InvalidStrictMode, "Cannot deconstruct to a variable named '", name.impl(), "' in strict mode");
- else if (kind == DestructureToLet || kind == DestructureToConst) {
- DeclarationResultMask declarationResult = declareVariable(&name, kind == DestructureToLet ? DeclarationType::LetDeclaration : DeclarationType::ConstDeclaration);
- if (declarationResult != DeclarationResult::Valid) {
- failIfTrueIfStrict(declarationResult & DeclarationResult::InvalidStrictMode, "Cannot destructure to a variable named '", name.impl(), "' in strict mode");
- failIfTrue(declarationResult & DeclarationResult::InvalidDuplicateDeclaration, "Cannot declare a lexical variable twice: '", name.impl(), "'");
- }
- } else if (kind == DestructureToParameters) {
+
+ if (kind == DestructureToVariables)
+ failIfTrueIfStrict(declareVariable(&name) & DeclarationResult::InvalidStrictMode, "Cannot declare a variable named '", name.impl(), "' in strict mode");
+ else if (kind == DestructureToLet || kind == DestructureToConst) {
+ DeclarationResultMask declarationResult = declareVariable(&name, kind == DestructureToLet ? DeclarationType::LetDeclaration : DeclarationType::ConstDeclaration);
+ if (declarationResult != DeclarationResult::Valid) {
+ failIfTrueIfStrict(declarationResult & DeclarationResult::InvalidStrictMode, "Cannot destructure to a variable named '", name.impl(), "' in strict mode");
+ failIfTrue(declarationResult & DeclarationResult::InvalidDuplicateDeclaration, "Cannot declare a lexical variable twice: '", name.impl(), "'");
+ }
+ } else if (kind == DestructureToParameters) {
+ if (depth) {
auto bindingResult = declareBoundParameter(&name);
if (bindingResult == Scope::StrictBindingFailed && strictMode()) {
semanticFailIfTrue(isEvalOrArguments(&name), "Cannot destructure to a parameter name '", name.impl(), "' in strict mode");
@@ -635,18 +636,7 @@
semanticFail("Cannot destructure to '", name.impl(), "' as it has already been declared");
semanticFail("Cannot destructure to a parameter named '", name.impl(), "'");
}
- }
-
- } else {
- if (kind == DestructureToVariables)
- failIfTrueIfStrict(declareVariable(&name) & DeclarationResult::InvalidStrictMode, "Cannot declare a variable named '", name.impl(), "' in strict mode");
- else if (kind == DestructureToLet || kind == DestructureToConst) {
- DeclarationResultMask declarationResult = declareVariable(&name, kind == DestructureToLet ? DeclarationType::LetDeclaration : DeclarationType::ConstDeclaration);
- if (declarationResult != DeclarationResult::Valid) {
- failIfTrueIfStrict(declarationResult & DeclarationResult::InvalidStrictMode, "Cannot destructure to a variable named '", name.impl(), "' in strict mode");
- failIfTrue(declarationResult & DeclarationResult::InvalidDuplicateDeclaration, "Cannot declare a lexical variable twice: '", name.impl(), "'");
- }
- } else if (kind == DestructureToParameters) {
+ } else {
DeclarationResultMask declarationResult = declareParameter(&name);
if ((declarationResult & DeclarationResult::InvalidStrictMode) && strictMode()) {
semanticFailIfTrue(isEvalOrArguments(&name), "Cannot destructure to a parameter name '", name.impl(), "' in strict mode");
@@ -657,6 +647,13 @@
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 context.createBindingLocation(token.m_location, name, token.m_startPosition, token.m_endPosition, bindingContext);
@@ -694,11 +691,11 @@
template <typename LexerType>
template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::tryParseDestructuringPatternExpression(TreeBuilder& context, AssignmentContext bindingContext)
{
- return parseDestructuringPattern(context, DestructureToExpressions, bindingContext);
+ return parseDestructuringPattern(context, DestructureToExpressions, nullptr, nullptr, bindingContext);
}
template <typename LexerType>
-template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseDestructuringPattern(TreeBuilder& context, DestructuringKind kind, AssignmentContext bindingContext, int depth)
+template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseDestructuringPattern(TreeBuilder& context, DestructuringKind kind, const Identifier** duplicateIdentifier, bool* hasDestructuringPattern, AssignmentContext bindingContext, int depth)
{
failIfStackOverflow();
int nonLHSCount = m_nonLHSCount;
@@ -709,6 +706,9 @@
auto arrayPattern = context.createArrayPattern(m_token.m_location);
next();
+ if (hasDestructuringPattern)
+ *hasDestructuringPattern = true;
+
bool restElementWasFound = false;
do {
@@ -724,7 +724,7 @@
if (UNLIKELY(match(DOTDOTDOT))) {
JSTokenLocation location = m_token.m_location;
next();
- auto innerPattern = parseDestructuringPattern(context, kind, bindingContext, depth + 1);
+ auto innerPattern = parseDestructuringPattern(context, kind, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1);
if (kind == DestructureToExpressions && !innerPattern)
return 0;
failIfFalse(innerPattern, "Cannot parse this destructuring pattern");
@@ -737,12 +737,11 @@
}
JSTokenLocation location = m_token.m_location;
- auto innerPattern = parseDestructuringPattern(context, kind, bindingContext, depth + 1);
+ auto innerPattern = parseDestructuringPattern(context, kind, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1);
if (kind == DestructureToExpressions && !innerPattern)
return 0;
failIfFalse(innerPattern, "Cannot parse this destructuring pattern");
TreeExpression defaultValue = parseDefaultValueForDestructuringPattern(context);
- failIfTrue(kind == DestructureToParameters && defaultValue, "Default values in destructuring parameters are currently not supported");
context.appendArrayPatternEntry(arrayPattern, location, innerPattern, defaultValue);
} while (consume(COMMA));
@@ -757,6 +756,9 @@
auto objectPattern = context.createObjectPattern(m_token.m_location);
next();
+ if (hasDestructuringPattern)
+ *hasDestructuringPattern = true;
+
do {
bool wasString = false;
@@ -772,9 +774,9 @@
JSToken identifierToken = m_token;
next();
if (consume(COLON))
- innerPattern = parseDestructuringPattern(context, kind, bindingContext, depth + 1);
+ innerPattern = parseDestructuringPattern(context, kind, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1);
else
- innerPattern = createBindingPattern(context, kind, *propertyName, depth, identifierToken, bindingContext);
+ innerPattern = createBindingPattern(context, kind, *propertyName, depth + 1, identifierToken, bindingContext, duplicateIdentifier);
} else {
JSTokenType tokenType = m_token.m_type;
switch (m_token.m_type) {
@@ -805,13 +807,12 @@
failWithMessage("Expected a ':' prior to a named destructuring property");
}
- innerPattern = parseDestructuringPattern(context, kind, bindingContext, depth + 1);
+ innerPattern = parseDestructuringPattern(context, kind, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1);
}
if (kind == DestructureToExpressions && !innerPattern)
return 0;
failIfFalse(innerPattern, "Cannot parse this destructuring pattern");
TreeExpression defaultValue = parseDefaultValueForDestructuringPattern(context);
- failIfTrue(kind == DestructureToParameters && defaultValue, "Default values in destructuring parameters are currently not supported");
ASSERT(propertyName);
context.appendObjectPatternEntry(objectPattern, location, wasString, *propertyName, innerPattern, defaultValue);
} while (consume(COMMA));
@@ -831,7 +832,7 @@
failWithMessage("Expected a parameter pattern or a ')' in parameter list");
}
failIfTrue(match(LET) && (kind == DestructureToLet || kind == DestructureToConst), "Can't use 'let' as an identifier name for a LexicalDeclaration");
- pattern = createBindingPattern(context, kind, *m_token.m_data.ident, depth, m_token, bindingContext);
+ pattern = createBindingPattern(context, kind, *m_token.m_data.ident, depth, m_token, bindingContext, duplicateIdentifier);
next();
break;
}
@@ -1446,17 +1447,32 @@
template <typename LexerType>
template <class TreeBuilder> bool Parser<LexerType>::parseFormalParameters(TreeBuilder& context, TreeFormalParameterList list, unsigned& parameterCount)
{
- auto parameter = parseDestructuringPattern(context, DestructureToParameters);
+#define failFromDuplicate() \
+ 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");\
+ }
+
+ const Identifier* duplicateParameter = nullptr;
+ bool hasDestructuringPattern = false;
+ auto parameter = parseDestructuringPattern(context, DestructureToParameters, &duplicateParameter, &hasDestructuringPattern);
failIfFalse(parameter, "Cannot parse parameter pattern");
- context.appendParameter(list, parameter);
+ auto defaultValue = parseDefaultValueForDestructuringPattern(context);
+ propagateError();
+ failFromDuplicate();
+ context.appendParameter(list, parameter, defaultValue);
parameterCount++;
while (consume(COMMA)) {
- parameter = parseDestructuringPattern(context, DestructureToParameters);
+ parameter = parseDestructuringPattern(context, DestructureToParameters, &duplicateParameter, &hasDestructuringPattern);
failIfFalse(parameter, "Cannot parse parameter pattern");
- context.appendParameter(list, parameter);
+ defaultValue = parseDefaultValueForDestructuringPattern(context);
+ propagateError();
+ failFromDuplicate();
+ context.appendParameter(list, parameter, defaultValue);
parameterCount++;
}
return true;
+#undef failFromDuplicate
}
template <typename LexerType>
@@ -1530,7 +1546,7 @@
functionInfo.parameterCount = 1;
auto parameter = parseDestructuringPattern(context, DestructureToParameters);
failIfFalse(parameter, "Cannot parse parameter pattern");
- context.appendParameter(parameterList, parameter);
+ context.appendParameter(parameterList, parameter, 0);
}
}
@@ -1547,9 +1563,13 @@
functionInfo.parameterCount = 0;
} else if (mode == SetterMode) {
failIfTrue(match(CLOSEPAREN), "setter functions must have one parameter");
- auto parameter = parseDestructuringPattern(context, DestructureToParameters);
+ const Identifier* duplicateParameter = nullptr;
+ auto parameter = parseDestructuringPattern(context, DestructureToParameters, &duplicateParameter);
failIfFalse(parameter, "setter functions must have one parameter");
- context.appendParameter(parameterList, parameter);
+ auto defaultValue = parseDefaultValueForDestructuringPattern(context);
+ propagateError();
+ semanticFailIfTrue(duplicateParameter && defaultValue, "Duplicate parameter '", duplicateParameter->impl(), "' not allowed in function with default parameter values");
+ context.appendParameter(parameterList, parameter, defaultValue);
functionInfo.parameterCount = 1;
failIfTrue(match(COMMA), "setter functions must have one parameter");
consumeOrFail(CLOSEPAREN, "Expected a ')' after a parameter declaration");
Modified: trunk/Source/_javascript_Core/parser/Parser.h (187350 => 187351)
--- trunk/Source/_javascript_Core/parser/Parser.h 2015-07-24 18:39:41 UTC (rev 187350)
+++ trunk/Source/_javascript_Core/parser/Parser.h 2015-07-24 18:40:58 UTC (rev 187351)
@@ -352,6 +352,8 @@
result |= DeclarationResult::InvalidStrictMode;
if (isArgumentsIdent)
m_shadowsArguments = true;
+ if (!addResult.isNewEntry)
+ result |= DeclarationResult::InvalidDuplicateDeclaration;
return result;
}
@@ -1048,8 +1050,8 @@
template <class TreeBuilder> TreeExpression parseVariableDeclarationList(TreeBuilder&, int& declarations, TreeDestructuringPattern& lastPattern, TreeExpression& lastInitializer, JSTextPosition& identStart, JSTextPosition& initStart, JSTextPosition& initEnd, VarDeclarationListContext, DeclarationType, bool& forLoopConstDoesNotHaveInitializer);
template <class TreeBuilder> TreeSourceElements parseArrowFunctionSingleExpressionBodySourceElements(TreeBuilder&);
template <class TreeBuilder> TreeExpression parseArrowFunctionExpression(TreeBuilder&);
- template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern createBindingPattern(TreeBuilder&, DestructuringKind, const Identifier&, int depth, JSToken, AssignmentContext);
- template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern parseDestructuringPattern(TreeBuilder&, DestructuringKind, AssignmentContext = AssignmentContext::DeclarationStatement, int depth = 0);
+ template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern createBindingPattern(TreeBuilder&, DestructuringKind, const Identifier&, int depth, JSToken, AssignmentContext, const Identifier** duplicateIdentifier);
+ template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern parseDestructuringPattern(TreeBuilder&, DestructuringKind, const Identifier** duplicateIdentifier = nullptr, bool* hasDestructuringPattern = nullptr, AssignmentContext = AssignmentContext::DeclarationStatement, int depth = 0);
template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern tryParseDestructuringPatternExpression(TreeBuilder&, AssignmentContext);
template <class TreeBuilder> NEVER_INLINE TreeExpression parseDefaultValueForDestructuringPattern(TreeBuilder&);
Modified: trunk/Source/_javascript_Core/parser/SyntaxChecker.h (187350 => 187351)
--- trunk/Source/_javascript_Core/parser/SyntaxChecker.h 2015-07-24 18:39:41 UTC (rev 187350)
+++ trunk/Source/_javascript_Core/parser/SyntaxChecker.h 2015-07-24 18:40:58 UTC (rev 187351)
@@ -219,7 +219,7 @@
int createElementList(int, int) { return ElementsListResult; }
int createElementList(int, int, int) { return ElementsListResult; }
int createFormalParameterList() { return FormalParameterListResult; }
- void appendParameter(int, DestructuringPattern) { }
+ void appendParameter(int, DestructuringPattern, int) { }
int createClause(int, int) { return ClauseResult; }
int createClauseList(int) { return ClauseListResult; }
int createClauseList(int, int) { return ClauseListResult; }
Added: trunk/Source/_javascript_Core/tests/stress/es6-default-parameters.js (0 => 187351)
--- trunk/Source/_javascript_Core/tests/stress/es6-default-parameters.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/es6-default-parameters.js 2015-07-24 18:40:58 UTC (rev 187351)
@@ -0,0 +1,244 @@
+function assert(cond, msg = "") {
+ if (!cond)
+ throw new Error(msg);
+}
+noInline(assert);
+
+function shouldThrow(func) {
+ var hadError = false;
+ try {
+ func()
+ } catch (e) {
+ hadError = true;
+ }
+ assert(hadError, "Did not throw");
+}
+noInline(shouldThrow);
+
+function shouldThrowSyntaxError(str) {
+ var hadError = false;
+ try {
+ eval(str);
+ } catch (e) {
+ if (e instanceof SyntaxError)
+ hadError = true;
+ }
+ assert(hadError, "Did not throw syntax error");
+}
+noInline(shouldThrowSyntaxError);
+
+function shouldThrowTDZ(func) {
+ var hasThrown = false;
+ try {
+ func();
+ } catch(e) {
+ if (e.name.indexOf("ReferenceError") !== -1)
+ hasThrown = true;
+ }
+ assert(hasThrown);
+}
+noInline(shouldThrowTDZ);
+
+function basic(x, y=x) {
+ assert(y === x, "basics don't work.")
+}
+basic(20);
+basic("hello");
+basic({foo: 20});
+basic(undefined);
+
+;(function(){
+ var scopeVariable = {hello: "world"};
+ function basicScope(x = scopeVariable) {
+ assert(x === scopeVariable);
+ }
+ basicScope();
+})();
+
+function basicFunctionCaptureInDefault(theArg = 20, y = function() {return theArg}) {
+ assert(theArg === y(), "y should return x.");
+ theArg = {};
+ assert(theArg === y(), "y should return x.");
+}
+basicFunctionCaptureInDefault()
+basicFunctionCaptureInDefault(undefined)
+
+function basicCaptured(x = 20, y = x) {
+ assert(x === y, "y should equal x");
+ function mutate() { x = "mutation"; }
+ mutate()
+ assert(x !== y, "y should not equal x");
+}
+basicCaptured()
+basicCaptured(undefined)
+
+function tricky(globalX = (globalX = "x"), y = (globalX = 20)) {
+ assert(globalX === 20);
+ assert(globalX === y);
+}
+shouldThrow(tricky);
+
+function strict(x, y = x) {
+ 'use strict';
+ assert(x === y);
+}
+strict(20);
+strict(undefined);
+
+function playground(x = "foo", y = "bar") {
+ return {x, y}
+}
+assert(playground().x === "foo")
+assert(playground(undefined).x === "foo")
+assert(playground(undefined, 20).x === "foo")
+assert(playground(null).x === null)
+assert(playground().y === "bar")
+assert(playground(undefined, undefined).y === "bar")
+assert(playground("hello", undefined).y === "bar")
+assert(playground("bar").x === playground(undefined, undefined).y)
+assert(playground(10).x === 10)
+assert(playground(undefined, 20).y === 20)
+assert(playground(undefined, null).y === null)
+
+function scoping(f = function() { return local;}) {
+ shouldThrow(f);
+ var local = 10;
+ shouldThrow(f);
+}
+scoping();
+
+function augmentsArguments1(x = 20) {
+ assert(x === 20);
+
+ arguments[0] = 10;
+ assert(x === 20);
+
+ x = 15;
+ assert(x === 15);
+ assert(arguments[0] === 10);
+}
+augmentsArguments1(undefined);
+
+function augmentsArguments2(x = 20) {
+ assert(x === 20);
+
+ arguments[0] = 10;
+ assert(x === 20);
+ assert(arguments[0] === 10);
+
+ x = 15;
+ assert(x === 15);
+ assert(arguments[0] === 10);
+
+ function augment() { x = 40 }
+ augment()
+ assert(x === 40);
+ assert(arguments[0] === 10);
+}
+augmentsArguments2(undefined);
+
+function augmentsArguments3(x = 10) {
+ assert(x === 10);
+
+ assert(arguments[0] === undefined);
+ x = 20;
+ assert(arguments[0] === undefined);
+}
+augmentsArguments3();
+
+function augmentsArguments4(x = 10) {
+ "use strict";
+ assert(x === 10);
+
+ assert(arguments[0] === undefined);
+ x = 20;
+ assert(arguments[0] === undefined);
+}
+augmentsArguments4();
+augmentsArguments4(undefined);
+
+function augmentsArguments5(x = 10) {
+ "use strict";
+ assert(x === 20);
+
+ assert(arguments[0] === 20);
+ x = 20;
+ assert(arguments[0] === 20);
+}
+augmentsArguments5(20);
+
+;(function () {
+ var outer = "outer";
+ function foo(a = outer, b = function() { return a; }, c = function(v) { a = v; }) {
+ var a;
+ assert(a === "outer");
+ a = 20;
+ assert(a === 20);
+ assert(b() === "outer");
+ c("hello");
+ assert(b() === "hello");
+ }
+
+ function bar(a = outer, b = function() { return a; }, c = function(v) { a = v; }) {
+ with({}) {
+ var a;
+ assert(a === "outer");
+ a = 20;
+ assert(a === 20);
+ assert(b() === "outer");
+ c("hello");
+ assert(b() === "hello");
+ }
+ }
+
+ function baz(x = function() { return y; }, y = "y") {
+ assert(x() === "y");
+ assert(x() === y);
+ assert(y === y);
+ }
+
+ function jaz(x = function() { return y; }, y = "y") {
+ return x;
+ }
+
+ function taz(x = 10, y = eval("x + 1")) {
+ assert(y === 11);
+ }
+
+ for (var i = 0; i < 1000; i++) {
+ foo();
+ bar();
+ baz();
+ assert(jaz(undefined, 20)() === 20);
+ assert(jaz(undefined, undefined)() === "y");
+ assert(jaz(undefined, {x: "x"})().x === "x");
+ taz();
+ }
+})();
+
+// TDZ errors.
+;(function() {
+ function basicError(x = y, y) { }
+ function basicError2(x = x) { }
+ function baz(z = {p: x}, x = z) {}
+ function bar(x = {p: [x]}) {}
+ function jaz(x = eval("y"), y) { }
+ function kaz(x = eval(";(function() { return y})();"), y) { }
+ for (var i = 0; i < 1000; i++) {
+ shouldThrowTDZ(basicError);
+ shouldThrowTDZ(basicError2);
+ shouldThrowTDZ(baz);
+ shouldThrowTDZ(bar);
+ shouldThrowTDZ(jaz);
+ shouldThrowTDZ(kaz);
+ }
+})();
+
+
+
+// Syntax errors.
+shouldThrowSyntaxError("function b(a = 20, a = 40) {}");
+shouldThrowSyntaxError("function b(aaaaa = 20,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v, aaaaa = 40) {}");
+shouldThrowSyntaxError("function b(a = 20, {a}) {}");
+shouldThrowSyntaxError("function b({a, a} = 20) {}");
+shouldThrowSyntaxError("function b({a, a} = 20) {}");