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
This commit is contained in:
parent
3841070294
commit
68c7523e63
@ -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.
|
||||
|
40
test/mjsunit/regress/regress-3138.js
Normal file
40
test/mjsunit/regress/regress-3138.js
Normal file
@ -0,0 +1,40 @@
|
||||
// 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);
|
||||
})();
|
Loading…
Reference in New Issue
Block a user