EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_object)
EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_spread_arrays)
EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_sharedarraybuffer)
+EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_default_parameters)
void Genesis::InstallNativeFunctions_harmony_proxies() {
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_destructuring)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_object)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_spread_arrays)
+EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_default_parameters)
void Genesis::InitializeGlobal_harmony_regexps() {
Handle<JSObject> builtins(native_context()->builtins());
static const char* harmony_spread_arrays_natives[] = {nullptr};
static const char* harmony_sharedarraybuffer_natives[] = {
"native harmony-sharedarraybuffer.js", NULL};
+ static const char* harmony_default_parameters_natives[] = {nullptr};
for (int i = ExperimentalNatives::GetDebuggerCount();
i < ExperimentalNatives::GetBuiltinsCount(); i++) {
V(harmony_reflect, "harmony Reflect API") \
V(harmony_destructuring, "harmony destructuring") \
V(harmony_spread_arrays, "harmony spread in array literals") \
- V(harmony_sharedarraybuffer, "harmony sharedarraybuffer")
+ V(harmony_sharedarraybuffer, "harmony sharedarraybuffer") \
+ V(harmony_default_parameters, "harmony default parameters")
// Features that are complete (but still behind --harmony/es-staging flag).
#define HARMONY_STAGED(V) \
T(ParamAfterRest, "Rest parameter must be last formal parameter") \
T(BadSetterRestParameter, \
"Setter function argument must not be a rest parameter") \
+ T(BadRestParameterInitializer, \
+ "Rest parameters must not have an initializer") \
T(ParenthesisInArgString, "Function arg string contains parenthesis") \
T(SingleFunctionLiteral, "Single function literal required") \
T(SloppyLexical, \
set_allow_harmony_spreadcalls(FLAG_harmony_spreadcalls);
set_allow_harmony_destructuring(FLAG_harmony_destructuring);
set_allow_harmony_spread_arrays(FLAG_harmony_spread_arrays);
+ set_allow_harmony_default_parameters(FLAG_harmony_default_parameters);
set_allow_strong_mode(FLAG_strong_mode);
for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount;
++feature) {
scope->set_start_position(shared_info->start_position());
ExpressionClassifier formals_classifier;
bool has_rest = false;
+ bool has_parameter_expressions = false;
+
+ // TODO(caitp): make default parameters work in arrow functions
+ ZoneList<Expression*>* initializers =
+ new (zone()) ZoneList<Expression*>(0, zone());
if (Check(Token::LPAREN)) {
// '(' StrictFormalParameters ')'
- ParseFormalParameterList(scope, &has_rest, &formals_classifier, &ok);
+ ParseFormalParameterList(scope, initializers,
+ &has_parameter_expressions, &has_rest,
+ &formals_classifier, &ok);
if (ok) ok = Check(Token::RPAREN);
} else {
// BindingIdentifier
}
+ZoneList<Statement*>* Parser::DesugarInitializeParameters(
+ Scope* scope, bool has_parameter_expressions,
+ ZoneList<Expression*>* initializers) {
+ DCHECK(scope->is_function_scope());
+
+ if (has_parameter_expressions) {
+ // If has_parameter_expressions for the function is true, each parameter is
+ // desugared as follows:
+ //
+ // SingleNameBinding :
+ // let <name> = %_Arguments(<index>);
+ // SingleNameBinding Initializer
+ // let <name> = IS_UNDEFINED(%_Arguments(<index>)) ? <initializer>
+ // : %_Arguments(<index>);
+ //
+ // TODO(caitp, dslomov): support BindingPatterns & rest parameters
+ //
+ scope->UndeclareParametersForExpressions();
+ ZoneList<Statement*>* body =
+ new (zone()) ZoneList<Statement*>(initializers->length(), zone());
+ for (int i = 0; i < initializers->length(); ++i) {
+ Expression* initializer = initializers->at(i);
+
+ // Position of parameter VariableProxy, for hole-checking
+ int pos = scope->parameter_position(i);
+
+ static const int kCapacity = 1;
+ static const bool kIsInitializerBlock = true;
+ Block* param_block =
+ factory()->NewBlock(nullptr, kCapacity, kIsInitializerBlock, pos);
+
+ VariableProxy* proxy =
+ NewUnresolved(scope->parameter(i)->raw_name(), LET);
+ VariableDeclaration* declaration = factory()->NewVariableDeclaration(
+ proxy, LET, scope, RelocInfo::kNoPosition);
+
+ bool ok = true;
+ // All formal parameters have been removed from the scope VariableMap,
+ // and so Declare() should not be able to fail.
+ proxy = factory()->NewVariableProxy(Declare(declaration, true, &ok), pos);
+ DCHECK(ok);
+
+ const AstRawString* fn_name = ast_value_factory()->empty_string();
+ const Runtime::Function* arguments =
+ Runtime::FunctionForId(Runtime::kInlineArguments);
+ ZoneList<Expression*>* arguments_i0 =
+ new (zone()) ZoneList<Expression*>(1, zone());
+ arguments_i0->Add(factory()->NewSmiLiteral(i, RelocInfo::kNoPosition),
+ zone());
+
+ if (initializer == nullptr) {
+ // let <name> = %_Arguments(i)
+ Expression* assign = factory()->NewAssignment(
+ Token::INIT_LET, proxy,
+ factory()->NewCallRuntime(fn_name, arguments, arguments_i0,
+ RelocInfo::kNoPosition),
+ RelocInfo::kNoPosition);
+ param_block->AddStatement(
+ factory()->NewExpressionStatement(assign, RelocInfo::kNoPosition),
+ zone());
+ proxy->var()->set_initializer_position(pos);
+ } else {
+ // IS_UNDEFINED(%_Arguments(i)) ? <initializer> : %_Arguments(i);
+ ZoneList<Expression*>* arguments_i1 =
+ new (zone()) ZoneList<Expression*>(1, zone());
+ arguments_i1->Add(factory()->NewSmiLiteral(i, RelocInfo::kNoPosition),
+ zone());
+
+ Expression* arg_or_default = factory()->NewConditional(
+ // condition:
+ factory()->NewCompareOperation(
+ Token::EQ_STRICT,
+ factory()->NewCallRuntime(fn_name, arguments, arguments_i0,
+ RelocInfo::kNoPosition),
+ factory()->NewUndefinedLiteral(RelocInfo::kNoPosition),
+ RelocInfo::kNoPosition),
+ // if true:
+ initializer,
+ // if false:
+ factory()->NewCallRuntime(fn_name, arguments, arguments_i1,
+ RelocInfo::kNoPosition),
+ RelocInfo::kNoPosition);
+
+ Expression* assign = factory()->NewAssignment(
+ Token::INIT_LET, proxy, arg_or_default, RelocInfo::kNoPosition);
+
+ param_block->AddStatement(
+ factory()->NewExpressionStatement(assign, RelocInfo::kNoPosition),
+ zone());
+ proxy->var()->set_initializer_position(initializer->position());
+ }
+ body->Add(param_block, zone());
+ }
+ return body;
+ } else {
+ // If has_parameter_expressions is false, remove the unnecessary parameter
+ // block scopes.
+ ZoneList<Scope*>* scopes = scope->inner_scopes();
+ for (int i = 0; i < scopes->length(); ++i) {
+ Scope* scope = scopes->at(i);
+ DCHECK(scope->is_block_scope());
+ scope->FinalizeBlockScope();
+ }
+ return nullptr;
+ }
+}
+
+
Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
bool* ok) {
// ForStatement ::
parser_->scope_->RemoveUnresolved(expr->AsVariableProxy());
bool is_rest = false;
- bool is_duplicate = DeclareFormalParameter(scope, raw_name, is_rest);
+ int pos = expr->position();
+ bool is_duplicate = DeclareFormalParameter(scope, raw_name, is_rest, pos);
if (is_duplicate && !duplicate_loc->IsValid()) {
*duplicate_loc = param_location;
}
bool has_rest = false;
+ bool has_parameter_expressions = false;
Expect(Token::LPAREN, CHECK_OK);
int start_position = scanner()->location().beg_pos;
scope_->set_start_position(start_position);
- num_parameters = ParseFormalParameterList(scope, &has_rest,
- &formals_classifier, CHECK_OK);
+ ZoneList<Expression*>* initializers =
+ new (zone()) ZoneList<Expression*>(0, zone());
+ num_parameters = ParseFormalParameterList(
+ scope, initializers, &has_parameter_expressions, &has_rest,
+ &formals_classifier, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
int formals_end_position = scanner()->location().end_pos;
}
}
if (!is_lazily_parsed) {
- body = ParseEagerFunctionBody(function_name, pos, fvar, fvar_init_op,
- kind, CHECK_OK);
+ body = DesugarInitializeParameters(scope, has_parameter_expressions,
+ initializers);
+ if (has_parameter_expressions) {
+ // TODO(caitp): Function body scope must be a declaration scope
+ Scope* function_body_scope = NewScope(scope, BLOCK_SCOPE);
+ function_body_scope->set_start_position(scope->start_position());
+ function_body_scope->SetScopeName(function_name);
+ BlockState function_body_state(&scope_, function_body_scope);
+ ZoneList<Statement*>* inner_body = ParseEagerFunctionBody(
+ function_name, pos, fvar, fvar_init_op, kind, CHECK_OK);
+
+ // Declare Block node
+ Block* block =
+ factory()->NewBlock(nullptr, inner_body->length(), false, pos);
+ block->set_scope(function_body_scope);
+ for (int i = 0; i < inner_body->length(); ++i) {
+ block->AddStatement(inner_body->at(i), zone());
+ }
+
+ scope->set_end_position(function_body_scope->end_position());
+ body->Add(block, zone());
+ } else {
+ body = ParseEagerFunctionBody(function_name, pos, fvar, fvar_init_op,
+ kind, CHECK_OK);
+ }
materialized_literal_count = function_state.materialized_literal_count();
expected_property_count = function_state.expected_property_count();
handler_count = function_state.handler_count();
allow_harmony_destructuring());
reusable_preparser_->set_allow_harmony_spread_arrays(
allow_harmony_spread_arrays());
+ reusable_preparser_->set_allow_harmony_default_parameters(
+ allow_harmony_default_parameters());
reusable_preparser_->set_allow_strong_mode(allow_strong_mode());
}
PreParser::PreParseResult result = reusable_preparser_->PreParseLazyFunction(
FunctionKind kind = kNormalFunction);
bool DeclareFormalParameter(Scope* scope, const AstRawString* name,
- bool is_rest) {
+ bool is_rest, int pos) {
bool is_duplicate = false;
- Variable* var = scope->DeclareParameter(name, VAR, is_rest, &is_duplicate);
+ Variable* var =
+ scope->DeclareParameter(name, VAR, is_rest, &is_duplicate, pos);
if (is_sloppy(scope->language_mode())) {
// TODO(sigurds) Mark every parameter as maybe assigned. This is a
// conservative approximation necessary to account for parameters
bool* ok_;
};
-
void ParseVariableDeclarations(VariableDeclarationContext var_context,
DeclarationParsingResult* parsing_result,
bool* ok);
ForStatement* loop, Statement* init, Expression* cond, Statement* next,
Statement* body, bool* ok);
+ ZoneList<Statement*>* DesugarInitializeParameters(
+ Scope* scope, bool has_parameter_expressions,
+ ZoneList<Expression*>* initializers);
+
FunctionLiteral* ParseFunctionLiteral(
const AstRawString* name, Scanner::Location function_name_location,
bool name_is_strict_reserved, FunctionKind kind,
ExpressionClassifier formals_classifier;
bool has_rest = false;
+ bool has_parameter_expressions = false;
+ PreParserExpressionList initializers = NewExpressionList(0, zone());
Expect(Token::LPAREN, CHECK_OK);
int start_position = scanner()->location().beg_pos;
function_scope->set_start_position(start_position);
int num_parameters;
{
DuplicateFinder duplicate_finder(scanner()->unicode_cache());
- num_parameters = ParseFormalParameterList(&duplicate_finder, &has_rest,
- &formals_classifier, CHECK_OK);
+ num_parameters = ParseFormalParameterList(
+ &duplicate_finder, initializers, &has_parameter_expressions, &has_rest,
+ &formals_classifier, CHECK_OK);
}
Expect(Token::RPAREN, CHECK_OK);
int formals_end_position = scanner()->location().end_pos;
public:
// Shorten type names defined by Traits.
typedef typename Traits::Type::Expression ExpressionT;
+ typedef typename Traits::Type::ExpressionList ExpressionListT;
typedef typename Traits::Type::Identifier IdentifierT;
typedef typename Traits::Type::FormalParameter FormalParameterT;
typedef typename Traits::Type::FormalParameterScope FormalParameterScopeT;
allow_harmony_computed_property_names_(false),
allow_harmony_rest_params_(false),
allow_harmony_spreadcalls_(false),
+ allow_harmony_default_parameters_(false),
allow_strong_mode_(false) {}
// Getters that indicate whether certain syntactical constructs are
bool allow_harmony_spread_arrays() const {
return allow_harmony_spread_arrays_;
}
+ bool allow_harmony_default_parameters() const {
+ return allow_harmony_default_parameters_;
+ }
bool allow_strong_mode() const { return allow_strong_mode_; }
void set_allow_harmony_spread_arrays(bool allow) {
allow_harmony_spread_arrays_ = allow;
}
+ void set_allow_harmony_default_parameters(bool allow) {
+ allow_harmony_default_parameters_ = allow;
+ }
+
protected:
enum AllowRestrictedIdentifiers {
void ParseFormalParameter(FormalParameterScopeT* scope, bool is_rest,
ExpressionClassifier* classifier, bool* ok);
- int ParseFormalParameterList(FormalParameterScopeT* scope, bool* has_rest,
+ int ParseFormalParameterList(FormalParameterScopeT* scope,
+ ExpressionListT initializers,
+ bool* has_parameter_expressions, bool* has_rest,
ExpressionClassifier* classifier, bool* ok);
void CheckArityRestrictions(
int param_count, FunctionLiteral::ArityRestriction arity_restriction,
bool allow_harmony_spreadcalls_;
bool allow_harmony_destructuring_;
bool allow_harmony_spread_arrays_;
+ bool allow_harmony_default_parameters_;
bool allow_strong_mode_;
};
}
V8_INLINE bool DeclareFormalParameter(DuplicateFinder* scope,
- PreParserIdentifier param,
- bool is_rest);
+ PreParserIdentifier param, bool is_rest,
+ int pos);
void CheckConflictingVarDeclarations(Scope* scope, bool* ok) {}
bool PreParserTraits::DeclareFormalParameter(
DuplicateFinder* duplicate_finder, PreParserIdentifier current_identifier,
- bool is_rest) {
+ bool is_rest, int pos) {
return pre_parser_->scanner()->FindSymbol(duplicate_finder, 1) != 0;
}
bool* ok) {
// FormalParameter[Yield,GeneratorParameter] :
// BindingElement[?Yield, ?GeneratorParameter]
+ int pos = peek_position();
IdentifierT name = ParseAndClassifyIdentifier(classifier, ok);
if (!*ok) return;
- bool was_declared = Traits::DeclareFormalParameter(scope, name, is_rest);
+ bool was_declared = Traits::DeclareFormalParameter(scope, name, is_rest, pos);
if (was_declared) {
classifier->RecordDuplicateFormalParameterError(scanner()->location());
}
template <class Traits>
int ParserBase<Traits>::ParseFormalParameterList(
- FormalParameterScopeT* scope, bool* is_rest,
+ FormalParameterScopeT* scope, ExpressionListT initializers,
+ bool* has_parameter_expressions, bool* is_rest,
ExpressionClassifier* classifier, bool* ok) {
// FormalParameters[Yield,GeneratorParameter] :
// [empty]
if (peek() != Token::RPAREN) {
do {
+ Scope* param_scope = NewScope(scope_, BLOCK_SCOPE);
+ BlockState param_state(&scope_, param_scope);
+ param_scope->set_start_position(peek_position());
if (++parameter_count > Code::kMaxArguments) {
ReportMessage(MessageTemplate::kTooManyParameters);
*ok = false;
return -1;
}
+
+ int start_pos = peek_position();
*is_rest = allow_harmony_rest_params() && Check(Token::ELLIPSIS);
ParseFormalParameter(scope, *is_rest, classifier, ok);
if (!*ok) return -1;
+
+ // TODO(caitp, dslomov): set *has_parameter_expressions to true if
+ // formal parameter is an ObjectBindingPattern containing computed
+ // property keys
+
+ ExpressionT initializer = this->EmptyExpression();
+ if (allow_harmony_default_parameters() && Check(Token::ASSIGN)) {
+ // Default parameter initializer
+ static const bool accept_IN = true;
+ ExpressionClassifier classifier;
+ initializer = ParseAssignmentExpression(accept_IN, &classifier, ok);
+ if (!*ok) return -1;
+ *has_parameter_expressions = true;
+
+ // A rest parameter cannot be initialized.
+ if (*is_rest) {
+ Scanner::Location loc(start_pos, scanner()->location().end_pos);
+ ReportMessageAt(loc, MessageTemplate::kBadRestParameterInitializer);
+ *ok = false;
+ return -1;
+ }
+ }
+ param_scope->set_end_position(scanner()->location().end_pos);
+ initializers->Add(initializer, zone());
} while (!*is_rest && Check(Token::COMMA));
if (*is_rest && peek() == Token::COMMA) {
internals_(4, zone),
temps_(4, zone),
params_(4, zone),
+ param_positions_(4, zone),
unresolved_(16, zone),
decls_(4, zone),
module_descriptor_(
internals_(4, zone),
temps_(4, zone),
params_(4, zone),
+ param_positions_(4, zone),
unresolved_(16, zone),
decls_(4, zone),
module_descriptor_(NULL),
internals_(0, zone),
temps_(0, zone),
params_(0, zone),
+ param_positions_(0, zone),
unresolved_(0, zone),
decls_(0, zone),
module_descriptor_(NULL),
module_var_ = NULL,
rest_parameter_ = NULL;
rest_index_ = -1;
+ has_parameter_expressions_ = false;
scope_info_ = scope_info;
start_position_ = RelocInfo::kNoPosition;
end_position_ = RelocInfo::kNoPosition;
Variable* Scope::DeclareParameter(const AstRawString* name, VariableMode mode,
- bool is_rest, bool* is_duplicate) {
+ bool is_rest, bool* is_duplicate, int pos) {
DCHECK(!already_resolved());
DCHECK(is_function_scope());
Variable* var = variables_.Declare(this, name, mode, Variable::NORMAL,
// TODO(wingo): Avoid O(n^2) check.
*is_duplicate = IsDeclaredParameter(name);
params_.Add(var, zone());
+ param_positions_.Add(pos, zone());
return var;
}
}
+void Scope::UndeclareParametersForExpressions() {
+ DCHECK(is_function_scope());
+ DCHECK(!has_parameter_expressions_);
+ has_parameter_expressions_ = true;
+ for (int i = 0; i < num_parameters(); ++i) {
+ Variable* p = parameter(i);
+ const AstRawString* name = p->raw_name();
+ variables_.Remove(const_cast<AstRawString*>(name), name->hash());
+ }
+}
+
+
void Scope::SetIllegalRedeclaration(Expression* expression) {
// Record only the first illegal redeclaration.
if (!HasIllegalRedeclaration()) {
// If it does, and if it is not copied into the context object, it must
// receive the highest parameter index for that parameter; thus iteration
// order is relevant!
- for (int i = params_.length() - 1; i >= 0; --i) {
- Variable* var = params_[i];
- if (var == rest_parameter_) continue;
-
- DCHECK(var->scope() == this);
- if (uses_sloppy_arguments || has_forced_context_allocation()) {
- // Force context allocation of the parameter.
- var->ForceContextAllocation();
+ //
+ // If hasParameterExpressions is true, parameters are redeclared during
+ // desugaring, and must not be allocated here.
+ if (!has_parameter_expressions_) {
+ for (int i = params_.length() - 1; i >= 0; --i) {
+ Variable* var = params_[i];
+ if (var == rest_parameter_) continue;
+
+ DCHECK(var->scope() == this);
+ if (uses_sloppy_arguments || has_forced_context_allocation()) {
+ // Force context allocation of the parameter.
+ var->ForceContextAllocation();
+ }
+ AllocateParameter(var, i);
}
- AllocateParameter(var, i);
}
}
// parameters the rightmost one 'wins'. However, the implementation
// expects all parameters to be declared and from left to right.
Variable* DeclareParameter(const AstRawString* name, VariableMode mode,
- bool is_rest, bool* is_duplicate);
+ bool is_rest, bool* is_duplicate, int pos);
// Declare a local variable in this scope. If the variable has been
// declared before, the previously declared variable is returned.
// the scope; see codegen.cc:ProcessDeclarations.
void AddDeclaration(Declaration* declaration);
+ // Formal parameters may be re-declared as lexical declarations in order to
+ // support TDZ semantics specified in ECMAScript 6.
+ void UndeclareParametersForExpressions();
+
// ---------------------------------------------------------------------------
// Illegal redeclaration support.
return params_[index];
}
+ // TODO(caitp): This probably won't work when BindingPatterns are supported
+ // in function parameters. Need a better way.
+ int parameter_position(int index) const {
+ DCHECK(is_function_scope());
+ return param_positions_[index];
+ }
+
// Returns the default function arity --- does not include rest parameters.
int default_function_length() const {
int count = params_.length();
ZoneList<Variable*> temps_;
// Parameter list in source order.
ZoneList<Variable*> params_;
+ ZoneList<int> param_positions_;
// Variables that must be looked up dynamically.
DynamicScopePart* dynamics_;
// Unresolved variables referred to from this scope.
Variable* rest_parameter_;
int rest_index_;
+ bool has_parameter_expressions_;
+
// Serialized scope info support.
Handle<ScopeInfo> scope_info_;
bool already_resolved() { return already_resolved_; }
Handle<String> name() const { return name_->string(); }
const AstRawString* raw_name() const { return name_; }
VariableMode mode() const { return mode_; }
+ void set_mode(VariableMode mode) {
+ // Don't use this unless you have a very good reason
+ mode_ = mode;
+ }
bool has_forced_context_allocation() const {
return force_context_allocation_;
}
--- /dev/null
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --expose-debug-as debug --harmony-default-parameters
+
+// Get the Debug object exposed from the debug context global object.
+Debug = debug.Debug
+
+listenerComplete = false;
+breakPointCount = 0;
+
+function listener(event, exec_state, event_data, data) {
+ if (event == Debug.DebugEvent.Break) {
+ breakPointCount++;
+ if (breakPointCount == 1) {
+ // Break point in initializer for parameter `a`, invoked by
+ // initializer for parameter `b`
+ assertEquals('default', exec_state.frame(1).evaluate('mode').value());
+
+ // initializer for `b` can't refer to `b`
+ assertThrows(function() {
+ return exec_state.frame(1).evaluate('b').value();
+ }, ReferenceError);
+
+ assertThrows(function() {
+ return exec_state.frame(1).evaluate('c');
+ }, ReferenceError);
+ } else if (breakPointCount == 2) {
+ // Break point in IIFE initializer for parameter `c`
+ assertEquals('modeFn', exec_state.frame(1).evaluate('a.name').value());
+ assertEquals('default', exec_state.frame(1).evaluate('b').value());
+ assertThrows(function() {
+ return exec_state.frame(1).evaluate('c');
+ }, ReferenceError);
+ } else if (breakPointCount == 3) {
+ // Break point in function body --- `c` parameter is shadowed
+ assertEquals('modeFn', exec_state.frame(0).evaluate('a.name').value());
+ assertEquals('default', exec_state.frame(0).evaluate('b').value());
+ // TODO(caitp): fix scoping so that parameter `c` can be shadowed by vars
+ //assertEquals(true, exec_state.frame(0).evaluate('c').value());
+ }
+ }
+};
+
+// Add the debug event listener.
+Debug.setListener(listener);
+
+function f(a = function modeFn(mode) {
+ debugger;
+ return mode;
+ },
+ b = a("default"),
+ c = (function() {
+ debugger;
+ })()) {
+ // TODO(caitp): fix scoping so that parameter `c` can be shadowed by vars
+ //var c = true;
+ debugger;
+};
+
+f();
+
+// Make sure that the debug event listener vas invoked.
+assertEquals(3, breakPointCount);
--- /dev/null
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --harmony-default-parameters --min-preparse-length=0
+
+var i = 0;
+function f(handler = function(b) { return b + "#" + (++i); }, b = "red") {
+ return handler(b);
+}
+
+assertEquals([
+ "blue#1",
+ "red#2",
+ "red",
+ "yellow#3"
+], [
+ f(undefined, "blue"),
+ f(),
+ f(function(b) { return b; }),
+ f(undefined, "yellow")
+]);
--- /dev/null
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --harmony-default-parameters --harmony-arrow-functions
+
+function return_specified() { return "specified"; }
+
+var method_returns_specified = {
+ method() { return "specified"; }
+};
+
+
+(function testDefaultFunctions() {
+ function optional_function(handler = function() { }) {
+ assertEquals("function", typeof handler);
+
+ // TODO(caitp): infer function name correctly
+ // (https://code.google.com/p/v8/issues/detail?id=3699)
+ // assertEquals("handler", handler.name);
+
+ return handler();
+ }
+ assertEquals(undefined, optional_function());
+ assertEquals(undefined, optional_function(undefined));
+ assertEquals("specified", optional_function(return_specified));
+})();
+
+
+(function testDefaultFunctionReferencesParameters() {
+ function fn1(handler = function() { return value; }, value) {
+ return handler();
+ }
+ assertEquals(undefined, fn1());
+ assertEquals(undefined, fn1(undefined, undefined));
+ assertEquals(1, fn1(undefined, 1));
+
+ function fn2(value, handler = function() { return value; }) {
+ return handler();
+ }
+ assertEquals(undefined, fn2());
+ assertEquals(undefined, fn2(undefined));
+ assertEquals(1, fn2(1));
+})();
+
+
+(function testDefaultObjects() {
+ function optional_object(object = { method() { return "method"; } }) {
+ assertEquals("object", typeof object);
+
+ assertEquals("function", typeof object.method);
+ return object.method();
+ }
+
+ assertEquals("method", optional_object());
+ assertEquals("method", optional_object(undefined));
+ assertEquals("specified", optional_object(method_returns_specified));
+
+
+ assertEquals(4, (function(x = { a: 4 }) { return x.a; })());
+ assertEquals(5, (function(x, y = { a: x }) { return y.a; })(5));
+ assertEquals(6, (function(x, y = { a: eval("x") }) { return y.a; })(6));
+})();
+
+
+// TDZ
+
+(function testReferencesUninitializedParameter() {
+ assertThrows(function(a = b, b) {}, ReferenceError);
+})();
+
+
+(function testEvalReferencesUninitializedParameter() {
+ assertThrows( function(x = { a: y }, y) { return x.a; }, ReferenceError);
+ assertThrows(function(a = eval("b"), b = 0) { return a; }, ReferenceError);
+ assertThrows(
+ function(x = { a: eval("y") }, y) { return x.a; }, ReferenceError);
+})();
+
+
+(function testReferencesInitializedParameter() {
+ assertEquals(1, (function(a = 1, b = a) { return b; })());
+})();
+
+
+// Scoping
+//
+// TODO(caitp): fix scoping --- var declarations in function body can't be
+// resolved in formal parameters
+// assertThrows(function referencesVariableBodyDeclaration(a = body_var) {
+// var body_var = true;
+// return a;
+// }, ReferenceError);
+
+
+// TODO(caitp): default function length does not include any parameters
+// following the first optional parameter
+// assertEquals(0, (function(a = 1) {}).length);
+// assertEquals(1, (function(a, b = 1) {}).length);
+// assertEquals(2, (function(a, b, c = 1) {}).length);
+// assertEquals(3, (function(a, b, c, d = 1) {}).length);
+// assertEquals(1, (function(a, b = 1, c, d = 1) {}).length);
+
+
+(function testInitializerReferencesThis() {
+ var O = {};
+ function fn(x = this) { return x; }
+ assertEquals(O, fn.call(O));
+
+ function fn2(x = () => this) { return x(); }
+ assertEquals(O, fn2.call(O));
+})();
+
+
+(function testInitializerReferencesSelf() {
+ function fn(x, y = fn) { return x ? y(false) + 1 : 0 }
+ assertEquals(1, fn(true));
+})();
+
+
+(function testInitializerEvalParameter() {
+ assertEquals(7, (function(x, y = eval("x")) { return y; })(7));
+ assertEquals(9, (function(x = () => eval("y"), y = 9) { return x(); })());
+})();
+
+
+(function testContextAllocatedUsedInBody() {
+ assertEquals("Monkey!Monkey!Monkey!", (function(x, y = eval("x")) {
+ return "Mon" + x + "Mon" + eval("y") + "Mon" + y;
+ })("key!"));
+ assertEquals("Monkey!", (function(x = "Mon", y = "key!") {
+ return eval("x") + eval("y");
+ })());
+})();
+
+
+(function testContextAllocatedEscapesFunction() {
+ assertEquals("Monkey!", (function(x = "Monkey!") {
+ return function() {
+ return x;
+ };
+ })()());
+})();