Avoid dynamic lookup when initializing let declared variables.
authorkeuchel@chromium.org <keuchel@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 6 Sep 2011 21:22:35 +0000 (21:22 +0000)
committerkeuchel@chromium.org <keuchel@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 6 Sep 2011 21:22:35 +0000 (21:22 +0000)
'Let's inside a 'with' would initialize the variable
using the StoreContextSlot runtime function which
would fail because it checks that the variable does
not hold the hole value.

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

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

src/arm/full-codegen-arm.cc
src/ia32/full-codegen-ia32.cc
src/parser.cc
src/x64/full-codegen-x64.cc
test/mjsunit/harmony/debug-blockscopes.js

index fc12bd0..f6d9906 100644 (file)
@@ -1936,12 +1936,26 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
     switch (slot->type()) {
       case Slot::PARAMETER:
       case Slot::LOCAL:
+        if (FLAG_debug_code && op == Token::INIT_LET) {
+          // Check for an uninitialized let binding.
+          __ ldr(r1, MemOperand(fp, SlotOffset(slot)));
+          __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+          __ cmp(r1, ip);
+          __ Check(eq, "Let binding re-initialization.");
+        }
         // Perform the assignment.
         __ str(result_register(), MemOperand(fp, SlotOffset(slot)));
         break;
 
       case Slot::CONTEXT: {
         MemOperand target = EmitSlotSearch(slot, r1);
+        if (FLAG_debug_code && op == Token::INIT_LET) {
+          // Check for an uninitialized let binding.
+          __ ldr(r3, target);
+          __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+          __ cmp(r3, ip);
+          __ Check(eq, "Let binding re-initialization.");
+        }
         // Perform the assignment and issue the write barrier.
         __ str(result_register(), target);
         // RecordWrite may destroy all its register arguments.
@@ -1952,6 +1966,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
       }
 
       case Slot::LOOKUP:
+        ASSERT(op != Token::INIT_LET);
         // Call the runtime for the assignment.
         __ push(r0);  // Value.
         __ mov(r1, Operand(slot->var()->name()));
index a0e4faa..ca4e824 100644 (file)
@@ -1928,12 +1928,24 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
     switch (slot->type()) {
       case Slot::PARAMETER:
       case Slot::LOCAL:
+        if (FLAG_debug_code && op == Token::INIT_LET) {
+          // Check for an uninitialized let binding.
+          __ mov(edx, Operand(ebp, SlotOffset(slot)));
+          __ cmp(edx, isolate()->factory()->the_hole_value());
+          __ Check(equal, "Let binding re-initialization.");
+        }
         // Perform the assignment.
         __ mov(Operand(ebp, SlotOffset(slot)), eax);
         break;
 
       case Slot::CONTEXT: {
         MemOperand target = EmitSlotSearch(slot, ecx);
+        if (FLAG_debug_code && op == Token::INIT_LET) {
+          // Check for an uninitialized let binding.
+          __ mov(edx, target);
+          __ cmp(edx, isolate()->factory()->the_hole_value());
+          __ Check(equal, "Let binding re-initialization.");
+        }
         // Perform the assignment and issue the write barrier.
         __ mov(target, eax);
         // The value of the assignment is in eax.  RecordWrite clobbers its
@@ -1945,6 +1957,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
       }
 
       case Slot::LOOKUP:
+        ASSERT(op != Token::INIT_LET);
         // Call the runtime for the assignment.
         __ push(eax);  // Value.
         __ push(esi);  // Context.
index 30d6f22..410d0cf 100644 (file)
@@ -1850,18 +1850,21 @@ Block* Parser::ParseVariableDeclarations(VariableDeclarationContext var_context,
       block->AddStatement(new(zone()) ExpressionStatement(initialize));
     }
 
-    // Add an assignment node to the initialization statement block if
-    // we still have a pending initialization value. We must distinguish
-    // between variables and constants: Variable initializations are simply
+    // Add an assignment node to the initialization statement block if we still
+    // have a pending initialization value. We must distinguish between
+    // different kinds of declarations: 'var' initializations are simply
     // assignments (with all the consequences if they are inside a 'with'
     // statement - they may change a 'with' object property). Constant
     // initializations always assign to the declared constant which is
     // always at the function scope level. This is only relevant for
     // dynamically looked-up variables and constants (the start context
     // for constant lookups is always the function context, while it is
-    // the top context for variables). Sigh...
+    // the top context for var declared variables). Sigh...
+    // For 'let' declared variables the initialization is in the same scope
+    // as the declaration. Thus dynamic lookups are unnecessary even if the
+    // block scope is inside a with.
     if (value != NULL) {
-      bool in_with = is_const ? false : inside_with();
+      bool in_with = mode == Variable::VAR ? inside_with() : false;
       VariableProxy* proxy =
           initialization_scope->NewUnresolved(name, in_with);
       Assignment* assignment =
index 6b3f45f..3f51057 100644 (file)
@@ -1848,12 +1848,24 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
     switch (slot->type()) {
       case Slot::PARAMETER:
       case Slot::LOCAL:
+        if (FLAG_debug_code && op == Token::INIT_LET) {
+          // Check for an uninitialized let binding.
+          __ movq(rdx, Operand(rbp, SlotOffset(slot)));
+          __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex);
+          __ Check(equal, "Let binding re-initialization.");
+        }
         // Perform the assignment.
         __ movq(Operand(rbp, SlotOffset(slot)), rax);
         break;
 
       case Slot::CONTEXT: {
         MemOperand target = EmitSlotSearch(slot, rcx);
+        if (FLAG_debug_code && op == Token::INIT_LET) {
+          // Check for an uninitialized let binding.
+          __ movq(rdx, target);
+          __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex);
+          __ Check(equal, "Let binding re-initialization.");
+        }
         // Perform the assignment and issue the write barrier.
         __ movq(target, rax);
         // The value of the assignment is in rax.  RecordWrite clobbers its
@@ -1865,6 +1877,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
       }
 
       case Slot::LOOKUP:
+        ASSERT(op != Token::INIT_LET);
         // Call the runtime for the assignment.
         __ push(rax);  // Value.
         __ push(rsi);  // Context.
index d02c9f6..0230e84 100644 (file)
@@ -415,6 +415,28 @@ with_block_4();
 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");