switch (slot->type()) {
case Slot::PARAMETER:
case Slot::LOCAL:
- if (mode == Variable::CONST) {
- __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
- __ str(ip, MemOperand(fp, SlotOffset(slot)));
- } else if (function != NULL) {
+ if (function != NULL) {
VisitForAccumulatorValue(function);
__ str(result_register(), MemOperand(fp, SlotOffset(slot)));
+ } else if (mode == Variable::CONST || mode == Variable::LET) {
+ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+ __ str(ip, MemOperand(fp, SlotOffset(slot)));
}
break;
__ CompareRoot(r1, Heap::kCatchContextMapRootIndex);
__ Check(ne, "Declaration in catch context.");
}
- if (mode == Variable::CONST) {
- __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
- __ str(ip, ContextOperand(cp, slot->index()));
- // No write barrier since the_hole_value is in old space.
- } else if (function != NULL) {
+ if (function != NULL) {
VisitForAccumulatorValue(function);
__ str(result_register(), ContextOperand(cp, slot->index()));
int offset = Context::SlotOffset(slot->index());
// We know that we have written a function, which is not a smi.
__ mov(r1, Operand(cp));
__ RecordWrite(r1, Operand(offset), r2, result_register());
+ } else if (mode == Variable::CONST || mode == Variable::LET) {
+ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+ __ str(ip, ContextOperand(cp, slot->index()));
+ // No write barrier since the_hole_value is in old space.
}
break;
// Note: For variables we must not push an initial value (such as
// 'undefined') because we may have a (legal) redeclaration and we
// must not destroy the current value.
- if (mode == Variable::CONST) {
- __ LoadRoot(r0, Heap::kTheHoleValueRootIndex);
- __ Push(cp, r2, r1, r0);
- } else if (function != NULL) {
+ if (function != NULL) {
__ Push(cp, r2, r1);
// Push initial value for function declaration.
VisitForStackValue(function);
+ } else if (mode == Variable::CONST || mode == Variable::LET) {
+ __ LoadRoot(r0, Heap::kTheHoleValueRootIndex);
+ __ Push(cp, r2, r1, r0);
} else {
__ mov(r0, Operand(Smi::FromInt(0))); // No initial value!
__ Push(cp, r2, r1, r0);
__ cmp(r0, ip);
__ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq);
context()->Plug(r0);
+ } else if (var->mode() == Variable::LET) {
+ // Let bindings may be the hole value if they have not been initialized.
+ // Throw a type error in this case.
+ Label done;
+ MemOperand slot_operand = EmitSlotSearch(slot, r0);
+ __ ldr(r0, slot_operand);
+ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+ __ cmp(r0, ip);
+ __ b(ne, &done);
+ __ mov(r0, Operand(var->name()));
+ __ push(r0);
+ __ CallRuntime(Runtime::kThrowReferenceError, 1);
+ __ bind(&done);
+ context()->Plug(r0);
} else {
context()->Plug(slot);
}
}
__ bind(&skip);
+ } else if (var->mode() == Variable::LET && op != Token::INIT_LET) {
+ // Perform the assignment for non-const variables. Const assignments
+ // are simply skipped.
+ Slot* slot = var->AsSlot();
+ switch (slot->type()) {
+ case Slot::PARAMETER:
+ case Slot::LOCAL: {
+ Label assign;
+ // Check for an initialized let binding.
+ __ ldr(r1, MemOperand(fp, SlotOffset(slot)));
+ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+ __ cmp(r1, ip);
+ __ b(ne, &assign);
+ __ mov(r1, Operand(var->name()));
+ __ push(r1);
+ __ CallRuntime(Runtime::kThrowReferenceError, 1);
+ // Perform the assignment.
+ __ bind(&assign);
+ __ str(result_register(), MemOperand(fp, SlotOffset(slot)));
+ break;
+ }
+ case Slot::CONTEXT: {
+ // Let variables may be the hole value if they have not been
+ // initialized. Throw a type error in this case.
+ Label assign;
+ MemOperand target = EmitSlotSearch(slot, r1);
+ // Check for an initialized let binding.
+ __ ldr(r3, target);
+ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+ __ cmp(r3, ip);
+ __ b(ne, &assign);
+ __ mov(r3, Operand(var->name()));
+ __ push(r3);
+ __ CallRuntime(Runtime::kThrowReferenceError, 1);
+ // Perform the assignment.
+ __ bind(&assign);
+ __ str(result_register(), target);
+ // RecordWrite may destroy all its register arguments.
+ __ mov(r3, result_register());
+ int offset = Context::SlotOffset(slot->index());
+ __ RecordWrite(r1, Operand(offset), r2, r3);
+ break;
+ }
+ case Slot::LOOKUP:
+ // Call the runtime for the assignment.
+ __ push(r0); // Value.
+ __ mov(r1, Operand(slot->var()->name()));
+ __ mov(r0, Operand(Smi::FromInt(strict_mode_flag())));
+ __ Push(cp, r1, r0); // Context, name, strict mode.
+ __ CallRuntime(Runtime::kStoreContextSlot, 4);
+ break;
+ }
+
} else if (var->mode() != Variable::CONST) {
// Perform the assignment for non-const variables. Const assignments
// are simply skipped.
Handle<Object> Context::Lookup(Handle<String> name,
ContextLookupFlags flags,
int* index_,
- PropertyAttributes* attributes) {
+ PropertyAttributes* attributes,
+ BindingFlags* binding_flags) {
Isolate* isolate = GetIsolate();
Handle<Context> context(this, isolate);
bool follow_context_chain = (flags & FOLLOW_CONTEXT_CHAIN) != 0;
*index_ = -1;
*attributes = ABSENT;
+ *binding_flags = MISSING_BINDING;
if (FLAG_trace_contexts) {
PrintF("Context::Lookup(");
}
*index_ = Context::THROWN_OBJECT_INDEX;
*attributes = NONE;
+ *binding_flags = MUTABLE_IS_INITIALIZED;
return context;
}
} else {
switch (mode) {
case Variable::INTERNAL: // Fall through.
case Variable::VAR:
+ *attributes = NONE;
+ *binding_flags = MUTABLE_IS_INITIALIZED;
+ break;
case Variable::LET:
*attributes = NONE;
+ *binding_flags = MUTABLE_CHECK_INITIALIZED;
break;
case Variable::CONST:
*attributes = READ_ONLY;
+ *binding_flags = IMMUTABLE_CHECK_INITIALIZED;
break;
case Variable::DYNAMIC:
case Variable::DYNAMIC_GLOBAL:
}
*index_ = index;
*attributes = READ_ONLY;
+ *binding_flags = IMMUTABLE_IS_INITIALIZED;
return context;
}
}
};
+// 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.
+//
+// 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.
+enum BindingFlags {
+ MUTABLE_IS_INITIALIZED,
+ MUTABLE_CHECK_INITIALIZED,
+ IMMUTABLE_IS_INITIALIZED,
+ IMMUTABLE_CHECK_INITIALIZED,
+ MISSING_BINDING
+};
+
+
// Heap-allocated activation contexts.
//
// Contexts are implemented as FixedArray objects; the Context
// 4) index_ < 0 && result.is_null():
// there was no context found with the corresponding property.
// attributes == ABSENT.
- Handle<Object> Lookup(Handle<String> name, ContextLookupFlags flags,
- int* index_, PropertyAttributes* attributes);
+ Handle<Object> Lookup(Handle<String> name,
+ ContextLookupFlags flags,
+ int* index_,
+ PropertyAttributes* attributes,
+ BindingFlags* binding_flags);
// Determine if a local variable with the given name exists in a
// context. Do not consider context extension objects. This is
SerializedScopeInfo* scope_info) {
Object* result;
{ MaybeObject* maybe_result =
- AllocateFixedArray(scope_info->NumberOfContextSlots());
+ AllocateFixedArrayWithHoles(scope_info->NumberOfContextSlots());
if (!maybe_result->ToObject(&result)) return maybe_result;
}
- // TODO(keuchel): properly initialize context slots.
Context* context = reinterpret_cast<Context*>(result);
context->set_map(block_context_map());
context->set_closure(function);
Variable* variable = expr->AsVariable();
if (variable == NULL) {
return Bailout("reference to rewritten variable");
+ } else if (variable->mode() == Variable::LET) {
+ return Bailout("reference to let variable");
} else if (variable->IsStackAllocated()) {
HValue* value = environment()->Lookup(variable);
if (variable->mode() == Variable::CONST &&
BinaryOperation* operation = expr->binary_operation();
if (var != NULL) {
- if (var->mode() == Variable::CONST) {
- return Bailout("unsupported const compound assignment");
+ if (var->mode() == Variable::CONST ||
+ var->mode() == Variable::LET) {
+ return Bailout("unsupported let or const compound assignment");
}
CHECK_ALIVE(VisitForValue(operation));
// variables (e.g. initialization inside a loop).
HValue* old_value = environment()->Lookup(var);
AddInstruction(new HUseConst(old_value));
+ } else if (var->mode() == Variable::LET) {
+ return Bailout("unsupported assignment to let");
}
if (proxy->IsArguments()) return Bailout("assignment to arguments");
void HGraphBuilder::VisitDeclaration(Declaration* decl) {
// We support only declarations that do not require code generation.
Variable* var = decl->proxy()->var();
- if (!var->IsStackAllocated() || decl->fun() != NULL) {
+ if (!var->IsStackAllocated() ||
+ decl->fun() != NULL ||
+ decl->mode() == Variable::LET) {
return Bailout("unsupported declaration");
}
switch (slot->type()) {
case Slot::PARAMETER:
case Slot::LOCAL:
- if (mode == Variable::CONST) {
- __ mov(Operand(ebp, SlotOffset(slot)),
- Immediate(isolate()->factory()->the_hole_value()));
- } else if (function != NULL) {
+ if (function != NULL) {
VisitForAccumulatorValue(function);
__ mov(Operand(ebp, SlotOffset(slot)), result_register());
+ } else if (mode == Variable::CONST || mode == Variable::LET) {
+ __ mov(Operand(ebp, SlotOffset(slot)),
+ Immediate(isolate()->factory()->the_hole_value()));
}
break;
__ cmp(ebx, isolate()->factory()->catch_context_map());
__ Check(not_equal, "Declaration in catch context.");
}
- if (mode == Variable::CONST) {
- __ mov(ContextOperand(esi, slot->index()),
- Immediate(isolate()->factory()->the_hole_value()));
- // No write barrier since the hole value is in old space.
- } else if (function != NULL) {
+ if (function != NULL) {
VisitForAccumulatorValue(function);
__ mov(ContextOperand(esi, slot->index()), result_register());
int offset = Context::SlotOffset(slot->index());
__ mov(ebx, esi);
__ RecordWrite(ebx, offset, result_register(), ecx);
+ } else if (mode == Variable::CONST || mode == Variable::LET) {
+ __ mov(ContextOperand(esi, slot->index()),
+ Immediate(isolate()->factory()->the_hole_value()));
+ // No write barrier since the hole value is in old space.
}
break;
// 'undefined') because we may have a (legal) redeclaration and we
// must not destroy the current value.
increment_stack_height(3);
- if (mode == Variable::CONST) {
+ if (function != NULL) {
+ VisitForStackValue(function);
+ } else if (mode == Variable::CONST || mode == Variable::LET) {
__ push(Immediate(isolate()->factory()->the_hole_value()));
increment_stack_height();
- } else if (function != NULL) {
- VisitForStackValue(function);
} else {
__ push(Immediate(Smi::FromInt(0))); // No initial value!
increment_stack_height();
__ mov(eax, isolate()->factory()->undefined_value());
__ bind(&done);
context()->Plug(eax);
+ } else if (var->mode() == Variable::LET) {
+ // Let bindings may be the hole value if they have not been initialized.
+ // Throw a type error in this case.
+ Label done;
+ MemOperand slot_operand = EmitSlotSearch(slot, eax);
+ __ mov(eax, slot_operand);
+ __ cmp(eax, isolate()->factory()->the_hole_value());
+ __ j(not_equal, &done, Label::kNear);
+ __ push(Immediate(var->name()));
+ __ CallRuntime(Runtime::kThrowReferenceError, 1);
+ __ bind(&done);
+ context()->Plug(eax);
} else {
context()->Plug(slot);
}
}
__ bind(&skip);
+ } else if (var->mode() == Variable::LET && op != Token::INIT_LET) {
+ // Perform the assignment for non-const variables. Const assignments
+ // are simply skipped.
+ Slot* slot = var->AsSlot();
+ switch (slot->type()) {
+ case Slot::PARAMETER:
+ case Slot::LOCAL: {
+ Label assign;
+ // Check for an initialized let binding.
+ __ mov(edx, Operand(ebp, SlotOffset(slot)));
+ __ cmp(edx, isolate()->factory()->the_hole_value());
+ __ j(not_equal, &assign);
+ __ push(Immediate(var->name()));
+ __ CallRuntime(Runtime::kThrowReferenceError, 1);
+ // Perform the assignment.
+ __ bind(&assign);
+ __ mov(Operand(ebp, SlotOffset(slot)), eax);
+ break;
+ }
+
+ case Slot::CONTEXT: {
+ // Let variables may be the hole value if they have not been
+ // initialized. Throw a type error in this case.
+ Label assign;
+ MemOperand target = EmitSlotSearch(slot, ecx);
+ // Check for an initialized let binding.
+ __ mov(edx, target);
+ __ cmp(edx, isolate()->factory()->the_hole_value());
+ __ j(not_equal, &assign, Label::kNear);
+ __ push(Immediate(var->name()));
+ __ CallRuntime(Runtime::kThrowReferenceError, 1);
+ // Perform the assignment.
+ __ bind(&assign);
+ __ mov(target, eax);
+ // The value of the assignment is in eax. RecordWrite clobbers its
+ // register arguments.
+ __ mov(edx, eax);
+ int offset = Context::SlotOffset(slot->index());
+ __ RecordWrite(ecx, offset, edx, ebx);
+ break;
+ }
+
+ case Slot::LOOKUP:
+ // Call the runtime for the assignment.
+ __ push(eax); // Value.
+ __ push(esi); // Context.
+ __ push(Immediate(var->name()));
+ __ push(Immediate(Smi::FromInt(strict_mode_flag())));
+ __ CallRuntime(Runtime::kStoreContextSlot, 4);
+ break;
+ }
} else if (var->mode() != Variable::CONST) {
// Perform the assignment for non-const variables. Const assignments
// are simply skipped.
// ('var' | 'const') (Identifier ('=' AssignmentExpression)?)+[',']
Variable::Mode mode = Variable::VAR;
+ // True if the binding needs initialization. 'let' and 'const' declared
+ // bindings are created uninitialized by their declaration nodes and
+ // need initialization. 'var' declared bindings are always initialized
+ // immediately by their declaration nodes.
+ bool needs_init = false;
bool is_const = false;
+ Token::Value init_op = Token::INIT_VAR;
if (peek() == Token::VAR) {
Consume(Token::VAR);
} else if (peek() == Token::CONST) {
}
mode = Variable::CONST;
is_const = true;
+ needs_init = true;
+ init_op = Token::INIT_CONST;
} else if (peek() == Token::LET) {
Consume(Token::LET);
if (var_context != kSourceElement &&
return NULL;
}
mode = Variable::LET;
+ needs_init = true;
+ init_op = Token::INIT_LET;
} else {
UNREACHABLE(); // by current callers
}
}
}
- // Make sure that 'const c' actually initializes 'c' to undefined
- // even though it seems like a stupid thing to do.
- if (value == NULL && is_const) {
+ // Make sure that 'const x' and 'let x' initialize 'x' to undefined.
+ if (value == NULL && needs_init) {
value = GetLiteralUndefined();
}
// for constant lookups is always the function context, while it is
// the top context for variables). Sigh...
if (value != NULL) {
- Token::Value op = (is_const ? Token::INIT_CONST : Token::INIT_VAR);
bool in_with = is_const ? false : inside_with();
VariableProxy* proxy =
initialization_scope->NewUnresolved(name, in_with);
Assignment* assignment =
- new(zone()) Assignment(isolate(), op, proxy, value, position);
+ new(zone()) Assignment(isolate(), init_op, proxy, value, position);
if (block) {
block->AddStatement(new(zone()) ExpressionStatement(assignment));
}
int index;
PropertyAttributes attributes;
ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
+ BindingFlags binding_flags;
Handle<Object> holder =
- context->Lookup(name, flags, &index, &attributes);
+ context->Lookup(name, flags, &index, &attributes, &binding_flags);
if (attributes != ABSENT) {
// The name was declared before; check for conflicting
int index;
PropertyAttributes attributes;
ContextLookupFlags flags = FOLLOW_CHAINS;
+ BindingFlags binding_flags;
Handle<Object> holder =
- context->Lookup(name, flags, &index, &attributes);
+ context->Lookup(name, flags, &index, &attributes, &binding_flags);
// In most situations, the property introduced by the const
// declaration should be present in the context extension object.
int index;
PropertyAttributes attributes;
ContextLookupFlags flags = FOLLOW_CHAINS;
- Handle<Object> holder = context->Lookup(name, flags, &index, &attributes);
+ BindingFlags binding_flags;
+ Handle<Object> holder = context->Lookup(name,
+ flags,
+ &index,
+ &attributes,
+ &binding_flags);
// If the slot was not found the result is true.
if (holder.is_null()) {
int index;
PropertyAttributes attributes;
ContextLookupFlags flags = FOLLOW_CHAINS;
- Handle<Object> holder = context->Lookup(name, flags, &index, &attributes);
+ BindingFlags binding_flags;
+ Handle<Object> holder = context->Lookup(name,
+ flags,
+ &index,
+ &attributes,
+ &binding_flags);
// If the index is non-negative, the slot has been found in a local
// variable or a parameter. Read it from the context object or the
MaybeObject* value = (holder->IsContext())
? Context::cast(*holder)->get(index)
: JSObject::cast(*holder)->GetElement(index);
- return MakePair(Unhole(isolate->heap(), value, attributes), *receiver);
+ // Check for uninitialized bindings.
+ if (holder->IsContext() &&
+ 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);
+ }
}
// If the holder is found, we read the property from it.
int index;
PropertyAttributes attributes;
ContextLookupFlags flags = FOLLOW_CHAINS;
- Handle<Object> holder = context->Lookup(name, flags, &index, &attributes);
+ BindingFlags binding_flags;
+ Handle<Object> holder = context->Lookup(name,
+ flags,
+ &index,
+ &attributes,
+ &binding_flags);
if (index >= 0) {
if (holder->IsContext()) {
+ Handle<Context> context = Handle<Context>::cast(holder);
+ if (binding_flags == MUTABLE_CHECK_INITIALIZED &&
+ context->get(index)->IsTheHole()) {
+ Handle<Object> error =
+ isolate->factory()->NewReferenceError("not_defined",
+ HandleVector(&name, 1));
+ return isolate->Throw(*error);
+ }
// Ignore if read_only variable.
if ((attributes & READ_ONLY) == 0) {
// Context is a fixed array and set cannot fail.
- Context::cast(*holder)->set(index, *value);
+ context->set(index, *value);
} else if (strict_mode == kStrictMode) {
// Setting read only property in strict mode.
Handle<Object> error =
// it is bound in the global context.
int index = -1;
PropertyAttributes attributes = ABSENT;
+ BindingFlags binding_flags;
while (true) {
receiver = context->Lookup(isolate->factory()->eval_symbol(),
FOLLOW_PROTOTYPE_CHAIN,
- &index, &attributes);
+ &index,
+ &attributes,
+ &binding_flags);
// Stop search when eval is found or when the global context is
// reached.
if (attributes != ABSENT || context->IsGlobalContext()) break;
/* this block of enum values being contiguous and sorted in the */ \
/* same order! */ \
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(ASSIGN, "=", 2) \
T(ASSIGN_BIT_OR, "|=", 2) \
switch (slot->type()) {
case Slot::PARAMETER:
case Slot::LOCAL:
- if (mode == Variable::CONST) {
- __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex);
- __ movq(Operand(rbp, SlotOffset(slot)), kScratchRegister);
- } else if (function != NULL) {
+ if (function != NULL) {
VisitForAccumulatorValue(function);
__ movq(Operand(rbp, SlotOffset(slot)), result_register());
+ } else if (mode == Variable::CONST || mode == Variable::LET) {
+ __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex);
+ __ movq(Operand(rbp, SlotOffset(slot)), kScratchRegister);
}
break;
__ CompareRoot(rbx, Heap::kCatchContextMapRootIndex);
__ Check(not_equal, "Declaration in catch context.");
}
- if (mode == Variable::CONST) {
- __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex);
- __ movq(ContextOperand(rsi, slot->index()), kScratchRegister);
- // No write barrier since the hole value is in old space.
- } else if (function != NULL) {
+ if (function != NULL) {
VisitForAccumulatorValue(function);
__ movq(ContextOperand(rsi, slot->index()), result_register());
int offset = Context::SlotOffset(slot->index());
__ movq(rbx, rsi);
__ RecordWrite(rbx, offset, result_register(), rcx);
+ } else if (mode == Variable::CONST || mode == Variable::LET) {
+ __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex);
+ __ movq(ContextOperand(rsi, slot->index()), kScratchRegister);
+ // No write barrier since the hole value is in old space.
}
break;
// Note: For variables we must not push an initial value (such as
// 'undefined') because we may have a (legal) redeclaration and we
// must not destroy the current value.
- if (mode == Variable::CONST) {
- __ PushRoot(Heap::kTheHoleValueRootIndex);
- } else if (function != NULL) {
+ if (function != NULL) {
VisitForStackValue(function);
+ } else if (mode == Variable::CONST || mode == Variable::LET) {
+ __ PushRoot(Heap::kTheHoleValueRootIndex);
} else {
__ Push(Smi::FromInt(0)); // no initial value!
}
__ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
__ bind(&done);
context()->Plug(rax);
+ } else if (var->mode() == Variable::LET) {
+ // Let bindings may be the hole value if they have not been initialized.
+ // Throw a type error in this case.
+ Label done;
+ MemOperand slot_operand = EmitSlotSearch(slot, rax);
+ __ movq(rax, slot_operand);
+ __ CompareRoot(rax, Heap::kTheHoleValueRootIndex);
+ __ j(not_equal, &done, Label::kNear);
+ __ Push(var->name());
+ __ CallRuntime(Runtime::kThrowReferenceError, 1);
+ __ bind(&done);
+ context()->Plug(rax);
} else {
context()->Plug(slot);
}
}
__ bind(&skip);
+ } else if (var->mode() == Variable::LET && op != Token::INIT_LET) {
+ // Perform the assignment for non-const variables. Const assignments
+ // are simply skipped.
+ Slot* slot = var->AsSlot();
+ switch (slot->type()) {
+ case Slot::PARAMETER:
+ case Slot::LOCAL: {
+ Label assign;
+ // Check for an initialized let binding.
+ __ movq(rdx, Operand(rbp, SlotOffset(slot)));
+ __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex);
+ __ j(not_equal, &assign);
+ __ Push(var->name());
+ __ CallRuntime(Runtime::kThrowReferenceError, 1);
+ // Perform the assignment.
+ __ bind(&assign);
+ __ movq(Operand(rbp, SlotOffset(slot)), rax);
+ break;
+ }
+
+ case Slot::CONTEXT: {
+ // Let variables may be the hole value if they have not been
+ // initialized. Throw a type error in this case.
+ Label assign;
+ MemOperand target = EmitSlotSearch(slot, rcx);
+ // Check for an initialized let binding.
+ __ movq(rdx, target);
+ __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex);
+ __ j(not_equal, &assign, Label::kNear);
+ __ Push(var->name());
+ __ CallRuntime(Runtime::kThrowReferenceError, 1);
+ // Perform the assignment.
+ __ bind(&assign);
+ __ movq(target, rax);
+ // The value of the assignment is in eax. RecordWrite clobbers its
+ // register arguments.
+ __ movq(rdx, rax);
+ int offset = Context::SlotOffset(slot->index());
+ __ RecordWrite(rcx, offset, rdx, rbx);
+ break;
+ }
+
+ case Slot::LOOKUP:
+ // Call the runtime for the assignment.
+ __ push(rax); // Value.
+ __ push(rsi); // Context.
+ __ Push(var->name());
+ __ Push(Smi::FromInt(strict_mode_flag()));
+ __ CallRuntime(Runtime::kStoreContextSlot, 4);
+ break;
+ }
} else if (var->mode() != Variable::CONST) {
// Perform the assignment for non-const variables. Const assignments
// are simply skipped.
--- /dev/null
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (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: --harmony-block-scoping --allow-natives-syntax
+
+// Test that temporal dead zone semantics for function and block scoped
+// ket bindings are handled by the optimizing compiler.
+
+function f(x, b) {
+ let y = (b ? y : x) + 42;
+ return y;
+}
+
+function g(x, b) {
+ {
+ let y = (b ? y : x) + 42;
+ return y;
+ }
+}
+
+for (var i=0; i<10; i++) {
+ f(i, false);
+ g(i, false);
+}
+
+%OptimizeFunctionOnNextCall(f);
+%OptimizeFunctionOnNextCall(g);
+
+try {
+ f(42, true);
+} catch (e) {
+ assertInstanceof(e, ReferenceError);
+}
+
+try {
+ g(42, true);
+} catch (e) {
+ assertInstanceof(e, ReferenceError);
+}
--- /dev/null
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (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: --harmony-block-scoping
+
+// Test temporal dead zone semantics of let bound variables in
+// function and block scopes.
+
+function TestFunctionLocal(s) {
+ try {
+ eval("(function(){" + s + "; })")();
+ } catch (e) {
+ assertInstanceof(e, ReferenceError);
+ return;
+ }
+ assertUnreachable();
+}
+
+function TestBlockLocal(s,e) {
+ try {
+ eval("(function(){ {" + s + ";} })")();
+ } catch (e) {
+ assertInstanceof(e, ReferenceError);
+ return;
+ }
+ assertUnreachable();
+}
+
+
+function TestAll(s) {
+ TestBlockLocal(s);
+ TestFunctionLocal(s);
+}
+
+// Use before initialization in declaration statement.
+TestAll('let x = x + 1');
+TestAll('let x = x += 1');
+TestAll('let x = x++');
+TestAll('let x = ++x');
+
+// Use before initialization in prior statement.
+TestAll('x + 1; let x;');
+TestAll('x = 1; let x;');
+TestAll('x += 1; let x;');
+TestAll('++x; let x;');
+TestAll('x++; let x;');
+
+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()(); 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++; } }');
+
+// Use in before initialization with a dynamic lookup.
+TestAll('eval("x + 1;"); let x;');
+TestAll('eval("x = 1;"); let x;');
+TestAll('eval("x += 1;"); let x;');
+TestAll('eval("++x;"); let x;');
+TestAll('eval("x++;"); let x;');
+
+// Test that variables introduced by function declarations are created and
+// initialized upon entering a function / block scope.
+function f() {
+ {
+ assertEquals(2, g1());
+ assertEquals(2, eval("g1()"));
+
+ // block scoped function declaration
+ function g1() {
+ return 2;
+ }
+ }
+
+ assertEquals(3, g2());
+ assertEquals(3, eval("g2()"));
+ // function scoped function declaration
+ function g2() {
+ return 3;
+ }
+}
+f();
+
+// Test that a function declaration introduces a block scoped variable.
+TestAll('{ function k() { return 0; } }; k(); ');
+
+// Test that a function declaration sees the scope it resides in.
+function f2() {
+ let m, n;
+ {
+ m = g;
+ function g() {
+ return a;
+ }
+ let a = 1;
+ }
+ assertEquals(1, m());
+
+ try {
+ throw 2;
+ } catch(b) {
+ n = h;
+ function h() {
+ return b + c;
+ }
+ let b = 3;
+ }
+ assertEquals(5, n());
+}