Reviewers: fschneider,

Message:
PTAL.

Description:
Avoid dynamic lookup when initializing let declared variables.

'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.


Please review this at http://codereview.chromium.org/7792098/

SVN Base: https://v8.googlecode.com/svn/branches/bleeding_edge

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


Index: src/arm/full-codegen-arm.cc
diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc
index fc12bd03fce88c9f7248a649f2f6a38d88f23a31..f6d99060b4f6025d37eba7a7fa0263d1dab569e2 100644
--- a/src/arm/full-codegen-arm.cc
+++ b/src/arm/full-codegen-arm.cc
@@ -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: src/ia32/full-codegen-ia32.cc
diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc
index a0e4faa38240a288dc7f145f3b2d03e8fc7444f5..ca4e824d915b327fb65cc92382091174b2779331 100644
--- a/src/ia32/full-codegen-ia32.cc
+++ b/src/ia32/full-codegen-ia32.cc
@@ -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: src/parser.cc
diff --git a/src/parser.cc b/src/parser.cc
index a863165b015ac78d883a88f5ffc5bfab5e727e51..abd06591c4057e6c91e130ccee7f7d43821544da 100644
--- a/src/parser.cc
+++ b/src/parser.cc
@@ -1849,18 +1849,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: src/x64/full-codegen-x64.cc
diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc
index 6b3f45f9b371453ec4753a46151b4015343015da..3f51057734253d4a8d3a64310c5800fa8530c6c0 100644
--- a/src/x64/full-codegen-x64.cc
+++ b/src/x64/full-codegen-x64.cc
@@ -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: test/mjsunit/harmony/debug-blockscopes.js
diff --git a/test/mjsunit/harmony/debug-blockscopes.js b/test/mjsunit/harmony/debug-blockscopes.js index d02c9f6a8e9257e980faa52a9727ad509cc4baa2..0230e84b5e9ebd91f0d811d3da85cb91e5d502a1 100644
--- a/test/mjsunit/harmony/debug-blockscopes.js
+++ b/test/mjsunit/harmony/debug-blockscopes.js
@@ -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");


--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to