Preliminary code for block scopes and block contexts.
authorkeuchel@chromium.org <keuchel@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 11 Aug 2011 16:29:28 +0000 (16:29 +0000)
committerkeuchel@chromium.org <keuchel@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 11 Aug 2011 16:29:28 +0000 (16:29 +0000)
BUG=
TEST=

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

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

32 files changed:
src/ast-inl.h
src/ast.h
src/contexts.cc
src/contexts.h
src/d8.cc
src/d8.js
src/factory.cc
src/factory.h
src/flag-definitions.h
src/full-codegen.cc
src/heap.cc
src/heap.h
src/hydrogen.cc
src/mirror-debugger.js
src/objects-inl.h
src/objects.h
src/parser.cc
src/parser.h
src/runtime.cc
src/runtime.h
src/scopeinfo.cc
src/scopeinfo.h
src/scopes.cc
src/scopes.h
src/serialize.h
src/variables.cc
src/variables.h
test/mjsunit/fuzz-natives.js
test/mjsunit/harmony/block-lazy-compile.js [new file with mode: 0644]
test/mjsunit/harmony/block-scoping.js [new file with mode: 0644]
test/mjsunit/harmony/debug-blockscopes.js [new file with mode: 0644]
test/mjsunit/harmony/debug-evaluate-blockscopes.js [new file with mode: 0644]

index c750e6b03c383cd87ea1de45a83bd9911f534057..731ad2ff3f2a8468ade4204ca5a78243ce1fa56c 100644 (file)
@@ -50,7 +50,8 @@ Block::Block(Isolate* isolate,
              bool is_initializer_block)
     : BreakableStatement(isolate, labels, TARGET_FOR_NAMED_ONLY),
       statements_(capacity),
-      is_initializer_block_(is_initializer_block) {
+      is_initializer_block_(is_initializer_block),
+      block_scope_(NULL) {
 }
 
 
index b4705f6ab853e2373bb66be3e4bee5b3044a7701..a735ee130d331feac5e633860920588c88a7ce89 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -359,9 +359,13 @@ class Block: public BreakableStatement {
   ZoneList<Statement*>* statements() { return &statements_; }
   bool is_initializer_block() const { return is_initializer_block_; }
 
+  Scope* block_scope() const { return block_scope_; }
+  void set_block_scope(Scope* block_scope) { block_scope_ = block_scope; }
+
  private:
   ZoneList<Statement*> statements_;
   bool is_initializer_block_;
+  Scope* block_scope_;
 };
 
 
index d066d347692931b887a1f767828293a0c2ab5d19..72a5ae4d6077fab4a067c57553f7348eeeb8bfa4 100644 (file)
@@ -109,7 +109,7 @@ Handle<Object> Context::Lookup(Handle<String> name,
     }
 
     // Check extension/with/global object.
-    if (context->has_extension()) {
+    if (!context->IsBlockContext() && context->has_extension()) {
       if (context->IsCatchContext()) {
         // Catch contexts have the variable name in the extension slot.
         if (name->Equals(String::cast(context->extension()))) {
@@ -121,6 +121,9 @@ Handle<Object> Context::Lookup(Handle<String> name,
           return context;
         }
       } else {
+        ASSERT(context->IsGlobalContext() ||
+               context->IsFunctionContext() ||
+               context->IsWithContext());
         // Global, function, and with contexts may have an object in the
         // extension slot.
         Handle<JSObject> extension(JSObject::cast(context->extension()),
@@ -145,11 +148,20 @@ Handle<Object> Context::Lookup(Handle<String> name,
       }
     }
 
-    // Only functions can have locals, parameters, and a function name.
-    if (context->IsFunctionContext()) {
+    // Check serialized scope information of functions and blocks. Only
+    // functions can have parameters, and a function name.
+    if (context->IsFunctionContext() || context->IsBlockContext()) {
       // We may have context-local slots.  Check locals in the context.
-      Handle<SerializedScopeInfo> scope_info(
-          context->closure()->shared()->scope_info(), isolate);
+      Handle<SerializedScopeInfo> scope_info;
+      if (context->IsFunctionContext()) {
+        scope_info = Handle<SerializedScopeInfo>(
+            context->closure()->shared()->scope_info(), isolate);
+      } else {
+        ASSERT(context->IsBlockContext());
+        scope_info = Handle<SerializedScopeInfo>(
+            SerializedScopeInfo::cast(context->extension()), isolate);
+      }
+
       Variable::Mode mode;
       int index = scope_info->ContextSlotIndex(*name, &mode);
       ASSERT(index < 0 || index >= MIN_CONTEXT_SLOTS);
index 53b40f127d8efac2f45ebfdca2dd55d116cf945b..3d9e7f4bfe06b481071d7f571a8240c756716980 100644 (file)
@@ -295,6 +295,10 @@ class Context: public FixedArray {
     Map* map = this->map();
     return map == map->GetHeap()->with_context_map();
   }
+  bool IsBlockContext() {
+    Map* map = this->map();
+    return map == map->GetHeap()->block_context_map();
+  }
 
   // Tells whether the global context is marked with out of memory.
   inline bool has_out_of_memory();
index 3b9c183c2ad709114911e54b90d401b5f4045bbc..780b0c0716e5d8b0dd4fc86197dc84fa0810d0ba 100644 (file)
--- a/src/d8.cc
+++ b/src/d8.cc
@@ -740,6 +740,7 @@ Persistent<Context> Shell::CreateEvaluationContext() {
   // Initialize the global objects
   Handle<ObjectTemplate> global_template = CreateGlobalTemplate();
   Persistent<Context> context = Context::New(NULL, global_template);
+  ASSERT(!context.IsEmpty());
   Context::Scope scope(context);
 
 #ifndef V8_SHARED
index 033455e9df94e29349f3e229c9ef16ab47b7ca30..3523e54abdc526ab78f692c507fb599c22a3c3a6 100644 (file)
--- a/src/d8.js
+++ b/src/d8.js
@@ -103,7 +103,8 @@ Debug.ScopeType = { Global: 0,
                     Local: 1,
                     With: 2,
                     Closure: 3,
-                    Catch: 4 };
+                    Catch: 4,
+                    Block: 5 };
 
 
 // Current debug state.
index 05dd5c96619ea25d61feb38d8f54a9834b61f7e7..ee5c37bf081a406cc803df1a9c50a5f556fa3d6c 100644 (file)
@@ -34,6 +34,7 @@
 #include "macro-assembler.h"
 #include "objects.h"
 #include "objects-visiting.h"
+#include "scopeinfo.h"
 
 namespace v8 {
 namespace internal {
@@ -291,6 +292,19 @@ Handle<Context> Factory::NewWithContext(Handle<JSFunction> function,
 }
 
 
+Handle<Context> Factory::NewBlockContext(
+    Handle<JSFunction> function,
+    Handle<Context> previous,
+    Handle<SerializedScopeInfo> scope_info) {
+  CALL_HEAP_FUNCTION(
+      isolate(),
+      isolate()->heap()->AllocateBlockContext(*function,
+                                              *previous,
+                                              *scope_info),
+      Context);
+}
+
+
 Handle<Struct> Factory::NewStruct(InstanceType type) {
   CALL_HEAP_FUNCTION(
       isolate(),
@@ -734,6 +748,14 @@ Handle<JSFunction> Factory::NewFunctionWithoutPrototype(Handle<String> name,
 }
 
 
+Handle<SerializedScopeInfo> Factory::NewSerializedScopeInfo(int length) {
+  CALL_HEAP_FUNCTION(
+      isolate(),
+      isolate()->heap()->AllocateSerializedScopeInfo(length),
+      SerializedScopeInfo);
+}
+
+
 Handle<Code> Factory::NewCode(const CodeDesc& desc,
                               Code::Flags flags,
                               Handle<Object> self_ref,
index 3217ca906b37cda09da77c2195f4b5eb9850e874..a69b05b38f00fc385b665bba6eea1ea039b3fb24 100644 (file)
@@ -167,6 +167,11 @@ class Factory {
                                  Handle<Context> previous,
                                  Handle<JSObject> extension);
 
+  // Create a 'block' context.
+  Handle<Context> NewBlockContext(Handle<JSFunction> function,
+                                  Handle<Context> previous,
+                                  Handle<SerializedScopeInfo> scope_info);
+
   // Return the Symbol matching the passed in string.
   Handle<String> SymbolFromString(Handle<String> value);
 
@@ -277,6 +282,8 @@ class Factory {
       Handle<Context> context,
       PretenureFlag pretenure = TENURED);
 
+  Handle<SerializedScopeInfo> NewSerializedScopeInfo(int length);
+
   Handle<Code> NewCode(const CodeDesc& desc,
                        Code::Flags flags,
                        Handle<Object> self_reference,
index e315af92af6a3c92697bc6b1efc8a253ea1e581d..2d8f6fa95a281f1064e15eee4673a2ddb760a0d0 100644 (file)
@@ -100,6 +100,7 @@ private:
 DEFINE_bool(harmony_typeof, false, "enable harmony semantics for typeof")
 DEFINE_bool(harmony_proxies, false, "enable harmony proxies")
 DEFINE_bool(harmony_weakmaps, false, "enable harmony weak maps")
+DEFINE_bool(harmony_block_scoping, false, "enable harmony block scoping")
 
 // Flags for experimental implementation features.
 DEFINE_bool(unbox_double_arrays, true, "automatically unbox arrays of doubles")
index dbf58b19576e5b18b659d764094993591a9e4c22..aabf6ca9a9a6becba35a099509d4dc89052b6f9d 100644 (file)
@@ -35,6 +35,7 @@
 #include "macro-assembler.h"
 #include "prettyprinter.h"
 #include "scopes.h"
+#include "scopeinfo.h"
 #include "stub-cache.h"
 
 namespace v8 {
@@ -845,8 +846,23 @@ void FullCodeGenerator::VisitBlock(Block* stmt) {
   Breakable nested_statement(this, stmt);
   SetStatementPosition(stmt);
 
+  Scope* saved_scope = scope();
+  if (stmt->block_scope() != NULL) {
+    { Comment cmnt(masm_, "[ Extend block context");
+      scope_ = stmt->block_scope();
+      __ Push(scope_->GetSerializedScopeInfo());
+      PushFunctionArgumentForContextAllocation();
+      __ CallRuntime(Runtime::kPushBlockContext, 2);
+      StoreToFrameField(StandardFrameConstants::kContextOffset,
+                        context_register());
+    }
+    { Comment cmnt(masm_, "[ Declarations");
+      VisitDeclarations(scope_->declarations());
+    }
+  }
   PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS);
   VisitStatements(stmt->statements());
+  scope_ = saved_scope;
   __ bind(nested_statement.break_target());
   PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
 }
index dbf3b95a941beca96cd1f77a35ea69de95a1a73c..e080cde3250b3d2780f4ab53b2538cca641878c5 100644 (file)
@@ -1745,6 +1745,12 @@ bool Heap::CreateInitialMaps() {
   set_fixed_cow_array_map(Map::cast(obj));
   ASSERT(fixed_array_map() != fixed_cow_array_map());
 
+  { MaybeObject* maybe_obj =
+        AllocateMap(FIXED_ARRAY_TYPE, kVariableSizeSentinel);
+    if (!maybe_obj->ToObject(&obj)) return false;
+  }
+  set_serialized_scope_info_map(Map::cast(obj));
+
   { MaybeObject* maybe_obj = AllocateMap(HEAP_NUMBER_TYPE, HeapNumber::kSize);
     if (!maybe_obj->ToObject(&obj)) return false;
   }
@@ -1906,6 +1912,12 @@ bool Heap::CreateInitialMaps() {
   }
   set_with_context_map(Map::cast(obj));
 
+  { MaybeObject* maybe_obj =
+        AllocateMap(FIXED_ARRAY_TYPE, kVariableSizeSentinel);
+    if (!maybe_obj->ToObject(&obj)) return false;
+  }
+  set_block_context_map(Map::cast(obj));
+
   { MaybeObject* maybe_obj =
         AllocateMap(FIXED_ARRAY_TYPE, kVariableSizeSentinel);
     if (!maybe_obj->ToObject(&obj)) return false;
@@ -4017,6 +4029,37 @@ MaybeObject* Heap::AllocateWithContext(JSFunction* function,
 }
 
 
+MaybeObject* Heap::AllocateBlockContext(JSFunction* function,
+                                        Context* previous,
+                                        SerializedScopeInfo* scope_info) {
+  Object* result;
+  { MaybeObject* maybe_result =
+        AllocateFixedArray(scope_info->NumberOfContextSlots());
+    if (!maybe_result->ToObject(&result)) return maybe_result;
+  }
+  // TODO(keuchel): properly initialize context slots.
+  Context* context = reinterpret_cast<Context*>(result);
+  context->set_map(block_context_map());
+  context->set_closure(function);
+  context->set_previous(previous);
+  context->set_extension(scope_info);
+  context->set_global(previous->global());
+  return context;
+}
+
+
+MaybeObject* Heap::AllocateSerializedScopeInfo(int length) {
+  Object* result;
+  { MaybeObject* maybe_result = AllocateFixedArray(length, TENURED);
+    if (!maybe_result->ToObject(&result)) return maybe_result;
+  }
+  SerializedScopeInfo* scope_info =
+      reinterpret_cast<SerializedScopeInfo*>(result);
+  scope_info->set_map(serialized_scope_info_map());
+  return scope_info;
+}
+
+
 MaybeObject* Heap::AllocateStruct(InstanceType type) {
   Map* map;
   switch (type) {
index 03a6484a67a588f34cac21df811e73db5e6b404a..c4ee4dbe2b29e84bbc3d40d8377f0fb8cb23a2bf 100644 (file)
@@ -65,6 +65,7 @@ inline Heap* _inline_get_heap_();
   V(Map, heap_number_map, HeapNumberMap)                                       \
   V(Map, global_context_map, GlobalContextMap)                                 \
   V(Map, fixed_array_map, FixedArrayMap)                                       \
+  V(Map, serialized_scope_info_map, SerializedScopeInfoMap)                    \
   V(Map, fixed_cow_array_map, FixedCOWArrayMap)                                \
   V(Map, fixed_double_array_map, FixedDoubleArrayMap)                          \
   V(Object, no_interceptor_result_sentinel, NoInterceptorResultSentinel)       \
@@ -111,6 +112,7 @@ inline Heap* _inline_get_heap_();
   V(Map, function_context_map, FunctionContextMap)                             \
   V(Map, catch_context_map, CatchContextMap)                                   \
   V(Map, with_context_map, WithContextMap)                                     \
+  V(Map, block_context_map, BlockContextMap)                                   \
   V(Map, code_map, CodeMap)                                                    \
   V(Map, oddball_map, OddballMap)                                              \
   V(Map, global_property_cell_map, GlobalPropertyCellMap)                      \
@@ -221,7 +223,8 @@ inline Heap* _inline_get_heap_();
   V(closure_symbol, "(closure)")                                         \
   V(use_strict, "use strict")                                            \
   V(dot_symbol, ".")                                                     \
-  V(anonymous_function_symbol, "(anonymous function)")
+  V(anonymous_function_symbol, "(anonymous function)")                   \
+  V(block_scope_symbol, ".block")
 
 // Forward declarations.
 class GCTracer;
@@ -484,6 +487,9 @@ class Heap {
   // Allocates an empty code cache.
   MUST_USE_RESULT MaybeObject* AllocateCodeCache();
 
+  // Allocates a serialized scope info.
+  MUST_USE_RESULT MaybeObject* AllocateSerializedScopeInfo(int length);
+
   // Allocates an empty PolymorphicCodeCache.
   MUST_USE_RESULT MaybeObject* AllocatePolymorphicCodeCache();
 
@@ -669,6 +675,11 @@ class Heap {
                                                    Context* previous,
                                                    JSObject* extension);
 
+  // Allocate a block context.
+  MUST_USE_RESULT MaybeObject* AllocateBlockContext(JSFunction* function,
+                                                    Context* previous,
+                                                    SerializedScopeInfo* info);
+
   // Allocates a new utility object in the old generation.
   MUST_USE_RESULT MaybeObject* AllocateStruct(InstanceType type);
 
index 5e7daf552c7f71c8927060b09fa9f1d47c7f85e1..faf124000080c71115d09aa05fc99e1d2178f8c1 100644 (file)
@@ -2480,6 +2480,9 @@ void HGraphBuilder::VisitBlock(Block* stmt) {
   ASSERT(!HasStackOverflow());
   ASSERT(current_block() != NULL);
   ASSERT(current_block()->HasPredecessor());
+  if (stmt->block_scope() != NULL) {
+    return Bailout("ScopedBlock");
+  }
   BreakAndContinueInfo break_info(stmt);
   { BreakAndContinueScope push(&break_info, this);
     CHECK_BAILOUT(VisitStatements(stmt->statements()));
index bad08002d7c5ae1ed4f12ca45619d8c49da3616e..e3f3c48bb55fd26aaba54c08e787fa5355e21938 100644 (file)
@@ -195,7 +195,8 @@ ScopeType = { Global: 0,
               Local: 1,
               With: 2,
               Closure: 3,
-              Catch: 4 };
+              Catch: 4,
+              Block: 5 };
 
 
 // Mirror hierarchy:
index 46e09ba123a0038e26573213cbb64e7bdb442dce..4aa59fce74b0bfa85386ec76bb00a1868d924658 100644 (file)
@@ -552,7 +552,8 @@ bool Object::IsContext() {
     return (map == heap->function_context_map() ||
             map == heap->catch_context_map() ||
             map == heap->with_context_map() ||
-            map == heap->global_context_map());
+            map == heap->global_context_map() ||
+            map == heap->block_context_map());
   }
   return false;
 }
@@ -565,6 +566,13 @@ bool Object::IsGlobalContext() {
 }
 
 
+bool Object::IsSerializedScopeInfo() {
+  return Object::IsHeapObject() &&
+      HeapObject::cast(this)->map() ==
+      HeapObject::cast(this)->GetHeap()->serialized_scope_info_map();
+}
+
+
 bool Object::IsJSFunction() {
   return Object::IsHeapObject()
       && HeapObject::cast(this)->map()->instance_type() == JS_FUNCTION_TYPE;
index 2a22ade9686affa3fa50c31d124d78d4590af546..f4f079ee9f3f538d7e59dfeb81b8f97581ecddc7 100644 (file)
@@ -743,6 +743,7 @@ class MaybeObject BASE_EMBEDDED {
   V(FixedDoubleArray)                          \
   V(Context)                                   \
   V(GlobalContext)                             \
+  V(SerializedScopeInfo)                       \
   V(JSFunction)                                \
   V(Code)                                      \
   V(Oddball)                                   \
index f32e9177b5a9120905a287fb1fdac1891c3eb7a6..1167f43df519321d34a8a7efad9e085b2af7e548 100644 (file)
@@ -584,7 +584,8 @@ Parser::Parser(Handle<Script> script,
       pre_data_(pre_data),
       fni_(NULL),
       stack_overflow_(false),
-      parenthesized_function_(false) {
+      parenthesized_function_(false),
+      harmony_block_scoping_(false) {
   AstNode::ResetIds();
 }
 
@@ -809,6 +810,9 @@ void Parser::ReportMessageAt(Scanner::Location source_location,
   isolate()->Throw(*result, &location);
 }
 
+void Parser::SetHarmonyBlockScoping(bool block_scoping) {
+  harmony_block_scoping_ = block_scoping;
+}
 
 // Base class containing common code for the different finder classes used by
 // the parser.
@@ -1487,6 +1491,8 @@ Statement* Parser::ParseFunctionDeclaration(bool* ok) {
 
 
 Block* Parser::ParseBlock(ZoneStringList* labels, bool* ok) {
+  if (harmony_block_scoping_) return ParseScopedBlock(labels, ok);
+
   // Block ::
   //   '{' Statement* '}'
 
@@ -1510,6 +1516,56 @@ Block* Parser::ParseBlock(ZoneStringList* labels, bool* ok) {
 }
 
 
+Block* Parser::ParseScopedBlock(ZoneStringList* labels, bool* ok) {
+  // Construct block expecting 16 statements.
+  Block* body = new(zone()) Block(isolate(), labels, 16, false);
+  Scope* saved_scope = top_scope_;
+  Scope* block_scope = NewScope(top_scope_,
+                                Scope::BLOCK_SCOPE,
+                                inside_with());
+  body->set_block_scope(block_scope);
+  block_scope->DeclareLocal(isolate()->factory()->block_scope_symbol(),
+                            Variable::VAR);
+  if (top_scope_->is_strict_mode()) {
+    block_scope->EnableStrictMode();
+  }
+  top_scope_ = block_scope;
+
+  // Parse the statements and collect escaping labels.
+  TargetCollector collector;
+  Target target(&this->target_stack_, &collector);
+  Expect(Token::LBRACE, CHECK_OK);
+  {
+    Target target_body(&this->target_stack_, body);
+    InitializationBlockFinder block_finder(top_scope_, target_stack_);
+
+    while (peek() != Token::RBRACE) {
+      Statement* stat = ParseStatement(NULL, CHECK_OK);
+      if (stat && !stat->IsEmpty()) {
+        body->AddStatement(stat);
+        block_finder.Update(stat);
+      }
+    }
+  }
+  Expect(Token::RBRACE, CHECK_OK);
+
+  // Create exit block.
+  Block* exit = new(zone()) Block(isolate(), NULL, 1, false);
+  exit->AddStatement(new(zone()) ExitContextStatement());
+
+  // Create a try-finally statement.
+  TryFinallyStatement* try_finally =
+      new(zone()) TryFinallyStatement(body, exit);
+  try_finally->set_escaping_targets(collector.targets());
+  top_scope_ = saved_scope;
+
+  // Create a result block.
+  Block* result = new(zone()) Block(isolate(), NULL, 1, false);
+  result->AddStatement(try_finally);
+  return result;
+}
+
+
 Block* Parser::ParseVariableStatement(bool* ok) {
   // VariableStatement ::
   //   VariableDeclarations ';'
@@ -5105,6 +5161,8 @@ bool ParserApi::Parse(CompilationInfo* info) {
   Handle<Script> script = info->script();
   if (info->is_lazy()) {
     Parser parser(script, true, NULL, NULL);
+    parser.SetHarmonyBlockScoping(!info->is_native() &&
+                                  FLAG_harmony_block_scoping);
     result = parser.ParseLazy(info);
   } else {
     // Whether we allow %identifier(..) syntax.
@@ -5112,6 +5170,8 @@ bool ParserApi::Parse(CompilationInfo* info) {
         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);
     if (pre_data != NULL && pre_data->has_error()) {
       Scanner::Location loc = pre_data->MessageLocation();
       const char* message = pre_data->BuildMessage();
@@ -5130,7 +5190,6 @@ bool ParserApi::Parse(CompilationInfo* info) {
                                    info->StrictMode());
     }
   }
-
   info->SetFunction(result);
   return (result != NULL);
 }
index 535b63945cc4d0e944db3dcc96f309732befd658..dd964cebd6627d0c5ef61d2ee4b06ba5e90a6ec8 100644 (file)
@@ -435,6 +435,7 @@ class Parser {
   void ReportMessageAt(Scanner::Location loc,
                        const char* message,
                        Vector<Handle<String> > args);
+  void SetHarmonyBlockScoping(bool block_scoping);
 
  private:
   // Limit on number of function parameters is chosen arbitrarily.
@@ -483,6 +484,7 @@ class Parser {
   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,
                                    Handle<String>* out,
@@ -715,6 +717,7 @@ class Parser {
   // Heuristically that means that the function will be called immediately,
   // so never lazily compile it.
   bool parenthesized_function_;
+  bool harmony_block_scoping_;
 
   friend class LexicalScope;
 };
index 38199f8c5826fbbee560d6dde3bb78a317009d35..6d417600362c619667d55aad2f6c1c799f0c8859 100644 (file)
@@ -8316,6 +8316,30 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_PushCatchContext) {
 }
 
 
+RUNTIME_FUNCTION(MaybeObject*, Runtime_PushBlockContext) {
+  NoHandleAllocation ha;
+  ASSERT(args.length() == 2);
+  SerializedScopeInfo* scope_info = SerializedScopeInfo::cast(args[0]);
+  JSFunction* function;
+  if (args[1]->IsSmi()) {
+    // A smi sentinel indicates a context nested inside global code rather
+    // than some function.  There is a canonical empty function that can be
+    // gotten from the global context.
+    function = isolate->context()->global_context()->closure();
+  } else {
+    function = JSFunction::cast(args[1]);
+  }
+  Context* context;
+  MaybeObject* maybe_context =
+      isolate->heap()->AllocateBlockContext(function,
+                                            isolate->context(),
+                                            scope_info);
+  if (!maybe_context->To(&context)) return maybe_context;
+  isolate->set_context(context);
+  return context;
+}
+
+
 RUNTIME_FUNCTION(MaybeObject*, Runtime_DeleteContextSlot) {
   HandleScope scope(isolate);
   ASSERT(args.length() == 2);
@@ -10641,6 +10665,34 @@ static Handle<JSObject> MaterializeCatchScope(Isolate* isolate,
 }
 
 
+// Create a plain JSObject which materializes the block scope for the specified
+// block context.
+static Handle<JSObject> MaterializeBlockScope(
+    Isolate* isolate,
+    Handle<Context> context) {
+  ASSERT(context->IsBlockContext());
+  Handle<SerializedScopeInfo> serialized_scope_info(
+      SerializedScopeInfo::cast(context->extension()));
+  ScopeInfo<> scope_info(*serialized_scope_info);
+
+  // Allocate and initialize a JSObject with all the arguments, stack locals
+  // heap locals and extension properties of the debugged function.
+  Handle<JSObject> block_scope =
+      isolate->factory()->NewJSObject(isolate->object_function());
+
+  // Fill all context locals.
+  if (scope_info.number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
+    if (!CopyContextLocalsToScopeObject(isolate,
+                                        serialized_scope_info, scope_info,
+                                        context, block_scope)) {
+      return Handle<JSObject>();
+    }
+  }
+
+  return block_scope;
+}
+
+
 // Iterate over the actual scopes visible from a stack frame. All scopes are
 // backed by an actual context except the local scope, which is inserted
 // "artifically" in the context chain.
@@ -10651,7 +10703,8 @@ class ScopeIterator {
     ScopeTypeLocal,
     ScopeTypeWith,
     ScopeTypeClosure,
-    ScopeTypeCatch
+    ScopeTypeCatch,
+    ScopeTypeBlock
   };
 
   ScopeIterator(Isolate* isolate,
@@ -10677,8 +10730,10 @@ class ScopeIterator {
     } else if (context_->IsFunctionContext()) {
       at_local_ = true;
     } else if (context_->closure() != *function_) {
-      // The context_ is a with or catch block from the outer function.
-      ASSERT(context_->IsWithContext() || context_->IsCatchContext());
+      // The context_ is a block or with or catch block from the outer function.
+      ASSERT(context_->IsWithContext() ||
+             context_->IsCatchContext() ||
+             context_->IsBlockContext());
       at_local_ = true;
     }
   }
@@ -10733,6 +10788,9 @@ class ScopeIterator {
     if (context_->IsCatchContext()) {
       return ScopeTypeCatch;
     }
+    if (context_->IsBlockContext()) {
+      return ScopeTypeBlock;
+    }
     ASSERT(context_->IsWithContext());
     return ScopeTypeWith;
   }
@@ -10753,6 +10811,8 @@ class ScopeIterator {
       case ScopeIterator::ScopeTypeClosure:
         // Materialize the content of the closure scope into a JSObject.
         return MaterializeClosure(isolate_, CurrentContext());
+      case ScopeIterator::ScopeTypeBlock:
+        return MaterializeBlockScope(isolate_, CurrentContext());
     }
     UNREACHABLE();
     return Handle<JSObject>();
@@ -11309,7 +11369,13 @@ static Handle<Context> CopyWithContextChain(Isolate* isolate,
                                             new_previous,
                                             name,
                                             thrown_object);
+  } else if (current->IsBlockContext()) {
+    Handle<SerializedScopeInfo> scope_info(
+        SerializedScopeInfo::cast(current->extension()));
+    new_current =
+        isolate->factory()->NewBlockContext(function, new_previous, scope_info);
   } else {
+    ASSERT(current->IsWithContext());
     Handle<JSObject> extension(JSObject::cast(current->extension()));
     new_current =
         isolate->factory()->NewWithContext(function, new_previous, extension);
index a52672ad7d999d81b0b40baecb49556cc53e0a8c..06d36845413773b6f8f95c49fc8a41d77bbd22d3 100644 (file)
@@ -310,6 +310,7 @@ namespace internal {
   F(NewFunctionContext, 1, 1) \
   F(PushWithContext, 2, 1) \
   F(PushCatchContext, 3, 1) \
+  F(PushBlockContext, 2, 1) \
   F(DeleteContextSlot, 2, 1) \
   F(LoadContextSlot, 2, 2) \
   F(LoadContextSlotNoReferenceError, 2, 2) \
index 3e18368f74bde67569d01223a66717b64ad10bd2..0eacc83c79a3b5d5c11d8bc50e2836317b8a78b2 100644 (file)
@@ -313,7 +313,7 @@ Handle<SerializedScopeInfo> ScopeInfo<Allocator>::Serialize() {
                stack_slots_.length();
 
   Handle<SerializedScopeInfo> data(
-      SerializedScopeInfo::cast(*FACTORY->NewFixedArray(length, TENURED)));
+      SerializedScopeInfo::cast(*FACTORY->NewSerializedScopeInfo(length)));
   AssertNoAllocation nogc;
 
   Object** p0 = data->data_start();
index 86c33f61ffaf3a1bd38bc8d38665a83ec0181c41..1c61f1115d23b50496c0e8815e6ac18fe51c49f6 100644 (file)
@@ -107,7 +107,7 @@ class SerializedScopeInfo : public FixedArray {
  public :
 
   static SerializedScopeInfo* cast(Object* object) {
-    ASSERT(object->IsFixedArray());
+    ASSERT(object->IsSerializedScopeInfo());
     return reinterpret_cast<SerializedScopeInfo*>(object);
   }
 
index 390a0b6e11ba8f1ccd4081fda789f582c8bc1554..eb0deb4964bb8cbc8ad5d1a151e381053c42bff1 100644 (file)
@@ -146,7 +146,9 @@ Scope::Scope(Scope* outer_scope, Type type)
 }
 
 
-Scope::Scope(Scope* inner_scope, Handle<SerializedScopeInfo> scope_info)
+Scope::Scope(Scope* inner_scope,
+             Type type,
+             Handle<SerializedScopeInfo> scope_info)
     : isolate_(Isolate::Current()),
       inner_scopes_(4),
       variables_(),
@@ -156,7 +158,7 @@ Scope::Scope(Scope* inner_scope, Handle<SerializedScopeInfo> scope_info)
       decls_(4),
       already_resolved_(true) {
   ASSERT(!scope_info.is_null());
-  SetDefaults(FUNCTION_SCOPE, NULL, scope_info);
+  SetDefaults(type, NULL, scope_info);
   if (scope_info->HasHeapAllocatedLocals()) {
     num_heap_slots_ = scope_info_->NumberOfContextSlots();
   }
@@ -232,8 +234,13 @@ Scope* Scope::DeserializeScopeChain(CompilationInfo* info,
       if (context->IsFunctionContext()) {
         SerializedScopeInfo* scope_info =
             context->closure()->shared()->scope_info();
-        current_scope =
-            new Scope(current_scope, Handle<SerializedScopeInfo>(scope_info));
+        current_scope = new Scope(current_scope, FUNCTION_SCOPE,
+            Handle<SerializedScopeInfo>(scope_info));
+      } else if (context->IsBlockContext()) {
+        SerializedScopeInfo* scope_info =
+            SerializedScopeInfo::cast(context->extension());
+        current_scope = new Scope(current_scope, BLOCK_SCOPE,
+            Handle<SerializedScopeInfo>(scope_info));
       } else {
         ASSERT(context->IsCatchContext());
         String* name = String::cast(context->extension());
@@ -294,10 +301,13 @@ void Scope::Initialize(bool inside_with) {
   // instead load them directly from the stack. Currently, the only
   // such parameter is 'this' which is passed on the stack when
   // invoking scripts
-  if (is_catch_scope()) {
+  if (is_catch_scope() || is_block_scope()) {
     ASSERT(outer_scope() != NULL);
     receiver_ = outer_scope()->receiver();
   } else {
+    ASSERT(is_function_scope() ||
+           is_global_scope() ||
+           is_eval_scope());
     Variable* var =
         variables_.Declare(this,
                            isolate_->factory()->this_symbol(),
@@ -559,13 +569,22 @@ int Scope::ContextChainLength(Scope* scope) {
 
 Scope* Scope::DeclarationScope() {
   Scope* scope = this;
-  while (scope->is_catch_scope()) {
+  while (scope->is_catch_scope() ||
+         scope->is_block_scope()) {
     scope = scope->outer_scope();
   }
   return scope;
 }
 
 
+Handle<SerializedScopeInfo> Scope::GetSerializedScopeInfo() {
+  if (scope_info_.is_null()) {
+    scope_info_ = SerializedScopeInfo::Create(this);
+  }
+  return scope_info_;
+}
+
+
 #ifdef DEBUG
 static const char* Header(Scope::Type type) {
   switch (type) {
@@ -573,6 +592,7 @@ static const char* Header(Scope::Type type) {
     case Scope::FUNCTION_SCOPE: return "function";
     case Scope::GLOBAL_SCOPE: return "global";
     case Scope::CATCH_SCOPE: return "catch";
+    case Scope::BLOCK_SCOPE: return "block";
   }
   UNREACHABLE();
   return NULL;
@@ -598,9 +618,11 @@ static void PrintVar(PrettyPrinter* printer, int indent, Variable* var) {
     PrintF(";  // ");
     if (var->rewrite() != NULL) {
       PrintF("%s, ", printer->Print(var->rewrite()));
-      if (var->is_accessed_from_inner_scope()) PrintF(", ");
+      if (var->is_accessed_from_inner_function_scope()) PrintF(", ");
+    }
+    if (var->is_accessed_from_inner_function_scope()) {
+      PrintF("inner scope access");
     }
-    if (var->is_accessed_from_inner_scope()) PrintF("inner scope access");
     PrintF("\n");
   }
 }
@@ -721,7 +743,7 @@ Variable* Scope::NonLocal(Handle<String> name, Variable::Mode mode) {
 // another variable that is introduced dynamically via an 'eval' call
 // or a 'with' statement).
 Variable* Scope::LookupRecursive(Handle<String> name,
-                                 bool inner_lookup,
+                                 bool from_inner_function,
                                  Variable** invalidated_local) {
   // If we find a variable, but the current scope calls 'eval', the found
   // variable may not be the correct one (the 'eval' may introduce a
@@ -737,7 +759,7 @@ Variable* Scope::LookupRecursive(Handle<String> name,
     // (Even if there is an 'eval' in this scope which introduces the
     // same variable again, the resulting variable remains the same.
     // Note that enclosing 'with' statements are handled at the call site.)
-    if (!inner_lookup)
+    if (!from_inner_function)
       return var;
 
   } else {
@@ -753,7 +775,10 @@ Variable* Scope::LookupRecursive(Handle<String> name,
       var = function_;
 
     } else if (outer_scope_ != NULL) {
-      var = outer_scope_->LookupRecursive(name, true, invalidated_local);
+      var = outer_scope_->LookupRecursive(
+          name,
+          is_function_scope() || from_inner_function,
+          invalidated_local);
       // We may have found a variable in an outer scope. However, if
       // the current scope is inside a 'with', the actual variable may
       // be a property introduced via the 'with' statement. Then, the
@@ -770,8 +795,8 @@ Variable* Scope::LookupRecursive(Handle<String> name,
   ASSERT(var != NULL);
 
   // If this is a lookup from an inner scope, mark the variable.
-  if (inner_lookup) {
-    var->MarkAsAccessedFromInnerScope();
+  if (from_inner_function) {
+    var->MarkAsAccessedFromInnerFunctionScope();
   }
 
   // If the variable we have found is just a guess, invalidate the
@@ -922,11 +947,12 @@ bool Scope::MustAllocate(Variable* var) {
   // via an eval() call.  This is only possible if the variable has a
   // visible name.
   if ((var->is_this() || var->name()->length() > 0) &&
-      (var->is_accessed_from_inner_scope() ||
+      (var->is_accessed_from_inner_function_scope() ||
        scope_calls_eval_ ||
        inner_scope_calls_eval_ ||
        scope_contains_with_ ||
-       is_catch_scope())) {
+       is_catch_scope() ||
+       is_block_scope())) {
     var->set_is_used(true);
   }
   // Global variables do not need to be allocated.
@@ -943,8 +969,8 @@ bool Scope::MustAllocateInContext(Variable* var) {
   // Exceptions: temporary variables are never allocated in a context;
   // catch-bound variables are always allocated in a context.
   if (var->mode() == Variable::TEMPORARY) return false;
-  if (is_catch_scope()) return true;
-  return var->is_accessed_from_inner_scope() ||
+  if (is_catch_scope() || is_block_scope()) return true;
+  return var->is_accessed_from_inner_function_scope() ||
       scope_calls_eval_ ||
       inner_scope_calls_eval_ ||
       scope_contains_with_ ||
@@ -1010,7 +1036,7 @@ void Scope::AllocateParameterLocals() {
     if (uses_nonstrict_arguments) {
       // Give the parameter a use from an inner scope, to force allocation
       // to the context.
-      var->MarkAsAccessedFromInnerScope();
+      var->MarkAsAccessedFromInnerFunctionScope();
     }
 
     if (MustAllocate(var)) {
index d4eb17cd5688888cea14ae9cee832d851f25f59b..c2c41799b93fdb94590bff2eb7e01a8d7016d700 100644 (file)
@@ -93,7 +93,8 @@ class Scope: public ZoneObject {
     EVAL_SCOPE,      // The top-level scope for an eval source.
     FUNCTION_SCOPE,  // The top-level scope for a function.
     GLOBAL_SCOPE,    // The top-level scope for a program or a top-level eval.
-    CATCH_SCOPE      // The scope introduced by catch.
+    CATCH_SCOPE,     // The scope introduced by catch.
+    BLOCK_SCOPE      // The scope introduced by a new block.
   };
 
   Scope(Scope* outer_scope, Type type);
@@ -204,6 +205,7 @@ class Scope: public ZoneObject {
   bool is_function_scope() const { return type_ == FUNCTION_SCOPE; }
   bool is_global_scope() const { return type_ == GLOBAL_SCOPE; }
   bool is_catch_scope() const { return type_ == CATCH_SCOPE; }
+  bool is_block_scope() const { return type_ == BLOCK_SCOPE; }
   bool is_strict_mode() const { return strict_mode_; }
   bool is_strict_mode_eval_scope() const {
     return is_eval_scope() && is_strict_mode();
@@ -294,6 +296,8 @@ class Scope: public ZoneObject {
   // where var declarations will be hoisted to in the implementation.
   Scope* DeclarationScope();
 
+  Handle<SerializedScopeInfo> GetSerializedScopeInfo();
+
   // ---------------------------------------------------------------------------
   // Strict mode support.
   bool IsDeclared(Handle<String> name) {
@@ -397,7 +401,7 @@ class Scope: public ZoneObject {
 
   // Variable resolution.
   Variable* LookupRecursive(Handle<String> name,
-                            bool inner_lookup,
+                            bool from_inner_function,
                             Variable** invalidated_local);
   void ResolveVariable(Scope* global_scope,
                        Handle<Context> context,
@@ -425,8 +429,8 @@ class Scope: public ZoneObject {
   void AllocateVariablesRecursively();
 
  private:
-  // Construct a function scope based on the scope info.
-  Scope(Scope* inner_scope, Handle<SerializedScopeInfo> scope_info);
+  // Construct a function or block scope based on the scope info.
+  Scope(Scope* inner_scope, Type type, Handle<SerializedScopeInfo> scope_info);
 
   // Construct a catch scope with a binding for the name.
   Scope(Scope* inner_scope, Handle<String> catch_variable_name);
index d83722d007c46fdeb335196148c0df5eecf4ec9c..f8282f652994fa10f752049f0efcba04f725db28 100644 (file)
@@ -544,6 +544,7 @@ class PartialSerializer : public Serializer {
     ASSERT(!o->IsScript());
     return o->IsString() || o->IsSharedFunctionInfo() ||
            o->IsHeapNumber() || o->IsCode() ||
+           o->IsSerializedScopeInfo() ||
            o->map() == HEAP->fixed_cow_array_map();
   }
 
index 67150ea13e1e55cb4e559c96f156fa0c78a123bb..03f11db9af3a82a6220739d2ca77e97dad5b4728 100644 (file)
@@ -92,7 +92,7 @@ Variable::Variable(Scope* scope,
     local_if_not_shadowed_(NULL),
     rewrite_(NULL),
     is_valid_LHS_(is_valid_LHS),
-    is_accessed_from_inner_scope_(false),
+    is_accessed_from_inner_function_scope_(false),
     is_used_(false) {
   // names must be canonicalized for fast equality checks
   ASSERT(name->IsSymbol());
index a9c06d1eeaa486411e95b66ec7eab69a729b29f9..ecdaeb0a6101cb08d186794c3e0c092b32ac97ab 100644 (file)
@@ -95,11 +95,12 @@ class Variable: public ZoneObject {
 
   Handle<String> name() const { return name_; }
   Mode mode() const { return mode_; }
-  bool is_accessed_from_inner_scope() const {
-    return is_accessed_from_inner_scope_;
+  bool is_accessed_from_inner_function_scope() const {
+    return is_accessed_from_inner_function_scope_;
   }
-  void MarkAsAccessedFromInnerScope() {
-    is_accessed_from_inner_scope_ = true;
+  void MarkAsAccessedFromInnerFunctionScope() {
+    ASSERT(mode_ != TEMPORARY);
+    is_accessed_from_inner_function_scope_ = true;
   }
   bool is_used() { return is_used_; }
   void set_is_used(bool flag) { is_used_ = flag; }
@@ -156,7 +157,7 @@ class Variable: public ZoneObject {
   bool is_valid_LHS_;
 
   // Usage info.
-  bool is_accessed_from_inner_scope_;  // set by variable resolver
+  bool is_accessed_from_inner_function_scope_;  // set by variable resolver
   bool is_used_;
 };
 
index ffa92684dda1206dd36776028c505673f0a1b811..f8f0a28e701b674daa23dd18b701bb24d7f00559 100644 (file)
@@ -146,6 +146,7 @@ var knownProblems = {
   "NewStrictArgumentsFast": true,
   "PushWithContext": true,
   "PushCatchContext": true,
+  "PushBlockContext": true,
   "LazyCompile": true,
   "LazyRecompile": true,
   "NotifyDeoptimized": true,
diff --git a/test/mjsunit/harmony/block-lazy-compile.js b/test/mjsunit/harmony/block-lazy-compile.js
new file mode 100644 (file)
index 0000000..a6efcbf
--- /dev/null
@@ -0,0 +1,50 @@
+// 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: --allow-natives-syntax
+// Test deserialization of block contexts during lazy compilation
+// of closures.
+
+function f() {
+  var g;
+  {
+    // TODO(keuchel): introduce let
+    var x = 0;
+    g = function () {
+      x = x + 1;
+      return x;
+    }
+  }
+  return g;
+}
+
+var o = f();
+assertEquals(1, o());
+assertEquals(2, o());
+assertEquals(3, o());
+%OptimizeFunctionOnNextCall(o);
+assertEquals(4, o());
diff --git a/test/mjsunit/harmony/block-scoping.js b/test/mjsunit/harmony/block-scoping.js
new file mode 100644 (file)
index 0000000..f974de8
--- /dev/null
@@ -0,0 +1,58 @@
+// 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: --allow-natives-syntax --harmony-block-scoping
+// Test functionality of block scopes.
+
+// Hoisting of var declarations.
+function f1() {
+  {
+    var x = 1;
+    var y;
+  }
+  assertEquals(1, x)
+  assertEquals(undefined, y)
+}
+f1();
+
+// Dynamic lookup through block scopes.
+function f2(one) {
+  var x = one + 1;
+  // TODO(keuchel): introduce let
+  // let y = one + 2;
+  if (one == 1) {
+    // Parameter
+    assertEquals(1, eval('one'));
+    // Function local var variable
+    assertEquals(2, eval('x'));
+    // Function local let variable
+    // TODO(keuchel): introduce let
+    // assertEquals(3, eval('y'));
+  }
+}
+f2(1);
+
diff --git a/test/mjsunit/harmony/debug-blockscopes.js b/test/mjsunit/harmony/debug-blockscopes.js
new file mode 100644 (file)
index 0000000..30f681f
--- /dev/null
@@ -0,0 +1,389 @@
+// 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--;
+  }
+
+  // TODO(keuchel): print('count' + count + ' scopesize' + 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 empty block scope in local scope.
+BeginTest("Local block 1");
+
+function local_block_1() {
+  {
+    debugger;
+  }
+}
+
+listener_delegate = function(exec_state) {
+  CheckScopeChain([debug.ScopeType.Block,
+                   debug.ScopeType.Local,
+                   debug.ScopeType.Global], exec_state);
+  CheckScopeContent({}, 0, exec_state);
+  CheckScopeContent({}, 1, exec_state);
+};
+local_block_1();
+EndTest();
+
+
+// Local scope with a parameter.
+BeginTest("Local 2");
+
+function local_2(a) {
+  {
+    debugger;
+  }
+}
+
+listener_delegate = function(exec_state) {
+  CheckScopeChain([debug.ScopeType.Block,
+                   debug.ScopeType.Local,
+                   debug.ScopeType.Global], exec_state);
+  CheckScopeContent({a:1}, 1, exec_state);
+};
+local_2(1);
+EndTest();
+
+
+// Local scope with a parameter and a local variable.
+BeginTest("Local 3");
+
+function local_3(a) {
+  var x = 3;
+  debugger;
+}
+
+listener_delegate = function(exec_state) {
+  CheckScopeChain([debug.ScopeType.Local,
+                   debug.ScopeType.Global], exec_state);
+  CheckScopeContent({a:1,x:3}, 0, exec_state);
+};
+local_3(1);
+EndTest();
+
+
+// Local scope with parameters and local variables.
+BeginTest("Local 4");
+
+function local_4(a, b) {
+  var x = 3;
+  var y = 4;
+  debugger;
+}
+
+listener_delegate = function(exec_state) {
+  CheckScopeChain([debug.ScopeType.Local,
+                   debug.ScopeType.Global], exec_state);
+  CheckScopeContent({a:1,b:2,x:3,y:4}, 0, exec_state);
+};
+local_4(1, 2);
+EndTest();
+
+
+// Single empty with block.
+BeginTest("With block 1");
+
+function with_block_1() {
+  with({}) {
+    debugger;
+  }
+}
+
+listener_delegate = function(exec_state) {
+  CheckScopeChain([debug.ScopeType.Block,
+                   debug.ScopeType.With,
+                   debug.ScopeType.Local,
+                   debug.ScopeType.Global], exec_state);
+  CheckScopeContent({}, 0, exec_state);
+  CheckScopeContent({}, 1, exec_state);
+};
+with_block_1();
+EndTest();
+
+
+// Nested empty with blocks.
+BeginTest("With block 2");
+
+function with_block_2() {
+  with({}) {
+    with({}) {
+      debugger;
+    }
+  }
+}
+
+listener_delegate = function(exec_state) {
+  CheckScopeChain([debug.ScopeType.Block,
+                   debug.ScopeType.With,
+                   debug.ScopeType.Block,
+                   debug.ScopeType.With,
+                   debug.ScopeType.Local,
+                   debug.ScopeType.Global], exec_state);
+  CheckScopeContent({}, 0, exec_state);
+  CheckScopeContent({}, 1, exec_state);
+  CheckScopeContent({}, 2, exec_state);
+  CheckScopeContent({}, 3, exec_state);
+};
+with_block_2();
+EndTest();
+
+
+// With block using an in-place object literal.
+BeginTest("With block 3");
+
+function with_block_3() {
+  with({a:1,b:2}) {
+    debugger;
+  }
+}
+
+listener_delegate = function(exec_state) {
+  CheckScopeChain([debug.ScopeType.Block,
+                   debug.ScopeType.With,
+                   debug.ScopeType.Local,
+                   debug.ScopeType.Global], exec_state);
+  CheckScopeContent({}, 0, exec_state);
+  CheckScopeContent({a:1,b:2}, 1, exec_state);
+};
+with_block_3();
+EndTest();
+
+
+// Nested with blocks using in-place object literals.
+BeginTest("With block 4");
+
+function with_block_4() {
+  with({a:1,b:2}) {
+    with({a:2,b:1}) {
+      debugger;
+    }
+  }
+}
+
+listener_delegate = function(exec_state) {
+  CheckScopeChain([debug.ScopeType.Block,
+                   debug.ScopeType.With,
+                   debug.ScopeType.Block,
+                   debug.ScopeType.With,
+                   debug.ScopeType.Local,
+                   debug.ScopeType.Global], exec_state);
+  CheckScopeContent({a:2,b:1}, 1, exec_state);
+  CheckScopeContent({a:1,b:2}, 3, exec_state);
+};
+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();
diff --git a/test/mjsunit/harmony/debug-evaluate-blockscopes.js b/test/mjsunit/harmony/debug-evaluate-blockscopes.js
new file mode 100644 (file)
index 0000000..8892416
--- /dev/null
@@ -0,0 +1,64 @@
+// 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
+
+// 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
+    try {             // Line 3.
+      throw 'stuff';  // Line 4.
+    } catch (e) {     // Line 5.
+      x = 2;          // Line 6.
+    }
+  }
+};
+
+// Get the Debug object exposed from the debug context global object.
+Debug = debug.Debug
+// Set breakpoint on line 6.
+var bp = Debug.setBreakPoint(f, 6);
+
+function listener(event, exec_state, event_data, data) {
+  if (event == Debug.DebugEvent.Break) {
+    result = exec_state.frame().evaluate("i").value();
+  }
+};
+
+// Add the debug event listener.
+Debug.setListener(listener);
+result = -1;
+f();
+assertEquals(1, result);
+
+// Clear breakpoint.
+Debug.clearBreakPoint(bp);
+// Get rid of the debug event listener.
+Debug.setListener(null);