// 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;
+ bool binding_needs_init = (function == NULL) &&
+ (mode == CONST || mode == CONST_HARMONY || mode == LET);
switch (variable->location()) {
case Variable::UNALLOCATED:
++(*global_count);
ScopeInfo::cast(context->extension()), isolate);
}
VariableMode mode;
- int slot_index = scope_info->ContextSlotIndex(*name, &mode);
+ InitializationFlag init_flag;
+ int slot_index = scope_info->ContextSlotIndex(*name, &mode, &init_flag);
ASSERT(slot_index < 0 || slot_index >= MIN_CONTEXT_SLOTS);
if (slot_index >= 0) {
if (FLAG_trace_contexts) {
break;
case LET:
*attributes = NONE;
- *binding_flags = MUTABLE_CHECK_INITIALIZED;
+ *binding_flags = (init_flag == kNeedsInitialization)
+ ? MUTABLE_CHECK_INITIALIZED : MUTABLE_IS_INITIALIZED;
break;
case CONST:
*attributes = READ_ONLY;
- *binding_flags = IMMUTABLE_CHECK_INITIALIZED;
+ *binding_flags = (init_flag == kNeedsInitialization)
+ ? IMMUTABLE_CHECK_INITIALIZED : IMMUTABLE_IS_INITIALIZED;
break;
case CONST_HARMONY:
*attributes = READ_ONLY;
- *binding_flags = IMMUTABLE_CHECK_INITIALIZED_HARMONY;
+ *binding_flags = (init_flag == kNeedsInitialization)
+ ? IMMUTABLE_CHECK_INITIALIZED_HARMONY :
+ IMMUTABLE_IS_INITIALIZED_HARMONY;
break;
case DYNAMIC:
case DYNAMIC_GLOBAL:
// Check non-parameter locals.
Handle<ScopeInfo> scope_info(context->closure()->shared()->scope_info());
VariableMode mode;
- int index = scope_info->ContextSlotIndex(*name, &mode);
+ InitializationFlag init_flag;
+ int index = scope_info->ContextSlotIndex(*name, &mode, &init_flag);
ASSERT(index < 0 || index >= MIN_CONTEXT_SLOTS);
if (index >= 0) return false;
// 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;
+ bool binding_needs_init = (function == NULL) &&
+ (mode == CONST || mode == CONST_HARMONY || mode == LET);
switch (variable->location()) {
case Variable::UNALLOCATED:
++(*global_count);
// 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;
+ bool binding_needs_init = (function == NULL) &&
+ (mode == CONST || mode == CONST_HARMONY || mode == LET);
switch (variable->location()) {
case Variable::UNALLOCATED:
++(*global_count);
// Return the mode of the given context local.
VariableMode ContextLocalMode(int var);
+ // Return the initialization flag of the given context local.
+ InitializationFlag ContextLocalInitFlag(int var);
+
// Lookup support for serialized scope info. Returns the
// the stack slot index for a given slot name if the slot is
// present; otherwise returns a value < 0. The name must be a symbol
// returns a value < 0. The name must be a symbol (canonicalized).
// If the slot is present and mode != NULL, sets *mode to the corresponding
// mode for that variable.
- int ContextSlotIndex(String* name, VariableMode* mode);
+ int ContextSlotIndex(String* name,
+ VariableMode* mode,
+ InitializationFlag* init_flag);
// Lookup support for serialized scope info. Returns the
// parameter index for a given parameter name if the parameter is present;
// index starting with Context::MIN_CONTEXT_SLOTS. One slot is used per
// context local, so in total this part occupies ContextLocalCount() slots
// in the array.
- // 4. ContextLocalModeEntries:
- // Contains the variable modes corresponding to the context locals in
- // ContextLocalNameEntries. One slot is used per context local, so in total
- // this part occupies ContextLocalCount() slots in the array.
+ // 4. ContextLocalInfoEntries:
+ // Contains the variable modes and initialization flags corresponding to
+ // the context locals in ContextLocalNameEntries. One slot is used per
+ // context local, so in total this part occupies ContextLocalCount()
+ // slots in the array.
// 5. FunctionNameEntryIndex:
// If the scope belongs to a named function expression this part contains
// information about the function variable. It always occupies two array
int ParameterEntriesIndex();
int StackLocalEntriesIndex();
int ContextLocalNameEntriesIndex();
- int ContextLocalModeEntriesIndex();
+ int ContextLocalInfoEntriesIndex();
int FunctionNameEntryIndex();
// Location of the function variable for named function expressions.
class StrictModeField: public BitField<bool, 4, 1> {};
class FunctionVariableField: public BitField<FunctionVariableInfo, 5, 2> {};
class FunctionVariableMode: public BitField<VariableMode, 7, 3> {};
+
+ // BitFields representing the encoded information for context locals in the
+ // ContextLocalInfoEntries part.
+ class ContextLocalMode: public BitField<VariableMode, 0, 3> {};
+ class ContextLocalInitFlag: public BitField<InitializationFlag, 3, 1> {};
};
var = declaration_scope->LocalLookup(name);
if (var == NULL) {
// Declare the name.
- var = declaration_scope->DeclareLocal(name, mode);
+ InitializationFlag init_flag = (fun != NULL || mode == VAR)
+ ? kCreatedInitialized : kNeedsInitialization;
+ var = declaration_scope->DeclareLocal(name, mode, init_flag);
} else {
// The name was declared in this scope before; check for conflicting
// re-declarations. We have a conflict if either of the declarations is
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);
+ var = new(zone()) Variable(declaration_scope,
+ name,
+ CONST,
+ true,
+ kind,
+ kNeedsInitialization);
}
// If requested and we have a local variable, bind the proxy to the variable
if (peek() == Token::LBRACE) {
Target target(&this->target_stack_, &catch_collector);
VariableMode mode = harmony_scoping_ ? LET : VAR;
- catch_variable = catch_scope->DeclareLocal(name, mode);
+ catch_variable =
+ catch_scope->DeclareLocal(name, mode, kCreatedInitialized);
SaveScope save_scope(this, catch_scope);
catch_block = ParseBlock(NULL, CHECK_OK);
for (; i < scope_info->LocalCount(); ++i) {
Handle<String> name(scope_info->LocalName(i));
VariableMode mode;
+ InitializationFlag init_flag;
locals->set(i * 2, *name);
- locals->set(i * 2 + 1,
- context->get(scope_info->ContextSlotIndex(*name, &mode)));
+ locals->set(i * 2 + 1, context->get(
+ scope_info->ContextSlotIndex(*name, &mode, &init_flag)));
}
}
// Fill all context locals to the context extension.
for (int i = 0; i < scope_info->ContextLocalCount(); i++) {
VariableMode mode;
+ InitializationFlag init_flag;
int context_index = scope_info->ContextSlotIndex(
- scope_info->ContextLocalName(i), &mode);
+ scope_info->ContextLocalName(i), &mode, &init_flag);
RETURN_IF_EMPTY_HANDLE_VALUE(
isolate,
if (scope_info->HasHeapAllocatedLocals()) {
VariableMode mode;
+ InitializationFlag init_flag;
index = scope_info->ContextSlotIndex(
- isolate->heap()->arguments_symbol(), &mode);
+ isolate->heap()->arguments_symbol(), &mode, &init_flag);
if (index != -1) {
return Handle<Object>(function_context->get(index), isolate);
}
scope_info->set(index++, *context_locals[i]->name());
}
- // Add context locals' modes.
- ASSERT(index == scope_info->ContextLocalModeEntriesIndex());
+ // Add context locals' info.
+ ASSERT(index == scope_info->ContextLocalInfoEntriesIndex());
for (int i = 0; i < context_local_count; ++i) {
- scope_info->set(index++, Smi::FromInt(context_locals[i]->mode()));
+ Variable* var = context_locals[i];
+ uint32_t value = ContextLocalMode::encode(var->mode()) |
+ ContextLocalInitFlag::encode(var->initialization_flag());
+ scope_info->set(index++, Smi::FromInt(value));
}
// If present, add the function variable name and its index.
VariableMode ScopeInfo::ContextLocalMode(int var) {
ASSERT(0 <= var && var < ContextLocalCount());
- int info_index = ContextLocalModeEntriesIndex() + var;
- return static_cast<VariableMode>(Smi::cast(get(info_index))->value());
+ int info_index = ContextLocalInfoEntriesIndex() + var;
+ int value = Smi::cast(get(info_index))->value();
+ return ContextLocalMode::decode(value);
+}
+
+
+InitializationFlag ScopeInfo::ContextLocalInitFlag(int var) {
+ ASSERT(0 <= var && var < ContextLocalCount());
+ int info_index = ContextLocalInfoEntriesIndex() + var;
+ int value = Smi::cast(get(info_index))->value();
+ return ContextLocalInitFlag::decode(value);
}
}
-int ScopeInfo::ContextSlotIndex(String* name, VariableMode* mode) {
+int ScopeInfo::ContextSlotIndex(String* name,
+ VariableMode* mode,
+ InitializationFlag* init_flag) {
ASSERT(name->IsSymbol());
ASSERT(mode != NULL);
+ ASSERT(init_flag != NULL);
if (length() > 0) {
ContextSlotCache* context_slot_cache = GetIsolate()->context_slot_cache();
- int result = context_slot_cache->Lookup(this, name, mode);
+ int result = context_slot_cache->Lookup(this, name, mode, init_flag);
if (result != ContextSlotCache::kNotFound) {
ASSERT(result < ContextLength());
return result;
if (name == get(i)) {
int var = i - start;
*mode = ContextLocalMode(var);
+ *init_flag = ContextLocalInitFlag(var);
result = Context::MIN_CONTEXT_SLOTS + var;
- context_slot_cache->Update(this, name, *mode, result);
+ context_slot_cache->Update(this, name, *mode, *init_flag, result);
ASSERT(result < ContextLength());
return result;
}
}
- context_slot_cache->Update(this, name, INTERNAL, -1);
+ context_slot_cache->Update(this, name, INTERNAL, kNeedsInitialization, -1);
}
return -1;
}
}
-int ScopeInfo::ContextLocalModeEntriesIndex() {
+int ScopeInfo::ContextLocalInfoEntriesIndex() {
return ContextLocalNameEntriesIndex() + ContextLocalCount();
}
int ScopeInfo::FunctionNameEntryIndex() {
- return ContextLocalModeEntriesIndex() + ContextLocalCount();
+ return ContextLocalInfoEntriesIndex() + ContextLocalCount();
}
int ContextSlotCache::Lookup(Object* data,
String* name,
- VariableMode* mode) {
+ VariableMode* mode,
+ InitializationFlag* init_flag) {
int index = Hash(data, name);
Key& key = keys_[index];
if ((key.data == data) && key.name->Equals(name)) {
Value result(values_[index]);
if (mode != NULL) *mode = result.mode();
+ if (init_flag != NULL) *init_flag = result.initialization_flag();
return result.index() + kNotFound;
}
return kNotFound;
void ContextSlotCache::Update(Object* data,
String* name,
VariableMode mode,
+ InitializationFlag init_flag,
int slot_index) {
String* symbol;
ASSERT(slot_index > kNotFound);
key.data = data;
key.name = symbol;
// Please note value only takes a uint as index.
- values_[index] = Value(mode, slot_index - kNotFound).raw();
+ values_[index] = Value(mode, init_flag, slot_index - kNotFound).raw();
#ifdef DEBUG
- ValidateEntry(data, name, mode, slot_index);
+ ValidateEntry(data, name, mode, init_flag, slot_index);
#endif
}
}
void ContextSlotCache::ValidateEntry(Object* data,
String* name,
VariableMode mode,
+ InitializationFlag init_flag,
int slot_index) {
String* symbol;
if (HEAP->LookupSymbolIfExists(name, &symbol)) {
ASSERT(key.name->Equals(name));
Value result(values_[index]);
ASSERT(result.mode() == mode);
+ ASSERT(result.initialization_flag() == init_flag);
ASSERT(result.index() + kNotFound == slot_index);
}
}
// If absent, kNotFound is returned.
int Lookup(Object* data,
String* name,
- VariableMode* mode);
+ VariableMode* mode,
+ InitializationFlag* init_flag);
// Update an element in the cache.
void Update(Object* data,
String* name,
VariableMode mode,
+ InitializationFlag init_flag,
int slot_index);
// Clear the cache.
void ValidateEntry(Object* data,
String* name,
VariableMode mode,
+ InitializationFlag init_flag,
int slot_index);
#endif
};
struct Value {
- Value(VariableMode mode, int index) {
+ Value(VariableMode mode,
+ InitializationFlag init_flag,
+ int index) {
ASSERT(ModeField::is_valid(mode));
+ ASSERT(InitField::is_valid(init_flag));
ASSERT(IndexField::is_valid(index));
- value_ = ModeField::encode(mode) | IndexField::encode(index);
+ value_ = ModeField::encode(mode) |
+ IndexField::encode(index) |
+ InitField::encode(init_flag);
ASSERT(mode == this->mode());
+ ASSERT(init_flag == this->initialization_flag());
ASSERT(index == this->index());
}
VariableMode mode() { return ModeField::decode(value_); }
+ InitializationFlag initialization_flag() {
+ return InitField::decode(value_);
+ }
+
int index() { return IndexField::decode(value_); }
// Bit fields in value_ (type, shift, size). Must be public so the
// constants can be embedded in generated code.
- class ModeField: public BitField<VariableMode, 0, 3> {};
- class IndexField: public BitField<int, 3, 32-3> {};
+ class ModeField: public BitField<VariableMode, 0, 3> {};
+ class InitField: public BitField<InitializationFlag, 3, 1> {};
+ class IndexField: public BitField<int, 4, 32-4> {};
+
private:
uint32_t value_;
};
VariableMap::~VariableMap() {}
-Variable* VariableMap::Declare(Scope* scope,
- Handle<String> name,
- VariableMode mode,
- bool is_valid_lhs,
- Variable::Kind kind) {
+Variable* VariableMap::Declare(
+ Scope* scope,
+ Handle<String> name,
+ VariableMode mode,
+ bool is_valid_lhs,
+ Variable::Kind kind,
+ InitializationFlag initialization_flag) {
HashMap::Entry* p = HashMap::Lookup(name.location(), name->Hash(), true);
if (p->value == NULL) {
// The variable has not been declared yet -> insert it.
ASSERT(p->key == name.location());
- p->value = new Variable(scope, name, mode, is_valid_lhs, kind);
+ p->value = new Variable(scope,
+ name,
+ mode,
+ is_valid_lhs,
+ kind,
+ initialization_flag);
}
return reinterpret_cast<Variable*>(p->value);
}
catch_variable_name,
VAR,
true, // Valid left-hand side.
- Variable::NORMAL);
+ Variable::NORMAL,
+ kCreatedInitialized);
AllocateHeapSlot(variable);
}
isolate_->factory()->this_symbol(),
VAR,
false,
- Variable::THIS);
+ Variable::THIS,
+ kCreatedInitialized);
var->AllocateTo(Variable::PARAMETER, -1);
receiver_ = var;
} else {
isolate_->factory()->arguments_symbol(),
VAR,
true,
- Variable::ARGUMENTS);
+ Variable::ARGUMENTS,
+ kCreatedInitialized);
}
}
// Check context slot lookup.
VariableMode mode;
- int index = scope_info_->ContextSlotIndex(*name, &mode);
+ InitializationFlag init_flag;
+ int index = scope_info_->ContextSlotIndex(*name, &mode, &init_flag);
if (index < 0) {
// Check parameters.
mode = VAR;
+ init_flag = kCreatedInitialized;
index = scope_info_->ParameterIndex(*name);
if (index < 0) {
// Check the function name.
}
Variable* var =
- variables_.Declare(this, name, mode, true, Variable::NORMAL);
+ variables_.Declare(this,
+ name,
+ mode,
+ true,
+ Variable::NORMAL,
+ init_flag);
var->AllocateTo(Variable::CONTEXT, index);
return var;
}
Variable* Scope::DeclareFunctionVar(Handle<String> name, VariableMode mode) {
ASSERT(is_function_scope() && function_ == NULL);
- Variable* function_var =
- new Variable(this, name, mode, true, Variable::NORMAL);
+ Variable* function_var = new Variable(
+ this, name, mode, true, Variable::NORMAL, kCreatedInitialized);
function_ = new(isolate_->zone()) VariableProxy(isolate_, function_var);
return function_var;
}
void Scope::DeclareParameter(Handle<String> name, VariableMode mode) {
ASSERT(!already_resolved());
ASSERT(is_function_scope());
- Variable* var =
- variables_.Declare(this, name, mode, true, Variable::NORMAL);
+ Variable* var = variables_.Declare(
+ this, name, mode, true, Variable::NORMAL, kCreatedInitialized);
params_.Add(var);
}
-Variable* Scope::DeclareLocal(Handle<String> name, VariableMode mode) {
+Variable* Scope::DeclareLocal(Handle<String> name,
+ VariableMode mode,
+ InitializationFlag init_flag) {
ASSERT(!already_resolved());
// This function handles VAR and CONST modes. DYNAMIC variables are
// introduces during variable allocation, INTERNAL variables are allocated
mode == CONST_HARMONY ||
mode == LET);
++num_var_or_const_;
- return variables_.Declare(this, name, mode, true, Variable::NORMAL);
+ return
+ variables_.Declare(this, name, mode, true, Variable::NORMAL, init_flag);
}
Variable* Scope::DeclareGlobal(Handle<String> name) {
ASSERT(is_global_scope());
- return variables_.Declare(this, name, DYNAMIC_GLOBAL,
+ return variables_.Declare(this,
+ name,
+ DYNAMIC_GLOBAL,
true,
- Variable::NORMAL);
+ Variable::NORMAL,
+ kCreatedInitialized);
}
name,
TEMPORARY,
true,
- Variable::NORMAL);
+ Variable::NORMAL,
+ kCreatedInitialized);
temps_.Add(var);
return var;
}
Variable* var = map->Lookup(name);
if (var == NULL) {
// Declare a new non-local.
- var = map->Declare(NULL, name, mode, true, Variable::NORMAL);
+ InitializationFlag init_flag = (mode == VAR)
+ ? kCreatedInitialized : kNeedsInitialization;
+ var = map->Declare(NULL,
+ name,
+ mode,
+ true,
+ Variable::NORMAL,
+ init_flag);
// Allocate it by giving it a dynamic lookup.
var->AllocateTo(Variable::LOOKUP, -1);
}
Handle<String> name,
VariableMode mode,
bool is_valid_lhs,
- Variable::Kind kind);
+ Variable::Kind kind,
+ InitializationFlag initialization_flag);
Variable* Lookup(Handle<String> name);
};
// Declare a local variable in this scope. If the variable has been
// declared before, the previously declared variable is returned.
- Variable* DeclareLocal(Handle<String> name, VariableMode mode);
+ Variable* DeclareLocal(Handle<String> name,
+ VariableMode mode,
+ InitializationFlag init_flag);
// Declare an implicit global variable in this scope which must be a
// global scope. The variable was introduced (possibly from an inner
};
+// ES6 Draft Rev3 10.2 specifies declarative environment records with mutable
+// and immutable bindings that can be in two states: initialized and
+// uninitialized. In ES5 only immutable bindings have these two states. When
+// accessing a binding, it needs to be checked for initialization. However in
+// the following cases the binding is initialized immediately after creation
+// so the initialization check can always be skipped:
+// 1. Var declared local variables.
+// var foo;
+// 2. A local variable introduced by a function declaration.
+// function foo() {}
+// 3. Parameters
+// function x(foo) {}
+// 4. Catch bound variables.
+// try {} catch (foo) {}
+// 6. Function variables of named function expressions.
+// var x = function foo() {}
+// 7. Implicit binding of 'this'.
+// 8. Implicit binding of 'arguments' in functions.
+//
+// ES5 specified object environment records which are introduced by ES elements
+// such as Program and WithStatement that associate identifier bindings with the
+// properties of some object. In the specification only mutable bindings exist
+// (which may be non-writable) and have no distinct initialization step. However
+// V8 allows const declarations in global code with distinct creation and
+// initialization steps which are represented by non-writable properties in the
+// global object. As a result also these bindings need to be checked for
+// initialization.
+//
+// The following enum specifies a flag that indicates if the binding needs a
+// distinct initialization step (kNeedsInitialization) or if the binding is
+// immediately initialized upon creation (kCreatedInitialized).
+enum InitializationFlag {
+ kNeedsInitialization,
+ kCreatedInitialized
+};
+
+
enum ClearExceptionFlag {
KEEP_EXCEPTION,
CLEAR_EXCEPTION
Handle<String> name,
VariableMode mode,
bool is_valid_LHS,
- Kind kind)
+ Kind kind,
+ InitializationFlag initialization_flag)
: scope_(scope),
name_(name),
mode_(mode),
local_if_not_shadowed_(NULL),
is_valid_LHS_(is_valid_LHS),
is_accessed_from_inner_scope_(false),
- is_used_(false) {
- // names must be canonicalized for fast equality checks
+ is_used_(false),
+ initialization_flag_(initialization_flag) {
+ // Names must be canonicalized for fast equality checks.
ASSERT(name->IsSymbol());
+ // Var declared variables never need initialization.
+ ASSERT(!(mode == VAR && initialization_flag == kNeedsInitialization));
}
Handle<String> name,
VariableMode mode,
bool is_valid_lhs,
- Kind kind);
+ Kind kind,
+ InitializationFlag initialization_flag);
// Printing support
static const char* Mode2String(VariableMode mode);
mode_ == CONST_HARMONY);
}
bool binding_needs_init() const {
- return (mode_ == LET ||
- mode_ == CONST ||
- mode_ == CONST_HARMONY);
+ return initialization_flag_ == kNeedsInitialization;
}
bool is_global() const;
Location location() const { return location_; }
int index() const { return index_; }
+ InitializationFlag initialization_flag() const {
+ return initialization_flag_;
+ }
void AllocateTo(Location location, int index) {
location_ = location;
// Usage info.
bool is_accessed_from_inner_scope_; // set by variable resolver
bool is_used_;
+ InitializationFlag initialization_flag_;
};
// 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;
+ bool binding_needs_init = (function == NULL) &&
+ (mode == CONST || mode == CONST_HARMONY || mode == LET);
switch (variable->location()) {
case Variable::UNALLOCATED:
++(*global_count);