Parse harmony let declarations.
authorkeuchel@chromium.org <keuchel@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 16 Aug 2011 14:24:12 +0000 (14:24 +0000)
committerkeuchel@chromium.org <keuchel@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 16 Aug 2011 14:24:12 +0000 (14:24 +0000)
Implementation of the harmony block scoped let bindings as proposed here:
http://wiki.ecmascript.org/doku.php?id=harmony:block_scoped_bindings

Changes to the syntax are explained there. They are active under the
harmony_block_scoping_ flag in the parser.

Review URL: http://codereview.chromium.org/7616009

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8944 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

26 files changed:
src/api.cc
src/arm/full-codegen-arm.cc
src/ast.h
src/compiler.cc
src/contexts.cc
src/ia32/full-codegen-ia32.cc
src/messages.js
src/parser.cc
src/parser.h
src/preparser-api.cc
src/preparser.cc
src/preparser.h
src/runtime.cc
src/scanner-base.cc
src/scanner-base.h
src/scopes.cc
src/token.h
src/variables.cc
src/variables.h
src/x64/full-codegen-x64.cc
test/cctest/test-parsing.cc
test/mjsunit/bugs/harmony/debug-blockscopes.js [new file with mode: 0644]
test/mjsunit/harmony/block-let-declaration.js [new file with mode: 0644]
test/mjsunit/harmony/block-scoping.js
test/mjsunit/harmony/debug-blockscopes.js
test/mjsunit/harmony/debug-evaluate-blockscopes.js

index 402f22055c64919ad060ec28b333cfad311aa55e..5a5f14dc657385cda8d99072134ebdcd4ef7f588 100644 (file)
@@ -35,6 +35,7 @@
 #include "debug.h"
 #include "deoptimizer.h"
 #include "execution.h"
+#include "flags.h"
 #include "global-handles.h"
 #include "heap-profiler.h"
 #include "messages.h"
@@ -1405,7 +1406,7 @@ void ObjectTemplate::SetInternalFieldCount(int value) {
 ScriptData* ScriptData::PreCompile(const char* input, int length) {
   i::Utf8ToUC16CharacterStream stream(
       reinterpret_cast<const unsigned char*>(input), length);
-  return i::ParserApi::PreParse(&stream, NULL);
+  return i::ParserApi::PreParse(&stream, NULL, i::FLAG_harmony_block_scoping);
 }
 
 
@@ -1414,10 +1415,10 @@ ScriptData* ScriptData::PreCompile(v8::Handle<String> source) {
   if (str->IsExternalTwoByteString()) {
     i::ExternalTwoByteStringUC16CharacterStream stream(
       i::Handle<i::ExternalTwoByteString>::cast(str), 0, str->length());
-    return i::ParserApi::PreParse(&stream, NULL);
+    return i::ParserApi::PreParse(&stream, NULL, i::FLAG_harmony_block_scoping);
   } else {
     i::GenericStringUC16CharacterStream stream(str, 0, str->length());
-    return i::ParserApi::PreParse(&stream, NULL);
+    return i::ParserApi::PreParse(&stream, NULL, i::FLAG_harmony_block_scoping);
   }
 }
 
index 3742d166ca3c23d73e5aad033406e454c2a0bca4..3116ca455bcd6b23646c98d2cd8a20da536bdb47 100644 (file)
@@ -742,9 +742,9 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable,
         __ mov(r2, Operand(variable->name()));
         // Declaration nodes are always introduced in one of two modes.
         ASSERT(mode == Variable::VAR ||
-               mode == Variable::CONST);
-        PropertyAttributes attr =
-            (mode == Variable::VAR) ? NONE : READ_ONLY;
+               mode == Variable::CONST ||
+               mode == Variable::LET);
+        PropertyAttributes attr = (mode == Variable::CONST) ? READ_ONLY : NONE;
         __ mov(r1, Operand(Smi::FromInt(attr)));
         // Push initial value, if any.
         // Note: For variables we must not push an initial value (such as
index fccf1aa1cc2f4b235afb3a0836390805bbe65818..4031b7d810037bf6141488e5b06bd2b09c054747 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -375,9 +375,11 @@ class Declaration: public AstNode {
       : proxy_(proxy),
         mode_(mode),
         fun_(fun) {
-    ASSERT(mode == Variable::VAR || mode == Variable::CONST);
+    ASSERT(mode == Variable::VAR ||
+           mode == Variable::CONST ||
+           mode == Variable::LET);
     // At the moment there are no "const functions"'s in JavaScript...
-    ASSERT(fun == NULL || mode == Variable::VAR);
+    ASSERT(fun == NULL || mode == Variable::VAR || mode == Variable::LET);
   }
 
   DECLARE_NODE_TYPE(Declaration)
index a87eecc3c55ebde14127a46393b2f587c9c99c41..c7e78067cfebce5161baaaf18ecc4520e33cfa92 100755 (executable)
@@ -478,15 +478,21 @@ Handle<SharedFunctionInfo> Compiler::Compile(Handle<String> source,
     // that would be compiled lazily anyway, so we skip the preparse step
     // in that case too.
     ScriptDataImpl* pre_data = input_pre_data;
+    bool harmony_block_scoping = natives != NATIVES_CODE &&
+                                 FLAG_harmony_block_scoping;
     if (pre_data == NULL
         && source_length >= FLAG_min_preparse_length) {
       if (source->IsExternalTwoByteString()) {
         ExternalTwoByteStringUC16CharacterStream stream(
             Handle<ExternalTwoByteString>::cast(source), 0, source->length());
-        pre_data = ParserApi::PartialPreParse(&stream, extension);
+        pre_data = ParserApi::PartialPreParse(&stream,
+                                              extension,
+                                              harmony_block_scoping);
       } else {
         GenericStringUC16CharacterStream stream(source, 0, source->length());
-        pre_data = ParserApi::PartialPreParse(&stream, extension);
+        pre_data = ParserApi::PartialPreParse(&stream,
+                                              extension,
+                                              harmony_block_scoping);
       }
     }
 
index 72a5ae4d6077fab4a067c57553f7348eeeb8bfa4..c0e724253f2767ed2a6bbbee436db3c99cd9f62a 100644 (file)
@@ -180,6 +180,7 @@ Handle<Object> Context::Lookup(Handle<String> name,
         switch (mode) {
           case Variable::INTERNAL:  // Fall through.
           case Variable::VAR:
+          case Variable::LET:
             *attributes = NONE;
             break;
           case Variable::CONST:
index 0056acc953ea477dbab1cf6f09062afcf6873682..bb75b1e6acb22922dd13505981523e1be1e4521d 100644 (file)
@@ -737,8 +737,10 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable,
         __ push(esi);
         __ push(Immediate(variable->name()));
         // Declaration nodes are always introduced in one of two modes.
-        ASSERT(mode == Variable::VAR || mode == Variable::CONST);
-        PropertyAttributes attr = (mode == Variable::VAR) ? NONE : READ_ONLY;
+        ASSERT(mode == Variable::VAR ||
+               mode == Variable::CONST ||
+               mode == Variable::LET);
+        PropertyAttributes attr = (mode == Variable::CONST) ? READ_ONLY : NONE;
         __ push(Immediate(Smi::FromInt(attr)));
         // Push initial value, if any.
         // Note: For variables we must not push an initial value (such as
index 6603185a3207bf330bf4c1a34d04de782bdbc5cd..58c9a5302560373123893868e9edc25bd4fab5c4 100644 (file)
@@ -247,6 +247,7 @@ function FormatMessage(message) {
       strict_cannot_assign:         ["Cannot assign to read only '", "%0", "' in strict mode"],
       strict_poison_pill:           ["'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them"],
       strict_caller:                ["Illegal access to a strict mode caller function."],
+      unprotected_let:              ["Illegal let declaration in unprotected statement context."],
     };
   }
   var message_type = %MessageGetType(message);
index c60ee679209d56f93af7ffd4159c775577c86077..844dd7060caf334ca240c0b2f697de8883dcf894 100644 (file)
@@ -811,6 +811,7 @@ void Parser::ReportMessageAt(Scanner::Location source_location,
 }
 
 void Parser::SetHarmonyBlockScoping(bool block_scoping) {
+  scanner().SetHarmonyBlockScoping(block_scoping);
   harmony_block_scoping_ = block_scoping;
 }
 
@@ -1093,6 +1094,25 @@ class ThisNamedPropertyAssigmentFinder : public ParserFinder {
 };
 
 
+Statement* Parser::ParseSourceElement(ZoneStringList* labels,
+                                      bool* ok) {
+  if (peek() == Token::FUNCTION) {
+    // FunctionDeclaration is only allowed in the context of SourceElements
+    // (Ecma 262 5th Edition, clause 14):
+    // SourceElement:
+    //    Statement
+    //    FunctionDeclaration
+    // Common language extension is to allow function declaration in place
+    // of any statement. This language extension is disabled in strict mode.
+    return ParseFunctionDeclaration(ok);
+  } else if (peek() == Token::LET) {
+    return ParseVariableStatement(kSourceElement, ok);
+  } else {
+    return ParseStatement(labels, ok);
+  }
+}
+
+
 void* Parser::ParseSourceElements(ZoneList<Statement*>* processor,
                                   int end_token,
                                   bool* ok) {
@@ -1116,21 +1136,7 @@ void* Parser::ParseSourceElements(ZoneList<Statement*>* processor,
     }
 
     Scanner::Location token_loc = scanner().peek_location();
-
-    Statement* stat;
-    if (peek() == Token::FUNCTION) {
-      // FunctionDeclaration is only allowed in the context of SourceElements
-      // (Ecma 262 5th Edition, clause 14):
-      // SourceElement:
-      //    Statement
-      //    FunctionDeclaration
-      // Common language extension is to allow function declaration in place
-      // of any statement. This language extension is disabled in strict mode.
-      stat = ParseFunctionDeclaration(CHECK_OK);
-    } else {
-      stat = ParseStatement(NULL, CHECK_OK);
-    }
-
+    Statement* stat = ParseSourceElement(NULL, CHECK_OK);
     if (stat == NULL || stat->IsEmpty()) {
       directive_prologue = false;   // End of directive prologue.
       continue;
@@ -1218,7 +1224,7 @@ Statement* Parser::ParseStatement(ZoneStringList* labels, bool* ok) {
 
     case Token::CONST:  // fall through
     case Token::VAR:
-      stmt = ParseVariableStatement(ok);
+      stmt = ParseVariableStatement(kStatement, ok);
       break;
 
     case Token::SEMICOLON:
@@ -1313,9 +1319,9 @@ VariableProxy* Parser::Declare(Handle<String> name,
                                bool resolve,
                                bool* ok) {
   Variable* var = NULL;
-  // If we are inside a function, a declaration of a variable
-  // is a truly local variable, and the scope of the variable
-  // is always the function scope.
+  // If we are inside a function, a declaration of a var/const variable is a
+  // truly local variable, and the scope of the variable is always the function
+  // scope.
 
   // If a function scope exists, then we can statically declare this
   // variable and also set its mode. In any case, a Declaration node
@@ -1325,24 +1331,28 @@ VariableProxy* Parser::Declare(Handle<String> name,
   // to the calling function context.
   // Similarly, strict mode eval scope does not leak variable declarations to
   // the caller's scope so we declare all locals, too.
-  Scope* declaration_scope = top_scope_->DeclarationScope();
+
+  Scope* declaration_scope = mode == Variable::LET ? top_scope_
+      : top_scope_->DeclarationScope();
   if (declaration_scope->is_function_scope() ||
-      declaration_scope->is_strict_mode_eval_scope()) {
+      declaration_scope->is_strict_mode_eval_scope() ||
+      declaration_scope->is_block_scope()) {
     // Declare the variable in the function scope.
     var = declaration_scope->LocalLookup(name);
     if (var == NULL) {
       // Declare the name.
       var = declaration_scope->DeclareLocal(name, mode);
     } else {
-      // The name was declared before; check for conflicting
-      // re-declarations. If the previous declaration was a const or the
-      // current declaration is a const then we have a conflict. There is
-      // similar code in runtime.cc in the Declare functions.
-      if ((mode == Variable::CONST) || (var->mode() == Variable::CONST)) {
-        // We only have vars and consts in declarations.
+      // The name was declared before; check for conflicting re-declarations.
+      // We have a conflict if either of the declarations is not a var. There
+      // is similar code in runtime.cc in the Declare functions.
+      if ((mode != Variable::VAR) || (var->mode() != Variable::VAR)) {
+        // We only have vars, consts and lets in declarations.
         ASSERT(var->mode() == Variable::VAR ||
-               var->mode() == Variable::CONST);
-        const char* type = (var->mode() == Variable::VAR) ? "var" : "const";
+               var->mode() == Variable::CONST ||
+               var->mode() == Variable::LET);
+        const char* type = (var->mode() == Variable::VAR) ? "var" :
+                           (var->mode() == Variable::CONST) ? "const" : "let";
         Handle<String> type_string =
             isolate()->factory()->NewStringFromUtf8(CStrVector(type), TENURED);
         Expression* expression =
@@ -1485,7 +1495,8 @@ Statement* Parser::ParseFunctionDeclaration(bool* ok) {
   // Even if we're not at the top-level of the global or a function
   // scope, we treat is as such and introduce the function with it's
   // initial value upon entering the corresponding scope.
-  Declare(name, Variable::VAR, fun, true, CHECK_OK);
+  Variable::Mode mode = harmony_block_scoping_ ? Variable::LET : Variable::VAR;
+  Declare(name, mode, fun, true, CHECK_OK);
   return EmptyStatement();
 }
 
@@ -1540,7 +1551,7 @@ Block* Parser::ParseScopedBlock(ZoneStringList* labels, bool* ok) {
     InitializationBlockFinder block_finder(top_scope_, target_stack_);
 
     while (peek() != Token::RBRACE) {
-      Statement* stat = ParseStatement(NULL, CHECK_OK);
+      Statement* stat = ParseSourceElement(NULL, CHECK_OK);
       if (stat && !stat->IsEmpty()) {
         body->AddStatement(stat);
         block_finder.Update(stat);
@@ -1566,12 +1577,15 @@ Block* Parser::ParseScopedBlock(ZoneStringList* labels, bool* ok) {
 }
 
 
-Block* Parser::ParseVariableStatement(bool* ok) {
+Block* Parser::ParseVariableStatement(VariableDeclarationContext var_context,
+                                      bool* ok) {
   // VariableStatement ::
   //   VariableDeclarations ';'
 
   Handle<String> ignore;
-  Block* result = ParseVariableDeclarations(true, &ignore, CHECK_OK);
+  Block* result = ParseVariableDeclarations(var_context,
+                                            &ignore,
+                                            CHECK_OK);
   ExpectSemicolon(CHECK_OK);
   return result;
 }
@@ -1588,7 +1602,7 @@ bool Parser::IsEvalOrArguments(Handle<String> string) {
 // *var is untouched; in particular, it is the caller's responsibility
 // to initialize it properly. This mechanism is used for the parsing
 // of 'for-in' loops.
-Block* Parser::ParseVariableDeclarations(bool accept_IN,
+Block* Parser::ParseVariableDeclarations(VariableDeclarationContext var_context,
                                          Handle<String>* out,
                                          bool* ok) {
   // VariableDeclarations ::
@@ -1596,25 +1610,36 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN,
 
   Variable::Mode mode = Variable::VAR;
   bool is_const = false;
-  Scope* declaration_scope = top_scope_->DeclarationScope();
   if (peek() == Token::VAR) {
     Consume(Token::VAR);
   } else if (peek() == Token::CONST) {
     Consume(Token::CONST);
-    if (declaration_scope->is_strict_mode()) {
+    if (top_scope_->is_strict_mode()) {
       ReportMessage("strict_const", Vector<const char*>::empty());
       *ok = false;
       return NULL;
     }
     mode = Variable::CONST;
     is_const = true;
+  } else if (peek() == Token::LET) {
+    Consume(Token::LET);
+    if (var_context != kSourceElement &&
+        var_context != kForStatement) {
+      ASSERT(var_context == kStatement);
+      ReportMessage("unprotected_let", Vector<const char*>::empty());
+      *ok = false;
+      return NULL;
+    }
+    mode = Variable::LET;
   } else {
     UNREACHABLE();  // by current callers
   }
 
-  // The scope of a variable/const declared anywhere inside a function
+  Scope* declaration_scope = mode == Variable::LET
+      ? top_scope_ : top_scope_->DeclarationScope();
+  // The scope of a var/const declared variable anywhere inside a function
   // is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). Thus we can
-  // transform a source-level variable/const declaration into a (Function)
+  // transform a source-level var/const declaration into a (Function)
   // Scope declaration, and rewrite the source-level initialization into an
   // assignment statement. We use a block to collect multiple assignments.
   //
@@ -1698,7 +1723,7 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN,
     if (peek() == Token::ASSIGN) {
       Expect(Token::ASSIGN, CHECK_OK);
       position = scanner().location().beg_pos;
-      value = ParseAssignmentExpression(accept_IN, CHECK_OK);
+      value = ParseAssignmentExpression(var_context != kForStatement, CHECK_OK);
       // Don't infer if it is "a = function(){...}();"-like expression.
       if (fni_ != NULL &&
           value->AsCall() == NULL &&
@@ -2298,7 +2323,7 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
     if (peek() == Token::VAR || peek() == Token::CONST) {
       Handle<String> name;
       Block* variable_statement =
-          ParseVariableDeclarations(false, &name, CHECK_OK);
+          ParseVariableDeclarations(kForStatement, &name, CHECK_OK);
 
       if (peek() == Token::IN && !name.is_null()) {
         VariableProxy* each = top_scope_->NewUnresolved(name, inside_with());
@@ -3657,8 +3682,11 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> function_name,
   }
 
   int num_parameters = 0;
-  // Function declarations are hoisted.
-  Scope* scope = (type == FunctionLiteral::DECLARATION)
+  // Function declarations are function scoped in normal mode, so they are
+  // hoisted. In harmony block scoping mode they are block scoped, so they
+  // are not hoisted.
+  Scope* scope = (type == FunctionLiteral::DECLARATION &&
+                  !harmony_block_scoping_)
       ? NewScope(top_scope_->DeclarationScope(), Scope::FUNCTION_SCOPE, false)
       : NewScope(top_scope_, Scope::FUNCTION_SCOPE, inside_with());
   ZoneList<Statement*>* body = new(zone()) ZoneList<Statement*>(8);
@@ -3960,7 +3988,7 @@ Literal* Parser::GetLiteralNumber(double value) {
 }
 
 
-// Parses and identifier that is valid for the current scope, in particular it
+// Parses an identifier that is valid for the current scope, in particular it
 // fails on strict mode future reserved keywords in a strict scope.
 Handle<String> Parser::ParseIdentifier(bool* ok) {
   if (top_scope_->is_strict_mode()) {
@@ -5041,9 +5069,11 @@ int ScriptDataImpl::ReadNumber(byte** source) {
 // Create a Scanner for the preparser to use as input, and preparse the source.
 static ScriptDataImpl* DoPreParse(UC16CharacterStream* source,
                                   bool allow_lazy,
-                                  ParserRecorder* recorder) {
+                                  ParserRecorder* recorder,
+                                  bool harmony_block_scoping) {
   Isolate* isolate = Isolate::Current();
   JavaScriptScanner scanner(isolate->unicode_cache());
+  scanner.SetHarmonyBlockScoping(harmony_block_scoping);
   scanner.Initialize(source);
   intptr_t stack_limit = isolate->stack_guard()->real_climit();
   if (!preparser::PreParser::PreParseProgram(&scanner,
@@ -5064,7 +5094,8 @@ static ScriptDataImpl* DoPreParse(UC16CharacterStream* source,
 // Preparse, but only collect data that is immediately useful,
 // even if the preparser data is only used once.
 ScriptDataImpl* ParserApi::PartialPreParse(UC16CharacterStream* source,
-                                           v8::Extension* extension) {
+                                           v8::Extension* extension,
+                                           bool harmony_block_scoping) {
   bool allow_lazy = FLAG_lazy && (extension == NULL);
   if (!allow_lazy) {
     // Partial preparsing is only about lazily compiled functions.
@@ -5072,16 +5103,17 @@ ScriptDataImpl* ParserApi::PartialPreParse(UC16CharacterStream* source,
     return NULL;
   }
   PartialParserRecorder recorder;
-  return DoPreParse(source, allow_lazy, &recorder);
+  return DoPreParse(source, allow_lazy, &recorder, harmony_block_scoping);
 }
 
 
 ScriptDataImpl* ParserApi::PreParse(UC16CharacterStream* source,
-                                    v8::Extension* extension) {
+                                    v8::Extension* extension,
+                                    bool harmony_block_scoping) {
   Handle<Script> no_script;
   bool allow_lazy = FLAG_lazy && (extension == NULL);
   CompleteParserRecorder recorder;
-  return DoPreParse(source, allow_lazy, &recorder);
+  return DoPreParse(source, allow_lazy, &recorder, harmony_block_scoping);
 }
 
 
@@ -5111,19 +5143,22 @@ bool ParserApi::Parse(CompilationInfo* info) {
   ASSERT(info->function() == NULL);
   FunctionLiteral* result = NULL;
   Handle<Script> script = info->script();
+  bool harmony_block_scoping = !info->is_native() &&
+                               FLAG_harmony_block_scoping;
   if (info->is_lazy()) {
     Parser parser(script, true, NULL, NULL);
-    parser.SetHarmonyBlockScoping(!info->is_native() &&
-                                  FLAG_harmony_block_scoping);
+    parser.SetHarmonyBlockScoping(harmony_block_scoping);
     result = parser.ParseLazy(info);
   } else {
     // Whether we allow %identifier(..) syntax.
     bool allow_natives_syntax =
         info->allows_natives_syntax() || FLAG_allow_natives_syntax;
     ScriptDataImpl* pre_data = info->pre_parse_data();
-    Parser parser(script, allow_natives_syntax, info->extension(), pre_data);
-    parser.SetHarmonyBlockScoping(!info->is_native() &&
-                                  FLAG_harmony_block_scoping);
+    Parser parser(script,
+                  allow_natives_syntax,
+                  info->extension(),
+                  pre_data);
+    parser.SetHarmonyBlockScoping(harmony_block_scoping);
     if (pre_data != NULL && pre_data->has_error()) {
       Scanner::Location loc = pre_data->MessageLocation();
       const char* message = pre_data->BuildMessage();
index b0c580712892c904bec78c5fa5a5ad3bd6a6acdd..686dac85afa81bde06b5d07099e4268372e3e754 100644 (file)
@@ -164,12 +164,14 @@ class ParserApi {
 
   // Generic preparser generating full preparse data.
   static ScriptDataImpl* PreParse(UC16CharacterStream* source,
-                                  v8::Extension* extension);
+                                  v8::Extension* extension,
+                                  bool harmony_block_scoping);
 
   // Preparser that only does preprocessing that makes sense if only used
   // immediately after.
   static ScriptDataImpl* PartialPreParse(UC16CharacterStream* source,
-                                         v8::Extension* extension);
+                                         v8::Extension* extension,
+                                         bool harmony_block_scoping);
 };
 
 // ----------------------------------------------------------------------------
@@ -452,6 +454,12 @@ class Parser {
     PARSE_EAGERLY
   };
 
+  enum VariableDeclarationContext {
+    kSourceElement,
+    kStatement,
+    kForStatement
+  };
+
   Isolate* isolate() { return isolate_; }
   Zone* zone() { return isolate_->zone(); }
 
@@ -480,13 +488,15 @@ class Parser {
   // for failure at the call sites.
   void* ParseSourceElements(ZoneList<Statement*>* processor,
                             int end_token, bool* ok);
+  Statement* ParseSourceElement(ZoneStringList* labels, bool* ok);
   Statement* ParseStatement(ZoneStringList* labels, bool* ok);
   Statement* ParseFunctionDeclaration(bool* ok);
   Statement* ParseNativeDeclaration(bool* ok);
   Block* ParseBlock(ZoneStringList* labels, bool* ok);
   Block* ParseScopedBlock(ZoneStringList* labels, bool* ok);
-  Block* ParseVariableStatement(bool* ok);
-  Block* ParseVariableDeclarations(bool accept_IN,
+  Block* ParseVariableStatement(VariableDeclarationContext var_context,
+                                bool* ok);
+  Block* ParseVariableDeclarations(VariableDeclarationContext var_context,
                                    Handle<String>* out,
                                    bool* ok);
   Statement* ParseExpressionOrLabelledStatement(ZoneStringList* labels,
index e0ab5001f5acd9bfe20cc7d30730e2b059254b61..80656d5d1225b863e389e018ff8cfa63a0098c70 100644 (file)
@@ -28,6 +28,7 @@
 #include "../include/v8-preparser.h"
 
 #include "globals.h"
+#include "flags.h"
 #include "checks.h"
 #include "allocation.h"
 #include "utils.h"
index 463df83dbbbfeda503c8a1fee115063dc8522b84..1a3dd737c5d75f998bbda9361fa04fbb5289e88e 100644 (file)
@@ -112,6 +112,16 @@ void PreParser::CheckOctalLiteral(int beg_pos, int end_pos, bool* ok) {
 #undef DUMMY
 
 
+PreParser::Statement PreParser::ParseSourceElement(bool* ok) {
+  switch (peek()) {
+    case i::Token::LET:
+      return ParseVariableStatement(kSourceElement, ok);
+    default:
+      return ParseStatement(ok);
+  }
+}
+
+
 PreParser::SourceElements PreParser::ParseSourceElements(int end_token,
                                                          bool* ok) {
   // SourceElements ::
@@ -119,7 +129,7 @@ PreParser::SourceElements PreParser::ParseSourceElements(int end_token,
 
   bool allow_directive_prologue = true;
   while (peek() != end_token) {
-    Statement statement = ParseStatement(CHECK_OK);
+    Statement statement = ParseSourceElement(CHECK_OK);
     if (allow_directive_prologue) {
       if (statement.IsUseStrictLiteral()) {
         set_strict_mode();
@@ -172,7 +182,7 @@ PreParser::Statement PreParser::ParseStatement(bool* ok) {
 
     case i::Token::CONST:
     case i::Token::VAR:
-      return ParseVariableStatement(ok);
+      return ParseVariableStatement(kStatement, ok);
 
     case i::Token::SEMICOLON:
       Next();
@@ -258,7 +268,7 @@ PreParser::Statement PreParser::ParseBlock(bool* ok) {
   Expect(i::Token::LBRACE, CHECK_OK);
   while (peek() != i::Token::RBRACE) {
     i::Scanner::Location start_location = scanner_->peek_location();
-    Statement statement = ParseStatement(CHECK_OK);
+    Statement statement = ParseSourceElement(CHECK_OK);
     i::Scanner::Location end_location = scanner_->location();
     if (strict_mode() && statement.IsFunctionDeclaration()) {
       ReportMessageAt(start_location.beg_pos, end_location.end_pos,
@@ -272,11 +282,15 @@ PreParser::Statement PreParser::ParseBlock(bool* ok) {
 }
 
 
-PreParser::Statement PreParser::ParseVariableStatement(bool* ok) {
+PreParser::Statement PreParser::ParseVariableStatement(
+    VariableDeclarationContext var_context,
+    bool* ok) {
   // VariableStatement ::
   //   VariableDeclarations ';'
 
-  Statement result = ParseVariableDeclarations(true, NULL, CHECK_OK);
+  Statement result = ParseVariableDeclarations(var_context,
+                                               NULL,
+                                               CHECK_OK);
   ExpectSemicolon(CHECK_OK);
   return result;
 }
@@ -287,9 +301,10 @@ PreParser::Statement PreParser::ParseVariableStatement(bool* ok) {
 // *var is untouched; in particular, it is the caller's responsibility
 // to initialize it properly. This mechanism is also used for the parsing
 // of 'for-in' loops.
-PreParser::Statement PreParser::ParseVariableDeclarations(bool accept_IN,
-                                                          int* num_decl,
-                                                          bool* ok) {
+PreParser::Statement PreParser::ParseVariableDeclarations(
+    VariableDeclarationContext var_context,
+    int* num_decl,
+    bool* ok) {
   // VariableDeclarations ::
   //   ('var' | 'const') (Identifier ('=' AssignmentExpression)?)+[',']
 
@@ -304,13 +319,25 @@ PreParser::Statement PreParser::ParseVariableDeclarations(bool accept_IN,
       return Statement::Default();
     }
     Consume(i::Token::CONST);
+  } else if (peek() == i::Token::LET) {
+    if (var_context != kSourceElement &&
+        var_context != kForStatement) {
+      i::Scanner::Location location = scanner_->peek_location();
+      ReportMessageAt(location.beg_pos, location.end_pos,
+                      "unprotected_let", NULL);
+      *ok = false;
+      return Statement::Default();
+    }
+    Consume(i::Token::LET);
   } else {
     *ok = false;
     return Statement::Default();
   }
 
-  // The scope of a variable/const declared anywhere inside a function
-  // is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). .
+  // The scope of a var/const declared variable anywhere inside a function
+  // is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). The scope
+  // of a let declared variable is the scope of the immediately enclosing
+  // block.
   int nvars = 0;  // the number of variables declared
   do {
     // Parse variable name.
@@ -326,7 +353,7 @@ PreParser::Statement PreParser::ParseVariableDeclarations(bool accept_IN,
     nvars++;
     if (peek() == i::Token::ASSIGN) {
       Expect(i::Token::ASSIGN, CHECK_OK);
-      ParseAssignmentExpression(accept_IN, CHECK_OK);
+      ParseAssignmentExpression(var_context != kForStatement, CHECK_OK);
     }
   } while (peek() == i::Token::COMMA);
 
@@ -535,9 +562,10 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) {
   Expect(i::Token::FOR, CHECK_OK);
   Expect(i::Token::LPAREN, CHECK_OK);
   if (peek() != i::Token::SEMICOLON) {
-    if (peek() == i::Token::VAR || peek() == i::Token::CONST) {
+    if (peek() == i::Token::VAR || peek() == i::Token::CONST ||
+        peek() == i::Token::LET) {
       int decl_count;
-      ParseVariableDeclarations(false, &decl_count, CHECK_OK);
+      ParseVariableDeclarations(kForStatement, &decl_count, CHECK_OK);
       if (peek() == i::Token::IN && decl_count == 1) {
         Expect(i::Token::IN, CHECK_OK);
         ParseExpression(true, CHECK_OK);
index 3d72c97e2691862349b16416591314641ee4695a..cd0a530e8d0bba8b368510601e30ff566a00cb49 100644 (file)
@@ -77,6 +77,12 @@ class PreParser {
     kFunctionScope
   };
 
+  enum VariableDeclarationContext {
+    kSourceElement,
+    kStatement,
+    kForStatement
+  };
+
   class Expression;
 
   class Identifier {
@@ -344,7 +350,8 @@ class PreParser {
         strict_mode_violation_type_(NULL),
         stack_overflow_(false),
         allow_lazy_(true),
-        parenthesized_function_(false) { }
+        parenthesized_function_(false),
+        harmony_block_scoping_(scanner->HarmonyBlockScoping()) { }
 
   // Preparse the program. Only called in PreParseProgram after creating
   // the instance.
@@ -377,12 +384,16 @@ class PreParser {
   // which is set to false if parsing failed; it is unchanged otherwise.
   // By making the 'exception handling' explicit, we are forced to check
   // for failure at the call sites.
+  Statement ParseSourceElement(bool* ok);
   SourceElements ParseSourceElements(int end_token, bool* ok);
   Statement ParseStatement(bool* ok);
   Statement ParseFunctionDeclaration(bool* ok);
   Statement ParseBlock(bool* ok);
-  Statement ParseVariableStatement(bool* ok);
-  Statement ParseVariableDeclarations(bool accept_IN, int* num_decl, bool* ok);
+  Statement ParseVariableStatement(VariableDeclarationContext var_context,
+                                   bool* ok);
+  Statement ParseVariableDeclarations(VariableDeclarationContext var_context,
+                                      int* num_decl,
+                                      bool* ok);
   Statement ParseExpressionOrLabelledStatement(bool* ok);
   Statement ParseIfStatement(bool* ok);
   Statement ParseContinueStatement(bool* ok);
@@ -496,6 +507,7 @@ class PreParser {
   bool stack_overflow_;
   bool allow_lazy_;
   bool parenthesized_function_;
+  bool harmony_block_scoping_;
 };
 } }  // v8::preparser
 
index ff099fc9e6db88e1597f37d80c1e0dccd0eb25ac..f629970ff28209a568124a23ef07bed9194cbe3c 100644 (file)
@@ -11374,6 +11374,11 @@ static Handle<Context> CopyWithContextChain(Isolate* isolate,
         SerializedScopeInfo::cast(current->extension()));
     new_current =
         isolate->factory()->NewBlockContext(function, new_previous, scope_info);
+    // Copy context slots.
+    int num_context_slots = scope_info->NumberOfContextSlots();
+    for (int i = Context::MIN_CONTEXT_SLOTS; i < num_context_slots; ++i) {
+      new_current->set(i, current->get(i));
+    }
   } else {
     ASSERT(current->IsWithContext());
     Handle<JSObject> extension(JSObject::cast(current->extension()));
index 14efa687730d23fda66a49004fd62a1583ea5969..2f98160f5f8343e0ad264c989a238136f440c812 100644 (file)
@@ -73,7 +73,8 @@ Scanner::Scanner(UnicodeCache* unicode_cache)
 // JavaScriptScanner
 
 JavaScriptScanner::JavaScriptScanner(UnicodeCache* scanner_contants)
-  : Scanner(scanner_contants), octal_pos_(Location::invalid()) { }
+    : Scanner(scanner_contants),
+      octal_pos_(Location::invalid()) { }
 
 
 void JavaScriptScanner::Initialize(UC16CharacterStream* source) {
@@ -820,71 +821,74 @@ uc32 JavaScriptScanner::ScanIdentifierUnicodeEscape() {
 // ----------------------------------------------------------------------------
 // Keyword Matcher
 
-#define KEYWORDS(KEYWORD_GROUP, KEYWORD)                      \
-  KEYWORD_GROUP('b')                                          \
-  KEYWORD("break", BREAK)                                     \
-  KEYWORD_GROUP('c')                                          \
-  KEYWORD("case", CASE)                                       \
-  KEYWORD("catch", CATCH)                                     \
-  KEYWORD("class", FUTURE_RESERVED_WORD)                      \
-  KEYWORD("const", CONST)                                     \
-  KEYWORD("continue", CONTINUE)                               \
-  KEYWORD_GROUP('d')                                          \
-  KEYWORD("debugger", DEBUGGER)                               \
-  KEYWORD("default", DEFAULT)                                 \
-  KEYWORD("delete", DELETE)                                   \
-  KEYWORD("do", DO)                                           \
-  KEYWORD_GROUP('e')                                          \
-  KEYWORD("else", ELSE)                                       \
-  KEYWORD("enum", FUTURE_RESERVED_WORD)                       \
-  KEYWORD("export", FUTURE_RESERVED_WORD)                     \
-  KEYWORD("extends", FUTURE_RESERVED_WORD)                    \
-  KEYWORD_GROUP('f')                                          \
-  KEYWORD("false", FALSE_LITERAL)                             \
-  KEYWORD("finally", FINALLY)                                 \
-  KEYWORD("for", FOR)                                         \
-  KEYWORD("function", FUNCTION)                               \
-  KEYWORD_GROUP('i')                                          \
-  KEYWORD("if", IF)                                           \
-  KEYWORD("implements", FUTURE_STRICT_RESERVED_WORD)          \
-  KEYWORD("import", FUTURE_RESERVED_WORD)                     \
-  KEYWORD("in", IN)                                           \
-  KEYWORD("instanceof", INSTANCEOF)                           \
-  KEYWORD("interface", FUTURE_STRICT_RESERVED_WORD)           \
-  KEYWORD_GROUP('l')                                          \
-  KEYWORD("let", FUTURE_STRICT_RESERVED_WORD)                 \
-  KEYWORD_GROUP('n')                                          \
-  KEYWORD("new", NEW)                                         \
-  KEYWORD("null", NULL_LITERAL)                               \
-  KEYWORD_GROUP('p')                                          \
-  KEYWORD("package", FUTURE_STRICT_RESERVED_WORD)             \
-  KEYWORD("private", FUTURE_STRICT_RESERVED_WORD)             \
-  KEYWORD("protected", FUTURE_STRICT_RESERVED_WORD)           \
-  KEYWORD("public", FUTURE_STRICT_RESERVED_WORD)              \
-  KEYWORD_GROUP('r')                                          \
-  KEYWORD("return", RETURN)                                   \
-  KEYWORD_GROUP('s')                                          \
-  KEYWORD("static", FUTURE_STRICT_RESERVED_WORD)              \
-  KEYWORD("super", FUTURE_RESERVED_WORD)                      \
-  KEYWORD("switch", SWITCH)                                   \
-  KEYWORD_GROUP('t')                                          \
-  KEYWORD("this", THIS)                                       \
-  KEYWORD("throw", THROW)                                     \
-  KEYWORD("true", TRUE_LITERAL)                               \
-  KEYWORD("try", TRY)                                         \
-  KEYWORD("typeof", TYPEOF)                                   \
-  KEYWORD_GROUP('v')                                          \
-  KEYWORD("var", VAR)                                         \
-  KEYWORD("void", VOID)                                       \
-  KEYWORD_GROUP('w')                                          \
-  KEYWORD("while", WHILE)                                     \
-  KEYWORD("with", WITH)                                       \
-  KEYWORD_GROUP('y')                                          \
-  KEYWORD("yield", FUTURE_STRICT_RESERVED_WORD)
+#define KEYWORDS(KEYWORD_GROUP, KEYWORD)                            \
+  KEYWORD_GROUP('b')                                                \
+  KEYWORD("break", Token::BREAK)                                    \
+  KEYWORD_GROUP('c')                                                \
+  KEYWORD("case", Token::CASE)                                      \
+  KEYWORD("catch", Token::CATCH)                                    \
+  KEYWORD("class", Token::FUTURE_RESERVED_WORD)                     \
+  KEYWORD("const", Token::CONST)                                    \
+  KEYWORD("continue", Token::CONTINUE)                              \
+  KEYWORD_GROUP('d')                                                \
+  KEYWORD("debugger", Token::DEBUGGER)                              \
+  KEYWORD("default", Token::DEFAULT)                                \
+  KEYWORD("delete", Token::DELETE)                                  \
+  KEYWORD("do", Token::DO)                                          \
+  KEYWORD_GROUP('e')                                                \
+  KEYWORD("else", Token::ELSE)                                      \
+  KEYWORD("enum", Token::FUTURE_RESERVED_WORD)                      \
+  KEYWORD("export", Token::FUTURE_RESERVED_WORD)                    \
+  KEYWORD("extends", Token::FUTURE_RESERVED_WORD)                   \
+  KEYWORD_GROUP('f')                                                \
+  KEYWORD("false", Token::FALSE_LITERAL)                            \
+  KEYWORD("finally", Token::FINALLY)                                \
+  KEYWORD("for", Token::FOR)                                        \
+  KEYWORD("function", Token::FUNCTION)                              \
+  KEYWORD_GROUP('i')                                                \
+  KEYWORD("if", Token::IF)                                          \
+  KEYWORD("implements", Token::FUTURE_STRICT_RESERVED_WORD)         \
+  KEYWORD("import", Token::FUTURE_RESERVED_WORD)                    \
+  KEYWORD("in", Token::IN)                                          \
+  KEYWORD("instanceof", Token::INSTANCEOF)                          \
+  KEYWORD("interface", Token::FUTURE_STRICT_RESERVED_WORD)          \
+  KEYWORD_GROUP('l')                                                \
+  KEYWORD("let", harmony_block_scoping                              \
+                 ? Token::LET : Token::FUTURE_STRICT_RESERVED_WORD) \
+  KEYWORD_GROUP('n')                                                \
+  KEYWORD("new", Token::NEW)                                        \
+  KEYWORD("null", Token::NULL_LITERAL)                              \
+  KEYWORD_GROUP('p')                                                \
+  KEYWORD("package", Token::FUTURE_STRICT_RESERVED_WORD)            \
+  KEYWORD("private", Token::FUTURE_STRICT_RESERVED_WORD)            \
+  KEYWORD("protected", Token::FUTURE_STRICT_RESERVED_WORD)          \
+  KEYWORD("public", Token::FUTURE_STRICT_RESERVED_WORD)             \
+  KEYWORD_GROUP('r')                                                \
+  KEYWORD("return", Token::RETURN)                                  \
+  KEYWORD_GROUP('s')                                                \
+  KEYWORD("static", Token::FUTURE_STRICT_RESERVED_WORD)             \
+  KEYWORD("super", Token::FUTURE_RESERVED_WORD)                     \
+  KEYWORD("switch", Token::SWITCH)                                  \
+  KEYWORD_GROUP('t')                                                \
+  KEYWORD("this", Token::THIS)                                      \
+  KEYWORD("throw", Token::THROW)                                    \
+  KEYWORD("true", Token::TRUE_LITERAL)                              \
+  KEYWORD("try", Token::TRY)                                        \
+  KEYWORD("typeof", Token::TYPEOF)                                  \
+  KEYWORD_GROUP('v')                                                \
+  KEYWORD("var", Token::VAR)                                        \
+  KEYWORD("void", Token::VOID)                                      \
+  KEYWORD_GROUP('w')                                                \
+  KEYWORD("while", Token::WHILE)                                    \
+  KEYWORD("with", Token::WITH)                                      \
+  KEYWORD_GROUP('y')                                                \
+  KEYWORD("yield", Token::FUTURE_STRICT_RESERVED_WORD)
 
 
 static Token::Value KeywordOrIdentifierToken(const char* input,
-                                             int input_length) {
+                                             int input_length,
+                                             bool harmony_block_scoping) {
+
   ASSERT(input_length >= 1);
   const int kMinLength = 2;
   const int kMaxLength = 10;
@@ -913,7 +917,7 @@ static Token::Value KeywordOrIdentifierToken(const char* input,
           (keyword_length <= 7 || input[7] == keyword[7]) &&  \
           (keyword_length <= 8 || input[8] == keyword[8]) &&  \
           (keyword_length <= 9 || input[9] == keyword[9])) {  \
-        return Token::token;                                  \
+        return token;                                         \
       }                                                       \
     }
     KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD)
@@ -954,7 +958,9 @@ Token::Value JavaScriptScanner::ScanIdentifierOrKeyword() {
 
   if (next_.literal_chars->is_ascii()) {
     Vector<const char> chars = next_.literal_chars->ascii_literal();
-    return KeywordOrIdentifierToken(chars.start(), chars.length());
+    return KeywordOrIdentifierToken(chars.start(),
+                                    chars.length(),
+                                    harmony_block_scoping_);
   }
 
   return Token::IDENTIFIER;
index 7e416651e7a78c76867639b7d4fb4825b0a02acc..da7475764840228091452be74eeaa4a0f6810f08 100644 (file)
@@ -507,6 +507,14 @@ class JavaScriptScanner : public Scanner {
   // tokens, which is what it is used for.
   void SeekForward(int pos);
 
+  bool HarmonyBlockScoping() const {
+    return harmony_block_scoping_;
+  }
+  void SetHarmonyBlockScoping(bool block_scoping) {
+    harmony_block_scoping_ = block_scoping;
+  }
+
+
  protected:
   bool SkipWhiteSpace();
   Token::Value SkipSingleLineComment();
@@ -540,6 +548,9 @@ class JavaScriptScanner : public Scanner {
   // Whether there is a multi-line comment that contains a
   // line-terminator after the current token, and before the next.
   bool has_multiline_comment_before_next_;
+  // Whether we scan 'let' as a keyword for harmony block scoped
+  // let bindings.
+  bool harmony_block_scoping_;
 };
 
 } }  // namespace v8::internal
index eb0deb4964bb8cbc8ad5d1a151e381053c42bff1..ddde48a77c50eaccb92ef21376dfc510f1a5aeb0 100644 (file)
@@ -397,7 +397,9 @@ Variable* Scope::DeclareLocal(Handle<String> name, Variable::Mode mode) {
   // This function handles VAR and CONST modes.  DYNAMIC variables are
   // introduces during variable allocation, INTERNAL variables are allocated
   // explicitly, and TEMPORARY variables are allocated via NewTemporary().
-  ASSERT(mode == Variable::VAR || mode == Variable::CONST);
+  ASSERT(mode == Variable::VAR ||
+         mode == Variable::CONST ||
+         mode == Variable::LET);
   ++num_var_or_const_;
   return variables_.Declare(this, name, mode, true, Variable::NORMAL);
 }
index 7e6c18cfe6402bca329635057292fc3dfcbe46ad..33af7fe6bf1edfe656cb9218f55b52b4647ce175 100644 (file)
@@ -168,6 +168,7 @@ namespace internal {
   T(FUTURE_RESERVED_WORD, NULL, 0)                                      \
   T(FUTURE_STRICT_RESERVED_WORD, NULL, 0)                               \
   K(CONST, "const", 0)                                                  \
+  K(LET, "let", 0)                                                      \
                                                                         \
   /* Illegal token - not able to scan. */                               \
   T(ILLEGAL, "ILLEGAL", 0)                                              \
index 03f11db9af3a82a6220739d2ca77e97dad5b4728..69495bb40fdaea610e688319dac480ce091e0747 100644 (file)
@@ -41,6 +41,7 @@ const char* Variable::Mode2String(Mode mode) {
   switch (mode) {
     case VAR: return "VAR";
     case CONST: return "CONST";
+    case LET: return "LET";
     case DYNAMIC: return "DYNAMIC";
     case DYNAMIC_GLOBAL: return "DYNAMIC_GLOBAL";
     case DYNAMIC_LOCAL: return "DYNAMIC_LOCAL";
index ecdaeb0a6101cb08d186794c3e0c092b32ac97ab..e92ba6d39abf7d629ae6df963db64de54472a8d3 100644 (file)
@@ -46,6 +46,8 @@ class Variable: public ZoneObject {
 
     CONST,     // declared via 'const' declarations
 
+    LET,       // declared via 'let' declarations
+
     // Variables introduced by the compiler:
     DYNAMIC,         // always require dynamic lookup (we don't know
                      // the declaration)
index edd9e8102dfc7e347f157a53751b71db951c5597..d0c71be12fb76aade88031dcfd1d734ec3945ecb 100644 (file)
@@ -712,8 +712,10 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable,
         __ push(rsi);
         __ Push(variable->name());
         // Declaration nodes are always introduced in one of two modes.
-        ASSERT(mode == Variable::VAR || mode == Variable::CONST);
-        PropertyAttributes attr = (mode == Variable::VAR) ? NONE : READ_ONLY;
+        ASSERT(mode == Variable::VAR ||
+               mode == Variable::CONST ||
+               mode == Variable::LET);
+        PropertyAttributes attr = (mode == Variable::CONST) ? READ_ONLY : NONE;
         __ Push(Smi::FromInt(attr));
         // Push initial value, if any.
         // Note: For variables we must not push an initial value (such as
index 654c6cf73286e533a04bb0b281c50d658362f52b..db3e42b4d796a0bf3eea6c6839f2454ad33fdff5 100755 (executable)
@@ -65,6 +65,8 @@ TEST(ScanKeywords) {
       i::Utf8ToUC16CharacterStream stream(keyword, length);
       i::JavaScriptScanner scanner(&unicode_cache);
       scanner.Initialize(&stream);
+      // The scanner should parse 'let' as Token::LET for this test.
+      scanner.SetHarmonyBlockScoping(true);
       CHECK_EQ(key_token.token, scanner.Next());
       CHECK_EQ(i::Token::EOS, scanner.Next());
     }
@@ -287,7 +289,7 @@ TEST(RegressChromium62639) {
   i::Utf8ToUC16CharacterStream stream(reinterpret_cast<const i::byte*>(program),
                                       static_cast<unsigned>(strlen(program)));
   i::ScriptDataImpl* data =
-      i::ParserApi::PreParse(&stream, NULL);
+      i::ParserApi::PreParse(&stream, NULL, false);
   CHECK(data->HasError());
   delete data;
 }
@@ -311,7 +313,7 @@ TEST(Regress928) {
   i::Utf8ToUC16CharacterStream stream(reinterpret_cast<const i::byte*>(program),
                                       static_cast<unsigned>(strlen(program)));
   i::ScriptDataImpl* data =
-      i::ParserApi::PartialPreParse(&stream, NULL);
+      i::ParserApi::PartialPreParse(&stream, NULL, false);
   CHECK(!data->HasError());
 
   data->Initialize();
diff --git a/test/mjsunit/bugs/harmony/debug-blockscopes.js b/test/mjsunit/bugs/harmony/debug-blockscopes.js
new file mode 100644 (file)
index 0000000..a407c53
--- /dev/null
@@ -0,0 +1,224 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --expose-debug-as debug --harmony-block-scoping
+// The functions used for testing backtraces. They are at the top to make the
+// testing of source line/column easier.
+
+
+// Get the Debug object exposed from the debug context global object.
+Debug = debug.Debug;
+
+var test_name;
+var listener_delegate;
+var listener_called;
+var exception;
+var begin_test_count = 0;
+var end_test_count = 0;
+var break_count = 0;
+
+
+// Debug event listener which delegates.
+function listener(event, exec_state, event_data, data) {
+  try {
+    if (event == Debug.DebugEvent.Break) {
+      break_count++;
+      listener_called = true;
+      listener_delegate(exec_state);
+    }
+  } catch (e) {
+    exception = e;
+  }
+}
+
+// Add the debug event listener.
+Debug.setListener(listener);
+
+
+// Initialize for a new test.
+function BeginTest(name) {
+  test_name = name;
+  listener_delegate = null;
+  listener_called = false;
+  exception = null;
+  begin_test_count++;
+}
+
+
+// Check result of a test.
+function EndTest() {
+  assertTrue(listener_called, "listerner not called for " + test_name);
+  assertNull(exception, test_name);
+  end_test_count++;
+}
+
+
+// Check that the scope chain contains the expected types of scopes.
+function CheckScopeChain(scopes, exec_state) {
+  assertEquals(scopes.length, exec_state.frame().scopeCount());
+  for (var i = 0; i < scopes.length; i++) {
+    var scope = exec_state.frame().scope(i);
+    assertTrue(scope.isScope());
+    assertEquals(scopes[i], scope.scopeType());
+
+    // Check the global object when hitting the global scope.
+    if (scopes[i] == debug.ScopeType.Global) {
+      // Objects don't have same class (one is "global", other is "Object",
+      // so just check the properties directly.
+      assertPropertiesEqual(this, scope.scopeObject().value());
+    }
+  }
+
+  // Get the debug command processor.
+  var dcp = exec_state.debugCommandProcessor("unspecified_running_state");
+
+  // Send a scopes request and check the result.
+  var json;
+  var request_json = '{"seq":0,"type":"request","command":"scopes"}';
+  var response_json = dcp.processDebugJSONRequest(request_json);
+  var response = JSON.parse(response_json);
+  assertEquals(scopes.length, response.body.scopes.length);
+  for (var i = 0; i < scopes.length; i++) {
+    assertEquals(i, response.body.scopes[i].index);
+    assertEquals(scopes[i], response.body.scopes[i].type);
+    if (scopes[i] == debug.ScopeType.Local ||
+        scopes[i] == debug.ScopeType.Closure) {
+      assertTrue(response.body.scopes[i].object.ref < 0);
+    } else {
+      assertTrue(response.body.scopes[i].object.ref >= 0);
+    }
+    var found = false;
+    for (var j = 0; j < response.refs.length && !found; j++) {
+      found = response.refs[j].handle == response.body.scopes[i].object.ref;
+    }
+    assertTrue(found, "Scope object " + response.body.scopes[i].object.ref + " not found");
+  }
+}
+
+// Check that the content of the scope is as expected. For functions just check
+// that there is a function.
+function CheckScopeContent(content, number, exec_state) {
+  var scope = exec_state.frame().scope(number);
+  var count = 0;
+  for (var p in content) {
+    var property_mirror = scope.scopeObject().property(p);
+    if (property_mirror.isUndefined()) {
+      print('property ' + p + ' not found in scope');
+    }
+    assertFalse(property_mirror.isUndefined(), 'property ' + p + ' not found in scope');
+    if (typeof(content[p]) === 'function') {
+      assertTrue(property_mirror.value().isFunction());
+    } else {
+      assertEquals(content[p], property_mirror.value().value(), 'property ' + p + ' has unexpected value');
+    }
+    count++;
+  }
+
+  // 'arguments' and might be exposed in the local and closure scope. Just
+  // ignore this.
+  var scope_size = scope.scopeObject().properties().length;
+  if (!scope.scopeObject().property('arguments').isUndefined()) {
+    scope_size--;
+  }
+  // Also ignore synthetic variable from catch block.
+  if (!scope.scopeObject().property('.catch-var').isUndefined()) {
+    scope_size--;
+  }
+  // Skip property with empty name.
+  if (!scope.scopeObject().property('').isUndefined()) {
+    scope_size--;
+  }
+  // Also ignore synthetic variable from block scopes.
+  if (!scope.scopeObject().property('.block').isUndefined()) {
+    scope_size--;
+  }
+
+  if (count != scope_size) {
+    print('Names found in scope:');
+    var names = scope.scopeObject().propertyNames();
+    for (var i = 0; i < names.length; i++) {
+      print(names[i]);
+    }
+  }
+  assertEquals(count, scope_size);
+
+  // Get the debug command processor.
+  var dcp = exec_state.debugCommandProcessor("unspecified_running_state");
+
+  // Send a scope request for information on a single scope and check the
+  // result.
+  var request_json = '{"seq":0,"type":"request","command":"scope","arguments":{"number":';
+  request_json += scope.scopeIndex();
+  request_json += '}}';
+  var response_json = dcp.processDebugJSONRequest(request_json);
+  var response = JSON.parse(response_json);
+  assertEquals(scope.scopeType(), response.body.type);
+  assertEquals(number, response.body.index);
+  if (scope.scopeType() == debug.ScopeType.Local ||
+      scope.scopeType() == debug.ScopeType.Closure) {
+    assertTrue(response.body.object.ref < 0);
+  } else {
+    assertTrue(response.body.object.ref >= 0);
+  }
+  var found = false;
+  for (var i = 0; i < response.refs.length && !found; i++) {
+    found = response.refs[i].handle == response.body.object.ref;
+  }
+  assertTrue(found, "Scope object " + response.body.object.ref + " not found");
+}
+
+
+// Simple closure formed by returning an inner function referering to an outer
+// block local variable and an outer function's parameter. Due to VM
+// optimizations parts of the actual closure is missing from the debugger
+// information.
+BeginTest("Closure 1");
+
+function closure_1(a) {
+  var x = 2;
+  let y = 3;
+  if (true) {
+    let z = 4;
+    function f() {
+      debugger;
+      return a + x + y + z;
+    };
+    return f;
+  }
+}
+
+listener_delegate = function(exec_state) {
+  CheckScopeChain([debug.ScopeType.Local,
+                   debug.ScopeType.Block,
+                   debug.ScopeType.Closure,
+                   debug.ScopeType.Global], exec_state);
+  CheckScopeContent({}, 0, exec_state);
+  CheckScopeContent({z:4}, 1, exec_state);
+  CheckScopeContent({a:1,x:2,y:3}, 2, exec_state);
+};
+closure_1(1)();
+EndTest();
diff --git a/test/mjsunit/harmony/block-let-declaration.js b/test/mjsunit/harmony/block-let-declaration.js
new file mode 100644 (file)
index 0000000..19c943f
--- /dev/null
@@ -0,0 +1,67 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --harmony-block-scoping
+
+// Test let declarations in various settings.
+
+// Global
+let x;
+let y = 2;
+
+// Block local
+{
+  let y;
+  let x = 3;
+}
+
+assertEquals(undefined, x);
+assertEquals(2,y);
+
+if (true) {
+  let y;
+  assertEquals(undefined, y);
+}
+
+function TestLocalThrows(str, expect) {
+  assertThrows("(function(){" + str + "})()", expect);
+}
+
+function TestLocalDoesNotThrow(str) {
+  assertDoesNotThrow("(function(){" + str + "})()");
+}
+
+// Unprotected statement
+TestLocalThrows("if (true) let x;", SyntaxError);
+TestLocalThrows("with ({}) let x;", SyntaxError);
+TestLocalThrows("do let x; while (false)", SyntaxError);
+TestLocalThrows("while (false) let x;", SyntaxError);
+
+TestLocalDoesNotThrow("if (true) var x;");
+TestLocalDoesNotThrow("with ({}) var x;");
+TestLocalDoesNotThrow("do var x; while (false)");
+TestLocalDoesNotThrow("while (false) var x;");
index f974de82b5ce35b94dc58acb0ed651246014e1f9..266e380725a4766cf34bb5c0c2b2925907ddf84e 100644 (file)
@@ -39,20 +39,178 @@ function f1() {
 }
 f1();
 
-// Dynamic lookup through block scopes.
+
+// Dynamic lookup in and through block contexts.
 function f2(one) {
   var x = one + 1;
-  // TODO(keuchel): introduce let
-  // let y = one + 2;
-  if (one == 1) {
-    // Parameter
+  let y = one + 2;
+  {
+    let z = one + 3;
     assertEquals(1, eval('one'));
-    // Function local var variable
     assertEquals(2, eval('x'));
-    // Function local let variable
-    // TODO(keuchel): introduce let
-    // assertEquals(3, eval('y'));
+    assertEquals(3, eval('y'));
+    assertEquals(4, eval('z'));
   }
 }
 f2(1);
 
+
+// Lookup in and through block contexts.
+function f3(one) {
+  var x = one + 1;
+  let y = one + 2;
+  {
+    let z = one + 3;
+    assertEquals(1, one);
+    assertEquals(2, x);
+    assertEquals(3, y);
+    assertEquals(4, z);
+  }
+}
+f3(1);
+
+
+// Dynamic lookup from closure.
+function f4(one) {
+  var x = one + 1;
+  let y = one + 2;
+  {
+    let z = one + 3;
+    function f() {
+      assertEquals(1, eval('one'));
+      assertEquals(2, eval('x'));
+      assertEquals(3, eval('y'));
+      assertEquals(4, eval('z'));
+    };
+  }
+}
+f4(1);
+
+
+// Lookup from closure.
+function f5(one) {
+  var x = one + 1;
+  let y = one + 2;
+  {
+    let z = one + 3;
+    function f() {
+      assertEquals(1, one);
+      assertEquals(2, x);
+      assertEquals(3, y);
+      assertEquals(4, z);
+    };
+  }
+}
+f5(1);
+
+
+// Return from block.
+function f6() {
+  let x = 1;
+  {
+    let y = 2;
+    return x + y;
+  }
+}
+assertEquals(3, f6(6));
+
+
+// Variable shadowing and lookup.
+function f7(a) {
+  let b = 1;
+  var c = 1;
+  var d = 1;
+  { // let variables shadowing argument, let and var variables
+    let a = 2;
+    let b = 2;
+    let c = 2;
+    assertEquals(2,a);
+    assertEquals(2,b);
+    assertEquals(2,c);
+  }
+  try {
+    throw 'stuff1';
+  } catch (a) {
+    assertEquals('stuff1',a);
+    // catch variable shadowing argument
+    a = 2;
+    assertEquals(2,a);
+    {
+      // let variable shadowing catch variable
+      let a = 3;
+      assertEquals(3,a);
+      try {
+        throw 'stuff2';
+      } catch (a) {
+        assertEquals('stuff2',a);
+        // catch variable shadowing let variable
+        a = 4;
+        assertEquals(4,a);
+      }
+      assertEquals(3,a);
+    }
+    assertEquals(2,a);
+  }
+  try {
+    throw 'stuff3';
+  } catch (c) {
+    // catch variable shadowing var variable
+    assertEquals('stuff3',c);
+    try {
+      throw 'stuff4';
+    } catch(c) {
+      assertEquals('stuff4',c);
+      // catch variable shadowing catch variable
+      c = 3;
+      assertEquals(3,c);
+    }
+    (function(c) {
+      // argument shadowing catch variable
+      c = 3;
+      assertEquals(3,c);
+    })();
+    assertEquals('stuff3', c);
+    (function() {
+      // var variable shadowing catch variable
+      var c = 3;
+    })();
+    assertEquals('stuff3', c);
+    c = 2;
+  }
+  assertEquals(1,c);
+  (function(a,b,c) {
+    // arguments shadowing argument, let and var variable
+    a = 2;
+    b = 2;
+    c = 2;
+    assertEquals(2,a);
+    assertEquals(2,b);
+    assertEquals(2,c);
+    // var variable shadowing var variable
+    var d = 2;
+  })(1,1);
+  assertEquals(1,a);
+  assertEquals(1,b);
+  assertEquals(1,c);
+  assertEquals(1,d);
+}
+f7(1);
+
+
+// Ensure let variables are block local and var variables function local.
+function f8() {
+  var let_accessors = [];
+  var var_accessors = [];
+  for (var i = 0; i < 10; i++) {
+    let x = i;
+    var y = i;
+    let_accessors[i] = function() { return x; }
+    var_accessors[i] = function() { return y; }
+  }
+  for (var j = 0; j < 10; j++) {
+    y = j + 10;
+    assertEquals(j, let_accessors[j]());
+    assertEquals(y, var_accessors[j]());
+  }
+}
+f8();
index 30f681f129da92cdc6a97fea1cd0bb26c1e91a23..e0df71b2dfe9a6df1898b3d0446a889934312e0b 100644 (file)
@@ -157,7 +157,6 @@ function CheckScopeContent(content, number, exec_state) {
     scope_size--;
   }
 
-  // TODO(keuchel): print('count' + count + ' scopesize' + scope_size);
   if (count != scope_size) {
     print('Names found in scope:');
     var names = scope.scopeObject().propertyNames();
@@ -236,7 +235,7 @@ EndTest();
 BeginTest("Local 3");
 
 function local_3(a) {
-  var x = 3;
+  let x = 3;
   debugger;
 }
 
@@ -253,8 +252,8 @@ EndTest();
 BeginTest("Local 4");
 
 function local_4(a, b) {
-  var x = 3;
-  var y = 4;
+  let x = 3;
+  let y = 4;
   debugger;
 }
 
@@ -361,29 +360,30 @@ with_block_4();
 EndTest();
 
 
-// TODO(keuchel):
 // Simple closure formed by returning an inner function referering to an outer
 // block local variable and an outer function's parameter.
-// BeginTest("Closure 1");
-//
-// function closure_1(a) {
-//   {
-//     let x = 3;
-//     function f() {
-//       debugger;
-//       return a + x;
-//     };
-//   }
-//   return f;
-// }
-//
-// listener_delegate = function(exec_state) {
-//   CheckScopeChain([debug.ScopeType.Local,
-//                    debug.ScopeType.Closure,
-//                    debug.ScopeType.Global], exec_state);
-//   // CheckScopeContent({}, 0, exec_state);
-//   // CheckScopeContent({}, 1, exec_state);
-//   // CheckScopeContent({a:1}, 2, exec_state);
-// };
-// closure_1(1)();
-// EndTest();
+BeginTest("Closure 1");
+
+function closure_1(a) {
+  var x = 2;
+  let y = 3;
+  if (true) {
+    let z = 4;
+    function f() {
+      debugger;
+      return a + x + y + z;
+    };
+    return f;
+  }
+}
+
+listener_delegate = function(exec_state) {
+  CheckScopeChain([debug.ScopeType.Local,
+                   debug.ScopeType.Block,
+                   debug.ScopeType.Closure,
+                   debug.ScopeType.Global], exec_state);
+  CheckScopeContent({}, 0, exec_state);
+  CheckScopeContent({a:1,x:2,y:3}, 2, exec_state);
+};
+closure_1(1)();
+EndTest();
index 8892416bd58419e545e94c9dc7abebf7f2fc505c..549960a2892f78db50364618622cf7c10614afc9 100644 (file)
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-// Flags: --expose-debug-as debug
+// Flags: --expose-debug-as debug --harmony-block-scoping
 
 // Test debug evaluation for functions without local context, but with
 // nested catch contexts.
 
 function f() {
   {                   // Line 1.
-    var i = 1;        // Line 2. // TODO(keuchel): introduce let
+    let i = 1;        // Line 2.
     try {             // Line 3.
       throw 'stuff';  // Line 4.
     } catch (e) {     // Line 5.