Revision: 9764
Author: [email protected]
Date: Tue Oct 25 01:33:08 2011
Log: Block scoped const variables.
This implements block scoped 'const' declared variables in harmony mode.
They
have a temporal dead zone semantics similar to 'let' bindings, i.e.
accessing
uninitialized 'const' bindings in throws a ReferenceError.
As for 'let' bindings, the semantics of 'const' bindings in global scope is
not
correctly implemented yet. Furthermore assignments to 'const's are silently
ignored. Another CL will introduce treatment of those assignments as early
errors.
Review URL: http://codereview.chromium.org/7992005
http://code.google.com/p/v8/source/detail?r=9764
Modified:
/branches/bleeding_edge/src/arm/full-codegen-arm.cc
/branches/bleeding_edge/src/ast.h
/branches/bleeding_edge/src/contexts.cc
/branches/bleeding_edge/src/contexts.h
/branches/bleeding_edge/src/full-codegen.cc
/branches/bleeding_edge/src/hydrogen.cc
/branches/bleeding_edge/src/ia32/full-codegen-ia32.cc
/branches/bleeding_edge/src/messages.js
/branches/bleeding_edge/src/objects.h
/branches/bleeding_edge/src/parser.cc
/branches/bleeding_edge/src/parser.h
/branches/bleeding_edge/src/preparser.cc
/branches/bleeding_edge/src/runtime.cc
/branches/bleeding_edge/src/scopeinfo.cc
/branches/bleeding_edge/src/scopes.cc
/branches/bleeding_edge/src/scopes.h
/branches/bleeding_edge/src/token.h
/branches/bleeding_edge/src/v8globals.h
/branches/bleeding_edge/src/variables.cc
/branches/bleeding_edge/src/variables.h
/branches/bleeding_edge/src/x64/full-codegen-x64.cc
/branches/bleeding_edge/test/mjsunit/harmony/block-conflicts.js
/branches/bleeding_edge/test/mjsunit/harmony/block-let-declaration.js
/branches/bleeding_edge/test/mjsunit/harmony/block-let-semantics.js
/branches/bleeding_edge/test/mjsunit/harmony/block-scoping.js
=======================================
--- /branches/bleeding_edge/src/arm/full-codegen-arm.cc Mon Oct 24 08:56:18
2011
+++ /branches/bleeding_edge/src/arm/full-codegen-arm.cc Tue Oct 25 01:33:08
2011
@@ -269,7 +269,10 @@
// constant.
if (scope()->is_function_scope() && scope()->function() != NULL) {
int ignored = 0;
- EmitDeclaration(scope()->function(), CONST, NULL, &ignored);
+ VariableProxy* proxy = scope()->function();
+ ASSERT(proxy->var()->mode() == CONST ||
+ proxy->var()->mode() == CONST_HARMONY);
+ EmitDeclaration(proxy, proxy->var()->mode(), NULL, &ignored);
}
VisitDeclarations(scope()->declarations());
}
@@ -718,6 +721,8 @@
// need to "declare" it at runtime to make sure it actually exists in the
// local context.
Variable* variable = proxy->var();
+ bool binding_needs_init =
+ mode == CONST || mode == CONST_HARMONY || mode == LET;
switch (variable->location()) {
case Variable::UNALLOCATED:
++(*global_count);
@@ -729,7 +734,7 @@
Comment cmnt(masm_, "[ Declaration");
VisitForAccumulatorValue(function);
__ str(result_register(), StackOperand(variable));
- } else if (mode == CONST || mode == LET) {
+ } else if (binding_needs_init) {
Comment cmnt(masm_, "[ Declaration");
__ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
__ str(ip, StackOperand(variable));
@@ -763,7 +768,7 @@
EMIT_REMEMBERED_SET,
OMIT_SMI_CHECK);
PrepareForBailoutForId(proxy->id(), NO_REGISTERS);
- } else if (mode == CONST || mode == LET) {
+ } else if (binding_needs_init) {
Comment cmnt(masm_, "[ Declaration");
__ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
__ str(ip, ContextOperand(cp, variable->index()));
@@ -775,9 +780,13 @@
case Variable::LOOKUP: {
Comment cmnt(masm_, "[ Declaration");
__ mov(r2, Operand(variable->name()));
- // Declaration nodes are always introduced in one of three modes.
- ASSERT(mode == VAR || mode == CONST || mode == LET);
- PropertyAttributes attr = (mode == CONST) ? READ_ONLY : NONE;
+ // Declaration nodes are always introduced in one of four modes.
+ ASSERT(mode == VAR ||
+ mode == CONST ||
+ mode == CONST_HARMONY ||
+ mode == LET);
+ PropertyAttributes attr = (mode == CONST || mode == CONST_HARMONY)
+ ? READ_ONLY : NONE;
__ mov(r1, Operand(Smi::FromInt(attr)));
// Push initial value, if any.
// Note: For variables we must not push an initial value (such as
@@ -787,7 +796,7 @@
__ Push(cp, r2, r1);
// Push initial value for function declaration.
VisitForStackValue(function);
- } else if (mode == CONST || mode == LET) {
+ } else if (binding_needs_init) {
__ LoadRoot(r0, Heap::kTheHoleValueRootIndex);
__ Push(cp, r2, r1, r0);
} else {
@@ -1242,11 +1251,12 @@
Variable* local = var->local_if_not_shadowed();
__ ldr(r0, ContextSlotOperandCheckExtensions(local, slow));
if (local->mode() == CONST ||
+ local->mode() == CONST_HARMONY ||
local->mode() == LET) {
__ CompareRoot(r0, Heap::kTheHoleValueRootIndex);
if (local->mode() == CONST) {
__ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq);
- } else { // LET
+ } else { // LET || CONST_HARMONY
__ b(ne, done);
__ mov(r0, Operand(var->name()));
__ push(r0);
@@ -1284,13 +1294,15 @@
Comment cmnt(masm_, var->IsContextSlot()
? "Context variable"
: "Stack variable");
- if (var->mode() != LET && var->mode() != CONST) {
+ if (!var->binding_needs_init()) {
context()->Plug(var);
} else {
// Let and const need a read barrier.
GetVar(r0, var);
__ CompareRoot(r0, Heap::kTheHoleValueRootIndex);
- if (var->mode() == LET) {
+ if (var->mode() == LET || var->mode() == CONST_HARMONY) {
+ // Throw a reference error when using an uninitialized let/const
+ // binding in harmony mode.
Label done;
__ b(ne, &done);
__ mov(r0, Operand(var->name()));
@@ -1298,6 +1310,8 @@
__ CallRuntime(Runtime::kThrowReferenceError, 1);
__ bind(&done);
} else {
+ // Uninitalized const bindings outside of harmony mode are
unholed.
+ ASSERT(var->mode() == CONST);
__ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq);
}
context()->Plug(r0);
@@ -1965,8 +1979,9 @@
}
}
- } else if (var->mode() != CONST) {
- // Assignment to var or initializing assignment to let.
+ } 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()) {
MemOperand location = VarOperand(var, r1);
if (FLAG_debug_code && op == Token::INIT_LET) {
=======================================
--- /branches/bleeding_edge/src/ast.h Mon Oct 24 00:47:22 2011
+++ /branches/bleeding_edge/src/ast.h Tue Oct 25 01:33:08 2011
@@ -405,7 +405,10 @@
mode_(mode),
fun_(fun),
scope_(scope) {
- ASSERT(mode == VAR || mode == CONST || mode == LET);
+ ASSERT(mode == VAR ||
+ mode == CONST ||
+ mode == CONST_HARMONY ||
+ mode == LET);
// At the moment there are no "const functions"'s in JavaScript...
ASSERT(fun == NULL || mode == VAR || mode == LET);
}
=======================================
--- /branches/bleeding_edge/src/contexts.cc Mon Oct 17 02:29:37 2011
+++ /branches/bleeding_edge/src/contexts.cc Tue Oct 25 01:33:08 2011
@@ -174,6 +174,10 @@
*attributes = READ_ONLY;
*binding_flags = IMMUTABLE_CHECK_INITIALIZED;
break;
+ case CONST_HARMONY:
+ *attributes = READ_ONLY;
+ *binding_flags = IMMUTABLE_CHECK_INITIALIZED_HARMONY;
+ break;
case DYNAMIC:
case DYNAMIC_GLOBAL:
case DYNAMIC_LOCAL:
@@ -187,7 +191,8 @@
// Check the slot corresponding to the intermediate context holding
// only the function name variable.
if (follow_context_chain && context->IsFunctionContext()) {
- int function_index = scope_info->FunctionContextSlotIndex(*name);
+ VariableMode mode;
+ int function_index = scope_info->FunctionContextSlotIndex(*name,
&mode);
if (function_index >= 0) {
if (FLAG_trace_contexts) {
PrintF("=> found intermediate function in context slot %d\n",
@@ -195,7 +200,9 @@
}
*index = function_index;
*attributes = READ_ONLY;
- *binding_flags = IMMUTABLE_IS_INITIALIZED;
+ ASSERT(mode == CONST || mode == CONST_HARMONY);
+ *binding_flags = (mode == CONST)
+ ? IMMUTABLE_IS_INITIALIZED :
IMMUTABLE_IS_INITIALIZED_HARMONY;
return context;
}
}
@@ -255,7 +262,7 @@
if (param_index >= 0) return false;
// Check context only holding the function name variable.
- index = scope_info->FunctionContextSlotIndex(*name);
+ index = scope_info->FunctionContextSlotIndex(*name, NULL);
if (index >= 0) return false;
context = context->previous();
}
=======================================
--- /branches/bleeding_edge/src/contexts.h Mon Oct 24 08:56:18 2011
+++ /branches/bleeding_edge/src/contexts.h Tue Oct 25 01:33:08 2011
@@ -46,24 +46,43 @@
// ES5 10.2 defines lexical environments with mutable and immutable
bindings.
// Immutable bindings have two states, initialized and uninitialized, and
-// their state is changed by the InitializeImmutableBinding method.
+// their state is changed by the InitializeImmutableBinding method. The
+// BindingFlags enum represents information if a binding has definitely
been
+// initialized. A mutable binding does not need to be checked and thus has
+// the BindingFlag MUTABLE_IS_INITIALIZED.
+//
+// There are two possibilities for immutable bindings
+// * 'const' declared variables. They are initialized when evaluating the
+// corresponding declaration statement. They need to be checked for
being
+// initialized and thus get the flag IMMUTABLE_CHECK_INITIALIZED.
+// * The function name of a named function literal. The binding is
immediately
+// initialized when entering the function and thus does not need to be
+// checked. it gets the BindingFlag IMMUTABLE_IS_INITIALIZED.
+// Accessing an uninitialized binding produces the undefined value.
//
// The harmony proposal for block scoped bindings also introduces the
-// uninitialized state for mutable bindings. A 'let' declared variable
-// is a mutable binding that is created uninitalized upon activation of its
-// lexical environment and it is initialized when evaluating its
declaration
-// statement. Var declared variables are mutable bindings that are
-// immediately initialized upon creation. The BindingFlags enum represents
-// information if a binding has definitely been initialized. 'const'
declared
-// variables are created as uninitialized immutable bindings.
-
-// In harmony mode accessing an uninitialized binding produces a reference
-// error.
+// uninitialized state for mutable bindings.
+// * A 'let' declared variable. They are initialized when evaluating the
+// corresponding declaration statement. They need to be checked for
being
+// initialized and thus get the flag MUTABLE_CHECK_INITIALIZED.
+// * A 'var' declared variable. It is initialized immediately upon
creation
+// and thus doesn't need to be checked. It gets the flag
+// MUTABLE_IS_INITIALIZED.
+// * Catch bound variables, function parameters and variables introduced
by
+// function declarations are initialized immediately and do not need to
be
+// checked. Thus they get the flag MUTABLE_IS_INITIALIZED.
+// Immutable bindings in harmony mode get the _HARMONY flag variants.
Accessing
+// an uninitialized binding produces a reference error.
+//
+// In V8 uninitialized bindings are set to the hole value upon creation
and set
+// to a different value upon initialization.
enum BindingFlags {
MUTABLE_IS_INITIALIZED,
MUTABLE_CHECK_INITIALIZED,
IMMUTABLE_IS_INITIALIZED,
IMMUTABLE_CHECK_INITIALIZED,
+ IMMUTABLE_IS_INITIALIZED_HARMONY,
+ IMMUTABLE_CHECK_INITIALIZED_HARMONY,
MISSING_BINDING
};
=======================================
--- /branches/bleeding_edge/src/full-codegen.cc Mon Oct 24 00:47:22 2011
+++ /branches/bleeding_edge/src/full-codegen.cc Tue Oct 25 01:33:08 2011
@@ -521,8 +521,8 @@
if (var->IsUnallocated()) {
array->set(j++, *(var->name()));
if (decl->fun() == NULL) {
- if (var->mode() == CONST) {
- // In case this is const property use the hole.
+ if (var->binding_needs_init()) {
+ // In case this binding needs initialization use the hole.
array->set_the_hole(j++);
} else {
array->set_undefined(j++);
=======================================
--- /branches/bleeding_edge/src/hydrogen.cc Mon Oct 24 06:53:08 2011
+++ /branches/bleeding_edge/src/hydrogen.cc Tue Oct 25 01:33:08 2011
@@ -6048,7 +6048,9 @@
void HGraphBuilder::HandleDeclaration(VariableProxy* proxy,
VariableMode mode,
FunctionLiteral* function) {
- if (mode == LET) return Bailout("unsupported let declaration");
+ if (mode == LET || mode == CONST_HARMONY) {
+ return Bailout("unsupported harmony declaration");
+ }
Variable* var = proxy->var();
switch (var->location()) {
case Variable::UNALLOCATED:
=======================================
--- /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Mon Oct 24
08:56:18 2011
+++ /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Tue Oct 25
01:33:08 2011
@@ -266,7 +266,10 @@
// constant.
if (scope()->is_function_scope() && scope()->function() != NULL) {
int ignored = 0;
- EmitDeclaration(scope()->function(), CONST, NULL, &ignored);
+ VariableProxy* proxy = scope()->function();
+ ASSERT(proxy->var()->mode() == CONST ||
+ proxy->var()->mode() == CONST_HARMONY);
+ EmitDeclaration(proxy, proxy->var()->mode(), NULL, &ignored);
}
VisitDeclarations(scope()->declarations());
}
@@ -711,6 +714,8 @@
// need to "declare" it at runtime to make sure it actually exists in the
// local context.
Variable* variable = proxy->var();
+ bool binding_needs_init =
+ mode == CONST || mode == CONST_HARMONY || mode == LET;
switch (variable->location()) {
case Variable::UNALLOCATED:
++(*global_count);
@@ -722,7 +727,7 @@
Comment cmnt(masm_, "[ Declaration");
VisitForAccumulatorValue(function);
__ mov(StackOperand(variable), result_register());
- } else if (mode == CONST || mode == LET) {
+ } else if (binding_needs_init) {
Comment cmnt(masm_, "[ Declaration");
__ mov(StackOperand(variable),
Immediate(isolate()->factory()->the_hole_value()));
@@ -754,7 +759,7 @@
EMIT_REMEMBERED_SET,
OMIT_SMI_CHECK);
PrepareForBailoutForId(proxy->id(), NO_REGISTERS);
- } else if (mode == CONST || mode == LET) {
+ } else if (binding_needs_init) {
Comment cmnt(masm_, "[ Declaration");
__ mov(ContextOperand(esi, variable->index()),
Immediate(isolate()->factory()->the_hole_value()));
@@ -767,9 +772,13 @@
Comment cmnt(masm_, "[ Declaration");
__ push(esi);
__ push(Immediate(variable->name()));
- // Declaration nodes are always introduced in one of three modes.
- ASSERT(mode == VAR || mode == CONST || mode == LET);
- PropertyAttributes attr = (mode == CONST) ? READ_ONLY : NONE;
+ // Declaration nodes are always introduced in one of four modes.
+ ASSERT(mode == VAR ||
+ mode == CONST ||
+ mode == CONST_HARMONY ||
+ mode == LET);
+ PropertyAttributes attr = (mode == CONST || mode == CONST_HARMONY)
+ ? READ_ONLY : NONE;
__ push(Immediate(Smi::FromInt(attr)));
// Push initial value, if any.
// Note: For variables we must not push an initial value (such as
@@ -778,7 +787,7 @@
increment_stack_height(3);
if (function != NULL) {
VisitForStackValue(function);
- } else if (mode == CONST || mode == LET) {
+ } else if (binding_needs_init) {
__ push(Immediate(isolate()->factory()->the_hole_value()));
increment_stack_height();
} else {
@@ -1226,12 +1235,13 @@
Variable* local = var->local_if_not_shadowed();
__ mov(eax, ContextSlotOperandCheckExtensions(local, slow));
if (local->mode() == CONST ||
+ local->mode() == CONST_HARMONY ||
local->mode() == LET) {
__ cmp(eax, isolate()->factory()->the_hole_value());
__ j(not_equal, done);
if (local->mode() == CONST) {
__ mov(eax, isolate()->factory()->undefined_value());
- } else { // LET
+ } else { // LET || CONST_HARMONY
__ push(Immediate(var->name()));
__ CallRuntime(Runtime::kThrowReferenceError, 1);
}
@@ -1267,7 +1277,7 @@
Comment cmnt(masm_, var->IsContextSlot()
? "Context variable"
: "Stack variable");
- if (var->mode() != LET && var->mode() != CONST) {
+ if (!var->binding_needs_init()) {
context()->Plug(var);
} else {
// Let and const need a read barrier.
@@ -1275,10 +1285,14 @@
GetVar(eax, var);
__ cmp(eax, isolate()->factory()->the_hole_value());
__ j(not_equal, &done, Label::kNear);
- if (var->mode() == LET) {
+ if (var->mode() == LET || var->mode() == CONST_HARMONY) {
+ // Throw a reference error when using an uninitialized let/const
+ // binding in harmony mode.
__ push(Immediate(var->name()));
__ CallRuntime(Runtime::kThrowReferenceError, 1);
- } else { // CONST
+ } else {
+ // Uninitalized const bindings outside of harmony mode are
unholed.
+ ASSERT(var->mode() == CONST);
__ mov(eax, isolate()->factory()->undefined_value());
}
__ bind(&done);
@@ -1961,8 +1975,9 @@
}
}
- } else if (var->mode() != CONST) {
- // Assignment to var or initializing assignment to let.
+ } 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()) {
MemOperand location = VarOperand(var, ecx);
if (FLAG_debug_code && op == Token::INIT_LET) {
=======================================
--- /branches/bleeding_edge/src/messages.js Mon Oct 24 09:25:30 2011
+++ /branches/bleeding_edge/src/messages.js Tue Oct 25 01:33:08 2011
@@ -241,6 +241,7 @@
"strict_poison_pill", ["'caller', 'callee',
and 'arguments' properties may not be accessed on strict mode functions or
the arguments objects for calls to them"],
"strict_caller", ["Illegal access to a strict mode
caller function."],
"unprotected_let", ["Illegal let declaration in
unprotected statement context."],
+ "unprotected_const", ["Illegal const declaration in
unprotected statement context."],
"cant_prevent_ext_external_array_elements", ["Cannot prevent
extension of an object with external array elements"],
"redef_external_array_element", ["Cannot redefine a property of an
object with external array elements"],
];
=======================================
--- /branches/bleeding_edge/src/objects.h Mon Oct 24 08:56:18 2011
+++ /branches/bleeding_edge/src/objects.h Tue Oct 25 01:33:08 2011
@@ -3113,7 +3113,7 @@
// function context slot index if the function name is present (named
// function expressions, only), otherwise returns a value < 0. The name
// must be a symbol (canonicalized).
- int FunctionContextSlotIndex(String* name);
+ int FunctionContextSlotIndex(String* name, VariableMode* mode);
static Handle<SerializedScopeInfo> Create(Scope* scope);
=======================================
--- /branches/bleeding_edge/src/parser.cc Mon Oct 24 00:47:22 2011
+++ /branches/bleeding_edge/src/parser.cc Tue Oct 25 01:33:08 2011
@@ -1138,14 +1138,14 @@
// In harmony mode we allow additionally the following productions
// SourceElement:
// LetDeclaration
+ // ConstDeclaration
if (peek() == Token::FUNCTION) {
return ParseFunctionDeclaration(ok);
- } else if (peek() == Token::LET) {
+ } else if (peek() == Token::LET || peek() == Token::CONST) {
return ParseVariableStatement(kSourceElement, ok);
- } else {
- return ParseStatement(labels, ok);
- }
+ }
+ return ParseStatement(labels, ok);
}
@@ -1363,6 +1363,10 @@
// If we are inside a function, a declaration of a var/const variable is
a
// truly local variable, and the scope of the variable is always the
function
// scope.
+ // Let/const variables in harmony mode are always added to the
immediately
+ // enclosing scope.
+ Scope* declaration_scope = (mode == LET || mode == CONST_HARMONY)
+ ? top_scope_ : top_scope_->DeclarationScope();
// If a function scope exists, then we can statically declare this
// variable and also set its mode. In any case, a Declaration node
@@ -1372,9 +1376,8 @@
// to the calling function context.
// Similarly, strict mode eval scope does not leak variable declarations
to
// the caller's scope so we declare all locals, too.
-
- Scope* declaration_scope = mode == LET ? top_scope_
- : top_scope_->DeclarationScope();
+ // Also for block scoped let/const bindings the variable can be
+ // statically declared.
if (declaration_scope->is_function_scope() ||
declaration_scope->is_strict_mode_eval_scope() ||
declaration_scope->is_block_scope()) {
@@ -1399,6 +1402,7 @@
// We only have vars, consts and lets in declarations.
ASSERT(var->mode() == VAR ||
var->mode() == CONST ||
+ var->mode() == CONST_HARMONY ||
var->mode() == LET);
if (harmony_scoping_) {
// In harmony mode we treat re-declarations as early errors. See
@@ -1410,8 +1414,8 @@
*ok = false;
return NULL;
}
- const char* type = (var->mode() == VAR) ? "var" :
- (var->mode() == CONST) ? "const" : "let";
+ const char* type = (var->mode() == VAR)
+ ? "var" : var->is_const_mode() ? "const" : "let";
Handle<String> type_string =
isolate()->factory()->NewStringFromUtf8(CStrVector(type),
TENURED);
Expression* expression =
@@ -1444,7 +1448,8 @@
new(zone()) Declaration(proxy, mode, fun, top_scope_));
// For global const variables we bind the proxy to a variable.
- if (mode == CONST && declaration_scope->is_global_scope()) {
+ if ((mode == CONST || mode == CONST_HARMONY) &&
+ declaration_scope->is_global_scope()) {
ASSERT(resolve); // should be set by all callers
Variable::Kind kind = Variable::NORMAL;
var = new(zone()) Variable(declaration_scope, name, CONST, true, kind);
@@ -1651,8 +1656,18 @@
Handle<String>* out,
bool* ok) {
// VariableDeclarations ::
- // ('var' | 'const') (Identifier ('=' AssignmentExpression)?)+[',']
-
+ // ('var' | 'const' | 'let') (Identifier ('='
AssignmentExpression)?)+[',']
+ //
+ // The ES6 Draft Rev3 specifies the following grammar for const
declarations
+ //
+ // ConstDeclaration ::
+ // const ConstBinding (',' ConstBinding)* ';'
+ // ConstBinding ::
+ // Identifier '=' AssignmentExpression
+ //
+ // TODO(ES6):
+ // ConstBinding ::
+ // BindingPattern '=' AssignmentExpression
VariableMode mode = VAR;
// True if the binding needs initialization. 'let' and 'const' declared
// bindings are created uninitialized by their declaration nodes and
@@ -1665,19 +1680,32 @@
Consume(Token::VAR);
} else if (peek() == Token::CONST) {
Consume(Token::CONST);
- if (top_scope_->is_strict_mode()) {
+ if (harmony_scoping_) {
+ if (var_context != kSourceElement &&
+ var_context != kForStatement) {
+ // In harmony mode 'const' declarations are only allowed in source
+ // element positions.
+ ReportMessage("unprotected_const", Vector<const char*>::empty());
+ *ok = false;
+ return NULL;
+ }
+ mode = CONST_HARMONY;
+ init_op = Token::INIT_CONST_HARMONY;
+ } else if (top_scope_->is_strict_mode()) {
ReportMessage("strict_const", Vector<const char*>::empty());
*ok = false;
return NULL;
- }
- mode = CONST;
+ } else {
+ mode = CONST;
+ init_op = Token::INIT_CONST;
+ }
is_const = true;
needs_init = true;
- init_op = Token::INIT_CONST;
} else if (peek() == Token::LET) {
Consume(Token::LET);
if (var_context != kSourceElement &&
var_context != kForStatement) {
+ // Let declarations are only allowed in source element positions.
ASSERT(var_context == kStatement);
ReportMessage("unprotected_let", Vector<const char*>::empty());
*ok = false;
@@ -1690,7 +1718,7 @@
UNREACHABLE(); // by current callers
}
- Scope* declaration_scope = (mode == LET)
+ Scope* declaration_scope = (mode == LET || mode == CONST_HARMONY)
? top_scope_ : top_scope_->DeclarationScope();
// The scope of a var/const declared variable anywhere inside a function
// is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). Thus we can
@@ -1735,8 +1763,10 @@
// If we have a const declaration, in an inner scope, the proxy is
always
// bound to the declared variable (independent of possibly surrounding
with
// statements).
- Declare(name, mode, NULL, is_const /* always bound for CONST! */,
- CHECK_OK);
+ // For let/const declarations in harmony mode, we can also immediately
+ // pre-resolve the proxy because it resides in the same scope as the
+ // declaration.
+ Declare(name, mode, NULL, mode != VAR, CHECK_OK);
nvars++;
if (declaration_scope->num_var_or_const() > kMaxNumFunctionLocals) {
ReportMessageAt(scanner().location(), "too_many_variables",
@@ -1775,7 +1805,8 @@
Scope* initialization_scope = is_const ? declaration_scope :
top_scope_;
Expression* value = NULL;
int position = -1;
- if (peek() == Token::ASSIGN) {
+ // Harmony consts have non-optional initializers.
+ if (peek() == Token::ASSIGN || mode == CONST_HARMONY) {
Expect(Token::ASSIGN, CHECK_OK);
position = scanner().location().beg_pos;
value = ParseAssignmentExpression(var_context != kForStatement,
CHECK_OK);
@@ -1814,7 +1845,6 @@
// declaration statement has been executed. This is important in
// browsers where the global object (window) has lots of
// properties defined in prototype objects.
-
if (initialization_scope->is_global_scope()) {
// Compute the arguments for the runtime call.
ZoneList<Expression*>* arguments = new(zone())
ZoneList<Expression*>(3);
@@ -1876,9 +1906,9 @@
// dynamically looked-up variables and constants (the start context
// for constant lookups is always the function context, while it is
// the top context for var declared variables). Sigh...
- // For 'let' declared variables the initialization is in the same scope
- // as the declaration. Thus dynamic lookups are unnecessary even if the
- // block scope is inside a with.
+ // For 'let' and 'const' declared variables in harmony mode the
+ // initialization is in the same scope as the declaration. Thus dynamic
+ // lookups are unnecessary even if the block scope is inside a with.
if (value != NULL) {
VariableProxy* proxy = initialization_scope->NewUnresolved(name);
Assignment* assignment =
@@ -3927,12 +3957,21 @@
// future we can change the AST to only refer to VariableProxies
// instead of Variables and Proxis as is the case now.
if (type == FunctionLiteral::NAMED_EXPRESSION) {
- Variable* fvar = top_scope_->DeclareFunctionVar(function_name);
+ VariableMode fvar_mode;
+ Token::Value fvar_init_op;
+ if (harmony_scoping_) {
+ fvar_mode = CONST_HARMONY;
+ fvar_init_op = Token::INIT_CONST_HARMONY;
+ } else {
+ fvar_mode = CONST;
+ fvar_init_op = Token::INIT_CONST;
+ }
+ Variable* fvar = top_scope_->DeclareFunctionVar(function_name,
fvar_mode);
VariableProxy* fproxy = top_scope_->NewUnresolved(function_name);
fproxy->BindTo(fvar);
body->Add(new(zone()) ExpressionStatement(
new(zone()) Assignment(isolate(),
- Token::INIT_CONST,
+ fvar_init_op,
fproxy,
new(zone()) ThisFunction(isolate()),
RelocInfo::kNoPosition)));
=======================================
--- /branches/bleeding_edge/src/parser.h Fri Oct 21 03:26:59 2011
+++ /branches/bleeding_edge/src/parser.h Tue Oct 25 01:33:08 2011
@@ -500,7 +500,6 @@
Statement* ParseFunctionDeclaration(bool* ok);
Statement* ParseNativeDeclaration(bool* ok);
Block* ParseBlock(ZoneStringList* labels, bool* ok);
- Block* ParseScopedBlock(ZoneStringList* labels, bool* ok);
Block* ParseVariableStatement(VariableDeclarationContext var_context,
bool* ok);
Block* ParseVariableDeclarations(VariableDeclarationContext var_context,
@@ -524,6 +523,9 @@
TryStatement* ParseTryStatement(bool* ok);
DebuggerStatement* ParseDebuggerStatement(bool* ok);
+ // Support for hamony block scoped bindings.
+ Block* ParseScopedBlock(ZoneStringList* labels, bool* ok);
+
Expression* ParseExpression(bool accept_IN, bool* ok);
Expression* ParseAssignmentExpression(bool accept_IN, bool* ok);
Expression* ParseConditionalExpression(bool accept_IN, bool* ok);
=======================================
--- /branches/bleeding_edge/src/preparser.cc Mon Oct 17 05:45:52 2011
+++ /branches/bleeding_edge/src/preparser.cc Tue Oct 25 01:33:08 2011
@@ -125,11 +125,13 @@
// In harmony mode we allow additionally the following productions
// SourceElement:
// LetDeclaration
+ // ConstDeclaration
switch (peek()) {
case i::Token::FUNCTION:
return ParseFunctionDeclaration(ok);
case i::Token::LET:
+ case i::Token::CONST:
return ParseVariableStatement(kSourceElement, ok);
default:
return ParseStatement(ok);
@@ -331,11 +333,32 @@
bool* ok) {
// VariableDeclarations ::
// ('var' | 'const') (Identifier ('=' AssignmentExpression)?)+[',']
-
+ //
+ // The ES6 Draft Rev3 specifies the following grammar for const
declarations
+ //
+ // ConstDeclaration ::
+ // const ConstBinding (',' ConstBinding)* ';'
+ // ConstBinding ::
+ // Identifier '=' AssignmentExpression
+ //
+ // TODO(ES6):
+ // ConstBinding ::
+ // BindingPattern '=' AssignmentExpression
+ bool require_initializer = false;
if (peek() == i::Token::VAR) {
Consume(i::Token::VAR);
} else if (peek() == i::Token::CONST) {
- if (strict_mode()) {
+ if (harmony_scoping_) {
+ if (var_context != kSourceElement &&
+ var_context != kForStatement) {
+ i::Scanner::Location location = scanner_->peek_location();
+ ReportMessageAt(location.beg_pos, location.end_pos,
+ "unprotected_const", NULL);
+ *ok = false;
+ return Statement::Default();
+ }
+ require_initializer = true;
+ } else if (strict_mode()) {
i::Scanner::Location location = scanner_->peek_location();
ReportMessageAt(location, "strict_const", NULL);
*ok = false;
@@ -374,7 +397,7 @@
return Statement::Default();
}
nvars++;
- if (peek() == i::Token::ASSIGN) {
+ if (peek() == i::Token::ASSIGN || require_initializer) {
Expect(i::Token::ASSIGN, CHECK_OK);
ParseAssignmentExpression(var_context != kForStatement, CHECK_OK);
if (decl_props != NULL) *decl_props = kHasInitializers;
=======================================
--- /branches/bleeding_edge/src/runtime.cc Mon Oct 24 08:56:18 2011
+++ /branches/bleeding_edge/src/runtime.cc Tue Oct 25 01:33:08 2011
@@ -8865,13 +8865,26 @@
Handle<Object> receiver = isolate->factory()->the_hole_value();
Object* value = Context::cast(*holder)->get(index);
// Check for uninitialized bindings.
- if (binding_flags == MUTABLE_CHECK_INITIALIZED && value->IsTheHole()) {
- Handle<Object> reference_error =
- isolate->factory()->NewReferenceError("not_defined",
- HandleVector(&name, 1));
- return MakePair(isolate->Throw(*reference_error), NULL);
- } else {
- return MakePair(Unhole(isolate->heap(), value, attributes),
*receiver);
+ switch (binding_flags) {
+ case MUTABLE_CHECK_INITIALIZED:
+ case IMMUTABLE_CHECK_INITIALIZED_HARMONY:
+ if (value->IsTheHole()) {
+ Handle<Object> reference_error =
+ isolate->factory()->NewReferenceError("not_defined",
+ HandleVector(&name,
1));
+ return MakePair(isolate->Throw(*reference_error), NULL);
+ }
+ // FALLTHROUGH
+ case MUTABLE_IS_INITIALIZED:
+ case IMMUTABLE_IS_INITIALIZED:
+ case IMMUTABLE_IS_INITIALIZED_HARMONY:
+ ASSERT(!value->IsTheHole());
+ return MakePair(value, *receiver);
+ case IMMUTABLE_CHECK_INITIALIZED:
+ return MakePair(Unhole(isolate->heap(), value, attributes),
*receiver);
+ case MISSING_BINDING:
+ UNREACHABLE();
+ return MakePair(NULL, NULL);
}
}
=======================================
--- /branches/bleeding_edge/src/scopeinfo.cc Fri Oct 21 03:26:59 2011
+++ /branches/bleeding_edge/src/scopeinfo.cc Tue Oct 25 01:33:08 2011
@@ -139,7 +139,7 @@
ASSERT(proxy->var()->index() - Context::MIN_CONTEXT_SLOTS ==
context_modes_.length());
context_slots_.Add(FACTORY->empty_symbol());
- context_modes_.Add(INTERNAL);
+ context_modes_.Add(proxy->var()->mode());
}
}
}
@@ -540,16 +540,24 @@
}
-int SerializedScopeInfo::FunctionContextSlotIndex(String* name) {
+int SerializedScopeInfo::FunctionContextSlotIndex(String* name,
+ VariableMode* mode) {
ASSERT(name->IsSymbol());
if (length() > 0) {
Object** p = data_start();
if (*p == name) {
p = ContextEntriesAddr();
int number_of_context_slots;
- ReadInt(p, &number_of_context_slots);
+ p = ReadInt(p, &number_of_context_slots);
ASSERT(number_of_context_slots != 0);
// The function context slot is the last entry.
+ if (mode != NULL) {
+ // Seek to context slot entry.
+ p += (number_of_context_slots - 1) * 2;
+ // Seek to mode.
+ ++p;
+ ReadInt(p, mode);
+ }
return number_of_context_slots + Context::MIN_CONTEXT_SLOTS - 1;
}
}
=======================================
--- /branches/bleeding_edge/src/scopes.cc Mon Oct 24 00:47:22 2011
+++ /branches/bleeding_edge/src/scopes.cc Tue Oct 25 01:33:08 2011
@@ -379,7 +379,7 @@
index = scope_info_->ParameterIndex(*name);
if (index < 0) {
// Check the function name.
- index = scope_info_->FunctionContextSlotIndex(*name);
+ index = scope_info_->FunctionContextSlotIndex(*name, NULL);
if (index < 0) return NULL;
}
}
@@ -402,10 +402,10 @@
}
-Variable* Scope::DeclareFunctionVar(Handle<String> name) {
+Variable* Scope::DeclareFunctionVar(Handle<String> name, VariableMode
mode) {
ASSERT(is_function_scope() && function_ == NULL);
Variable* function_var =
- new Variable(this, name, CONST, true, Variable::NORMAL);
+ new Variable(this, name, mode, true, Variable::NORMAL);
function_ = new(isolate_->zone()) VariableProxy(isolate_, function_var);
return function_var;
}
@@ -425,7 +425,10 @@
// This function handles VAR and CONST modes. DYNAMIC variables are
// introduces during variable allocation, INTERNAL variables are
allocated
// explicitly, and TEMPORARY variables are allocated via NewTemporary().
- ASSERT(mode == VAR || mode == CONST || mode == LET);
+ ASSERT(mode == VAR ||
+ mode == CONST ||
+ mode == CONST_HARMONY ||
+ mode == LET);
++num_var_or_const_;
return variables_.Declare(this, name, mode, true, Variable::NORMAL);
}
=======================================
--- /branches/bleeding_edge/src/scopes.h Mon Oct 24 00:47:22 2011
+++ /branches/bleeding_edge/src/scopes.h Tue Oct 25 01:33:08 2011
@@ -122,7 +122,7 @@
// Declare the function variable for a function literal. This variable
// is in an intermediate scope between this function scope and the the
// outer scope. Only possible for function scopes; at most one variable.
- Variable* DeclareFunctionVar(Handle<String> name);
+ Variable* DeclareFunctionVar(Handle<String> name, VariableMode mode);
// Declare a parameter in this scope. When there are duplicated
// parameters the rightmost one 'wins'. However, the implementation
=======================================
--- /branches/bleeding_edge/src/token.h Mon Sep 19 07:50:33 2011
+++ /branches/bleeding_edge/src/token.h Tue Oct 25 01:33:08 2011
@@ -73,6 +73,7 @@
T(INIT_VAR, "=init_var", 2) /* AST-use only. */ \
T(INIT_LET, "=init_let", 2) /* AST-use only. */ \
T(INIT_CONST, "=init_const", 2) /* AST-use only. */ \
+ T(INIT_CONST_HARMONY, "=init_const_harmony", 2) /* AST-use only. */ \
T(ASSIGN, "=", 2) \
T(ASSIGN_BIT_OR, "|=", 2) \
T(ASSIGN_BIT_XOR, "^=", 2) \
=======================================
--- /branches/bleeding_edge/src/v8globals.h Fri Oct 21 03:26:59 2011
+++ /branches/bleeding_edge/src/v8globals.h Tue Oct 25 01:33:08 2011
@@ -531,11 +531,13 @@
enum VariableMode {
// User declared variables:
- VAR, // declared via 'var', and 'function' declarations
-
- CONST, // declared via 'const' declarations
-
- LET, // declared via 'let' declarations
+ VAR, // declared via 'var', and 'function' declarations
+
+ CONST, // declared via 'const' declarations
+
+ CONST_HARMONY, // declared via 'const' declarations in harmony mode
+
+ LET, // declared via 'let' declarations
// Variables introduced by the compiler:
DYNAMIC, // always require dynamic lookup (we don't know
=======================================
--- /branches/bleeding_edge/src/variables.cc Tue Oct 11 01:41:19 2011
+++ /branches/bleeding_edge/src/variables.cc Tue Oct 25 01:33:08 2011
@@ -41,6 +41,7 @@
switch (mode) {
case VAR: return "VAR";
case CONST: return "CONST";
+ case CONST_HARMONY: return "CONST";
case LET: return "LET";
case DYNAMIC: return "DYNAMIC";
case DYNAMIC_GLOBAL: return "DYNAMIC_GLOBAL";
=======================================
--- /branches/bleeding_edge/src/variables.h Mon Oct 17 02:29:37 2011
+++ /branches/bleeding_edge/src/variables.h Tue Oct 25 01:33:08 2011
@@ -118,6 +118,15 @@
mode_ == DYNAMIC_GLOBAL ||
mode_ == DYNAMIC_LOCAL);
}
+ bool is_const_mode() const {
+ return (mode_ == CONST ||
+ mode_ == CONST_HARMONY);
+ }
+ bool binding_needs_init() const {
+ return (mode_ == LET ||
+ mode_ == CONST ||
+ mode_ == CONST_HARMONY);
+ }
bool is_global() const;
bool is_this() const { return kind_ == THIS; }
=======================================
--- /branches/bleeding_edge/src/x64/full-codegen-x64.cc Mon Oct 24 08:56:18
2011
+++ /branches/bleeding_edge/src/x64/full-codegen-x64.cc Tue Oct 25 01:33:08
2011
@@ -254,7 +254,10 @@
// constant.
if (scope()->is_function_scope() && scope()->function() != NULL) {
int ignored = 0;
- EmitDeclaration(scope()->function(), CONST, NULL, &ignored);
+ VariableProxy* proxy = scope()->function();
+ ASSERT(proxy->var()->mode() == CONST ||
+ proxy->var()->mode() == CONST_HARMONY);
+ EmitDeclaration(proxy, proxy->var()->mode(), NULL, &ignored);
}
VisitDeclarations(scope()->declarations());
}
@@ -684,6 +687,8 @@
// need to "declare" it at runtime to make sure it actually exists in the
// local context.
Variable* variable = proxy->var();
+ bool binding_needs_init =
+ mode == CONST || mode == CONST_HARMONY || mode == LET;
switch (variable->location()) {
case Variable::UNALLOCATED:
++(*global_count);
@@ -695,7 +700,7 @@
Comment cmnt(masm_, "[ Declaration");
VisitForAccumulatorValue(function);
__ movq(StackOperand(variable), result_register());
- } else if (mode == CONST || mode == LET) {
+ } else if (binding_needs_init) {
Comment cmnt(masm_, "[ Declaration");
__ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex);
__ movq(StackOperand(variable), kScratchRegister);
@@ -728,7 +733,7 @@
EMIT_REMEMBERED_SET,
OMIT_SMI_CHECK);
PrepareForBailoutForId(proxy->id(), NO_REGISTERS);
- } else if (mode == CONST || mode == LET) {
+ } else if (binding_needs_init) {
Comment cmnt(masm_, "[ Declaration");
__ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex);
__ movq(ContextOperand(rsi, variable->index()), kScratchRegister);
@@ -741,9 +746,13 @@
Comment cmnt(masm_, "[ Declaration");
__ push(rsi);
__ Push(variable->name());
- // Declaration nodes are always introduced in one of three modes.
- ASSERT(mode == VAR || mode == CONST || mode == LET);
- PropertyAttributes attr = (mode == CONST) ? READ_ONLY : NONE;
+ // Declaration nodes are always introduced in one of four modes.
+ ASSERT(mode == VAR ||
+ mode == CONST ||
+ mode == CONST_HARMONY ||
+ mode == LET);
+ PropertyAttributes attr =
+ (mode == CONST || mode == CONST_HARMONY) ? READ_ONLY : NONE;
__ Push(Smi::FromInt(attr));
// Push initial value, if any.
// Note: For variables we must not push an initial value (such as
@@ -751,7 +760,7 @@
// must not destroy the current value.
if (function != NULL) {
VisitForStackValue(function);
- } else if (mode == CONST || mode == LET) {
+ } else if (binding_needs_init) {
__ PushRoot(Heap::kTheHoleValueRootIndex);
} else {
__ Push(Smi::FromInt(0)); // Indicates no initial value.
@@ -1201,12 +1210,14 @@
} else if (var->mode() == DYNAMIC_LOCAL) {
Variable* local = var->local_if_not_shadowed();
__ movq(rax, ContextSlotOperandCheckExtensions(local, slow));
- if (local->mode() == CONST || local->mode() == LET) {
+ if (local->mode() == CONST ||
+ local->mode() == CONST_HARMONY ||
+ local->mode() == LET) {
__ CompareRoot(rax, Heap::kTheHoleValueRootIndex);
__ j(not_equal, done);
if (local->mode() == CONST) {
__ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
- } else { // LET
+ } else { // LET || CONST_HARMONY
__ Push(var->name());
__ CallRuntime(Runtime::kThrowReferenceError, 1);
}
@@ -1240,7 +1251,7 @@
case Variable::LOCAL:
case Variable::CONTEXT: {
Comment cmnt(masm_, var->IsContextSlot() ? "Context slot" : "Stack
slot");
- if (var->mode() != LET && var->mode() != CONST) {
+ if (!var->binding_needs_init()) {
context()->Plug(var);
} else {
// Let and const need a read barrier.
@@ -1248,10 +1259,14 @@
GetVar(rax, var);
__ CompareRoot(rax, Heap::kTheHoleValueRootIndex);
__ j(not_equal, &done, Label::kNear);
- if (var->mode() == LET) {
+ if (var->mode() == LET || var->mode() == CONST_HARMONY) {
+ // Throw a reference error when using an uninitialized let/const
+ // binding in harmony mode.
__ Push(var->name());
__ CallRuntime(Runtime::kThrowReferenceError, 1);
- } else { // CONST
+ } else {
+ // Uninitalized const bindings outside of harmony mode are
unholed.
+ ASSERT(var->mode() == CONST);
__ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
}
__ bind(&done);
@@ -1873,8 +1888,9 @@
}
}
- } else if (var->mode() != CONST) {
- // Assignment to var or initializing assignment to let.
+ } 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()) {
MemOperand location = VarOperand(var, rcx);
if (FLAG_debug_code && op == Token::INIT_LET) {
=======================================
--- /branches/bleeding_edge/test/mjsunit/harmony/block-conflicts.js Wed Oct
12 05:23:06 2011
+++ /branches/bleeding_edge/test/mjsunit/harmony/block-conflicts.js Tue Oct
25 01:33:08 2011
@@ -80,6 +80,11 @@
"let x = function() {}",
"let x, y",
"let y, x",
+ "const x = 0",
+ "const x = undefined",
+ "const x = function() {}",
+ "const x = 2, y = 3",
+ "const y = 4, x = 5",
];
var varbinds = [ "var x",
"var x = 0",
=======================================
--- /branches/bleeding_edge/test/mjsunit/harmony/block-let-declaration.js
Mon Oct 17 04:59:08 2011
+++ /branches/bleeding_edge/test/mjsunit/harmony/block-let-declaration.js
Tue Oct 25 01:33:08 2011
@@ -32,15 +32,18 @@
// Global
let x;
let y = 2;
+const z = 4;
// Block local
{
let y;
let x = 3;
+ const z = 5;
}
assertEquals(undefined, x);
assertEquals(2,y);
+assertEquals(4,z);
if (true) {
let y;
@@ -58,7 +61,7 @@
assertDoesNotThrow("(function(){" + str + "})()");
}
-// Test let declarations statement positions.
+// Test let declarations in statement positions.
TestLocalThrows("if (true) let x;", SyntaxError);
TestLocalThrows("if (true) {} else let x;", SyntaxError);
TestLocalThrows("do let x; while (false)", SyntaxError);
@@ -68,7 +71,32 @@
TestLocalThrows("switch (true) { case true: let x; }", SyntaxError);
TestLocalThrows("switch (true) { default: let x; }", SyntaxError);
-// Test var declarations statement positions.
+// Test const declarations with initialisers in statement positions.
+TestLocalThrows("if (true) const x = 1;", SyntaxError);
+TestLocalThrows("if (true) {} else const x = 1;", SyntaxError);
+TestLocalThrows("do const x = 1; while (false)", SyntaxError);
+TestLocalThrows("while (false) const x = 1;", SyntaxError);
+TestLocalThrows("label: const x = 1;", SyntaxError);
+TestLocalThrows("for (;false;) const x = 1;", SyntaxError);
+TestLocalThrows("switch (true) { case true: const x = 1; }", SyntaxError);
+TestLocalThrows("switch (true) { default: const x = 1; }", SyntaxError);
+
+// Test const declarations without initialisers.
+TestLocalThrows("const x;", SyntaxError);
+TestLocalThrows("const x = 1, y;", SyntaxError);
+TestLocalThrows("const x, y = 1;", SyntaxError);
+
+// Test const declarations without initialisers in statement positions.
+TestLocalThrows("if (true) const x;", SyntaxError);
+TestLocalThrows("if (true) {} else const x;", SyntaxError);
+TestLocalThrows("do const x; while (false)", SyntaxError);
+TestLocalThrows("while (false) const x;", SyntaxError);
+TestLocalThrows("label: const x;", SyntaxError);
+TestLocalThrows("for (;false;) const x;", SyntaxError);
+TestLocalThrows("switch (true) { case true: const x; }", SyntaxError);
+TestLocalThrows("switch (true) { default: const x; }", SyntaxError);
+
+// Test var declarations in statement positions.
TestLocalDoesNotThrow("if (true) var x;");
TestLocalDoesNotThrow("if (true) {} else var x;");
TestLocalDoesNotThrow("do var x; while (false)");
=======================================
--- /branches/bleeding_edge/test/mjsunit/harmony/block-let-semantics.js Wed
Oct 12 05:23:06 2011
+++ /branches/bleeding_edge/test/mjsunit/harmony/block-let-semantics.js Tue
Oct 25 01:33:08 2011
@@ -61,6 +61,7 @@
TestAll('let x = x += 1');
TestAll('let x = x++');
TestAll('let x = ++x');
+TestAll('const x = x + 1');
// Use before initialization in prior statement.
TestAll('x + 1; let x;');
@@ -68,18 +69,21 @@
TestAll('x += 1; let x;');
TestAll('++x; let x;');
TestAll('x++; let x;');
+TestAll('let y = x; const x = 1;');
TestAll('f(); let x; function f() { return x + 1; }');
TestAll('f(); let x; function f() { x = 1; }');
TestAll('f(); let x; function f() { x += 1; }');
TestAll('f(); let x; function f() { ++x; }');
TestAll('f(); let x; function f() { x++; }');
+TestAll('f(); const x = 1; function f() { return x; }');
TestAll('f()(); let x; function f() { return function() { return x + 1; }
}');
TestAll('f()(); let x; function f() { return function() { x = 1; } }');
TestAll('f()(); let x; function f() { return function() { x += 1; } }');
TestAll('f()(); let x; function f() { return function() { ++x; } }');
TestAll('f()(); let x; function f() { return function() { x++; } }');
+TestAll('f()(); const x = 1; function f() { return function() { return x;
} }');
// Use before initialization with a dynamic lookup.
TestAll('eval("x + 1;"); let x;');
@@ -87,6 +91,7 @@
TestAll('eval("x += 1;"); let x;');
TestAll('eval("++x;"); let x;');
TestAll('eval("x++;"); let x;');
+TestAll('eval("x"); const x = 1;');
// Use before initialization with check for eval-shadowed bindings.
TestAll('function f() { eval("var y = 2;"); x + 1; }; f(); let x;');
@@ -139,10 +144,31 @@
function h() {
return b + c;
}
- let b = 3;
+ let c = 3;
}
assertEquals(5, n());
-}
+
+ {
+ o = i;
+ function i() {
+ return d;
+ }
+ let d = 4;
+ }
+ assertEquals(4, o());
+
+ try {
+ throw 5;
+ } catch(e) {
+ p = j;
+ function j() {
+ return e + f;
+ }
+ let f = 6;
+ }
+ assertEquals(11, p());
+}
+f2();
// Test that resolution of let bound variables works with scopes that call
eval.
function outer() {
=======================================
--- /branches/bleeding_edge/test/mjsunit/harmony/block-scoping.js Wed Oct
12 05:23:06 2011
+++ /branches/bleeding_edge/test/mjsunit/harmony/block-scoping.js Tue Oct
25 01:33:08 2011
@@ -44,12 +44,16 @@
function f2(one) {
var x = one + 1;
let y = one + 2;
+ const u = one + 4;
{
let z = one + 3;
+ const v = one + 5;
assertEquals(1, eval('one'));
assertEquals(2, eval('x'));
assertEquals(3, eval('y'));
assertEquals(4, eval('z'));
+ assertEquals(5, eval('u'));
+ assertEquals(6, eval('v'));
}
}
f2(1);
@@ -59,12 +63,17 @@
function f3(one) {
var x = one + 1;
let y = one + 2;
+ const u = one + 4;
{
let z = one + 3;
+ const v = one + 5;
assertEquals(1, one);
assertEquals(2, x);
assertEquals(3, y);
assertEquals(4, z);
+ assertEquals(5, u);
+ assertEquals(6, v);
+
}
}
f3(1);
@@ -74,13 +83,17 @@
function f4(one) {
var x = one + 1;
let y = one + 2;
+ const u = one + 4;
{
let z = one + 3;
+ const v = one + 5;
function f() {
assertEquals(1, eval('one'));
assertEquals(2, eval('x'));
assertEquals(3, eval('y'));
assertEquals(4, eval('z'));
+ assertEquals(5, eval('u'));
+ assertEquals(6, eval('v'));
};
}
}
@@ -91,13 +104,17 @@
function f5(one) {
var x = one + 1;
let y = one + 2;
+ const u = one + 4;
{
let z = one + 3;
+ const v = one + 5;
function f() {
assertEquals(1, one);
assertEquals(2, x);
assertEquals(3, y);
assertEquals(4, z);
+ assertEquals(5, u);
+ assertEquals(6, v);
};
}
}
@@ -107,8 +124,10 @@
// Return from block.
function f6() {
let x = 1;
+ const u = 3;
{
let y = 2;
+ const v = 4;
return x + y;
}
}
@@ -120,13 +139,26 @@
let b = 1;
var c = 1;
var d = 1;
- { // let variables shadowing argument, let and var variables
+ const e = 1;
+ { // let variables shadowing argument, let, const and var variables
let a = 2;
let b = 2;
let c = 2;
+ let e = 2;
assertEquals(2,a);
assertEquals(2,b);
assertEquals(2,c);
+ assertEquals(2,e);
+ }
+ { // const variables shadowing argument, let, const and var variables
+ const a = 2;
+ const b = 2;
+ const c = 2;
+ const e = 2;
+ assertEquals(2,a);
+ assertEquals(2,b);
+ assertEquals(2,c);
+ assertEquals(2,e);
}
try {
throw 'stuff1';
@@ -156,6 +188,12 @@
} catch (c) {
// catch variable shadowing var variable
assertEquals('stuff3',c);
+ {
+ // const variable shadowing catch variable
+ const c = 3;
+ assertEquals(3,c);
+ }
+ assertEquals('stuff3',c);
try {
throw 'stuff4';
} catch(c) {
@@ -178,14 +216,16 @@
c = 2;
}
assertEquals(1,c);
- (function(a,b,c) {
- // arguments shadowing argument, let and var variable
+ (function(a,b,c,e) {
+ // arguments shadowing argument, let, const and var variable
a = 2;
b = 2;
c = 2;
+ e = 2;
assertEquals(2,a);
assertEquals(2,b);
assertEquals(2,c);
+ assertEquals(2,e);
// var variable shadowing var variable
var d = 2;
})(1,1);
@@ -193,24 +233,30 @@
assertEquals(1,b);
assertEquals(1,c);
assertEquals(1,d);
+ assertEquals(1,e);
}
f7(1);
-// Ensure let variables are block local and var variables function local.
+// Ensure let and const variables are block local
+// and var variables function local.
function f8() {
var let_accessors = [];
var var_accessors = [];
+ var const_accessors = [];
for (var i = 0; i < 10; i++) {
let x = i;
var y = i;
+ const z = i;
let_accessors[i] = function() { return x; }
var_accessors[i] = function() { return y; }
+ const_accessors[i] = function() { return z; }
}
for (var j = 0; j < 10; j++) {
y = j + 10;
assertEquals(j, let_accessors[j]());
assertEquals(y, var_accessors[j]());
+ assertEquals(j, const_accessors[j]());
}
}
f8();
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev