Fix a bug in with and catch context allocation.
authorkmillikin@chromium.org <kmillikin@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 4 Jul 2011 09:34:47 +0000 (09:34 +0000)
committerkmillikin@chromium.org <kmillikin@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 4 Jul 2011 09:34:47 +0000 (09:34 +0000)
We were only looking one level up the scope chain to decide which
closure to use in the fresh context.  Instead, we should look to the
first non-catch scope.

R=vegorov@chromium.org
BUG=1528
TEST=regress-1528

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

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

src/arm/full-codegen-arm.cc
src/ia32/full-codegen-ia32.cc
src/mips/full-codegen-mips.cc
src/parser.cc
src/parser.h
src/scopes.cc
src/scopes.h
src/x64/full-codegen-x64.cc
test/mjsunit/regress/regress-1528.js [new file with mode: 0644]

index eb5d8fb8211babc5467e7361802f18e53df888d0..4b55915e916d8419b9f1f6dbe7029295d1bd77bc 100644 (file)
@@ -4243,19 +4243,20 @@ void FullCodeGenerator::LoadContextField(Register dst, int context_index) {
 
 
 void FullCodeGenerator::PushFunctionArgumentForContextAllocation() {
-  if (scope()->is_global_scope()) {
+  Scope* declaration_scope = scope()->DeclarationScope();
+  if (declaration_scope->is_global_scope()) {
     // Contexts nested in the global context have a canonical empty function
     // as their closure, not the anonymous closure containing the global
     // code.  Pass a smi sentinel and let the runtime look up the empty
     // function.
     __ mov(ip, Operand(Smi::FromInt(0)));
-  } else if (scope()->is_eval_scope()) {
+  } else if (declaration_scope->is_eval_scope()) {
     // Contexts created by a call to eval have the same closure as the
     // context calling eval, not the anonymous closure containing the eval
     // code.  Fetch it from the context.
     __ ldr(ip, ContextOperand(cp, Context::CLOSURE_INDEX));
   } else {
-    ASSERT(scope()->is_function_scope() || scope()->is_catch_scope());
+    ASSERT(declaration_scope->is_function_scope());
     __ ldr(ip, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
   }
   __ push(ip);
index 60d9525b398e56457d52201126f26ccfad8f2f18..75cc4b8608deb679284b68e3a86ece92b3f012a4 100644 (file)
@@ -4202,19 +4202,20 @@ void FullCodeGenerator::LoadContextField(Register dst, int context_index) {
 
 
 void FullCodeGenerator::PushFunctionArgumentForContextAllocation() {
-  if (scope()->is_global_scope()) {
+  Scope* declaration_scope = scope()->DeclarationScope();
+  if (declaration_scope->is_global_scope()) {
     // Contexts nested in the global context have a canonical empty function
     // as their closure, not the anonymous closure containing the global
     // code.  Pass a smi sentinel and let the runtime look up the empty
     // function.
     __ push(Immediate(Smi::FromInt(0)));
-  } else if (scope()->is_eval_scope()) {
-    // Contexts created by a call to eval have the same closure as the
-    // context calling eval, not the anonymous closure containing the eval
-    // code.  Fetch it from the context.
+  } else if (declaration_scope->is_eval_scope()) {
+    // Contexts nested inside eval code have the same closure as the context
+    // calling eval, not the anonymous closure containing the eval code.
+    // Fetch it from the context.
     __ push(ContextOperand(esi, Context::CLOSURE_INDEX));
   } else {
-    ASSERT(scope()->is_function_scope() || scope()->is_catch_scope());
+    ASSERT(declaration_scope->is_function_scope());
     __ push(Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
   }
 }
index 396127aef1ea591e3eef0a2178daea6ef9cb5231..5b9bbb57895aeea51bca13202bf28a37ed83668d 100644 (file)
@@ -4262,19 +4262,20 @@ void FullCodeGenerator::LoadContextField(Register dst, int context_index) {
 
 
 void FullCodeGenerator::PushFunctionArgumentForContextAllocation() {
-  if (scope()->is_global_scope()) {
+  Scope* declaration_scope = scope()->DeclarationScope();
+  if (declaration_scope->is_global_scope()) {
     // Contexts nested in the global context have a canonical empty function
     // as their closure, not the anonymous closure containing the global
     // code.  Pass a smi sentinel and let the runtime look up the empty
     // function.
     __ li(at, Operand(Smi::FromInt(0)));
-  } else if (scope()->is_eval_scope()) {
+  } else if (declaration_scope->is_eval_scope()) {
     // Contexts created by a call to eval have the same closure as the
     // context calling eval, not the anonymous closure containing the eval
     // code.  Fetch it from the context.
     __ lw(at, ContextOperand(cp, Context::CLOSURE_INDEX));
   } else {
-    ASSERT(scope()->is_function_scope() || scope()->is_catch_scope());
+    ASSERT(declaration_scope->is_function_scope());
     __ lw(at, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
   }
   __ push(at);
index bd45f9d54ade55aaaff721e6be2d1c70b4577713..184f0a2a27efd2c7104c67dcfa63469b1f71ed84 100644 (file)
@@ -412,14 +412,6 @@ Scope* Parser::NewScope(Scope* parent, Scope::Type type, bool inside_with) {
 }
 
 
-Scope* Parser::DeclarationScope() {
-  Scope* scope = top_scope_;
-  while (scope->is_catch_scope()) {
-    scope = scope->outer_scope();
-  }
-  return scope;
-}
-
 // ----------------------------------------------------------------------------
 // Target is a support class to facilitate manipulation of the
 // Parser's target_stack_ (the stack of potential 'break' and
@@ -1310,7 +1302,7 @@ VariableProxy* Parser::Declare(Handle<String> name,
   // to the calling function context.
   // Similarly, strict mode eval scope does not leak variable declarations to
   // the caller's scope so we declare all locals, too.
-  Scope* declaration_scope = DeclarationScope();
+  Scope* declaration_scope = top_scope_->DeclarationScope();
   if (declaration_scope->is_function_scope() ||
       declaration_scope->is_strict_mode_eval_scope()) {
     // Declare the variable in the function scope.
@@ -1421,7 +1413,7 @@ Statement* Parser::ParseNativeDeclaration(bool* ok) {
   // isn't lazily compiled. The extension structures are only
   // accessible while parsing the first time not when reparsing
   // because of lazy compilation.
-  DeclarationScope()->ForceEagerCompilation();
+  top_scope_->DeclarationScope()->ForceEagerCompilation();
 
   // Compute the function template for the native function.
   v8::Handle<v8::FunctionTemplate> fun_template =
@@ -1525,7 +1517,7 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN,
 
   Variable::Mode mode = Variable::VAR;
   bool is_const = false;
-  Scope* declaration_scope = DeclarationScope();
+  Scope* declaration_scope = top_scope_->DeclarationScope();
   if (peek() == Token::VAR) {
     Consume(Token::VAR);
   } else if (peek() == Token::CONST) {
@@ -1915,7 +1907,7 @@ Statement* Parser::ParseReturnStatement(bool* ok) {
   // function. See ECMA-262, section 12.9, page 67.
   //
   // To be consistent with KJS we report the syntax error at runtime.
-  Scope* declaration_scope = DeclarationScope();
+  Scope* declaration_scope = top_scope_->DeclarationScope();
   if (declaration_scope->is_global_scope() ||
       declaration_scope->is_eval_scope()) {
     Handle<String> type = isolate()->factory()->illegal_return_symbol();
@@ -1944,7 +1936,7 @@ Block* Parser::WithHelper(Expression* obj, ZoneStringList* labels, bool* ok) {
   Statement* stat;
   { Target target(&this->target_stack_, &collector);
     with_nesting_level_++;
-    DeclarationScope()->RecordWithStatement();
+    top_scope_->DeclarationScope()->RecordWithStatement();
     stat = ParseStatement(labels, CHECK_OK);
     with_nesting_level_--;
   }
@@ -3802,7 +3794,7 @@ Expression* Parser::ParseV8Intrinsic(bool* ok) {
   if (extension_ != NULL) {
     // The extension structures are only accessible while parsing the
     // very first time not when reparsing because of lazy compilation.
-    DeclarationScope()->ForceEagerCompilation();
+    top_scope_->DeclarationScope()->ForceEagerCompilation();
   }
 
   const Runtime::Function* function = Runtime::FunctionForSymbol(name);
index 8afc3e39b1e2d66c4b0ff600edbe0341534324a9..9ce1026c9819ba78879bd7953c17fff1964059a0 100644 (file)
@@ -470,8 +470,6 @@ class Parser {
   Mode mode() const { return mode_; }
   ScriptDataImpl* pre_data() const { return pre_data_; }
 
-  Scope* DeclarationScope();
-
   // Check if the given string is 'eval' or 'arguments'.
   bool IsEvalOrArguments(Handle<String> string);
 
index 29712de6a8e04d0ab8e00567584ada6ef4c55011..5546875c4bc42536ecfb36be047b104da169df5d 100644 (file)
@@ -544,6 +544,15 @@ int Scope::ContextChainLength(Scope* scope) {
 }
 
 
+Scope* Scope::DeclarationScope() {
+  Scope* scope = this;
+  while (scope->is_catch_scope()) {
+    scope = scope->outer_scope();
+  }
+  return scope;
+}
+
+
 #ifdef DEBUG
 static const char* Header(Scope::Type type) {
   switch (type) {
index a7623a39a734db650699b5e1560f51e43a227cb7..a493d57520a7d44998efa6d27a06c3b783a041a6 100644 (file)
@@ -290,6 +290,10 @@ class Scope: public ZoneObject {
   // The number of contexts between this and scope; zero if this == scope.
   int ContextChainLength(Scope* scope);
 
+  // Find the first function, global, or eval scope.  This is the scope
+  // where var declarations will be hoisted to in the implementation.
+  Scope* DeclarationScope();
+
   // ---------------------------------------------------------------------------
   // Strict mode support.
   bool IsDeclared(Handle<String> name) {
index 18820767fdbb53a6a979eaa73ff13953903843b5..6629927675b73449e08a763b4c6db1ea0e75977d 100644 (file)
@@ -4180,19 +4180,20 @@ void FullCodeGenerator::LoadContextField(Register dst, int context_index) {
 
 
 void FullCodeGenerator::PushFunctionArgumentForContextAllocation() {
-  if (scope()->is_global_scope()) {
+  Scope* declaration_scope = scope()->DeclarationScope();
+  if (declaration_scope->is_global_scope()) {
     // Contexts nested in the global context have a canonical empty function
     // as their closure, not the anonymous closure containing the global
     // code.  Pass a smi sentinel and let the runtime look up the empty
     // function.
     __ Push(Smi::FromInt(0));
-  } else if (scope()->is_eval_scope()) {
+  } else if (declaration_scope->is_eval_scope()) {
     // Contexts created by a call to eval have the same closure as the
     // context calling eval, not the anonymous closure containing the eval
     // code.  Fetch it from the context.
     __ push(ContextOperand(rsi, Context::CLOSURE_INDEX));
   } else {
-    ASSERT(scope()->is_function_scope() || scope()->is_catch_scope());
+    ASSERT(declaration_scope->is_function_scope());
     __ push(Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
   }
 }
diff --git a/test/mjsunit/regress/regress-1528.js b/test/mjsunit/regress/regress-1528.js
new file mode 100644 (file)
index 0000000..2eb6be1
--- /dev/null
@@ -0,0 +1,40 @@
+// 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.
+
+// With (or catch) scopes nested inside catch scopes should look at the
+// first outer non-catch scope to decide which closure to use when
+// allocating the new context.
+
+// Code below should not assert or crash.
+try {
+  fail;
+} catch (e) {
+  with({}) {  // With scope inside catch scope.
+    // Dynamic declaration forces runtime lookup to observe the context chain.
+    eval('const x = 7');
+  }
+}