[es6] implement default parameters via desugaring
authorcaitpotter88 <caitpotter88@gmail.com>
Mon, 1 Jun 2015 17:10:39 +0000 (10:10 -0700)
committerCommit bot <commit-bot@chromium.org>
Mon, 1 Jun 2015 17:10:50 +0000 (17:10 +0000)
Stage 1 implementation:

- Parameters can't be referenced before initialized (from left-to-right)
- SingleNameBindings only, no support for BindingPatterns

Known issues:

- Incorrect scoping (parameter expressions may reference variables declared in function body)
- Function arity is untouched
- Hole-checking needs work
- Rest parameters are broken when mixed with optional arguments

BUG=v8:2160
LOG=N
R=arv@chromium.org, rossberg@chromium.org

Review URL: https://codereview.chromium.org/1127063003

Cr-Commit-Position: refs/heads/master@{#28739}

13 files changed:
src/bootstrapper.cc
src/flag-definitions.h
src/messages.h
src/parser.cc
src/parser.h
src/preparser.cc
src/preparser.h
src/scopes.cc
src/scopes.h
src/variables.h
test/mjsunit/harmony/default-parameters-debug.js [new file with mode: 0644]
test/mjsunit/harmony/default-parameters-lazy.js [new file with mode: 0644]
test/mjsunit/harmony/default-parameters.js [new file with mode: 0644]

index 3a26e40b4ba666e13345225bbe914379c267110f..566188e3c5645fa818581314e63165f42da745ce 100644 (file)
@@ -1766,6 +1766,7 @@ EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_destructuring)
 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() {
@@ -1797,6 +1798,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_spreadcalls)
 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());
@@ -2441,6 +2443,7 @@ bool Genesis::InstallExperimentalNatives() {
   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++) {
index 03d33071f428366dc27d99926072690cb3d25958..290aec75db177de4198354169bad53b5515998e8 100644 (file)
@@ -194,7 +194,8 @@ DEFINE_IMPLICATION(es_staging, harmony)
   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)                               \
index 50cb2c60e58da7ee5c191a73d598bd22474a788a..7e45d1f81a1f9355da9d66f70457a109840a4005 100644 (file)
@@ -331,6 +331,8 @@ class CallSite {
   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,                                                             \
index 196853c0236e3b8fca9d2dca5f67481138d9fa17..8d90fb6bfbab410a8837e8eed8f36608b1d5836f 100644 (file)
@@ -884,6 +884,7 @@ Parser::Parser(ParseInfo* info)
   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) {
@@ -1148,9 +1149,16 @@ FunctionLiteral* Parser::ParseLazy(Isolate* isolate, ParseInfo* info,
       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
@@ -3404,6 +3412,114 @@ Statement* Parser::DesugarLexicalBindingsInForStatement(
 }
 
 
+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 ::
@@ -3774,7 +3890,8 @@ void ParserTraits::DeclareArrowFunctionParameters(
   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;
@@ -3885,11 +4002,15 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
     }
 
     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;
 
@@ -3990,8 +4111,31 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
       }
     }
     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();
@@ -4275,6 +4419,8 @@ PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser(
         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(
index eba677c94bb945da45e6f2b4f3fbc3fbbc3e2845..584d69b4f1c8d56e14cd2ea14fb90ae2dcdd412a 100644 (file)
@@ -748,9 +748,10 @@ class ParserTraits {
                             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
@@ -1025,7 +1026,6 @@ class Parser : public ParserBase<ParserTraits> {
     bool* ok_;
   };
 
-
   void ParseVariableDeclarations(VariableDeclarationContext var_context,
                                  DeclarationParsingResult* parsing_result,
                                  bool* ok);
@@ -1071,6 +1071,10 @@ class Parser : public ParserBase<ParserTraits> {
       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,
index 2644b4374ad8c16deb50ee22db604f045a3d6fd3..8390f70bdeb63dc3f263e533664c20a0fc479257 100644 (file)
@@ -1035,14 +1035,17 @@ PreParser::Expression PreParser::ParseFunctionLiteral(
   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;
index 2fe5db1d08e9e0f1b63d68c69950ca29ae31371a..6c12be95a3d26c4d901d377d1c88ba017408eb4b 100644 (file)
@@ -66,6 +66,7 @@ class ParserBase : public Traits {
  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;
@@ -97,6 +98,7 @@ class ParserBase : public Traits {
         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
@@ -126,6 +128,9 @@ class ParserBase : public Traits {
   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_; }
 
@@ -167,6 +172,10 @@ class ParserBase : public Traits {
   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 {
@@ -919,7 +928,9 @@ class ParserBase : public Traits {
 
   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,
@@ -1022,6 +1033,7 @@ class ParserBase : public Traits {
   bool allow_harmony_spreadcalls_;
   bool allow_harmony_destructuring_;
   bool allow_harmony_spread_arrays_;
+  bool allow_harmony_default_parameters_;
   bool allow_strong_mode_;
 };
 
@@ -1799,8 +1811,8 @@ class PreParserTraits {
   }
 
   V8_INLINE bool DeclareFormalParameter(DuplicateFinder* scope,
-                                        PreParserIdentifier param,
-                                        bool is_rest);
+                                        PreParserIdentifier param, bool is_rest,
+                                        int pos);
 
   void CheckConflictingVarDeclarations(Scope* scope, bool* ok) {}
 
@@ -1996,7 +2008,7 @@ PreParserExpression PreParserTraits::SpreadCallNew(PreParserExpression function,
 
 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;
 }
 
@@ -3673,10 +3685,11 @@ void ParserBase<Traits>::ParseFormalParameter(FormalParameterScopeT* scope,
                                               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());
   }
@@ -3685,7 +3698,8 @@ void ParserBase<Traits>::ParseFormalParameter(FormalParameterScopeT* scope,
 
 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]
@@ -3705,14 +3719,43 @@ int ParserBase<Traits>::ParseFormalParameterList(
 
   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) {
index 21a8805c54aad396364829e66110c72752cfd3c2..98c689b4c5ce23a8a9d5ee849ffd852d265c6961 100644 (file)
@@ -77,6 +77,7 @@ Scope::Scope(Zone* zone, Scope* outer_scope, ScopeType scope_type,
       internals_(4, zone),
       temps_(4, zone),
       params_(4, zone),
+      param_positions_(4, zone),
       unresolved_(16, zone),
       decls_(4, zone),
       module_descriptor_(
@@ -100,6 +101,7 @@ Scope::Scope(Zone* zone, Scope* inner_scope, ScopeType scope_type,
       internals_(4, zone),
       temps_(4, zone),
       params_(4, zone),
+      param_positions_(4, zone),
       unresolved_(16, zone),
       decls_(4, zone),
       module_descriptor_(NULL),
@@ -126,6 +128,7 @@ Scope::Scope(Zone* zone, Scope* inner_scope,
       internals_(0, zone),
       temps_(0, zone),
       params_(0, zone),
+      param_positions_(0, zone),
       unresolved_(0, zone),
       decls_(0, zone),
       module_descriptor_(NULL),
@@ -183,6 +186,7 @@ void Scope::SetDefaults(ScopeType scope_type, Scope* outer_scope,
   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;
@@ -470,7 +474,7 @@ Variable* Scope::Lookup(const AstRawString* name) {
 
 
 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,
@@ -483,6 +487,7 @@ Variable* Scope::DeclareParameter(const AstRawString* name, VariableMode mode,
   // TODO(wingo): Avoid O(n^2) check.
   *is_duplicate = IsDeclaredParameter(name);
   params_.Add(var, zone());
+  param_positions_.Add(pos, zone());
   return var;
 }
 
@@ -553,6 +558,18 @@ void Scope::AddDeclaration(Declaration* declaration) {
 }
 
 
+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()) {
@@ -1419,16 +1436,21 @@ void Scope::AllocateParameterLocals(Isolate* isolate) {
   // 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);
   }
 }
 
index d56018532152909538635d95cdb50286ddfc0d34..85ab773400a2b36b10c4848b534b2f2f6bb300cc 100644 (file)
@@ -126,7 +126,7 @@ class Scope: public ZoneObject {
   // 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.
@@ -182,6 +182,10 @@ class Scope: public ZoneObject {
   // 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.
 
@@ -373,6 +377,13 @@ class Scope: public ZoneObject {
     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();
@@ -563,6 +574,7 @@ class Scope: public ZoneObject {
   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.
@@ -637,6 +649,8 @@ class Scope: public ZoneObject {
   Variable* rest_parameter_;
   int rest_index_;
 
+  bool has_parameter_expressions_;
+
   // Serialized scope info support.
   Handle<ScopeInfo> scope_info_;
   bool already_resolved() { return already_resolved_; }
index 384a8859545045ac502c5f80cc7a8229ddd81d75..72680514ca739c4dbe11ca0725127e4cb6c17e11 100644 (file)
@@ -67,6 +67,10 @@ class Variable: public ZoneObject {
   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_;
   }
diff --git a/test/mjsunit/harmony/default-parameters-debug.js b/test/mjsunit/harmony/default-parameters-debug.js
new file mode 100644 (file)
index 0000000..0170d29
--- /dev/null
@@ -0,0 +1,65 @@
+// 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);
diff --git a/test/mjsunit/harmony/default-parameters-lazy.js b/test/mjsunit/harmony/default-parameters-lazy.js
new file mode 100644 (file)
index 0000000..3637a0f
--- /dev/null
@@ -0,0 +1,22 @@
+// 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")
+]);
diff --git a/test/mjsunit/harmony/default-parameters.js b/test/mjsunit/harmony/default-parameters.js
new file mode 100644 (file)
index 0000000..e54b5a8
--- /dev/null
@@ -0,0 +1,143 @@
+// 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;
+    };
+  })()());
+})();