The ScopeIterator uses recorded scope position - as detailed in scopes.h - and
authorkeuchel@chromium.org <keuchel@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 28 Nov 2011 12:47:39 +0000 (12:47 +0000)
committerkeuchel@chromium.org <keuchel@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 28 Nov 2011 12:47:39 +0000 (12:47 +0000)
source code positions it gets from the program counter to recreate the scope
chain by reparsing the function or program.

This CL includes the following changes
* Adds source code positions for the assignment added by the rewriter.
* Run the preparser over global code first.
* Use the ScopeType from the ScopeInfo to determine if the code being debugged
  is eval, function or global code instead of looking up the '.result' symbol.

TEST=mjsunit/debug-stepout-scope.js

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

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

src/ast.h
src/parser.cc
src/parser.h
src/rewriter.cc
src/runtime.cc
src/scopes.cc
test/cctest/test-parsing.cc
test/mjsunit/debug-stepout-scope.js [new file with mode: 0644]

index 628391c..94a20ad 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -1176,6 +1176,7 @@ class VariableProxy: public Expression {
                 bool is_this,
                 int position = RelocInfo::kNoPosition);
 
+  friend class Rewriter;
   friend class Scope;
 };
 
index b795891..efffe5a 100644 (file)
@@ -5541,7 +5541,7 @@ 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,
+ScriptDataImpl* ParserApi::PartialPreParse(Handle<String> source,
                                            v8::Extension* extension,
                                            int flags) {
   bool allow_lazy = FLAG_lazy && (extension == NULL);
@@ -5552,7 +5552,15 @@ ScriptDataImpl* ParserApi::PartialPreParse(UC16CharacterStream* source,
   }
   flags |= kAllowLazy;
   PartialParserRecorder recorder;
-  return DoPreParse(source, flags, &recorder);
+  int source_length = source->length();
+  if (source->IsExternalTwoByteString()) {
+    ExternalTwoByteStringUC16CharacterStream stream(
+        Handle<ExternalTwoByteString>::cast(source), 0, source_length);
+    return DoPreParse(&stream, flags, &recorder);
+  } else {
+    GenericStringUC16CharacterStream stream(source, 0, source_length);
+    return DoPreParse(&stream, flags, &recorder);
+  }
 }
 
 
index dc5882b..75f8e10 100644 (file)
@@ -178,7 +178,7 @@ class ParserApi {
 
   // Preparser that only does preprocessing that makes sense if only used
   // immediately after.
-  static ScriptDataImpl* PartialPreParse(UC16CharacterStream* source,
+  static ScriptDataImpl* PartialPreParse(Handle<String> source,
                                          v8::Extension* extension,
                                          int flags);
 };
index 3d4c2dc..a70cd82 100644 (file)
@@ -236,10 +236,22 @@ bool Rewriter::Rewrite(CompilationInfo* info) {
     if (processor.HasStackOverflow()) return false;
 
     if (processor.result_assigned()) {
+      ASSERT(function->end_position() != RelocInfo::kNoPosition);
       Isolate* isolate = info->isolate();
       Zone* zone = isolate->zone();
-      VariableProxy* result_proxy = new(zone) VariableProxy(isolate, result);
-      body->Add(new(zone) ReturnStatement(result_proxy));
+      // Set the position of the assignment statement one character past the
+      // source code, such that it definitely is not in the source code range
+      // of an immediate inner scope. For example in
+      //   eval('with ({x:1}) x = 1');
+      // the end position of the function generated for executing the eval code
+      // coincides with the end of the with scope which is the position of '1'.
+      int position = function->end_position();
+      VariableProxy* result_proxy = new(zone) VariableProxy(
+          isolate, result->name(), false, position);
+      result_proxy->BindTo(result);
+      Statement* result_statement = new(zone) ReturnStatement(result_proxy);
+      result_statement->set_statement_pos(position);
+      body->Add(result_statement);
     }
   }
 
index 43e1a6d..fe8e137 100644 (file)
@@ -11201,27 +11201,82 @@ class ScopeIterator {
       inlined_frame_index_(inlined_frame_index),
       function_(JSFunction::cast(frame->function())),
       context_(Context::cast(frame->context())),
-      local_done_(false),
-      at_local_(false) {
-
-    // Check whether the first scope is actually a local scope.
-    // If there is a stack slot for .result then this local scope has been
-    // created for evaluating top level code and it is not a real local scope.
-    // Checking for the existence of .result seems fragile, but the scope info
-    // saved with the code object does not otherwise have that information.
-    int index = function_->shared()->scope_info()->
-        StackSlotIndex(isolate_->heap()->result_symbol());
-    if (index >= 0) {
-      local_done_ = true;
-    } else if (context_->IsGlobalContext() ||
-               context_->IsFunctionContext()) {
-      at_local_ = true;
-    } else if (context_->closure() != *function_) {
-      // The context_ is a block or with or catch block from the outer function.
-      ASSERT(context_->IsWithContext() ||
-             context_->IsCatchContext() ||
-             context_->IsBlockContext());
-      at_local_ = true;
+      nested_scope_chain_(4) {
+
+    // Catch the case when the debugger stops in an internal function.
+    Handle<SharedFunctionInfo> shared_info(function_->shared());
+    Handle<ScopeInfo> scope_info(shared_info->scope_info());
+    if (shared_info->script() == isolate->heap()->undefined_value()) {
+      while (context_->closure() == *function_) {
+        context_ = Handle<Context>(context_->previous(), isolate_);
+      }
+      return;
+    }
+
+    // Get the start of the frame exit code.
+    Handle<Code> code(shared_info->code());
+    RelocIterator it(*code, RelocInfo::ModeMask(RelocInfo::JS_RETURN));
+    RelocInfo* info = it.rinfo();
+    Address frame_exit_code = info->pc();
+    it.next();
+    ASSERT(it.done());
+
+    Address frame_exit_return =
+        frame_exit_code + Assembler::kCallInstructionLength;
+    if (frame_->pc() == frame_exit_return) {
+      // We are within the return sequence. At the momemt it is not possible to
+      // get a source position which is consistent with the current scope chain.
+      // Thus all nested with, catch and block contexts are skipped and we only
+      // provide the function scope.
+      if (scope_info->HasContext()) {
+        context_ = Handle<Context>(context_->declaration_context(), isolate_);
+      } else {
+        while (context_->closure() == *function_) {
+          context_ = Handle<Context>(context_->previous(), isolate_);
+        }
+      }
+      if (scope_info->Type() != EVAL_SCOPE) nested_scope_chain_.Add(scope_info);
+    } else {
+      // Reparse the code and analyze the scopes.
+      ZoneScope zone_scope(isolate, DELETE_ON_EXIT);
+      Handle<Script> script(Script::cast(shared_info->script()));
+      Scope* scope = NULL;
+
+      // Check whether we are in global, eval or function code.
+      Handle<ScopeInfo> scope_info(shared_info->scope_info());
+      if (scope_info->Type() != FUNCTION_SCOPE) {
+        // Global or eval code.
+        CompilationInfo info(script);
+        if (scope_info->Type() == GLOBAL_SCOPE) {
+          info.MarkAsGlobal();
+        } else {
+          ASSERT(scope_info->Type() == EVAL_SCOPE);
+          info.MarkAsEval();
+          info.SetCallingContext(Handle<Context>(function_->context()));
+        }
+        if (ParserApi::Parse(&info, kNoParsingFlags) && Scope::Analyze(&info)) {
+          scope = info.function()->scope();
+        }
+      } else {
+        // Function code
+        CompilationInfo info(shared_info);
+        if (ParserApi::Parse(&info, kNoParsingFlags) && Scope::Analyze(&info)) {
+          scope = info.function()->scope();
+        }
+      }
+
+      // Retrieve the scope chain for the current position.
+      if (scope != NULL) {
+        int source_position = shared_info->code()->SourcePosition(frame_->pc());
+        scope->GetNestedScopeChain(&nested_scope_chain_, source_position);
+      } else {
+        // A failed reparse indicates that the preparser has diverged from the
+        // parser or that the preparse data given to the initial parse has been
+        // faulty. We fail in debug mode but in release mode we only provide the
+        // information we get from the context chain but nothing about
+        // completely stack allocated scopes or stack allocated locals.
+        UNREACHABLE();
+      }
     }
   }
 
@@ -11230,40 +11285,49 @@ class ScopeIterator {
 
   // Move to the next scope.
   void Next() {
-    // If at a local scope mark the local scope as passed.
-    if (at_local_) {
-      at_local_ = false;
-      local_done_ = true;
-
-      // If the current context is not associated with the local scope the
-      // current context is the next real scope, so don't move to the next
-      // context in this case.
-      if (context_->closure() != *function_) {
-        return;
-      }
-    }
-
-    // The global scope is always the last in the chain.
-    if (context_->IsGlobalContext()) {
+    ScopeType scope_type = Type();
+    if (scope_type == ScopeTypeGlobal) {
+      // The global scope is always the last in the chain.
+      ASSERT(context_->IsGlobalContext());
       context_ = Handle<Context>();
       return;
     }
-
-    // Move to the next context.
-    context_ = Handle<Context>(context_->previous(), isolate_);
-
-    // If passing the local scope indicate that the current scope is now the
-    // local scope.
-    if (!local_done_ &&
-        (context_->IsGlobalContext() || context_->IsFunctionContext())) {
-      at_local_ = true;
+    if (nested_scope_chain_.is_empty()) {
+      context_ = Handle<Context>(context_->previous(), isolate_);
+    } else {
+      if (nested_scope_chain_.last()->HasContext()) {
+        ASSERT(context_->previous() != NULL);
+        context_ = Handle<Context>(context_->previous(), isolate_);
+      }
+      nested_scope_chain_.RemoveLast();
     }
   }
 
   // Return the type of the current scope.
   ScopeType Type() {
-    if (at_local_) {
-      return ScopeTypeLocal;
+    if (!nested_scope_chain_.is_empty()) {
+      Handle<ScopeInfo> scope_info = nested_scope_chain_.last();
+      switch (scope_info->Type()) {
+        case FUNCTION_SCOPE:
+          ASSERT(context_->IsFunctionContext() ||
+                 !scope_info->HasContext());
+          return ScopeTypeLocal;
+        case GLOBAL_SCOPE:
+          ASSERT(context_->IsGlobalContext());
+          return ScopeTypeGlobal;
+        case WITH_SCOPE:
+          ASSERT(context_->IsWithContext());
+          return ScopeTypeWith;
+        case CATCH_SCOPE:
+          ASSERT(context_->IsCatchContext());
+          return ScopeTypeCatch;
+        case BLOCK_SCOPE:
+          ASSERT(!scope_info->HasContext() ||
+                 context_->IsBlockContext());
+          return ScopeTypeBlock;
+        case EVAL_SCOPE:
+          UNREACHABLE();
+      }
     }
     if (context_->IsGlobalContext()) {
       ASSERT(context_->global()->IsGlobalObject());
@@ -11289,6 +11353,7 @@ class ScopeIterator {
         return Handle<JSObject>(CurrentContext()->global());
       case ScopeIterator::ScopeTypeLocal:
         // Materialize the content of the local scope into a JSObject.
+        ASSERT(nested_scope_chain_.length() == 1);
         return MaterializeLocalScope(isolate_, frame_, inlined_frame_index_);
       case ScopeIterator::ScopeTypeWith:
         // Return the with object.
@@ -11305,13 +11370,28 @@ class ScopeIterator {
     return Handle<JSObject>();
   }
 
+  Handle<ScopeInfo> CurrentScopeInfo() {
+    if (!nested_scope_chain_.is_empty()) {
+      return nested_scope_chain_.last();
+    } else if (context_->IsBlockContext()) {
+      return Handle<ScopeInfo>(ScopeInfo::cast(context_->extension()));
+    } else if (context_->IsFunctionContext()) {
+      return Handle<ScopeInfo>(context_->closure()->shared()->scope_info());
+    }
+    return Handle<ScopeInfo>::null();
+  }
+
   // Return the context for this scope. For the local context there might not
   // be an actual context.
   Handle<Context> CurrentContext() {
-    if (at_local_ && context_->closure() != *function_) {
+    if (Type() == ScopeTypeGlobal ||
+        nested_scope_chain_.is_empty()) {
+      return context_;
+    } else if (nested_scope_chain_.last()->HasContext()) {
+      return context_;
+    } else {
       return Handle<Context>();
     }
-    return context_;
   }
 
 #ifdef DEBUG
@@ -11373,8 +11453,7 @@ class ScopeIterator {
   int inlined_frame_index_;
   Handle<JSFunction> function_;
   Handle<Context> context_;
-  bool local_done_;
-  bool at_local_;
+  List<Handle<ScopeInfo> > nested_scope_chain_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
 };
@@ -11837,45 +11916,65 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ClearStepping) {
 
 // Creates a copy of the with context chain. The copy of the context chain is
 // is linked to the function context supplied.
-static Handle<Context> CopyWithContextChain(Isolate* isolate,
-                                            Handle<JSFunction> function,
-                                            Handle<Context> current,
-                                            Handle<Context> base) {
-  // At the end of the chain. Return the base context to link to.
-  if (current->IsFunctionContext() || current->IsGlobalContext()) {
-    return base;
+static Handle<Context> CopyNestedScopeContextChain(Isolate* isolate,
+                                                   Handle<JSFunction> function,
+                                                   Handle<Context> base,
+                                                   JavaScriptFrame* frame,
+                                                   int inlined_frame_index) {
+  HandleScope scope(isolate);
+  List<Handle<ScopeInfo> > scope_chain;
+  List<Handle<Context> > context_chain;
+
+  ScopeIterator it(isolate, frame, inlined_frame_index);
+  for (; it.Type() != ScopeIterator::ScopeTypeGlobal &&
+         it.Type() != ScopeIterator::ScopeTypeLocal ; it.Next()) {
+    ASSERT(!it.Done());
+    scope_chain.Add(it.CurrentScopeInfo());
+    context_chain.Add(it.CurrentContext());
   }
 
-  // Recursively copy the with and catch contexts.
-  HandleScope scope(isolate);
-  Handle<Context> previous(current->previous());
-  Handle<Context> new_previous =
-      CopyWithContextChain(isolate, function, previous, base);
-  Handle<Context> new_current;
-  if (current->IsCatchContext()) {
-    Handle<String> name(String::cast(current->extension()));
-    Handle<Object> thrown_object(current->get(Context::THROWN_OBJECT_INDEX));
-    new_current =
-        isolate->factory()->NewCatchContext(function,
-                                            new_previous,
-                                            name,
-                                            thrown_object);
-  } else if (current->IsBlockContext()) {
-    Handle<ScopeInfo> scope_info(ScopeInfo::cast(current->extension()));
-    new_current =
-        isolate->factory()->NewBlockContext(function, new_previous, scope_info);
-    // Copy context slots.
-    int num_context_slots = scope_info->ContextLength();
-    for (int i = Context::MIN_CONTEXT_SLOTS; i < num_context_slots; ++i) {
-      new_current->set(i, current->get(i));
+  // At the end of the chain. Return the base context to link to.
+  Handle<Context> context = base;
+
+  // Iteratively copy and or materialize the nested contexts.
+  while (!scope_chain.is_empty()) {
+    Handle<ScopeInfo> scope_info = scope_chain.RemoveLast();
+    Handle<Context> current = context_chain.RemoveLast();
+    ASSERT(!(scope_info->HasContext() & current.is_null()));
+
+    if (scope_info->Type() == CATCH_SCOPE) {
+      Handle<String> name(String::cast(current->extension()));
+      Handle<Object> thrown_object(current->get(Context::THROWN_OBJECT_INDEX));
+      context =
+          isolate->factory()->NewCatchContext(function,
+                                              context,
+                                              name,
+                                              thrown_object);
+    } else if (scope_info->Type() == BLOCK_SCOPE) {
+      // Materialize the contents of the block scope into a JSObject.
+      Handle<JSObject> block_scope_object =
+          MaterializeBlockScope(isolate, current);
+      if (block_scope_object.is_null()) {
+        return Handle<Context>::null();
+      }
+      // Allocate a new function context for the debug evaluation and set the
+      // extension object.
+      Handle<Context> new_context =
+          isolate->factory()->NewFunctionContext(Context::MIN_CONTEXT_SLOTS,
+                                                 function);
+      new_context->set_extension(*block_scope_object);
+      new_context->set_previous(*context);
+      context = new_context;
+    } else {
+      ASSERT(scope_info->Type() == WITH_SCOPE);
+      ASSERT(current->IsWithContext());
+      Handle<JSObject> extension(JSObject::cast(current->extension()));
+      context =
+          isolate->factory()->NewWithContext(function, context, extension);
     }
-  } else {
-    ASSERT(current->IsWithContext());
-    Handle<JSObject> extension(JSObject::cast(current->extension()));
-    new_current =
-        isolate->factory()->NewWithContext(function, new_previous, extension);
   }
-  return scope.CloseAndEscape(new_current);
+
+  return scope.CloseAndEscape(context);
 }
 
 
@@ -12010,7 +12109,11 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluate) {
   if (scope_info->HasContext()) {
     function_context = Handle<Context>(frame_context->declaration_context());
   }
-  context = CopyWithContextChain(isolate, go_between, frame_context, context);
+  context = CopyNestedScopeContextChain(isolate,
+                                        go_between,
+                                        context,
+                                        frame,
+                                        inlined_frame_index);
 
   if (additional_context->IsJSObject()) {
     Handle<JSObject> extension = Handle<JSObject>::cast(additional_context);
index 29fd5eb..e05ca17 100644 (file)
@@ -661,14 +661,14 @@ Handle<ScopeInfo> Scope::GetScopeInfo() {
 void Scope::GetNestedScopeChain(
     List<Handle<ScopeInfo> >* chain,
     int position) {
-  chain->Add(Handle<ScopeInfo>(GetScopeInfo()));
+  if (!is_eval_scope()) chain->Add(Handle<ScopeInfo>(GetScopeInfo()));
 
   for (int i = 0; i < inner_scopes_.length(); i++) {
     Scope* scope = inner_scopes_[i];
     int beg_pos = scope->start_position();
     int end_pos = scope->end_position();
     ASSERT(beg_pos >= 0 && end_pos >= 0);
-    if (beg_pos <= position && position <= end_pos) {
+    if (beg_pos <= position && position < end_pos) {
       scope->GetNestedScopeChain(chain, position);
       return;
     }
index 15efd08..bd1e24e 100755 (executable)
@@ -349,10 +349,10 @@ TEST(Regress928) {
       "try { } catch (e) { var foo = function () { /* first */ } }"
       "var bar = function () { /* second */ }";
 
-  i::Utf8ToUC16CharacterStream stream(reinterpret_cast<const i::byte*>(program),
-                                      static_cast<unsigned>(strlen(program)));
-  i::ScriptDataImpl* data =
-      i::ParserApi::PartialPreParse(&stream, NULL, false);
+  v8::HandleScope handles;
+  i::Handle<i::String> source(
+      FACTORY->NewStringFromAscii(i::CStrVector(program)));
+  i::ScriptDataImpl* data = i::ParserApi::PartialPreParse(source, NULL, false);
   CHECK(!data->HasError());
 
   data->Initialize();
diff --git a/test/mjsunit/debug-stepout-scope.js b/test/mjsunit/debug-stepout-scope.js
new file mode 100644 (file)
index 0000000..9c040da
--- /dev/null
@@ -0,0 +1,423 @@
+// 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 --expose-natives-as=builtins
+
+// Check that the ScopeIterator can properly recreate the scope at
+// every point when stepping through functions.
+
+var Debug = debug.Debug;
+
+function listener(event, exec_state, event_data, data) {
+  if (event == Debug.DebugEvent.Break) {
+    // Access scope details.
+    var scope_count = exec_state.frame().scopeCount();
+    for (var i = 0; i < scope_count; i++) {
+      var scope = exec_state.frame().scope(i);
+      // assertTrue(scope.isScope());
+      scope.scopeType();
+      scope.scopeObject();
+    }
+
+    // Do steps until we reach the global scope again.
+    if (true) {
+      exec_state.prepareStep(Debug.StepAction.StepInMin, 1);
+    }
+  }
+}
+
+Debug.setListener(listener);
+
+
+function test1() {
+  debugger;
+  with ({x:1}) {
+    x = 2;
+  }
+}
+test1();
+
+
+function test2() {
+  if (true) {
+    with ({}) {
+      debugger;
+    }
+  } else {
+    with ({}) {
+      return 10;
+    }
+  }
+}
+test2();
+
+
+function test3() {
+  if (true) {
+    debugger;
+  } else {
+    with ({}) {
+      return 10;
+    }
+  }
+}
+test3();
+
+
+function test4() {
+  debugger;
+  with ({x:1}) x = 1
+}
+test4();
+
+
+function test5() {
+  debugger;
+  var dummy = 1;
+  with ({}) {
+    with ({}) {
+      dummy = 2;
+    }
+  }
+  dummy = 3;
+}
+test5();
+
+
+function test6() {
+  debugger;
+  try {
+    throw 'stuff';
+  } catch (e) {
+    e = 1;
+  }
+}
+test6();
+
+
+function test7() {
+  debugger;
+  function foo() {}
+}
+test7();
+
+
+function test8() {
+  debugger;
+  (function foo() {})();
+}
+test8();
+
+
+var q = 42;
+var prefixes = [ "debugger; ",
+                 "if (false) { try { throw 0; } catch(x) { return x; } }; debugger; " ];
+var bodies = [ "1",
+               "1 ",
+               "1;",
+               "1; ",
+               "q",
+               "q ",
+               "q;",
+               "q; ",
+               "try { throw 'stuff' } catch (e) { e = 1; }",
+               "try { throw 'stuff' } catch (e) { e = 1; } ",
+               "try { throw 'stuff' } catch (e) { e = 1; };",
+               "try { throw 'stuff' } catch (e) { e = 1; }; " ];
+var with_bodies = [ "with ({}) {}",
+                    "with ({x:1}) x",
+                    "with ({x:1}) x = 1",
+                    "with ({x:1}) x ",
+                    "with ({x:1}) x = 1 ",
+                    "with ({x:1}) x;",
+                    "with ({x:1}) x = 1;",
+                    "with ({x:1}) x; ",
+                    "with ({x:1}) x = 1; " ];
+
+
+function test9() {
+  debugger;
+  for (var i = 0; i < prefixes.length; ++i) {
+    var pre = prefixes[i];
+    for (var j = 0; j < bodies.length; ++j) {
+      var body = bodies[j];
+      eval(pre + body);
+      eval("'use strict'; " + pre + body);
+    }
+    for (var j = 0; j < with_bodies.length; ++j) {
+      var body = with_bodies[j];
+      eval(pre + body);
+    }
+  }
+}
+test9();
+
+
+function test10() {
+  debugger;
+  with ({}) {
+    return 10;
+  }
+}
+test10();
+
+
+function test11() {
+  debugger;
+  try {
+    throw 'stuff';
+  } catch (e) {
+    return 10;
+  }
+}
+test11();
+
+
+// Test global eval and function constructor.
+for (var i = 0; i < prefixes.length; ++i) {
+  var pre = prefixes[i];
+  for (var j = 0; j < bodies.length; ++j) {
+    var body = bodies[j];
+    eval(pre + body);
+    eval("'use strict'; " + pre + body);
+    Function(pre + body)();
+  }
+  for (var j = 0; j < with_bodies.length; ++j) {
+    var body = with_bodies[j];
+    eval(pre + body);
+    Function(pre + body)();
+  }
+}
+
+
+try {
+  with({}) {
+    debugger;
+    eval("{}$%:^");
+  }
+} catch(e) {
+  nop();
+}
+
+// Return from function constructed with Function constructor.
+var anon = 12;
+for (var i = 0; i < prefixes.length; ++i) {
+  var pre = prefixes[i];
+  Function(pre + "return 42")();
+  Function(pre + "return 42 ")();
+  Function(pre + "return 42;")();
+  Function(pre + "return 42; ")();
+  Function(pre + "return anon")();
+  Function(pre + "return anon ")();
+  Function(pre + "return anon;")();
+  Function(pre + "return anon; ")();
+}
+
+
+function nop() {}
+
+
+function stress() {
+  debugger;
+
+  L: with ({x:12}) {
+    break L;
+  }
+
+
+  with ({x: 'outer'}) {
+    label: {
+      with ({x: 'inner'}) {
+        break label;
+      }
+    }
+  }
+
+
+  with ({x: 'outer'}) {
+    label: {
+      with ({x: 'inner'}) {
+        break label;
+      }
+    }
+    nop();
+  }
+
+
+  with ({x: 'outer'}) {
+    label: {
+      with ({x: 'middle'}) {
+        with ({x: 'inner'}) {
+          break label;
+        }
+      }
+    }
+  }
+
+
+  with ({x: 'outer'}) {
+    label: {
+      with ({x: 'middle'}) {
+        with ({x: 'inner'}) {
+          break label;
+        }
+      }
+    }
+    nop();
+  }
+
+
+  with ({x: 'outer'}) {
+    for (var i = 0; i < 3; ++i) {
+      with ({x: 'inner' + i}) {
+        continue;
+      }
+    }
+  }
+
+
+  with ({x: 'outer'}) {
+    label: for (var i = 0; i < 3; ++i) {
+      with ({x: 'middle' + i}) {
+        for (var j = 0; j < 3; ++j) {
+          with ({x: 'inner' + j}) {
+            continue label;
+          }
+        }
+      }
+    }
+  }
+
+
+  with ({x: 'outer'}) {
+    try {
+      with ({x: 'inner'}) {
+        throw 0;
+      }
+    } catch (e) {
+    }
+  }
+
+
+  with ({x: 'outer'}) {
+    try {
+      with ({x: 'inner'}) {
+        throw 0;
+      }
+    } catch (e) {
+      nop();
+    }
+  }
+
+
+  with ({x: 'outer'}) {
+    try {
+      with ({x: 'middle'}) {
+        with ({x: 'inner'}) {
+          throw 0;
+        }
+      }
+    } catch (e) {
+    }
+  }
+
+
+  try {
+    with ({x: 'outer'}) {
+      try {
+        with ({x: 'inner'}) {
+          throw 0;
+        }
+      } finally {
+      }
+    }
+  } catch (e) {
+  }
+
+
+  try {
+    with ({x: 'outer'}) {
+      try {
+        with ({x: 'inner'}) {
+          throw 0;
+        }
+      } finally {
+        nop();
+      }
+    }
+  } catch (e) {
+  }
+
+
+  function stress1() {
+    with ({x:12}) {
+      return x;
+    }
+  }
+  stress1();
+
+
+  function stress2() {
+    with ({x: 'outer'}) {
+      with ({x: 'inner'}) {
+        return x;
+      }
+    }
+  }
+  stress2();
+
+  function stress3() {
+    try {
+      with ({x: 'inner'}) {
+        throw 0;
+      }
+    } catch (e) {
+      return e;
+    }
+  }
+  stress3();
+
+
+  function stress4() {
+    try {
+      with ({x: 'inner'}) {
+        throw 0;
+      }
+    } catch (e) {
+      with ({x: 'inner'}) {
+        return e;
+      }
+    }
+  }
+  stress4();
+
+}
+stress();
+
+
+// With block as the last(!) statement in global code.
+with ({}) { debugger; }
\ No newline at end of file