Commit 68c7523e authored by yangguo@chromium.org's avatar yangguo@chromium.org

Fix assignment of function name constant.

If it's shadowed by a variable of the same name and both are forcibly
context-allocated, the function is assigned to the wrong context slot.

R=rossberg@chromium.org
BUG=v8:3138
LOG=Y

Review URL: https://codereview.chromium.org/159903008

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@19379 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 38410702
......@@ -2155,6 +2155,32 @@ void FullCodeGenerator::EmitAssignment(Expression* expr) {
}
void FullCodeGenerator::EmitStoreToStackLocalOrContextSlot(
Variable* var, MemOperand location) {
__ Str(result_register(), location);
if (var->IsContextSlot()) {
// RecordWrite may destroy all its register arguments.
__ Mov(x10, result_register());
int offset = Context::SlotOffset(var->index());
__ RecordWriteContextSlot(
x1, offset, x10, x11, kLRHasBeenSaved, kDontSaveFPRegs);
}
}
void FullCodeGenerator::EmitCallStoreContextSlot(
Handle<String> name, LanguageMode mode) {
__ Mov(x11, Operand(name));
__ Mov(x10, Operand(Smi::FromInt(mode)));
// jssp[0] : mode.
// jssp[8] : name.
// jssp[16] : context.
// jssp[24] : value.
__ Push(x0, cp, x11, x10);
__ CallRuntime(Runtime::kStoreContextSlot, 4);
}
void FullCodeGenerator::EmitVariableAssignment(Variable* var,
Token::Value op) {
ASM_LOCATION("FullCodeGenerator::EmitVariableAssignment");
......@@ -2167,33 +2193,25 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
} else if (op == Token::INIT_CONST) {
// Const initializers need a write barrier.
ASSERT(!var->IsParameter()); // No const parameters.
if (var->IsStackLocal()) {
Label skip;
__ Ldr(x1, StackOperand(var));
__ JumpIfNotRoot(x1, Heap::kTheHoleValueRootIndex, &skip);
__ Str(result_register(), StackOperand(var));
__ Bind(&skip);
} else {
ASSERT(var->IsContextSlot() || var->IsLookupSlot());
// Like var declarations, const declarations are hoisted to function
// scope. However, unlike var initializers, const initializers are
// able to drill a hole to that function context, even from inside a
// 'with' context. We thus bypass the normal static scope lookup for
// var->IsContextSlot().
if (var->IsLookupSlot()) {
__ Push(x0);
__ Mov(x0, Operand(var->name()));
__ Push(cp, x0); // Context and name.
__ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
} else {
ASSERT(var->IsStackLocal() || var->IsContextSlot());
Label skip;
MemOperand location = VarOperand(var, x1);
__ Ldr(x10, location);
__ JumpIfNotRoot(x10, Heap::kTheHoleValueRootIndex, &skip);
EmitStoreToStackLocalOrContextSlot(var, location);
__ Bind(&skip);
}
} else if (var->mode() == LET && op != Token::INIT_LET) {
// Non-initializing assignment to let variable needs a write barrier.
if (var->IsLookupSlot()) {
__ Push(x0, cp); // Context, value.
__ Mov(x11, Operand(var->name()));
__ Mov(x10, Operand(Smi::FromInt(language_mode())));
__ Push(x11, x10); // Strict mode, name.
__ CallRuntime(Runtime::kStoreContextSlot, 4);
EmitCallStoreContextSlot(var->name(), language_mode());
} else {
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
Label assign;
......@@ -2205,44 +2223,23 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
__ CallRuntime(Runtime::kThrowReferenceError, 1);
// Perform the assignment.
__ Bind(&assign);
__ Str(result_register(), location);
if (var->IsContextSlot()) {
// RecordWrite may destroy all its register arguments.
__ Mov(x10, result_register());
int offset = Context::SlotOffset(var->index());
__ RecordWriteContextSlot(
x1, offset, x10, x11, kLRHasBeenSaved, kDontSaveFPRegs);
}
EmitStoreToStackLocalOrContextSlot(var, location);
}
} else if (!var->is_const_mode() || op == Token::INIT_CONST_HARMONY) {
// Assignment to var or initializing assignment to let/const
// in harmony mode.
if (var->IsStackAllocated() || var->IsContextSlot()) {
if (var->IsLookupSlot()) {
EmitCallStoreContextSlot(var->name(), language_mode());
} else {
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
MemOperand location = VarOperand(var, x1);
if (FLAG_debug_code && op == Token::INIT_LET) {
__ Ldr(x10, location);
__ CompareRoot(x10, Heap::kTheHoleValueRootIndex);
__ Check(eq, kLetBindingReInitialization);
}
// Perform the assignment.
__ Str(x0, location);
if (var->IsContextSlot()) {
__ Mov(x10, x0);
int offset = Context::SlotOffset(var->index());
__ RecordWriteContextSlot(
x1, offset, x10, x11, kLRHasBeenSaved, kDontSaveFPRegs);
}
} else {
ASSERT(var->IsLookupSlot());
__ Mov(x11, Operand(var->name()));
__ Mov(x10, Operand(Smi::FromInt(language_mode())));
// jssp[0] : mode.
// jssp[8] : name.
// jssp[16] : context.
// jssp[24] : value.
__ Push(x0, cp, x11, x10);
__ CallRuntime(Runtime::kStoreContextSlot, 4);
EmitStoreToStackLocalOrContextSlot(var, location);
}
}
// Non-initializing assignments to consts are ignored.
......
......@@ -2456,6 +2456,29 @@ void FullCodeGenerator::EmitAssignment(Expression* expr) {
}
void FullCodeGenerator::EmitStoreToStackLocalOrContextSlot(
Variable* var, MemOperand location) {
__ str(result_register(), location);
if (var->IsContextSlot()) {
// RecordWrite may destroy all its register arguments.
__ mov(r3, result_register());
int offset = Context::SlotOffset(var->index());
__ RecordWriteContextSlot(
r1, offset, r3, r2, kLRHasBeenSaved, kDontSaveFPRegs);
}
}
void FullCodeGenerator::EmitCallStoreContextSlot(
Handle<String> name, LanguageMode mode) {
__ push(r0); // Value.
__ mov(r1, Operand(name));
__ mov(r0, Operand(Smi::FromInt(mode)));
__ Push(cp, r1, r0); // Context, name, strict mode.
__ CallRuntime(Runtime::kStoreContextSlot, 4);
}
void FullCodeGenerator::EmitVariableAssignment(Variable* var,
Token::Value op) {
if (var->IsUnallocated()) {
......@@ -2463,34 +2486,30 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
__ mov(r2, Operand(var->name()));
__ ldr(r1, GlobalObjectOperand());
CallStoreIC();
} else if (op == Token::INIT_CONST) {
// Const initializers need a write barrier.
ASSERT(!var->IsParameter()); // No const parameters.
if (var->IsStackLocal()) {
__ ldr(r1, StackOperand(var));
__ CompareRoot(r1, Heap::kTheHoleValueRootIndex);
__ str(result_register(), StackOperand(var), eq);
} else {
ASSERT(var->IsContextSlot() || var->IsLookupSlot());
// Like var declarations, const declarations are hoisted to function
// scope. However, unlike var initializers, const initializers are
// able to drill a hole to that function context, even from inside a
// 'with' context. We thus bypass the normal static scope lookup for
// var->IsContextSlot().
if (var->IsLookupSlot()) {
__ push(r0);
__ mov(r0, Operand(var->name()));
__ Push(cp, r0); // Context and name.
__ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
} else {
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
Label skip;
MemOperand location = VarOperand(var, r1);
__ ldr(r2, location);
__ CompareRoot(r2, Heap::kTheHoleValueRootIndex);
__ b(ne, &skip);
EmitStoreToStackLocalOrContextSlot(var, location);
__ bind(&skip);
}
} else if (var->mode() == LET && op != Token::INIT_LET) {
// Non-initializing assignment to let variable needs a write barrier.
if (var->IsLookupSlot()) {
__ push(r0); // Value.
__ mov(r1, Operand(var->name()));
__ mov(r0, Operand(Smi::FromInt(language_mode())));
__ Push(cp, r1, r0); // Context, name, strict mode.
__ CallRuntime(Runtime::kStoreContextSlot, 4);
EmitCallStoreContextSlot(var->name(), language_mode());
} else {
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
Label assign;
......@@ -2503,20 +2522,16 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
__ CallRuntime(Runtime::kThrowReferenceError, 1);
// Perform the assignment.
__ bind(&assign);
__ str(result_register(), location);
if (var->IsContextSlot()) {
// RecordWrite may destroy all its register arguments.
__ mov(r3, result_register());
int offset = Context::SlotOffset(var->index());
__ RecordWriteContextSlot(
r1, offset, r3, r2, kLRHasBeenSaved, kDontSaveFPRegs);
}
EmitStoreToStackLocalOrContextSlot(var, location);
}
} else if (!var->is_const_mode() || op == Token::INIT_CONST_HARMONY) {
// Assignment to var or initializing assignment to let/const
// in harmony mode.
if (var->IsStackAllocated() || var->IsContextSlot()) {
if (var->IsLookupSlot()) {
EmitCallStoreContextSlot(var->name(), language_mode());
} else {
ASSERT((var->IsStackAllocated() || var->IsContextSlot()));
MemOperand location = VarOperand(var, r1);
if (generate_debug_code_ && op == Token::INIT_LET) {
// Check for an uninitialized let binding.
......@@ -2524,21 +2539,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
__ CompareRoot(r2, Heap::kTheHoleValueRootIndex);
__ Check(eq, kLetBindingReInitialization);
}
// Perform the assignment.
__ str(r0, location);
if (var->IsContextSlot()) {
__ mov(r3, r0);
int offset = Context::SlotOffset(var->index());
__ RecordWriteContextSlot(
r1, offset, r3, r2, kLRHasBeenSaved, kDontSaveFPRegs);
}
} else {
ASSERT(var->IsLookupSlot());
__ push(r0); // Value.
__ mov(r1, Operand(var->name()));
__ mov(r0, Operand(Smi::FromInt(language_mode())));
__ Push(cp, r1, r0); // Context, name, strict mode.
__ CallRuntime(Runtime::kStoreContextSlot, 4);
EmitStoreToStackLocalOrContextSlot(var, location);
}
}
// Non-initializing assignments to consts are ignored.
......
......@@ -558,6 +558,11 @@ class FullCodeGenerator: public AstVisitor {
void EmitVariableAssignment(Variable* var,
Token::Value op);
// Helper functions to EmitVariableAssignment
void EmitStoreToStackLocalOrContextSlot(Variable* var,
MemOperand location);
void EmitCallStoreContextSlot(Handle<String> name, LanguageMode mode);
// Complete a named property assignment. The receiver is expected on top
// of the stack and the right-hand-side value in the accumulator.
void EmitNamedPropertyAssignment(Assignment* expr);
......
......@@ -2408,6 +2408,27 @@ void FullCodeGenerator::EmitAssignment(Expression* expr) {
}
void FullCodeGenerator::EmitStoreToStackLocalOrContextSlot(
Variable* var, MemOperand location) {
__ mov(location, eax);
if (var->IsContextSlot()) {
__ mov(edx, eax);
int offset = Context::SlotOffset(var->index());
__ RecordWriteContextSlot(ecx, offset, edx, ebx, kDontSaveFPRegs);
}
}
void FullCodeGenerator::EmitCallStoreContextSlot(
Handle<String> name, LanguageMode mode) {
__ push(eax); // Value.
__ push(esi); // Context.
__ push(Immediate(name));
__ push(Immediate(Smi::FromInt(mode)));
__ CallRuntime(Runtime::kStoreContextSlot, 4);
}
void FullCodeGenerator::EmitVariableAssignment(Variable* var,
Token::Value op) {
if (var->IsUnallocated()) {
......@@ -2415,37 +2436,30 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
__ mov(ecx, var->name());
__ mov(edx, GlobalObjectOperand());
CallStoreIC();
} else if (op == Token::INIT_CONST) {
// Const initializers need a write barrier.
ASSERT(!var->IsParameter()); // No const parameters.
if (var->IsStackLocal()) {
Label skip;
__ mov(edx, StackOperand(var));
__ cmp(edx, isolate()->factory()->the_hole_value());
__ j(not_equal, &skip);
__ mov(StackOperand(var), eax);
__ bind(&skip);
} else {
ASSERT(var->IsContextSlot() || var->IsLookupSlot());
// Like var declarations, const declarations are hoisted to function
// scope. However, unlike var initializers, const initializers are
// able to drill a hole to that function context, even from inside a
// 'with' context. We thus bypass the normal static scope lookup for
// var->IsContextSlot().
if (var->IsLookupSlot()) {
__ push(eax);
__ push(esi);
__ push(Immediate(var->name()));
__ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
} else {
ASSERT(var->IsStackLocal() || var->IsContextSlot());
Label skip;
MemOperand location = VarOperand(var, ecx);
__ mov(edx, location);
__ cmp(edx, isolate()->factory()->the_hole_value());
__ j(not_equal, &skip, Label::kNear);
EmitStoreToStackLocalOrContextSlot(var, location);
__ bind(&skip);
}
} else if (var->mode() == LET && op != Token::INIT_LET) {
// Non-initializing assignment to let variable needs a write barrier.
if (var->IsLookupSlot()) {
__ push(eax); // Value.
__ push(esi); // Context.
__ push(Immediate(var->name()));
__ push(Immediate(Smi::FromInt(language_mode())));
__ CallRuntime(Runtime::kStoreContextSlot, 4);
EmitCallStoreContextSlot(var->name(), language_mode());
} else {
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
Label assign;
......@@ -2456,18 +2470,16 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
__ push(Immediate(var->name()));
__ CallRuntime(Runtime::kThrowReferenceError, 1);
__ bind(&assign);
__ mov(location, eax);
if (var->IsContextSlot()) {
__ mov(edx, eax);
int offset = Context::SlotOffset(var->index());
__ RecordWriteContextSlot(ecx, offset, edx, ebx, kDontSaveFPRegs);
}
EmitStoreToStackLocalOrContextSlot(var, location);
}
} else if (!var->is_const_mode() || op == Token::INIT_CONST_HARMONY) {
// Assignment to var or initializing assignment to let/const
// in harmony mode.
if (var->IsStackAllocated() || var->IsContextSlot()) {
if (var->IsLookupSlot()) {
EmitCallStoreContextSlot(var->name(), language_mode());
} else {
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
MemOperand location = VarOperand(var, ecx);
if (generate_debug_code_ && op == Token::INIT_LET) {
// Check for an uninitialized let binding.
......@@ -2475,20 +2487,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
__ cmp(edx, isolate()->factory()->the_hole_value());
__ Check(equal, kLetBindingReInitialization);
}
// Perform the assignment.
__ mov(location, eax);
if (var->IsContextSlot()) {
__ mov(edx, eax);
int offset = Context::SlotOffset(var->index());
__ RecordWriteContextSlot(ecx, offset, edx, ebx, kDontSaveFPRegs);
}
} else {
ASSERT(var->IsLookupSlot());
__ push(eax); // Value.
__ push(esi); // Context.
__ push(Immediate(var->name()));
__ push(Immediate(Smi::FromInt(language_mode())));
__ CallRuntime(Runtime::kStoreContextSlot, 4);
EmitStoreToStackLocalOrContextSlot(var, location);
}
}
// Non-initializing assignments to consts are ignored.
......
......@@ -2400,6 +2400,27 @@ void FullCodeGenerator::EmitAssignment(Expression* expr) {
}
void FullCodeGenerator::EmitStoreToStackLocalOrContextSlot(
Variable* var, MemOperand location) {
__ movp(location, rax);
if (var->IsContextSlot()) {
__ movp(rdx, rax);
__ RecordWriteContextSlot(
rcx, Context::SlotOffset(var->index()), rdx, rbx, kDontSaveFPRegs);
}
}
void FullCodeGenerator::EmitCallStoreContextSlot(
Handle<String> name, LanguageMode mode) {
__ push(rax); // Value.
__ push(rsi); // Context.
__ Push(name);
__ Push(Smi::FromInt(mode));
__ CallRuntime(Runtime::kStoreContextSlot, 4);
}
void FullCodeGenerator::EmitVariableAssignment(Variable* var,
Token::Value op) {
if (var->IsUnallocated()) {
......@@ -2407,37 +2428,30 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
__ Move(rcx, var->name());
__ movp(rdx, GlobalObjectOperand());
CallStoreIC();
} else if (op == Token::INIT_CONST) {
// Const initializers need a write barrier.
ASSERT(!var->IsParameter()); // No const parameters.
if (var->IsStackLocal()) {
Label skip;
__ movp(rdx, StackOperand(var));
__ CompareRoot(rdx, Heap::kTheHoleValueRootIndex);
__ j(not_equal, &skip);
__ movp(StackOperand(var), rax);
__ bind(&skip);
} else {
ASSERT(var->IsContextSlot() || var->IsLookupSlot());
// Like var declarations, const declarations are hoisted to function
// scope. However, unlike var initializers, const initializers are
// able to drill a hole to that function context, even from inside a
// 'with' context. We thus bypass the normal static scope lookup for
// var->IsContextSlot().
if (var->IsLookupSlot()) {
__ push(rax);
__ push(rsi);
__ Push(var->name());
__ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
} else {
ASSERT(var->IsStackLocal() || var->IsContextSlot());
Label skip;
MemOperand location = VarOperand(var, rcx);
__ movp(rdx, location);
__ CompareRoot(rdx, Heap::kTheHoleValueRootIndex);
__ j(not_equal, &skip);
EmitStoreToStackLocalOrContextSlot(var, location);
__ bind(&skip);
}
} else if (var->mode() == LET && op != Token::INIT_LET) {
// Non-initializing assignment to let variable needs a write barrier.
if (var->IsLookupSlot()) {
__ push(rax); // Value.
__ push(rsi); // Context.
__ Push(var->name());
__ Push(Smi::FromInt(language_mode()));
__ CallRuntime(Runtime::kStoreContextSlot, 4);
EmitCallStoreContextSlot(var->name(), language_mode());
} else {
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
Label assign;
......@@ -2448,18 +2462,16 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
__ Push(var->name());
__ CallRuntime(Runtime::kThrowReferenceError, 1);
__ bind(&assign);
__ movp(location, rax);
if (var->IsContextSlot()) {
__ movp(rdx, rax);
__ RecordWriteContextSlot(
rcx, Context::SlotOffset(var->index()), rdx, rbx, kDontSaveFPRegs);
}
EmitStoreToStackLocalOrContextSlot(var, location);
}
} else if (!var->is_const_mode() || op == Token::INIT_CONST_HARMONY) {
// Assignment to var or initializing assignment to let/const
// in harmony mode.
if (var->IsStackAllocated() || var->IsContextSlot()) {
if (var->IsLookupSlot()) {
EmitCallStoreContextSlot(var->name(), language_mode());
} else {
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
MemOperand location = VarOperand(var, rcx);
if (generate_debug_code_ && op == Token::INIT_LET) {
// Check for an uninitialized let binding.
......@@ -2467,20 +2479,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
__ CompareRoot(rdx, Heap::kTheHoleValueRootIndex);
__ Check(equal, kLetBindingReInitialization);
}
// Perform the assignment.
__ movp(location, rax);
if (var->IsContextSlot()) {
__ movp(rdx, rax);
__ RecordWriteContextSlot(
rcx, Context::SlotOffset(var->index()), rdx, rbx, kDontSaveFPRegs);
}
} else {
ASSERT(var->IsLookupSlot());
__ push(rax); // Value.
__ push(rsi); // Context.
__ Push(var->name());
__ Push(Smi::FromInt(language_mode()));
__ CallRuntime(Runtime::kStoreContextSlot, 4);
EmitStoreToStackLocalOrContextSlot(var, location);
}
}
// Non-initializing assignments to consts are ignored.
......
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
(function f(){
assertEquals("function", typeof f);
})();
(function f(){
var f; // Variable shadows function name.
assertEquals("undefined", typeof f);
})();
(function f(){
var f;
assertEquals("undefined", typeof f);
with ({}); // Force context allocation of both variable and function name.
})();
assertEquals("undefined", typeof f);
// var initialization is intercepted by with scope.
(function() {
var o = { a: 1 };
with (o) {
var a = 2;
}
assertEquals("undefined", typeof a);
assertEquals(2, o.a);
})();
// const initialization is not intercepted by with scope.
(function() {
var o = { a: 1 };
with (o) {
const a = 2;
}
assertEquals(2, a);
assertEquals(1, o.a);
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment