Simplfy handling of exits from scoped blocks.
authorkeuchel@chromium.org <keuchel@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 6 Sep 2011 21:48:32 +0000 (21:48 +0000)
committerkeuchel@chromium.org <keuchel@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 6 Sep 2011 21:48:32 +0000 (21:48 +0000)
BUG=
TEST=mjsunit/harmony/block-leave.js

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

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

src/full-codegen.cc
src/full-codegen.h
src/parser.cc
test/mjsunit/harmony/block-leave.js [new file with mode: 0644]
test/mjsunit/harmony/debug-blockscopes.js

index 0f44cf2..960b4b1 100644 (file)
@@ -851,7 +851,7 @@ void FullCodeGenerator::VisitInCurrentContext(Expression* expr) {
 
 void FullCodeGenerator::VisitBlock(Block* stmt) {
   Comment cmnt(masm_, "[ Block");
-  Breakable nested_statement(this, stmt);
+  NestedBlock nested_block(this, stmt);
   SetStatementPosition(stmt);
 
   Scope* saved_scope = scope();
@@ -871,7 +871,7 @@ void FullCodeGenerator::VisitBlock(Block* stmt) {
   PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS);
   VisitStatements(stmt->statements());
   scope_ = saved_scope;
-  __ bind(nested_statement.break_label());
+  __ bind(nested_block.break_label());
   PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
 }
 
index 28be008..7aaeaec 100644 (file)
@@ -191,6 +191,22 @@ class FullCodeGenerator: public AstVisitor {
     Label continue_label_;
   };
 
+  // A nested block statement.
+  class NestedBlock : public Breakable {
+   public:
+    NestedBlock(FullCodeGenerator* codegen, Block* block)
+        : Breakable(codegen, block) {
+    }
+    virtual ~NestedBlock() {}
+
+    virtual NestedStatement* Exit(int* stack_depth, int* context_length) {
+      if (statement()->AsBlock()->block_scope() != NULL) {
+        ++(*context_length);
+      }
+      return previous_;
+    };
+  };
+
   // The try block of a try/catch statement.
   class TryCatch : public NestedStatement {
    public:
index 410d0cf..ef10870 100644 (file)
@@ -1588,19 +1588,11 @@ Block* Parser::ParseScopedBlock(ZoneStringList* labels, bool* ok) {
   body->set_block_scope(block_scope);
 
   if (block_scope != NULL) {
-    // Create exit block.
-    Block* exit = new(zone()) Block(isolate(), NULL, 1, false);
+    // Rewrite the block { B } to a block: { { B } ExitContext; }
+    Block* exit = new(zone()) Block(isolate(), NULL, 2, false);
+    exit->AddStatement(body);
     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());
-
-    // Create a result block.
-    Block* result = new(zone()) Block(isolate(), NULL, 1, false);
-    result->AddStatement(try_finally);
-    return result;
+    return exit;
   } else {
     return body;
   }
diff --git a/test/mjsunit/harmony/block-leave.js b/test/mjsunit/harmony/block-leave.js
new file mode 100644 (file)
index 0000000..73eaf29
--- /dev/null
@@ -0,0 +1,225 @@
+// 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
+
+// We want to test the context chain shape.  In each of the tests cases
+// below, the outer with is to force a runtime lookup of the identifier 'x'
+// to actually verify that the inner context has been discarded.  A static
+// lookup of 'x' might accidentally succeed.
+
+{
+  let x = 2;
+  L: {
+    let x = 3;
+    assertEquals(3, x);
+    break L;
+    assertTrue(false);
+  }
+  assertEquals(2, x);
+}
+
+do {
+  let x = 4;
+  assertEquals(4,x);
+  {
+    let x = 5;
+    assertEquals(5, x);
+    continue;
+    assertTrue(false);
+  }
+} while (false);
+
+var caught = false;
+try {
+  {
+    let xx = 18;
+    throw 25;
+    assertTrue(false);
+  }
+} catch (e) {
+  caught = true;
+  assertEquals(25, e);
+  with ({y:19}) {
+    assertEquals(19, y);
+    try {
+      // NOTE: This checks that the block scope containing xx has been
+      // removed from the context chain.
+      xx;
+      assertTrue(false);  // should not reach here
+    } catch (e2) {
+      assertTrue(e2 instanceof ReferenceError);
+    }
+  }
+}
+assertTrue(caught);
+
+
+with ({x: 'outer'}) {
+  label: {
+    let x = 'inner';
+    break label;
+  }
+  assertEquals('outer', x);
+}
+
+
+with ({x: 'outer'}) {
+  label: {
+    let x = 'middle';
+    {
+      let x = 'inner';
+      break label;
+    }
+  }
+  assertEquals('outer', x);
+}
+
+
+with ({x: 'outer'}) {
+  for (var i = 0; i < 10; ++i) {
+    let x = 'inner' + i;
+    continue;
+  }
+  assertEquals('outer', x);
+}
+
+
+with ({x: 'outer'}) {
+  label: for (var i = 0; i < 10; ++i) {
+    let x = 'middle' + i;
+    for (var j = 0; j < 10; ++j) {
+      let x = 'inner' + j;
+      continue label;
+    }
+  }
+  assertEquals('outer', x);
+}
+
+
+with ({x: 'outer'}) {
+  try {
+    let x = 'inner';
+    throw 0;
+  } catch (e) {
+    assertEquals('outer', x);
+  }
+}
+
+
+with ({x: 'outer'}) {
+  try {
+    let x = 'middle';
+    {
+      let x = 'inner';
+      throw 0;
+    }
+  } catch (e) {
+    assertEquals('outer', x);
+  }
+}
+
+
+try {
+  with ({x: 'outer'}) {
+    try {
+      let x = 'inner';
+      throw 0;
+    } finally {
+      assertEquals('outer', x);
+    }
+  }
+} catch (e) {
+  if (e instanceof MjsUnitAssertionError) throw e;
+}
+
+
+try {
+  with ({x: 'outer'}) {
+    try {
+      let x = 'middle';
+      {
+        let x = 'inner';
+        throw 0;
+      }
+    } finally {
+      assertEquals('outer', x);
+    }
+  }
+} catch (e) {
+  if (e instanceof MjsUnitAssertionError) throw e;
+}
+
+
+// Verify that the context is correctly set in the stack frame after exiting
+// from with.
+function f() {}
+
+with ({x: 'outer'}) {
+  label: {
+    let x = 'inner';
+    break label;
+  }
+  f();  // The context could be restored from the stack after the call.
+  assertEquals('outer', x);
+}
+
+
+with ({x: 'outer'}) {
+  for (var i = 0; i < 10; ++i) {
+    let x = 'inner';
+    continue;
+  }
+  f();
+  assertEquals('outer', x);
+}
+
+
+with ({x: 'outer'}) {
+  try {
+    let x = 'inner';
+    throw 0;
+  } catch (e) {
+    f();
+    assertEquals('outer', x);
+  }
+}
+
+
+try {
+  with ({x: 'outer'}) {
+    try {
+      let x = 'inner';
+      throw 0;
+    } finally {
+      f();
+      assertEquals('outer', x);
+    }
+  }
+} catch (e) {
+  if (e instanceof MjsUnitAssertionError) throw e;
+}
index 0230e84..3dfc911 100644 (file)
@@ -437,6 +437,28 @@ with_block_5();
 EndTest();
 
 
+// With block and a block local variable.
+BeginTest("With block 5");
+
+function with_block_5() {
+  with({a:1}) {
+    let a = 2;
+    debugger;
+  }
+}
+
+listener_delegate = function(exec_state) {
+  CheckScopeChain([debug.ScopeType.Block,
+                   debug.ScopeType.With,
+                   debug.ScopeType.Local,
+                   debug.ScopeType.Global], exec_state);
+  CheckScopeContent({a:2}, 0, exec_state);
+  CheckScopeContent({a:1}, 1, exec_state);
+};
+with_block_5();
+EndTest();
+
+
 // Simple closure formed by returning an inner function referering to an outer
 // block local variable and an outer function's parameter.
 BeginTest("Closure 1");