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(
__ mov(r0, Operand(Smi::FromInt(0))); // Indicates no initial value.
__ Push(cp, r2, r1, r0);
}
__ CallRuntime(Runtime::kDeclareContextSlot, 4);
__ CallRuntime(Runtime::kDeclareLookupSlot, 4);
break;
}
}
......@@ -911,7 +911,7 @@ void FullCodeGenerator::VisitFunctionDeclaration(
__ Push(cp, r2, r1);
// Push initial value for function declaration.
VisitForStackValue(declaration->fun());
__ CallRuntime(Runtime::kDeclareContextSlot, 4);
__ CallRuntime(Runtime::kDeclareLookupSlot, 4);
break;
}
}
......@@ -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) {
if (var->IsUnallocated()) {
// Global var, const, or let.
......@@ -2482,7 +2472,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, Token::Value op) {
__ push(r0);
__ mov(r0, Operand(var->name()));
__ Push(cp, r0); // Context and name.
__ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
__ CallRuntime(Runtime::kInitializeLegacyConstLookupSlot, 3);
} else {
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
Label skip;
......@@ -2496,29 +2486,33 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, Token::Value op) {
} else if (var->mode() == LET && op != Token::INIT_LET) {
// Non-initializing assignment to let variable needs a write barrier.
if (var->IsLookupSlot()) {
EmitCallStoreContextSlot(var->name(), strict_mode());
} else {
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
Label assign;
MemOperand location = VarOperand(var, r1);
__ ldr(r3, location);
__ CompareRoot(r3, Heap::kTheHoleValueRootIndex);
__ b(ne, &assign);
__ mov(r3, Operand(var->name()));
__ push(r3);
__ CallRuntime(Runtime::kThrowReferenceError, 1);
// Perform the assignment.
__ bind(&assign);
EmitStoreToStackLocalOrContextSlot(var, location);
}
ASSERT(!var->IsLookupSlot());
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
Label assign;
MemOperand location = VarOperand(var, r1);
__ ldr(r3, location);
__ CompareRoot(r3, Heap::kTheHoleValueRootIndex);
__ b(ne, &assign);
__ mov(r3, Operand(var->name()));
__ push(r3);
__ CallRuntime(Runtime::kThrowReferenceError, 1);
// Perform the assignment.
__ bind(&assign);
EmitStoreToStackLocalOrContextSlot(var, location);
} 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()) {
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 {
// Assignment to var or initializing assignment to let/const in harmony
// mode.
ASSERT((var->IsStackAllocated() || var->IsContextSlot()));
MemOperand location = VarOperand(var, r1);
if (generate_debug_code_ && op == Token::INIT_LET) {
......
......@@ -859,7 +859,7 @@ void FullCodeGenerator::VisitVariableDeclaration(
// Pushing 0 (xzr) indicates no initial value.
__ Push(cp, x2, x1, xzr);
}
__ CallRuntime(Runtime::kDeclareContextSlot, 4);
__ CallRuntime(Runtime::kDeclareLookupSlot, 4);
break;
}
}
......@@ -915,7 +915,7 @@ void FullCodeGenerator::VisitFunctionDeclaration(
__ Push(cp, x2, x1);
// Push initial value for function declaration.
VisitForStackValue(declaration->fun());
__ CallRuntime(Runtime::kDeclareContextSlot, 4);
__ CallRuntime(Runtime::kDeclareLookupSlot, 4);
break;
}
}
......@@ -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,
Token::Value op) {
ASM_LOCATION("FullCodeGenerator::EmitVariableAssignment");
......@@ -2170,7 +2157,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
if (var->IsLookupSlot()) {
__ Mov(x1, Operand(var->name()));
__ Push(x0, cp, x1);
__ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
__ CallRuntime(Runtime::kInitializeLegacyConstLookupSlot, 3);
} else {
ASSERT(var->IsStackLocal() || var->IsContextSlot());
Label skip;
......@@ -2183,28 +2170,35 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
} else if (var->mode() == LET && op != Token::INIT_LET) {
// Non-initializing assignment to let variable needs a write barrier.
if (var->IsLookupSlot()) {
EmitCallStoreContextSlot(var->name(), strict_mode());
} else {
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
Label assign;
MemOperand location = VarOperand(var, x1);
__ Ldr(x10, location);
__ JumpIfNotRoot(x10, Heap::kTheHoleValueRootIndex, &assign);
__ Mov(x10, Operand(var->name()));
__ Push(x10);
__ CallRuntime(Runtime::kThrowReferenceError, 1);
// Perform the assignment.
__ Bind(&assign);
EmitStoreToStackLocalOrContextSlot(var, location);
}
ASSERT(!var->IsLookupSlot());
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
Label assign;
MemOperand location = VarOperand(var, x1);
__ Ldr(x10, location);
__ JumpIfNotRoot(x10, Heap::kTheHoleValueRootIndex, &assign);
__ Mov(x10, Operand(var->name()));
__ Push(x10);
__ CallRuntime(Runtime::kThrowReferenceError, 1);
// Perform the assignment.
__ Bind(&assign);
EmitStoreToStackLocalOrContextSlot(var, location);
} 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()) {
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 {
// Assignment to var or initializing assignment to let/const in harmony
// mode.
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
MemOperand location = VarOperand(var, x1);
if (FLAG_debug_code && op == Token::INIT_LET) {
......
......@@ -550,7 +550,6 @@ class FullCodeGenerator: public AstVisitor {
// Helper functions to EmitVariableAssignment
void EmitStoreToStackLocalOrContextSlot(Variable* var,
MemOperand location);
void EmitCallStoreContextSlot(Handle<String> name, StrictMode strict_mode);
// Complete a named property assignment. The receiver is expected on top
// of the stack and the right-hand-side value in the accumulator.
......
......@@ -802,7 +802,7 @@ void FullCodeGenerator::VisitVariableDeclaration(
} else {
__ push(Immediate(Smi::FromInt(0))); // Indicates no initial value.
}
__ CallRuntime(Runtime::kDeclareContextSlot, 4);
__ CallRuntime(Runtime::kDeclareLookupSlot, 4);
break;
}
}
......@@ -855,7 +855,7 @@ void FullCodeGenerator::VisitFunctionDeclaration(
__ push(Immediate(variable->name()));
__ push(Immediate(Smi::FromInt(NONE)));
VisitForStackValue(declaration->fun());
__ CallRuntime(Runtime::kDeclareContextSlot, 4);
__ CallRuntime(Runtime::kDeclareLookupSlot, 4);
break;
}
}
......@@ -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,
Token::Value op) {
if (var->IsUnallocated()) {
......@@ -2410,7 +2400,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
__ push(eax);
__ push(esi);
__ push(Immediate(var->name()));
__ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
__ CallRuntime(Runtime::kInitializeLegacyConstLookupSlot, 3);
} else {
ASSERT(var->IsStackLocal() || var->IsContextSlot());
Label skip;
......@@ -2424,27 +2414,31 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
} else if (var->mode() == LET && op != Token::INIT_LET) {
// Non-initializing assignment to let variable needs a write barrier.
if (var->IsLookupSlot()) {
EmitCallStoreContextSlot(var->name(), strict_mode());
} else {
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
Label assign;
MemOperand location = VarOperand(var, ecx);
__ mov(edx, location);
__ cmp(edx, isolate()->factory()->the_hole_value());
__ j(not_equal, &assign, Label::kNear);
__ push(Immediate(var->name()));
__ CallRuntime(Runtime::kThrowReferenceError, 1);
__ bind(&assign);
EmitStoreToStackLocalOrContextSlot(var, location);
}
ASSERT(!var->IsLookupSlot());
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
Label assign;
MemOperand location = VarOperand(var, ecx);
__ mov(edx, location);
__ cmp(edx, isolate()->factory()->the_hole_value());
__ j(not_equal, &assign, Label::kNear);
__ push(Immediate(var->name()));
__ CallRuntime(Runtime::kThrowReferenceError, 1);
__ bind(&assign);
EmitStoreToStackLocalOrContextSlot(var, location);
} 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()) {
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 {
// Assignment to var or initializing assignment to let/const in harmony
// mode.
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
MemOperand location = VarOperand(var, ecx);
if (generate_debug_code_ && op == Token::INIT_LET) {
......
......@@ -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
// semantic issue as long as we keep the source order, but it may be
// a performance issue since it may lead to repeated
// RuntimeHidden_DeclareContextSlot calls.
// RuntimeHidden_DeclareLookupSlot calls.
declaration_scope->AddDeclaration(declaration);
if (mode == CONST_LEGACY && declaration_scope->is_global_scope()) {
......@@ -1718,7 +1718,7 @@ void Parser::Declare(Declaration* declaration, bool resolve, bool* ok) {
declaration_scope->strict_mode() == SLOPPY) {
// For variable declarations in a sloppy eval scope the proxy is bound
// to a lookup variable to force a dynamic declaration using the
// DeclareContextSlot runtime function.
// DeclareLookupSlot runtime function.
Variable::Kind kind = Variable::NORMAL;
var = new(zone()) Variable(
declaration_scope, name, mode, true, kind,
......@@ -2189,21 +2189,22 @@ Block* Parser::ParseVariableDeclarations(
if (value != NULL && !inside_with()) {
arguments->Add(value, zone());
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(
factory()->NewExpressionStatement(initialize, RelocInfo::kNoPosition),
zone());
if (initialize != NULL) {
block->AddStatement(factory()->NewExpressionStatement(
initialize, RelocInfo::kNoPosition),
zone());
}
} else if (needs_init) {
// Constant initializations always assign to the declared constant which
// 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(
} else {
__ Push(Smi::FromInt(0)); // Indicates no initial value.
}
__ CallRuntime(Runtime::kDeclareContextSlot, 4);
__ CallRuntime(Runtime::kDeclareLookupSlot, 4);
break;
}
}
......@@ -878,7 +878,7 @@ void FullCodeGenerator::VisitFunctionDeclaration(
__ Push(variable->name());
__ Push(Smi::FromInt(NONE));
VisitForStackValue(declaration->fun());
__ CallRuntime(Runtime::kDeclareContextSlot, 4);
__ CallRuntime(Runtime::kDeclareLookupSlot, 4);
break;
}
}
......@@ -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,
Token::Value op) {
if (var->IsUnallocated()) {
......@@ -2408,7 +2398,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
__ Push(rax);
__ Push(rsi);
__ Push(var->name());
__ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
__ CallRuntime(Runtime::kInitializeLegacyConstLookupSlot, 3);
} else {
ASSERT(var->IsStackLocal() || var->IsContextSlot());
Label skip;
......@@ -2422,27 +2412,31 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
} else if (var->mode() == LET && op != Token::INIT_LET) {
// Non-initializing assignment to let variable needs a write barrier.
if (var->IsLookupSlot()) {
EmitCallStoreContextSlot(var->name(), strict_mode());
} else {
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
Label assign;
MemOperand location = VarOperand(var, rcx);
__ movp(rdx, location);
__ CompareRoot(rdx, Heap::kTheHoleValueRootIndex);
__ j(not_equal, &assign, Label::kNear);
__ Push(var->name());
__ CallRuntime(Runtime::kThrowReferenceError, 1);
__ bind(&assign);
EmitStoreToStackLocalOrContextSlot(var, location);
}
ASSERT(!var->IsLookupSlot());
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
Label assign;
MemOperand location = VarOperand(var, rcx);
__ movp(rdx, location);
__ CompareRoot(rdx, Heap::kTheHoleValueRootIndex);
__ j(not_equal, &assign, Label::kNear);
__ Push(var->name());
__ CallRuntime(Runtime::kThrowReferenceError, 1);
__ bind(&assign);
EmitStoreToStackLocalOrContextSlot(var, location);
} 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()) {
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 {
// Assignment to var or initializing assignment to let/const in harmony
// mode.
ASSERT(var->IsStackAllocated() || var->IsContextSlot());
MemOperand location = VarOperand(var, rcx);
if (generate_debug_code_ && op == Token::INIT_LET) {
......
This diff is collapsed.
......@@ -36,29 +36,29 @@ function testIntroduceGlobal() {
var source =
// Deleting 'x' removes the local const property.
"delete x;" +
// Initialization turns into assignment to global 'x'.
// Initialization redefines global 'x'.
"const x = 3; assertEquals(3, x);" +
// No constness of the global 'x'.
"x = 4; assertEquals(4, x);";
// Test constness of the global 'x'.
"x = 4; assertEquals(3, x);";
eval(source);
}
testIntroduceGlobal();
assertEquals(4, x);
assertEquals("undefined", typeof x);
function testAssignExistingGlobal() {
var source =
// Delete 'x' to remove the local const property.
"delete x;" +
// Initialization turns into assignment to global 'x'.
// Initialization redefines global 'x'.
"const x = 5; assertEquals(5, x);" +
// No constness of the global 'x'.
"x = 6; assertEquals(6, x);";
// Test constness of the global 'x'.
"x = 6; assertEquals(5, x);";
eval(source);
}
testAssignExistingGlobal();
assertEquals(6, x);
assertEquals("undefined", typeof x);
function testAssignmentArgument(x) {
function local() {
......@@ -66,7 +66,7 @@ function testAssignmentArgument(x) {
eval(source);
}
local();
assertEquals(7, x);
assertEquals("undefined", typeof x);
}
for (var i = 0; i < 5; i++) {
......@@ -74,17 +74,18 @@ for (var i = 0; i < 5; i++) {
}
%OptimizeFunctionOnNextCall(testAssignmentArgument);
testAssignmentArgument();
assertEquals(6, x);
assertEquals("undefined", typeof x);
__defineSetter__('x', function() { throw 42; });
function testAssignGlobalThrows() {
// Initialization turns into assignment to global 'x' which
// throws an exception.
var source = "delete x; const x = 8";
var finished = false;
function testRedefineGlobal() {
// Initialization redefines global 'x'.
var source = "delete x; const x = 8; finished = true;";
eval(source);
}
assertThrows("testAssignGlobalThrows()");
testRedefineGlobal();
assertTrue(finished);
function testInitFastCaseExtension() {
var source = "const x = 9; assertEquals(9, x); x = 10; assertEquals(9, x)";
......@@ -111,7 +112,7 @@ function testAssignSurroundingContextSlot() {
eval(source);
}
local();
assertEquals(13, x);
assertEquals(12, x);
}
testAssignSurroundingContextSlot();
......@@ -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) {
try {
// Use a with-statement to force the system to do dynamic
......@@ -98,8 +67,6 @@ function TestAll(expected,s,opt_e) {
var msg = s;
if (opt_e) { e = opt_e; msg += "; " + opt_e; }
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 + "'");
}
......@@ -112,7 +79,7 @@ function TestConflict(def0, def1) {
// Eval first definition.
TestAll("TypeError", 'eval("' + def0 +'"); ' + def1);
// Eval second definition.
TestAll("TypeError", def0 + '; eval("' + def1 + '")');
TestAll("TypeError", def0 + '; eval("' + def1 +'")');
// Eval both definitions separately.
TestAll("TypeError", 'eval("' + def0 +'"); eval("' + def1 + '")');
}
......@@ -234,47 +201,26 @@ var undefined = 1; // Should be silently ignored.
assertEquals(original_undef, undefined, "undefined got overwritten");
undefined = original_undef;
var a; const a; const a = 1;
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;
const e = 1; eval('var e = 2');
assertEquals(1, e, "e has wrong value");
const f = 1; const f;
assertEquals(1, f, "f 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");
const h; eval('var h = 1');
assertEquals(undefined, h, "h has wrong value");
eval("Object.defineProperty(this, 'i', { writable: true });"
+ "const i = 7;"
+ "assertEquals(7, i, \"i has wrong value\");");
var global = this;
assertThrows(function() {
Object.defineProperty(global, 'j', { writable: true })
}, TypeError);
const j = 2; // This is what makes the function above throw, because the
// const declaration gets hoisted and makes the property non-configurable.
Object.defineProperty(global, 'j', { value: 100, writable: true });
assertEquals(100, j);
// The const declaration stays configurable, so the declaration above goes
// through even though the const declaration is hoisted above.
const j = 2;
assertEquals(2, j, "j has wrong value");
var k = 1; const k;
// You could argue about the expected result here. For now, the winning
// argument is that "const k;" is equivalent to "const k = undefined;".
assertEquals(undefined, k, "k has wrong value");
var k = 1;
try { eval('const k'); } catch(e) { }
assertEquals(1, 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);
assertEquals(0, b);
try { eval("var b = 1"); } catch (e) { caught++; assertTrue(e instanceof TypeError); }
assertEquals(0, b);
assertEquals(0, caught);
eval("var c");
try { eval("const c"); } catch (e) { caught++; assertTrue(e instanceof TypeError); }
assertTrue(typeof c == 'undefined');
assertEquals(1, caught);
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");
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); }
assertEquals(1, d);
assertEquals(0, caught);
assertEquals(0, d);
assertEquals(4, caught);
......@@ -25,8 +25,6 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --es52_globals
var setter_value = 0;
this.__defineSetter__("a", function(v) { setter_value = v; });
......@@ -35,8 +33,9 @@ assertEquals(1, setter_value);
assertFalse("value" in Object.getOwnPropertyDescriptor(this, "a"));
eval("with({}) { eval('var a = 2') }");
assertEquals(2, setter_value);
assertTrue("get" 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
// not call setters for them.
......@@ -47,10 +46,8 @@ assertTrue("value" in Object.getOwnPropertyDescriptor(this, "a"));
this.__defineSetter__("b", function(v) { setter_value = v; });
try {
eval("const b = 3");
} catch(e) {
assertUnreachable();
}
assertEquals(3, setter_value);
} catch(e) { }
assertEquals(2, setter_value);
try {
eval("with({}) { eval('const b = 23') }");
......
......@@ -37,4 +37,4 @@ try {
assertTrue(e instanceof TypeError);
caught = true;
}
assertFalse(caught);
assertTrue(caught);
......@@ -2,6 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
__defineSetter__('x', function() { });
Object.defineProperty(this, 'x', {set: function() { }});
Object.freeze(this);
eval('const x = 1');
eval('"use strict"; x = 20;');
......@@ -642,10 +642,6 @@
# We do not correctly handle assignments within "with"
'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 #####################
'ecma/extensions/15.1.2.1-1': [FAIL_OK],
......
......@@ -49,7 +49,7 @@ EXPAND_MACROS = [
# to parse them!
EXPECTED_FUNCTION_COUNT = 417
EXPECTED_FUZZABLE_COUNT = 332
EXPECTED_CCTEST_COUNT = 6
EXPECTED_CCTEST_COUNT = 9
EXPECTED_UNKNOWN_COUNT = 4
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