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