Commit aa7198df authored by verwaest@chromium.org's avatar verwaest@chromium.org

This CL simplifies var / const by ensuring the behavior is consistent in...

This CL simplifies var / const by ensuring the behavior is consistent in itself, and with regular JS semantics; between regular var/const and eval-ed var/const.

Legacy const is changed so that a declaration declares a configurable, but non-writable, slot, and the initializer reconfigures it (when possible) to non-configurable non-writable. This avoids the need for "the hole" as marker value in JSContextExtensionObjects and GlobalObjects. Undefined is used instead.

BUG=
R=rossberg@chromium.org

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22379 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 942fe191
...@@ -855,7 +855,7 @@ void FullCodeGenerator::VisitVariableDeclaration( ...@@ -855,7 +855,7 @@ void FullCodeGenerator::VisitVariableDeclaration(
__ mov(r0, Operand(Smi::FromInt(0))); // Indicates no initial value. __ mov(r0, Operand(Smi::FromInt(0))); // Indicates no initial value.
__ Push(cp, r2, r1, r0); __ Push(cp, r2, r1, r0);
} }
__ CallRuntime(Runtime::kDeclareContextSlot, 4); __ CallRuntime(Runtime::kDeclareLookupSlot, 4);
break; break;
} }
} }
...@@ -911,7 +911,7 @@ void FullCodeGenerator::VisitFunctionDeclaration( ...@@ -911,7 +911,7 @@ void FullCodeGenerator::VisitFunctionDeclaration(
__ Push(cp, r2, r1); __ Push(cp, r2, r1);
// Push initial value for function declaration. // Push initial value for function declaration.
VisitForStackValue(declaration->fun()); VisitForStackValue(declaration->fun());
__ CallRuntime(Runtime::kDeclareContextSlot, 4); __ CallRuntime(Runtime::kDeclareLookupSlot, 4);
break; break;
} }
} }
...@@ -2458,16 +2458,6 @@ void FullCodeGenerator::EmitStoreToStackLocalOrContextSlot( ...@@ -2458,16 +2458,6 @@ void FullCodeGenerator::EmitStoreToStackLocalOrContextSlot(
} }
void FullCodeGenerator::EmitCallStoreContextSlot(
Handle<String> name, StrictMode strict_mode) {
__ push(r0); // Value.
__ mov(r1, Operand(name));
__ mov(r0, Operand(Smi::FromInt(strict_mode)));
__ Push(cp, r1, r0); // Context, name, strict mode.
__ CallRuntime(Runtime::kStoreContextSlot, 4);
}
void FullCodeGenerator::EmitVariableAssignment(Variable* var, Token::Value op) { void FullCodeGenerator::EmitVariableAssignment(Variable* var, Token::Value op) {
if (var->IsUnallocated()) { if (var->IsUnallocated()) {
// Global var, const, or let. // Global var, const, or let.
...@@ -2482,7 +2472,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, Token::Value op) { ...@@ -2482,7 +2472,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, Token::Value op) {
__ push(r0); __ push(r0);
__ mov(r0, Operand(var->name())); __ mov(r0, Operand(var->name()));
__ Push(cp, r0); // Context and name. __ Push(cp, r0); // Context and name.
__ CallRuntime(Runtime::kInitializeConstContextSlot, 3); __ CallRuntime(Runtime::kInitializeLegacyConstLookupSlot, 3);
} else { } else {
ASSERT(var->IsStackAllocated() || var->IsContextSlot()); ASSERT(var->IsStackAllocated() || var->IsContextSlot());
Label skip; Label skip;
...@@ -2496,29 +2486,33 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, Token::Value op) { ...@@ -2496,29 +2486,33 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, Token::Value op) {
} else if (var->mode() == LET && op != Token::INIT_LET) { } else if (var->mode() == LET && op != Token::INIT_LET) {
// Non-initializing assignment to let variable needs a write barrier. // Non-initializing assignment to let variable needs a write barrier.
if (var->IsLookupSlot()) { ASSERT(!var->IsLookupSlot());
EmitCallStoreContextSlot(var->name(), strict_mode()); ASSERT(var->IsStackAllocated() || var->IsContextSlot());
} else { Label assign;
ASSERT(var->IsStackAllocated() || var->IsContextSlot()); MemOperand location = VarOperand(var, r1);
Label assign; __ ldr(r3, location);
MemOperand location = VarOperand(var, r1); __ CompareRoot(r3, Heap::kTheHoleValueRootIndex);
__ ldr(r3, location); __ b(ne, &assign);
__ CompareRoot(r3, Heap::kTheHoleValueRootIndex); __ mov(r3, Operand(var->name()));
__ b(ne, &assign); __ push(r3);
__ mov(r3, Operand(var->name())); __ CallRuntime(Runtime::kThrowReferenceError, 1);
__ push(r3); // Perform the assignment.
__ CallRuntime(Runtime::kThrowReferenceError, 1); __ bind(&assign);
// Perform the assignment. EmitStoreToStackLocalOrContextSlot(var, location);
__ bind(&assign);
EmitStoreToStackLocalOrContextSlot(var, location);
}
} else if (!var->is_const_mode() || op == Token::INIT_CONST) { } else if (!var->is_const_mode() || op == Token::INIT_CONST) {
// Assignment to var or initializing assignment to let/const
// in harmony mode.
if (var->IsLookupSlot()) { if (var->IsLookupSlot()) {
EmitCallStoreContextSlot(var->name(), strict_mode()); ASSERT(op == Token::ASSIGN || op == Token::INIT_VAR ||
op == Token::ASSIGN_ADD);
// Assignment to var.
__ push(r0); // Value.
__ mov(r1, Operand(var->name()));
__ mov(r0, Operand(Smi::FromInt(strict_mode())));
__ Push(cp, r1, r0); // Context, name, strict mode.
__ CallRuntime(Runtime::kStoreLookupSlot, 4);
} else { } else {
// Assignment to var or initializing assignment to let/const in harmony
// mode.
ASSERT((var->IsStackAllocated() || var->IsContextSlot())); ASSERT((var->IsStackAllocated() || var->IsContextSlot()));
MemOperand location = VarOperand(var, r1); MemOperand location = VarOperand(var, r1);
if (generate_debug_code_ && op == Token::INIT_LET) { if (generate_debug_code_ && op == Token::INIT_LET) {
......
...@@ -859,7 +859,7 @@ void FullCodeGenerator::VisitVariableDeclaration( ...@@ -859,7 +859,7 @@ void FullCodeGenerator::VisitVariableDeclaration(
// Pushing 0 (xzr) indicates no initial value. // Pushing 0 (xzr) indicates no initial value.
__ Push(cp, x2, x1, xzr); __ Push(cp, x2, x1, xzr);
} }
__ CallRuntime(Runtime::kDeclareContextSlot, 4); __ CallRuntime(Runtime::kDeclareLookupSlot, 4);
break; break;
} }
} }
...@@ -915,7 +915,7 @@ void FullCodeGenerator::VisitFunctionDeclaration( ...@@ -915,7 +915,7 @@ void FullCodeGenerator::VisitFunctionDeclaration(
__ Push(cp, x2, x1); __ Push(cp, x2, x1);
// Push initial value for function declaration. // Push initial value for function declaration.
VisitForStackValue(declaration->fun()); VisitForStackValue(declaration->fun());
__ CallRuntime(Runtime::kDeclareContextSlot, 4); __ CallRuntime(Runtime::kDeclareLookupSlot, 4);
break; break;
} }
} }
...@@ -2142,19 +2142,6 @@ void FullCodeGenerator::EmitStoreToStackLocalOrContextSlot( ...@@ -2142,19 +2142,6 @@ void FullCodeGenerator::EmitStoreToStackLocalOrContextSlot(
} }
void FullCodeGenerator::EmitCallStoreContextSlot(
Handle<String> name, StrictMode strict_mode) {
__ Mov(x11, Operand(name));
__ Mov(x10, Smi::FromInt(strict_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, void FullCodeGenerator::EmitVariableAssignment(Variable* var,
Token::Value op) { Token::Value op) {
ASM_LOCATION("FullCodeGenerator::EmitVariableAssignment"); ASM_LOCATION("FullCodeGenerator::EmitVariableAssignment");
...@@ -2170,7 +2157,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, ...@@ -2170,7 +2157,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
if (var->IsLookupSlot()) { if (var->IsLookupSlot()) {
__ Mov(x1, Operand(var->name())); __ Mov(x1, Operand(var->name()));
__ Push(x0, cp, x1); __ Push(x0, cp, x1);
__ CallRuntime(Runtime::kInitializeConstContextSlot, 3); __ CallRuntime(Runtime::kInitializeLegacyConstLookupSlot, 3);
} else { } else {
ASSERT(var->IsStackLocal() || var->IsContextSlot()); ASSERT(var->IsStackLocal() || var->IsContextSlot());
Label skip; Label skip;
...@@ -2183,28 +2170,35 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, ...@@ -2183,28 +2170,35 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
} else if (var->mode() == LET && op != Token::INIT_LET) { } else if (var->mode() == LET && op != Token::INIT_LET) {
// Non-initializing assignment to let variable needs a write barrier. // Non-initializing assignment to let variable needs a write barrier.
if (var->IsLookupSlot()) { ASSERT(!var->IsLookupSlot());
EmitCallStoreContextSlot(var->name(), strict_mode()); ASSERT(var->IsStackAllocated() || var->IsContextSlot());
} else { Label assign;
ASSERT(var->IsStackAllocated() || var->IsContextSlot()); MemOperand location = VarOperand(var, x1);
Label assign; __ Ldr(x10, location);
MemOperand location = VarOperand(var, x1); __ JumpIfNotRoot(x10, Heap::kTheHoleValueRootIndex, &assign);
__ Ldr(x10, location); __ Mov(x10, Operand(var->name()));
__ JumpIfNotRoot(x10, Heap::kTheHoleValueRootIndex, &assign); __ Push(x10);
__ Mov(x10, Operand(var->name())); __ CallRuntime(Runtime::kThrowReferenceError, 1);
__ Push(x10); // Perform the assignment.
__ CallRuntime(Runtime::kThrowReferenceError, 1); __ Bind(&assign);
// Perform the assignment. EmitStoreToStackLocalOrContextSlot(var, location);
__ Bind(&assign);
EmitStoreToStackLocalOrContextSlot(var, location);
}
} else if (!var->is_const_mode() || op == Token::INIT_CONST) { } else if (!var->is_const_mode() || op == Token::INIT_CONST) {
// Assignment to var or initializing assignment to let/const
// in harmony mode.
if (var->IsLookupSlot()) { if (var->IsLookupSlot()) {
EmitCallStoreContextSlot(var->name(), strict_mode()); ASSERT(op == Token::ASSIGN || op == Token::INIT_VAR ||
op == Token::ASSIGN_ADD);
// Assignment to var.
__ Mov(x11, Operand(var->name()));
__ Mov(x10, Smi::FromInt(strict_mode()));
// jssp[0] : mode.
// jssp[8] : name.
// jssp[16] : context.
// jssp[24] : value.
__ Push(x0, cp, x11, x10);
__ CallRuntime(Runtime::kStoreLookupSlot, 4);
} else { } else {
// Assignment to var or initializing assignment to let/const in harmony
// mode.
ASSERT(var->IsStackAllocated() || var->IsContextSlot()); ASSERT(var->IsStackAllocated() || var->IsContextSlot());
MemOperand location = VarOperand(var, x1); MemOperand location = VarOperand(var, x1);
if (FLAG_debug_code && op == Token::INIT_LET) { if (FLAG_debug_code && op == Token::INIT_LET) {
......
...@@ -550,7 +550,6 @@ class FullCodeGenerator: public AstVisitor { ...@@ -550,7 +550,6 @@ class FullCodeGenerator: public AstVisitor {
// Helper functions to EmitVariableAssignment // Helper functions to EmitVariableAssignment
void EmitStoreToStackLocalOrContextSlot(Variable* var, void EmitStoreToStackLocalOrContextSlot(Variable* var,
MemOperand location); MemOperand location);
void EmitCallStoreContextSlot(Handle<String> name, StrictMode strict_mode);
// Complete a named property assignment. The receiver is expected on top // Complete a named property assignment. The receiver is expected on top
// of the stack and the right-hand-side value in the accumulator. // of the stack and the right-hand-side value in the accumulator.
......
...@@ -802,7 +802,7 @@ void FullCodeGenerator::VisitVariableDeclaration( ...@@ -802,7 +802,7 @@ void FullCodeGenerator::VisitVariableDeclaration(
} else { } else {
__ push(Immediate(Smi::FromInt(0))); // Indicates no initial value. __ push(Immediate(Smi::FromInt(0))); // Indicates no initial value.
} }
__ CallRuntime(Runtime::kDeclareContextSlot, 4); __ CallRuntime(Runtime::kDeclareLookupSlot, 4);
break; break;
} }
} }
...@@ -855,7 +855,7 @@ void FullCodeGenerator::VisitFunctionDeclaration( ...@@ -855,7 +855,7 @@ void FullCodeGenerator::VisitFunctionDeclaration(
__ push(Immediate(variable->name())); __ push(Immediate(variable->name()));
__ push(Immediate(Smi::FromInt(NONE))); __ push(Immediate(Smi::FromInt(NONE)));
VisitForStackValue(declaration->fun()); VisitForStackValue(declaration->fun());
__ CallRuntime(Runtime::kDeclareContextSlot, 4); __ CallRuntime(Runtime::kDeclareLookupSlot, 4);
break; break;
} }
} }
...@@ -2385,16 +2385,6 @@ void FullCodeGenerator::EmitStoreToStackLocalOrContextSlot( ...@@ -2385,16 +2385,6 @@ void FullCodeGenerator::EmitStoreToStackLocalOrContextSlot(
} }
void FullCodeGenerator::EmitCallStoreContextSlot(
Handle<String> name, StrictMode strict_mode) {
__ push(eax); // Value.
__ push(esi); // Context.
__ push(Immediate(name));
__ push(Immediate(Smi::FromInt(strict_mode)));
__ CallRuntime(Runtime::kStoreContextSlot, 4);
}
void FullCodeGenerator::EmitVariableAssignment(Variable* var, void FullCodeGenerator::EmitVariableAssignment(Variable* var,
Token::Value op) { Token::Value op) {
if (var->IsUnallocated()) { if (var->IsUnallocated()) {
...@@ -2410,7 +2400,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, ...@@ -2410,7 +2400,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
__ push(eax); __ push(eax);
__ push(esi); __ push(esi);
__ push(Immediate(var->name())); __ push(Immediate(var->name()));
__ CallRuntime(Runtime::kInitializeConstContextSlot, 3); __ CallRuntime(Runtime::kInitializeLegacyConstLookupSlot, 3);
} else { } else {
ASSERT(var->IsStackLocal() || var->IsContextSlot()); ASSERT(var->IsStackLocal() || var->IsContextSlot());
Label skip; Label skip;
...@@ -2424,27 +2414,31 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, ...@@ -2424,27 +2414,31 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
} else if (var->mode() == LET && op != Token::INIT_LET) { } else if (var->mode() == LET && op != Token::INIT_LET) {
// Non-initializing assignment to let variable needs a write barrier. // Non-initializing assignment to let variable needs a write barrier.
if (var->IsLookupSlot()) { ASSERT(!var->IsLookupSlot());
EmitCallStoreContextSlot(var->name(), strict_mode()); ASSERT(var->IsStackAllocated() || var->IsContextSlot());
} else { Label assign;
ASSERT(var->IsStackAllocated() || var->IsContextSlot()); MemOperand location = VarOperand(var, ecx);
Label assign; __ mov(edx, location);
MemOperand location = VarOperand(var, ecx); __ cmp(edx, isolate()->factory()->the_hole_value());
__ mov(edx, location); __ j(not_equal, &assign, Label::kNear);
__ cmp(edx, isolate()->factory()->the_hole_value()); __ push(Immediate(var->name()));
__ j(not_equal, &assign, Label::kNear); __ CallRuntime(Runtime::kThrowReferenceError, 1);
__ push(Immediate(var->name())); __ bind(&assign);
__ CallRuntime(Runtime::kThrowReferenceError, 1); EmitStoreToStackLocalOrContextSlot(var, location);
__ bind(&assign);
EmitStoreToStackLocalOrContextSlot(var, location);
}
} else if (!var->is_const_mode() || op == Token::INIT_CONST) { } else if (!var->is_const_mode() || op == Token::INIT_CONST) {
// Assignment to var or initializing assignment to let/const
// in harmony mode.
if (var->IsLookupSlot()) { if (var->IsLookupSlot()) {
EmitCallStoreContextSlot(var->name(), strict_mode()); ASSERT(op == Token::ASSIGN || op == Token::INIT_VAR ||
op == Token::ASSIGN_ADD);
// Assignment to var.
__ push(eax); // Value.
__ push(esi); // Context.
__ push(Immediate(var->name()));
__ push(Immediate(Smi::FromInt(strict_mode())));
__ CallRuntime(Runtime::kStoreLookupSlot, 4);
} else { } else {
// Assignment to var or initializing assignment to let/const in harmony
// mode.
ASSERT(var->IsStackAllocated() || var->IsContextSlot()); ASSERT(var->IsStackAllocated() || var->IsContextSlot());
MemOperand location = VarOperand(var, ecx); MemOperand location = VarOperand(var, ecx);
if (generate_debug_code_ && op == Token::INIT_LET) { if (generate_debug_code_ && op == Token::INIT_LET) {
......
...@@ -1704,7 +1704,7 @@ void Parser::Declare(Declaration* declaration, bool resolve, bool* ok) { ...@@ -1704,7 +1704,7 @@ void Parser::Declare(Declaration* declaration, bool resolve, bool* ok) {
// same variable if it is declared several times. This is not a // same variable if it is declared several times. This is not a
// semantic issue as long as we keep the source order, but it may be // semantic issue as long as we keep the source order, but it may be
// a performance issue since it may lead to repeated // a performance issue since it may lead to repeated
// RuntimeHidden_DeclareContextSlot calls. // RuntimeHidden_DeclareLookupSlot calls.
declaration_scope->AddDeclaration(declaration); declaration_scope->AddDeclaration(declaration);
if (mode == CONST_LEGACY && declaration_scope->is_global_scope()) { if (mode == CONST_LEGACY && declaration_scope->is_global_scope()) {
...@@ -1718,7 +1718,7 @@ void Parser::Declare(Declaration* declaration, bool resolve, bool* ok) { ...@@ -1718,7 +1718,7 @@ void Parser::Declare(Declaration* declaration, bool resolve, bool* ok) {
declaration_scope->strict_mode() == SLOPPY) { declaration_scope->strict_mode() == SLOPPY) {
// For variable declarations in a sloppy eval scope the proxy is bound // For variable declarations in a sloppy eval scope the proxy is bound
// to a lookup variable to force a dynamic declaration using the // to a lookup variable to force a dynamic declaration using the
// DeclareContextSlot runtime function. // DeclareLookupSlot runtime function.
Variable::Kind kind = Variable::NORMAL; Variable::Kind kind = Variable::NORMAL;
var = new(zone()) Variable( var = new(zone()) Variable(
declaration_scope, name, mode, true, kind, declaration_scope, name, mode, true, kind,
...@@ -2189,21 +2189,22 @@ Block* Parser::ParseVariableDeclarations( ...@@ -2189,21 +2189,22 @@ Block* Parser::ParseVariableDeclarations(
if (value != NULL && !inside_with()) { if (value != NULL && !inside_with()) {
arguments->Add(value, zone()); arguments->Add(value, zone());
value = NULL; // zap the value to avoid the unnecessary assignment value = NULL; // zap the value to avoid the unnecessary assignment
// Construct the call to Runtime_InitializeVarGlobal
// and add it to the initialization statement block.
initialize = factory()->NewCallRuntime(
ast_value_factory_->initialize_var_global_string(),
Runtime::FunctionForId(Runtime::kInitializeVarGlobal), arguments,
pos);
} else {
initialize = NULL;
} }
// Construct the call to Runtime_InitializeVarGlobal
// and add it to the initialization statement block.
// Note that the function does different things depending on
// the number of arguments (2 or 3).
initialize = factory()->NewCallRuntime(
ast_value_factory_->initialize_var_global_string(),
Runtime::FunctionForId(Runtime::kInitializeVarGlobal),
arguments, pos);
} }
block->AddStatement( if (initialize != NULL) {
factory()->NewExpressionStatement(initialize, RelocInfo::kNoPosition), block->AddStatement(factory()->NewExpressionStatement(
zone()); initialize, RelocInfo::kNoPosition),
zone());
}
} else if (needs_init) { } else if (needs_init) {
// Constant initializations always assign to the declared constant which // Constant initializations always assign to the declared constant which
// is always at the function scope level. This is only relevant for // is always at the function scope level. This is only relevant for
......
This diff is collapsed.
This diff is collapsed.
...@@ -824,7 +824,7 @@ void FullCodeGenerator::VisitVariableDeclaration( ...@@ -824,7 +824,7 @@ void FullCodeGenerator::VisitVariableDeclaration(
} else { } else {
__ Push(Smi::FromInt(0)); // Indicates no initial value. __ Push(Smi::FromInt(0)); // Indicates no initial value.
} }
__ CallRuntime(Runtime::kDeclareContextSlot, 4); __ CallRuntime(Runtime::kDeclareLookupSlot, 4);
break; break;
} }
} }
...@@ -878,7 +878,7 @@ void FullCodeGenerator::VisitFunctionDeclaration( ...@@ -878,7 +878,7 @@ void FullCodeGenerator::VisitFunctionDeclaration(
__ Push(variable->name()); __ Push(variable->name());
__ Push(Smi::FromInt(NONE)); __ Push(Smi::FromInt(NONE));
VisitForStackValue(declaration->fun()); VisitForStackValue(declaration->fun());
__ CallRuntime(Runtime::kDeclareContextSlot, 4); __ CallRuntime(Runtime::kDeclareLookupSlot, 4);
break; break;
} }
} }
...@@ -2383,16 +2383,6 @@ void FullCodeGenerator::EmitStoreToStackLocalOrContextSlot( ...@@ -2383,16 +2383,6 @@ void FullCodeGenerator::EmitStoreToStackLocalOrContextSlot(
} }
void FullCodeGenerator::EmitCallStoreContextSlot(
Handle<String> name, StrictMode strict_mode) {
__ Push(rax); // Value.
__ Push(rsi); // Context.
__ Push(name);
__ Push(Smi::FromInt(strict_mode));
__ CallRuntime(Runtime::kStoreContextSlot, 4);
}
void FullCodeGenerator::EmitVariableAssignment(Variable* var, void FullCodeGenerator::EmitVariableAssignment(Variable* var,
Token::Value op) { Token::Value op) {
if (var->IsUnallocated()) { if (var->IsUnallocated()) {
...@@ -2408,7 +2398,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, ...@@ -2408,7 +2398,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
__ Push(rax); __ Push(rax);
__ Push(rsi); __ Push(rsi);
__ Push(var->name()); __ Push(var->name());
__ CallRuntime(Runtime::kInitializeConstContextSlot, 3); __ CallRuntime(Runtime::kInitializeLegacyConstLookupSlot, 3);
} else { } else {
ASSERT(var->IsStackLocal() || var->IsContextSlot()); ASSERT(var->IsStackLocal() || var->IsContextSlot());
Label skip; Label skip;
...@@ -2422,27 +2412,31 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, ...@@ -2422,27 +2412,31 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
} else if (var->mode() == LET && op != Token::INIT_LET) { } else if (var->mode() == LET && op != Token::INIT_LET) {
// Non-initializing assignment to let variable needs a write barrier. // Non-initializing assignment to let variable needs a write barrier.
if (var->IsLookupSlot()) { ASSERT(!var->IsLookupSlot());
EmitCallStoreContextSlot(var->name(), strict_mode()); ASSERT(var->IsStackAllocated() || var->IsContextSlot());
} else { Label assign;
ASSERT(var->IsStackAllocated() || var->IsContextSlot()); MemOperand location = VarOperand(var, rcx);
Label assign; __ movp(rdx, location);
MemOperand location = VarOperand(var, rcx); __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex);
__ movp(rdx, location); __ j(not_equal, &assign, Label::kNear);
__ CompareRoot(rdx, Heap::kTheHoleValueRootIndex); __ Push(var->name());
__ j(not_equal, &assign, Label::kNear); __ CallRuntime(Runtime::kThrowReferenceError, 1);
__ Push(var->name()); __ bind(&assign);
__ CallRuntime(Runtime::kThrowReferenceError, 1); EmitStoreToStackLocalOrContextSlot(var, location);
__ bind(&assign);
EmitStoreToStackLocalOrContextSlot(var, location);
}
} else if (!var->is_const_mode() || op == Token::INIT_CONST) { } else if (!var->is_const_mode() || op == Token::INIT_CONST) {
// Assignment to var or initializing assignment to let/const
// in harmony mode.
if (var->IsLookupSlot()) { if (var->IsLookupSlot()) {
EmitCallStoreContextSlot(var->name(), strict_mode()); ASSERT(op == Token::ASSIGN || op == Token::INIT_VAR ||
op == Token::ASSIGN_ADD);
// Assignment to var.
__ Push(rax); // Value.
__ Push(rsi); // Context.
__ Push(var->name());
__ Push(Smi::FromInt(strict_mode()));
__ CallRuntime(Runtime::kStoreLookupSlot, 4);
} else { } else {
// Assignment to var or initializing assignment to let/const in harmony
// mode.
ASSERT(var->IsStackAllocated() || var->IsContextSlot()); ASSERT(var->IsStackAllocated() || var->IsContextSlot());
MemOperand location = VarOperand(var, rcx); MemOperand location = VarOperand(var, rcx);
if (generate_debug_code_ && op == Token::INIT_LET) { if (generate_debug_code_ && op == Token::INIT_LET) {
......
This diff is collapsed.
...@@ -36,29 +36,29 @@ function testIntroduceGlobal() { ...@@ -36,29 +36,29 @@ function testIntroduceGlobal() {
var source = var source =
// Deleting 'x' removes the local const property. // Deleting 'x' removes the local const property.
"delete x;" + "delete x;" +
// Initialization turns into assignment to global 'x'. // Initialization redefines global 'x'.
"const x = 3; assertEquals(3, x);" + "const x = 3; assertEquals(3, x);" +
// No constness of the global 'x'. // Test constness of the global 'x'.
"x = 4; assertEquals(4, x);"; "x = 4; assertEquals(3, x);";
eval(source); eval(source);
} }
testIntroduceGlobal(); testIntroduceGlobal();
assertEquals(4, x); assertEquals("undefined", typeof x);
function testAssignExistingGlobal() { function testAssignExistingGlobal() {
var source = var source =
// Delete 'x' to remove the local const property. // Delete 'x' to remove the local const property.
"delete x;" + "delete x;" +
// Initialization turns into assignment to global 'x'. // Initialization redefines global 'x'.
"const x = 5; assertEquals(5, x);" + "const x = 5; assertEquals(5, x);" +
// No constness of the global 'x'. // Test constness of the global 'x'.
"x = 6; assertEquals(6, x);"; "x = 6; assertEquals(5, x);";
eval(source); eval(source);
} }
testAssignExistingGlobal(); testAssignExistingGlobal();
assertEquals(6, x); assertEquals("undefined", typeof x);
function testAssignmentArgument(x) { function testAssignmentArgument(x) {
function local() { function local() {
...@@ -66,7 +66,7 @@ function testAssignmentArgument(x) { ...@@ -66,7 +66,7 @@ function testAssignmentArgument(x) {
eval(source); eval(source);
} }
local(); local();
assertEquals(7, x); assertEquals("undefined", typeof x);
} }
for (var i = 0; i < 5; i++) { for (var i = 0; i < 5; i++) {
...@@ -74,17 +74,18 @@ for (var i = 0; i < 5; i++) { ...@@ -74,17 +74,18 @@ for (var i = 0; i < 5; i++) {
} }
%OptimizeFunctionOnNextCall(testAssignmentArgument); %OptimizeFunctionOnNextCall(testAssignmentArgument);
testAssignmentArgument(); testAssignmentArgument();
assertEquals(6, x); assertEquals("undefined", typeof x);
__defineSetter__('x', function() { throw 42; }); __defineSetter__('x', function() { throw 42; });
function testAssignGlobalThrows() { var finished = false;
// Initialization turns into assignment to global 'x' which function testRedefineGlobal() {
// throws an exception. // Initialization redefines global 'x'.
var source = "delete x; const x = 8"; var source = "delete x; const x = 8; finished = true;";
eval(source); eval(source);
} }
assertThrows("testAssignGlobalThrows()"); testRedefineGlobal();
assertTrue(finished);
function testInitFastCaseExtension() { function testInitFastCaseExtension() {
var source = "const x = 9; assertEquals(9, x); x = 10; assertEquals(9, x)"; var source = "const x = 9; assertEquals(9, x); x = 10; assertEquals(9, x)";
...@@ -111,7 +112,7 @@ function testAssignSurroundingContextSlot() { ...@@ -111,7 +112,7 @@ function testAssignSurroundingContextSlot() {
eval(source); eval(source);
} }
local(); local();
assertEquals(13, x); assertEquals(12, x);
} }
testAssignSurroundingContextSlot(); testAssignSurroundingContextSlot();
...@@ -49,37 +49,6 @@ function TestLocal(s,e) { ...@@ -49,37 +49,6 @@ function TestLocal(s,e) {
} }
// NOTE: TestGlobal usually only tests the given string in the context
// of a global object in dictionary mode. This is because we use
// delete to get rid of any added properties.
function TestGlobal(s,e) {
// Collect the global properties before the call.
var properties = [];
for (var key in this) properties.push(key);
// Compute the result.
var result;
try {
var code = s + (e ? "; $$$result=" + e : "");
if (this.execScript) {
execScript(code);
} else {
this.eval(code);
}
// Avoid issues if $$$result is not defined by
// reading it through this.
result = this.$$$result;
} catch (x) {
result = CheckException(x);
}
// Get rid of any introduced global properties before
// returning the result.
for (var key in this) {
if (properties.indexOf(key) == -1) delete this[key];
}
return result;
}
function TestContext(s,e) { function TestContext(s,e) {
try { try {
// Use a with-statement to force the system to do dynamic // Use a with-statement to force the system to do dynamic
...@@ -98,8 +67,6 @@ function TestAll(expected,s,opt_e) { ...@@ -98,8 +67,6 @@ function TestAll(expected,s,opt_e) {
var msg = s; var msg = s;
if (opt_e) { e = opt_e; msg += "; " + opt_e; } if (opt_e) { e = opt_e; msg += "; " + opt_e; }
assertEquals(expected, TestLocal(s,e), "local:'" + msg + "'"); assertEquals(expected, TestLocal(s,e), "local:'" + msg + "'");
// Redeclarations of global consts do not throw, they are silently ignored.
assertEquals(42, TestGlobal(s, 42), "global:'" + msg + "'");
assertEquals(expected, TestContext(s,e), "context:'" + msg + "'"); assertEquals(expected, TestContext(s,e), "context:'" + msg + "'");
} }
...@@ -112,7 +79,7 @@ function TestConflict(def0, def1) { ...@@ -112,7 +79,7 @@ function TestConflict(def0, def1) {
// Eval first definition. // Eval first definition.
TestAll("TypeError", 'eval("' + def0 +'"); ' + def1); TestAll("TypeError", 'eval("' + def0 +'"); ' + def1);
// Eval second definition. // Eval second definition.
TestAll("TypeError", def0 + '; eval("' + def1 + '")'); TestAll("TypeError", def0 + '; eval("' + def1 +'")');
// Eval both definitions separately. // Eval both definitions separately.
TestAll("TypeError", 'eval("' + def0 +'"); eval("' + def1 + '")'); TestAll("TypeError", 'eval("' + def0 +'"); eval("' + def1 + '")');
} }
...@@ -234,47 +201,26 @@ var undefined = 1; // Should be silently ignored. ...@@ -234,47 +201,26 @@ var undefined = 1; // Should be silently ignored.
assertEquals(original_undef, undefined, "undefined got overwritten"); assertEquals(original_undef, undefined, "undefined got overwritten");
undefined = original_undef; undefined = original_undef;
var a; const a; const a = 1; const e = 1; eval('var e = 2');
assertEquals(1, a, "a has wrong value");
a = 2;
assertEquals(2, a, "a should be writable");
var b = 1; const b = 2;
assertEquals(2, b, "b has wrong value");
var c = 1; const c = 2; const c = 3;
assertEquals(3, c, "c has wrong value");
const d = 1; const d = 2;
assertEquals(1, d, "d has wrong value");
const e = 1; var e = 2;
assertEquals(1, e, "e has wrong value"); assertEquals(1, e, "e has wrong value");
const f = 1; const f; const h; eval('var h = 1');
assertEquals(1, f, "f has wrong value"); assertEquals(undefined, h, "h has wrong value");
var g; const g = 1;
assertEquals(1, g, "g has wrong value");
g = 2;
assertEquals(2, g, "g should be writable");
const h; var h = 1;
assertEquals(undefined,h, "h has wrong value");
eval("Object.defineProperty(this, 'i', { writable: true });" eval("Object.defineProperty(this, 'i', { writable: true });"
+ "const i = 7;" + "const i = 7;"
+ "assertEquals(7, i, \"i has wrong value\");"); + "assertEquals(7, i, \"i has wrong value\");");
var global = this; var global = this;
assertThrows(function() { Object.defineProperty(global, 'j', { value: 100, writable: true });
Object.defineProperty(global, 'j', { writable: true }) assertEquals(100, j);
}, TypeError); // The const declaration stays configurable, so the declaration above goes
const j = 2; // This is what makes the function above throw, because the // through even though the const declaration is hoisted above.
// const declaration gets hoisted and makes the property non-configurable. const j = 2;
assertEquals(2, j, "j has wrong value"); assertEquals(2, j, "j has wrong value");
var k = 1; const k; var k = 1;
// You could argue about the expected result here. For now, the winning try { eval('const k'); } catch(e) { }
// argument is that "const k;" is equivalent to "const k = undefined;". assertEquals(1, k, "k has wrong value");
assertEquals(undefined, k, "k has wrong value"); try { eval('const k = 10'); } catch(e) { }
assertEquals(1, k, "k has wrong value");
...@@ -41,17 +41,20 @@ try { eval("var b"); } catch (e) { caught++; assertTrue(e instanceof TypeError); ...@@ -41,17 +41,20 @@ try { eval("var b"); } catch (e) { caught++; assertTrue(e instanceof TypeError);
assertEquals(0, b); assertEquals(0, b);
try { eval("var b = 1"); } catch (e) { caught++; assertTrue(e instanceof TypeError); } try { eval("var b = 1"); } catch (e) { caught++; assertTrue(e instanceof TypeError); }
assertEquals(0, b); assertEquals(0, b);
assertEquals(0, caught);
eval("var c"); eval("var c");
try { eval("const c"); } catch (e) { caught++; assertTrue(e instanceof TypeError); } try { eval("const c"); } catch (e) { caught++; assertTrue(e instanceof TypeError); }
assertTrue(typeof c == 'undefined'); assertTrue(typeof c == 'undefined');
assertEquals(1, caught);
try { eval("const c = 1"); } catch (e) { caught++; assertTrue(e instanceof TypeError); } try { eval("const c = 1"); } catch (e) { caught++; assertTrue(e instanceof TypeError); }
assertEquals(1, c); assertEquals(undefined, c);
assertEquals(2, caught);
eval("var d = 0"); eval("var d = 0");
try { eval("const d"); } catch (e) { caught++; assertTrue(e instanceof TypeError); } try { eval("const d"); } catch (e) { caught++; assertTrue(e instanceof TypeError); }
assertEquals(undefined, d); assertEquals(0, d);
assertEquals(3, caught);
try { eval("const d = 1"); } catch (e) { caught++; assertTrue(e instanceof TypeError); } try { eval("const d = 1"); } catch (e) { caught++; assertTrue(e instanceof TypeError); }
assertEquals(1, d); assertEquals(0, d);
assertEquals(4, caught);
assertEquals(0, caught);
...@@ -25,8 +25,6 @@ ...@@ -25,8 +25,6 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --es52_globals
var setter_value = 0; var setter_value = 0;
this.__defineSetter__("a", function(v) { setter_value = v; }); this.__defineSetter__("a", function(v) { setter_value = v; });
...@@ -35,8 +33,9 @@ assertEquals(1, setter_value); ...@@ -35,8 +33,9 @@ assertEquals(1, setter_value);
assertFalse("value" in Object.getOwnPropertyDescriptor(this, "a")); assertFalse("value" in Object.getOwnPropertyDescriptor(this, "a"));
eval("with({}) { eval('var a = 2') }"); eval("with({}) { eval('var a = 2') }");
assertEquals(2, setter_value); assertTrue("get" in Object.getOwnPropertyDescriptor(this, "a"));
assertFalse("value" in Object.getOwnPropertyDescriptor(this, "a")); assertFalse("value" in Object.getOwnPropertyDescriptor(this, "a"));
assertEquals(2, setter_value);
// Function declarations are treated specially to match Safari. We do // Function declarations are treated specially to match Safari. We do
// not call setters for them. // not call setters for them.
...@@ -47,10 +46,8 @@ assertTrue("value" in Object.getOwnPropertyDescriptor(this, "a")); ...@@ -47,10 +46,8 @@ assertTrue("value" in Object.getOwnPropertyDescriptor(this, "a"));
this.__defineSetter__("b", function(v) { setter_value = v; }); this.__defineSetter__("b", function(v) { setter_value = v; });
try { try {
eval("const b = 3"); eval("const b = 3");
} catch(e) { } catch(e) { }
assertUnreachable(); assertEquals(2, setter_value);
}
assertEquals(3, setter_value);
try { try {
eval("with({}) { eval('const b = 23') }"); eval("with({}) { eval('const b = 23') }");
......
...@@ -37,4 +37,4 @@ try { ...@@ -37,4 +37,4 @@ try {
assertTrue(e instanceof TypeError); assertTrue(e instanceof TypeError);
caught = true; caught = true;
} }
assertFalse(caught); assertTrue(caught);
...@@ -2,6 +2,6 @@ ...@@ -2,6 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
__defineSetter__('x', function() { }); Object.defineProperty(this, 'x', {set: function() { }});
Object.freeze(this); Object.freeze(this);
eval('const x = 1'); eval('"use strict"; x = 20;');
...@@ -642,10 +642,6 @@ ...@@ -642,10 +642,6 @@
# We do not correctly handle assignments within "with" # We do not correctly handle assignments within "with"
'ecma_3/Statements/12.10-01': [FAIL], 'ecma_3/Statements/12.10-01': [FAIL],
# We do not throw an exception when a const is redeclared.
# (We only fail section 1 of the test.)
'js1_5/Regress/regress-103602': [FAIL],
##################### MOZILLA EXTENSION TESTS ##################### ##################### MOZILLA EXTENSION TESTS #####################
'ecma/extensions/15.1.2.1-1': [FAIL_OK], 'ecma/extensions/15.1.2.1-1': [FAIL_OK],
......
...@@ -49,7 +49,7 @@ EXPAND_MACROS = [ ...@@ -49,7 +49,7 @@ EXPAND_MACROS = [
# to parse them! # to parse them!
EXPECTED_FUNCTION_COUNT = 417 EXPECTED_FUNCTION_COUNT = 417
EXPECTED_FUZZABLE_COUNT = 332 EXPECTED_FUZZABLE_COUNT = 332
EXPECTED_CCTEST_COUNT = 6 EXPECTED_CCTEST_COUNT = 9
EXPECTED_UNKNOWN_COUNT = 4 EXPECTED_UNKNOWN_COUNT = 4
EXPECTED_BUILTINS_COUNT = 810 EXPECTED_BUILTINS_COUNT = 810
......
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