static const int kNodeIsIndependentShift = 3;
static const int kNodeIsPartiallyDependentShift = 4;
- static const int kJSObjectType = 0xb6;
+ static const int kJSObjectType = 0xb7;
static const int kFirstNonstringType = 0x80;
static const int kOddballType = 0x83;
static const int kForeignType = 0x87;
for (int i = 0; i < outdated_contexts->length(); ++i) {
Context* context = Context::cast(outdated_contexts->get(i));
if (context->IsScriptContext()) {
- ScopeInfo* scope_info = ScopeInfo::cast(context->extension());
+ ScopeInfo* scope_info = context->scope_info();
int slot = scope_info->ReceiverContextSlotIndex();
if (slot >= 0) {
DCHECK_EQ(slot, Context::MIN_CONTEXT_SLOTS);
VisitStatements(stmt->statements());
} else {
// Visit declarations and statements in a block scope.
- if (stmt->scope()->ContextLocalCount() > 0) {
+ if (stmt->scope()->NeedsContext()) {
Node* context = BuildLocalBlockContext(stmt->scope());
ContextScope scope(this, stmt->scope(), context);
VisitDeclarations(stmt->scope()->declarations());
for (int i = 0; i < table->used(); i++) {
Handle<Context> context = GetContext(table, i);
DCHECK(context->IsScriptContext());
- Handle<ScopeInfo> scope_info(ScopeInfo::cast(context->extension()));
+ Handle<ScopeInfo> scope_info(context->scope_info());
int slot_index = ScopeInfo::ContextSlotIndex(
scope_info, name, &result->mode, &result->location, &result->init_flag,
&result->maybe_assigned_flag);
}
+bool Context::is_declaration_context() {
+ if (IsFunctionContext() || IsNativeContext() || IsScriptContext()) {
+ return true;
+ }
+ if (!IsBlockContext()) return false;
+ Object* ext = extension();
+ // If we have the special extension, we immediately know it must be a
+ // declaration scope. That's just a small performance shortcut.
+ return ext->IsSloppyBlockWithEvalContextExtension()
+ || ScopeInfo::cast(ext)->is_declaration_scope();
+}
+
+
Context* Context::declaration_context() {
Context* current = this;
- while (!current->IsFunctionContext() && !current->IsNativeContext() &&
- !current->IsScriptContext()) {
+ while (!current->is_declaration_context()) {
current = current->previous();
DCHECK(current->closure() == closure());
}
}
+JSObject* Context::extension_object() {
+ DCHECK(IsNativeContext() || IsFunctionContext() || IsBlockContext());
+ Object* object = extension();
+ if (object == nullptr) return nullptr;
+ if (IsBlockContext()) {
+ if (!object->IsSloppyBlockWithEvalContextExtension()) return nullptr;
+ object = SloppyBlockWithEvalContextExtension::cast(object)->extension();
+ }
+ DCHECK(object->IsJSContextExtensionObject() ||
+ (IsNativeContext() && object->IsJSGlobalObject()));
+ return JSObject::cast(object);
+}
+
+
+JSReceiver* Context::extension_receiver() {
+ DCHECK(IsNativeContext() || IsWithContext() ||
+ IsFunctionContext() || IsBlockContext());
+ return IsWithContext() ? JSReceiver::cast(extension()) : extension_object();
+}
+
+
+ScopeInfo* Context::scope_info() {
+ DCHECK(IsModuleContext() || IsScriptContext() || IsBlockContext());
+ Object* object = extension();
+ if (object->IsSloppyBlockWithEvalContextExtension()) {
+ DCHECK(IsBlockContext());
+ object = SloppyBlockWithEvalContextExtension::cast(object)->scope_info();
+ }
+ return ScopeInfo::cast(object);
+}
+
+
+String* Context::catch_name() {
+ DCHECK(IsCatchContext());
+ return String::cast(extension());
+}
+
+
JSBuiltinsObject* Context::builtins() {
GlobalObject* object = global_object();
if (object->IsJSGlobalObject()) {
PrintF("\n");
}
-
// 1. Check global objects, subjects of with, and extension objects.
- if (context->IsNativeContext() ||
- context->IsWithContext() ||
- (context->IsFunctionContext() && context->has_extension())) {
- Handle<JSReceiver> object(
- JSReceiver::cast(context->extension()), isolate);
+ if ((context->IsNativeContext() || context->IsWithContext() ||
+ context->IsFunctionContext() || context->IsBlockContext()) &&
+ context->extension_receiver() != nullptr) {
+ Handle<JSReceiver> object(context->extension_receiver());
if (context->IsNativeContext()) {
if (FLAG_trace_contexts) {
context->IsScriptContext()) {
// Use serialized scope information of functions and blocks to search
// for the context index.
- Handle<ScopeInfo> scope_info;
- if (context->IsFunctionContext()) {
- scope_info = Handle<ScopeInfo>(
- context->closure()->shared()->scope_info(), isolate);
- } else {
- scope_info = Handle<ScopeInfo>(
- ScopeInfo::cast(context->extension()), isolate);
- }
+ Handle<ScopeInfo> scope_info(context->IsFunctionContext()
+ ? context->closure()->shared()->scope_info()
+ : context->scope_info());
VariableMode mode;
VariableLocation location;
InitializationFlag init_flag;
} else if (context->IsCatchContext()) {
// Catch contexts have the variable name in the extension slot.
- if (String::Equals(name, handle(String::cast(context->extension())))) {
+ if (String::Equals(name, handle(context->catch_name()))) {
if (FLAG_trace_contexts) {
PrintF("=> found in catch context\n");
}
DCHECK(IsScriptContext());
DisallowHeapAllocation no_gc;
- ScopeInfo* scope_info = ScopeInfo::cast(extension());
+ ScopeInfo* scope_info = this->scope_info();
int context_globals = scope_info->ContextGlobalCount();
if (context_globals > 0) {
// Dynamically declared variables/functions are also added
// to lazily allocated extension object. Context::Lookup
// searches the extension object for properties.
-// For global and block contexts, contains the respective
-// ScopeInfo.
+// For script and block contexts, contains the respective
+// ScopeInfo. For block contexts representing sloppy declaration
+// block scopes, it may also be a struct being a
+// SloppyBlockWithEvalContextExtension, pairing the ScopeInfo
+// with an extension object.
// For module contexts, points back to the respective JSModule.
//
// [ global_object ] A pointer to the global object. Provided for quick
}
void set_previous(Context* context) { set(PREVIOUS_INDEX, context); }
- bool has_extension() { return extension() != NULL; }
+ bool has_extension() { return extension() != nullptr; }
Object* extension() { return get(EXTENSION_INDEX); }
void set_extension(Object* object) { set(EXTENSION_INDEX, object); }
+ JSObject* extension_object();
+ JSReceiver* extension_receiver();
+ ScopeInfo* scope_info();
+ String* catch_name();
JSModule* module() { return JSModule::cast(get(EXTENSION_INDEX)); }
void set_module(JSModule* module) { set(EXTENSION_INDEX, module); }
// Get the context where var declarations will be hoisted to, which
// may be the context itself.
Context* declaration_context();
+ bool is_declaration_context();
GlobalObject* global_object() {
Object* result = get(GLOBAL_OBJECT_INDEX);
return MaterializeLocalScope();
case ScopeIterator::ScopeTypeWith:
// Return the with object.
- return Handle<JSObject>(JSObject::cast(CurrentContext()->extension()));
+ // TODO(neis): This breaks for proxies.
+ return handle(JSObject::cast(CurrentContext()->extension_receiver()));
case ScopeIterator::ScopeTypeCatch:
return MaterializeCatchScope();
case ScopeIterator::ScopeTypeClosure:
if (!nested_scope_chain_.is_empty()) {
return nested_scope_chain_.last();
} else if (context_->IsBlockContext()) {
- return Handle<ScopeInfo>(ScopeInfo::cast(context_->extension()));
+ return Handle<ScopeInfo>(context_->scope_info());
} else if (context_->IsFunctionContext()) {
return Handle<ScopeInfo>(context_->closure()->shared()->scope_info());
}
context_index++) {
Handle<Context> context =
ScriptContextTable::GetContext(script_contexts, context_index);
- Handle<ScopeInfo> scope_info(ScopeInfo::cast(context->extension()));
+ Handle<ScopeInfo> scope_info(context->scope_info());
CopyContextLocalsToScopeObject(scope_info, context, script_scope);
}
return script_scope;
// Finally copy any properties from the function context extension.
// These will be variables introduced by eval.
- if (function_context->closure() == *function) {
- if (function_context->has_extension() &&
- !function_context->IsNativeContext()) {
- Handle<JSObject> ext(JSObject::cast(function_context->extension()));
- Handle<FixedArray> keys;
- ASSIGN_RETURN_ON_EXCEPTION(
- isolate_, keys, JSReceiver::GetKeys(ext, JSReceiver::INCLUDE_PROTOS),
- JSObject);
-
- for (int i = 0; i < keys->length(); i++) {
- // Names of variables introduced by eval are strings.
- DCHECK(keys->get(i)->IsString());
- Handle<String> key(String::cast(keys->get(i)));
- Handle<Object> value;
- ASSIGN_RETURN_ON_EXCEPTION(
- isolate_, value, Object::GetPropertyOrElement(ext, key), JSObject);
- RETURN_ON_EXCEPTION(isolate_,
- Runtime::SetObjectProperty(isolate_, local_scope,
- key, value, SLOPPY),
- JSObject);
- }
- }
+ if (function_context->closure() == *function &&
+ function_context->has_extension() &&
+ !function_context->IsNativeContext()) {
+ bool success = CopyContextExtensionToScopeObject(
+ handle(function_context->extension_object(), isolate_),
+ local_scope, JSReceiver::INCLUDE_PROTOS);
+ if (!success) return MaybeHandle<JSObject>();
}
return local_scope;
// Finally copy any properties from the function context extension. This will
// be variables introduced by eval.
if (context->has_extension()) {
- Handle<JSObject> ext(JSObject::cast(context->extension()));
- DCHECK(ext->IsJSContextExtensionObject());
- Handle<FixedArray> keys =
- JSReceiver::GetKeys(ext, JSReceiver::OWN_ONLY).ToHandleChecked();
-
- for (int i = 0; i < keys->length(); i++) {
- HandleScope scope(isolate_);
- // Names of variables introduced by eval are strings.
- DCHECK(keys->get(i)->IsString());
- Handle<String> key(String::cast(keys->get(i)));
- Handle<Object> value = Object::GetProperty(ext, key).ToHandleChecked();
- JSObject::SetOwnPropertyIgnoreAttributes(closure_scope, key, value, NONE)
- .Check();
- }
+ bool success = CopyContextExtensionToScopeObject(
+ handle(context->extension_object(), isolate_), closure_scope,
+ JSReceiver::OWN_ONLY);
+ DCHECK(success);
+ USE(success);
}
return closure_scope;
Handle<JSObject> ScopeIterator::MaterializeCatchScope() {
Handle<Context> context = CurrentContext();
DCHECK(context->IsCatchContext());
- Handle<String> name(String::cast(context->extension()));
+ Handle<String> name(context->catch_name());
Handle<Object> thrown_object(context->get(Context::THROWN_OBJECT_INDEX),
isolate_);
Handle<JSObject> catch_scope =
}
if (!context.is_null()) {
- Handle<ScopeInfo> scope_info_from_context(
- ScopeInfo::cast(context->extension()));
// Fill all context locals.
- CopyContextLocalsToScopeObject(scope_info_from_context, context,
- block_scope);
+ CopyContextLocalsToScopeObject(handle(context->scope_info()),
+ context, block_scope);
+ // Fill all extension variables.
+ if (context->extension_object() != nullptr) {
+ bool success = CopyContextExtensionToScopeObject(
+ handle(context->extension_object()), block_scope,
+ JSReceiver::OWN_ONLY);
+ DCHECK(success);
+ USE(success);
+ }
}
return block_scope;
}
MaybeHandle<JSObject> ScopeIterator::MaterializeModuleScope() {
Handle<Context> context = CurrentContext();
DCHECK(context->IsModuleContext());
- Handle<ScopeInfo> scope_info(ScopeInfo::cast(context->extension()));
+ Handle<ScopeInfo> scope_info(context->scope_info());
// Allocate and initialize a JSObject with all the members of the debugged
// module.
if (function_context->closure() == *function) {
if (function_context->has_extension() &&
!function_context->IsNativeContext()) {
- Handle<JSObject> ext(JSObject::cast(function_context->extension()));
+ Handle<JSObject> ext(function_context->extension_object());
Maybe<bool> maybe = JSReceiver::HasProperty(ext, variable_name);
DCHECK(maybe.IsJust());
}
if (HasContext()) {
- return SetContextLocalValue(scope_info, CurrentContext(), variable_name,
- new_value);
+ Handle<Context> context = CurrentContext();
+ if (SetContextLocalValue(scope_info, context, variable_name, new_value)) {
+ return true;
+ }
+
+ Handle<JSObject> ext(context->extension_object(), isolate_);
+ if (!ext.is_null()) {
+ Maybe<bool> maybe = JSReceiver::HasOwnProperty(ext, variable_name);
+ DCHECK(maybe.IsJust());
+ if (maybe.FromJust()) {
+ // We don't expect this to do anything except replacing property value.
+ JSObject::SetOwnPropertyIgnoreAttributes(ext, variable_name, new_value,
+ NONE)
+ .Check();
+ return true;
+ }
+ }
}
+
return false;
}
// Properties from the function context extension. This will
// be variables introduced by eval.
if (context->has_extension()) {
- Handle<JSObject> ext(JSObject::cast(context->extension()));
- DCHECK(ext->IsJSContextExtensionObject());
+ Handle<JSObject> ext(JSObject::cast(context->extension_object()));
Maybe<bool> maybe = JSReceiver::HasOwnProperty(ext, variable_name);
DCHECK(maybe.IsJust());
if (maybe.FromJust()) {
Handle<Object> new_value) {
Handle<Context> context = CurrentContext();
DCHECK(context->IsCatchContext());
- Handle<String> name(String::cast(context->extension()));
+ Handle<String> name(context->catch_name());
if (!String::Equals(name, variable_name)) {
return false;
}
}
}
+
+bool ScopeIterator::CopyContextExtensionToScopeObject(
+ Handle<JSObject> extension, Handle<JSObject> scope_object,
+ JSReceiver::KeyCollectionType type) {
+ Handle<FixedArray> keys;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate_, keys, JSReceiver::GetKeys(extension, type), false);
+
+ for (int i = 0; i < keys->length(); i++) {
+ // Names of variables introduced by eval are strings.
+ DCHECK(keys->get(i)->IsString());
+ Handle<String> key(String::cast(keys->get(i)));
+ Handle<Object> value;
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+ isolate_, value, Object::GetPropertyOrElement(extension, key), false);
+ RETURN_ON_EXCEPTION_VALUE(
+ isolate_, JSObject::SetOwnPropertyIgnoreAttributes(
+ scope_object, key, value, NONE), false);
+ }
+ return true;
+}
+
} // namespace internal
} // namespace v8
void CopyContextLocalsToScopeObject(Handle<ScopeInfo> scope_info,
Handle<Context> context,
Handle<JSObject> scope_object);
+ bool CopyContextExtensionToScopeObject(Handle<JSObject> extension,
+ Handle<JSObject> scope_object,
+ JSReceiver::KeyCollectionType type);
DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
};
}
+Handle<SloppyBlockWithEvalContextExtension>
+Factory::NewSloppyBlockWithEvalContextExtension(
+ Handle<ScopeInfo> scope_info, Handle<JSObject> extension) {
+ DCHECK(scope_info->is_declaration_scope());
+ Handle<SloppyBlockWithEvalContextExtension> result =
+ Handle<SloppyBlockWithEvalContextExtension>::cast(
+ NewStruct(SLOPPY_BLOCK_WITH_EVAL_CONTEXT_EXTENSION_TYPE));
+ result->set_scope_info(*scope_info);
+ result->set_extension(*extension);
+ return result;
+}
+
+
Handle<Oddball> Factory::NewOddball(Handle<Map> map, const char* to_string,
Handle<Object> to_number,
const char* type_of, byte kind) {
// Create a new PrototypeInfo struct.
Handle<PrototypeInfo> NewPrototypeInfo();
+ // Create a new SloppyBlockWithEvalContextExtension struct.
+ Handle<SloppyBlockWithEvalContextExtension>
+ NewSloppyBlockWithEvalContextExtension(Handle<ScopeInfo> scope_info,
+ Handle<JSObject> extension);
+
// Create a pre-tenured empty AccessorPair.
Handle<AccessorPair> NewAccessorPair();
codegen_->PrepareForBailoutForId(entry_id, NO_REGISTERS);
needs_block_context_ = false;
} else {
- needs_block_context_ = scope->ContextLocalCount() > 0;
+ needs_block_context_ = scope->NeedsContext();
codegen_->scope_ = scope;
{
if (needs_block_context_) {
{ BreakAndContinueScope push(&break_info, this);
if (scope != NULL) {
- if (scope->ContextLocalCount() > 0) {
+ if (scope->NeedsContext()) {
// Load the function object.
Scope* declaration_scope = scope->DeclarationScope();
HInstruction* function;
}
+void SloppyBlockWithEvalContextExtension::
+ SloppyBlockWithEvalContextExtensionVerify() {
+ CHECK(IsSloppyBlockWithEvalContextExtension());
+ VerifyObjectField(kScopeInfoOffset);
+ VerifyObjectField(kExtensionOffset);
+}
+
+
void ExecutableAccessorInfo::ExecutableAccessorInfoVerify() {
CHECK(IsExecutableAccessorInfo());
AccessorInfoVerify();
ACCESSORS(PrototypeInfo, validity_cell, Object, kValidityCellOffset)
ACCESSORS(PrototypeInfo, constructor_name, Object, kConstructorNameOffset)
+ACCESSORS(SloppyBlockWithEvalContextExtension, scope_info, ScopeInfo,
+ kScopeInfoOffset)
+ACCESSORS(SloppyBlockWithEvalContextExtension, extension, JSObject,
+ kExtensionOffset)
+
ACCESSORS(AccessorPair, getter, Object, kGetterOffset)
ACCESSORS(AccessorPair, setter, Object, kSetterOffset)
}
+void SloppyBlockWithEvalContextExtension::
+ SloppyBlockWithEvalContextExtensionPrint(std::ostream& os) { // NOLINT
+ HeapObject::PrintHeader(os, "SloppyBlockWithEvalContextExtension");
+ os << "\n - scope_info: " << Brief(scope_info());
+ os << "\n - extension: " << Brief(extension());
+ os << "\n";
+}
+
+
void AccessorPair::AccessorPairPrint(std::ostream& os) { // NOLINT
HeapObject::PrintHeader(os, "AccessorPair");
os << "\n - getter: " << Brief(getter());
// Check the context extension (if any) if it can have references.
if (context->has_extension() && !context->IsCatchContext()) {
- // With harmony scoping, a JSFunction may have a global context.
+ // With harmony scoping, a JSFunction may have a script context.
// TODO(mvstanton): walk into the ScopeInfo.
if (context->IsScriptContext()) {
return false;
}
- return JSObject::cast(context->extension())->ReferencesObject(obj);
+ return context->extension_object()->ReferencesObject(obj);
}
}
V(ALIASED_ARGUMENTS_ENTRY_TYPE) \
V(BOX_TYPE) \
V(PROTOTYPE_INFO_TYPE) \
+ V(SLOPPY_BLOCK_WITH_EVAL_CONTEXT_EXTENSION_TYPE) \
\
V(FIXED_ARRAY_TYPE) \
V(FIXED_DOUBLE_ARRAY_TYPE) \
V(ALIASED_ARGUMENTS_ENTRY, AliasedArgumentsEntry, aliased_arguments_entry) \
V(DEBUG_INFO, DebugInfo, debug_info) \
V(BREAK_POINT_INFO, BreakPointInfo, break_point_info) \
- V(PROTOTYPE_INFO, PrototypeInfo, prototype_info)
+ V(PROTOTYPE_INFO, PrototypeInfo, prototype_info) \
+ V(SLOPPY_BLOCK_WITH_EVAL_CONTEXT_EXTENSION, \
+ SloppyBlockWithEvalContextExtension, \
+ sloppy_block_with_eval_context_extension)
// We use the full 8 bits of the instance_type field to encode heap object
// instance types. The high-order bit (bit 7) is set if the object is not a
WEAK_CELL_TYPE,
PROPERTY_CELL_TYPE,
PROTOTYPE_INFO_TYPE,
+ SLOPPY_BLOCK_WITH_EVAL_CONTEXT_EXTENSION_TYPE,
// All the following types are subtypes of JSReceiver, which corresponds to
// objects in the JS sense. The first and the last type in this range are
// Return the language mode of this scope.
LanguageMode language_mode();
+ // True if this scope is a (var) declaration scope.
+ bool is_declaration_scope();
+
// Does this scope make a sloppy eval call?
bool CallsSloppyEval() { return CallsEval() && is_sloppy(language_mode()); }
STATIC_ASSERT(LANGUAGE_END == 3);
class LanguageModeField
: public BitField<LanguageMode, CallsEvalField::kNext, 2> {};
+ class DeclarationScopeField
+ : public BitField<bool, LanguageModeField::kNext, 1> {};
class ReceiverVariableField
- : public BitField<VariableAllocationInfo, LanguageModeField::kNext, 2> {};
+ : public BitField<VariableAllocationInfo, DeclarationScopeField::kNext,
+ 2> {};
class FunctionVariableField
: public BitField<VariableAllocationInfo, ReceiverVariableField::kNext,
2> {};
};
+// Pair used to store both a ScopeInfo and an extension object in the extension
+// slot of a block context. Needed in the rare case where a declaration block
+// scope (a "varblock" as used to desugar parameter destructuring) also contains
+// a sloppy direct eval. (In no other case both are needed at the same time.)
+class SloppyBlockWithEvalContextExtension : public Struct {
+ public:
+ // [scope_info]: Scope info.
+ DECL_ACCESSORS(scope_info, ScopeInfo)
+ // [extension]: Extension object.
+ DECL_ACCESSORS(extension, JSObject)
+
+ DECLARE_CAST(SloppyBlockWithEvalContextExtension)
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(SloppyBlockWithEvalContextExtension)
+ DECLARE_VERIFIER(SloppyBlockWithEvalContextExtension)
+
+ static const int kScopeInfoOffset = HeapObject::kHeaderSize;
+ static const int kExtensionOffset = kScopeInfoOffset + kPointerSize;
+ static const int kSize = kExtensionOffset + kPointerSize;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(SloppyBlockWithEvalContextExtension);
+};
+
+
// Script describes a script which has been added to the VM.
class Script: public Struct {
public:
Variable* Parser::Declare(Declaration* declaration,
DeclarationDescriptor::Kind declaration_kind,
- bool resolve, bool* ok) {
+ bool resolve, bool* ok, Scope* scope) {
VariableProxy* proxy = declaration->proxy();
DCHECK(proxy->raw_name() != NULL);
const AstRawString* name = proxy->raw_name();
VariableMode mode = declaration->mode();
- Scope* declaration_scope = DeclarationScope(mode);
+ if (scope == nullptr) scope = scope_;
+ Scope* declaration_scope =
+ IsLexicalVariableMode(mode) ? scope : scope->DeclarationScope();
Variable* var = NULL;
// If a suitable scope exists, then we can statically declare this
parsing_result->descriptor.declaration_scope =
DeclarationScope(parsing_result->descriptor.mode);
parsing_result->descriptor.scope = scope_;
+ parsing_result->descriptor.hoist_scope = nullptr;
bool first_declaration = true;
descriptor.parser = this;
descriptor.declaration_scope = scope_;
descriptor.scope = scope_;
+ descriptor.hoist_scope = nullptr;
descriptor.mode = LET;
descriptor.is_const = false;
descriptor.needs_init = true;
RelocInfo::kNoPosition);
descriptor.initialization_pos = parameter.initializer->position();
}
- DeclarationParsingResult::Declaration decl(
- parameter.pattern, parameter.pattern->position(), initial_value);
- PatternRewriter::DeclareAndInitializeVariables(init_block, &descriptor,
- &decl, nullptr, CHECK_OK);
+
+ Scope* param_scope = scope_;
+ Block* param_block = init_block;
+ if (parameter.initializer != nullptr && scope_->calls_sloppy_eval()) {
+ param_scope = NewScope(scope_, BLOCK_SCOPE);
+ param_scope->set_is_declaration_scope();
+ param_scope->set_start_position(parameter.pattern->position());
+ param_scope->set_end_position(RelocInfo::kNoPosition);
+ param_scope->RecordEvalCall();
+ param_block = factory()->NewBlock(NULL, 8, true, RelocInfo::kNoPosition);
+ param_block->set_scope(param_scope);
+ descriptor.hoist_scope = scope_;
+ }
+
+ {
+ BlockState block_state(&scope_, param_scope);
+ DeclarationParsingResult::Declaration decl(
+ parameter.pattern, parameter.pattern->position(), initial_value);
+ PatternRewriter::DeclareAndInitializeVariables(param_block, &descriptor,
+ &decl, nullptr, CHECK_OK);
+ }
+
+ if (parameter.initializer != nullptr && scope_->calls_sloppy_eval()) {
+ param_scope = param_scope->FinalizeBlockScope();
+ if (param_scope != nullptr) {
+ CheckConflictingVarDeclarations(param_scope, CHECK_OK);
+ }
+ init_block->AddStatement(param_block, zone());
+ }
}
return init_block;
}
}
ZoneList<Statement*>* body = result;
- Scope* inner_scope = nullptr;
+ Scope* inner_scope = scope_;
Block* inner_block = nullptr;
if (!parameters.is_simple) {
inner_scope = NewScope(scope_, BLOCK_SCOPE);
}
{
- BlockState block_state(&scope_, inner_scope ? inner_scope : scope_);
+ BlockState block_state(&scope_, inner_scope);
// For generators, allocate and yield an iterator on function entry.
if (IsGeneratorFunction(kind)) {
Parser* parser;
Scope* declaration_scope;
Scope* scope;
+ Scope* hoist_scope;
VariableMode mode;
bool is_const;
bool needs_init;
VariableProxy* NewUnresolved(const AstRawString* name, VariableMode mode);
Variable* Declare(Declaration* declaration,
DeclarationDescriptor::Kind declaration_kind, bool resolve,
- bool* ok);
+ bool* ok, Scope* declaration_scope = nullptr);
bool TargetStackContainsLabel(const AstRawString* label);
BreakableStatement* LookupBreakTarget(const AstRawString* label, bool* ok);
proxy, descriptor_->mode, descriptor_->scope,
descriptor_->declaration_pos);
Variable* var = parser->Declare(declaration, descriptor_->declaration_kind,
- descriptor_->mode != VAR, ok_);
+ descriptor_->mode != VAR, ok_,
+ descriptor_->hoist_scope);
if (!*ok_) return;
DCHECK_NOT_NULL(var);
DCHECK(!proxy->is_resolved() || proxy->var() == var);
DCHECK(script_context->get(slot)->IsPropertyCell());
// Lookup the named property on the global object.
- Handle<ScopeInfo> scope_info(ScopeInfo::cast(script_context->extension()),
- isolate);
+ Handle<ScopeInfo> scope_info(script_context->scope_info(), isolate);
Handle<Name> name(scope_info->ContextSlotName(slot), isolate);
Handle<GlobalObject> global_object(script_context->global_object(), isolate);
LookupIterator it(global_object, name, LookupIterator::HIDDEN);
DCHECK(script_context->get(slot)->IsPropertyCell());
// Lookup the named property on the global object.
- Handle<ScopeInfo> scope_info(ScopeInfo::cast(script_context->extension()),
- isolate);
+ Handle<ScopeInfo> scope_info(script_context->scope_info(), isolate);
Handle<Name> name(scope_info->ContextSlotName(slot), isolate);
Handle<GlobalObject> global_object(script_context->global_object(), isolate);
LookupIterator it(global_object, name, LookupIterator::HIDDEN);
Object* DeclareLookupSlot(Isolate* isolate, Handle<String> name,
Handle<Object> initial_value,
PropertyAttributes attr) {
- // Declarations are always made in a function, eval or script context. In
- // the case of eval code, the context passed is the context of the caller,
+ // Declarations are always made in a function, eval or script context, or
+ // a declaration block scope.
+ // In the case of eval code, the context passed is the context of the caller,
// which may be some nested context and not the declaration context.
Handle<Context> context_arg(isolate->context(), isolate);
Handle<Context> context(context_arg->declaration_context(), isolate);
return DeclareGlobals(isolate, Handle<JSGlobalObject>::cast(holder), name,
value, attr, is_var, is_const, is_function);
}
- if (context_arg->has_extension() &&
- context_arg->extension()->IsJSGlobalObject()) {
+ if (context_arg->extension()->IsJSGlobalObject()) {
Handle<JSGlobalObject> global(
JSGlobalObject::cast(context_arg->extension()), isolate);
return DeclareGlobals(isolate, global, name, value, attr, is_var, is_const,
object = Handle<JSObject>::cast(holder);
} else if (context->has_extension()) {
- object = handle(JSObject::cast(context->extension()));
+ // Sloppy varblock contexts might not have an extension object yet,
+ // in which case their extension is a ScopeInfo.
+ if (context->extension()->IsScopeInfo()) {
+ DCHECK(context->IsBlockContext());
+ object = isolate->factory()->NewJSObject(
+ isolate->context_extension_function());
+ Handle<Object> extension =
+ isolate->factory()->NewSloppyBlockWithEvalContextExtension(
+ handle(context->scope_info()), object);
+ context->set_extension(*extension);
+ } else {
+ object = handle(context->extension_object(), isolate);
+ }
DCHECK(object->IsJSContextExtensionObject() || object->IsJSGlobalObject());
} else {
DCHECK(context->IsFunctionContext());
if (declaration_context->IsScriptContext()) {
holder = handle(declaration_context->global_object(), isolate);
} else {
- DCHECK(declaration_context->has_extension());
- holder = handle(declaration_context->extension(), isolate);
+ holder = handle(declaration_context->extension_object(), isolate);
+ DCHECK(!holder.is_null());
}
CHECK(holder->IsJSObject());
} else {
int flags = ScopeTypeField::encode(scope->scope_type()) |
CallsEvalField::encode(scope->calls_eval()) |
LanguageModeField::encode(scope->language_mode()) |
+ DeclarationScopeField::encode(scope->is_declaration_scope()) |
ReceiverVariableField::encode(receiver_info) |
FunctionVariableField::encode(function_name_info) |
FunctionVariableMode::encode(function_variable_mode) |
int flags = ScopeTypeField::encode(SCRIPT_SCOPE) |
CallsEvalField::encode(false) |
LanguageModeField::encode(SLOPPY) |
+ DeclarationScopeField::encode(true) |
ReceiverVariableField::encode(receiver_info) |
FunctionVariableField::encode(function_name_info) |
FunctionVariableMode::encode(function_variable_mode) |
}
+bool ScopeInfo::is_declaration_scope() {
+ return DeclarationScopeField::decode(Flags());
+}
+
+
int ScopeInfo::LocalCount() {
return StackLocalCount() + ContextLocalCount();
}
bool has_context = context_locals > 0 || context_globals > 0 ||
function_name_context_slot ||
scope_type() == WITH_SCOPE ||
+ (scope_type() == BLOCK_SCOPE && CallsSloppyEval() &&
+ is_declaration_scope()) ||
(scope_type() == ARROW_SCOPE && CallsSloppyEval()) ||
(scope_type() == FUNCTION_SCOPE && CallsSloppyEval()) ||
scope_type() == MODULE_SCOPE;
if (!scope_info.is_null()) {
scope_calls_eval_ = scope_info->CallsEval();
language_mode_ = scope_info->language_mode();
+ is_declaration_scope_ = scope_info->is_declaration_scope();
function_kind_ = scope_info->function_kind();
}
}
s->scope_inside_with_ = true;
}
} else if (context->IsScriptContext()) {
- ScopeInfo* scope_info = ScopeInfo::cast(context->extension());
+ ScopeInfo* scope_info = context->scope_info();
current_scope = new (zone) Scope(zone, current_scope, SCRIPT_SCOPE,
Handle<ScopeInfo>(scope_info),
script_scope->ast_value_factory_);
} else if (context->IsModuleContext()) {
- ScopeInfo* scope_info = ScopeInfo::cast(context->module()->scope_info());
+ ScopeInfo* scope_info = context->module()->scope_info();
current_scope = new (zone) Scope(zone, current_scope, MODULE_SCOPE,
Handle<ScopeInfo>(scope_info),
script_scope->ast_value_factory_);
if (scope_info->IsAsmFunction()) current_scope->asm_function_ = true;
if (scope_info->IsAsmModule()) current_scope->asm_module_ = true;
} else if (context->IsBlockContext()) {
- ScopeInfo* scope_info = ScopeInfo::cast(context->extension());
+ ScopeInfo* scope_info = context->scope_info();
current_scope = new (zone)
Scope(zone, current_scope, BLOCK_SCOPE, Handle<ScopeInfo>(scope_info),
script_scope->ast_value_factory_);
} else {
DCHECK(context->IsCatchContext());
- String* name = String::cast(context->extension());
+ String* name = context->catch_name();
current_scope = new (zone) Scope(
zone, current_scope,
script_scope->ast_value_factory_->GetString(Handle<String>(name)),
DCHECK(temps_.is_empty());
DCHECK(params_.is_empty());
- if (num_var_or_const() > 0) return this;
+ if (num_var_or_const() > 0 ||
+ (is_declaration_scope() && calls_sloppy_eval())) {
+ return this;
+ }
// Remove this scope from outer scope.
for (int i = 0; i < outer_scope_->inner_scopes_.length(); i++) {
#ifdef DEBUG
-static const char* Header(ScopeType scope_type) {
+static const char* Header(ScopeType scope_type, bool is_declaration_scope) {
switch (scope_type) {
case EVAL_SCOPE: return "eval";
case FUNCTION_SCOPE: return "function";
case MODULE_SCOPE: return "module";
case SCRIPT_SCOPE: return "global";
case CATCH_SCOPE: return "catch";
- case BLOCK_SCOPE: return "block";
+ case BLOCK_SCOPE: return is_declaration_scope ? "varblock" : "block";
case WITH_SCOPE: return "with";
case ARROW_SCOPE: return "arrow";
}
int n1 = n0 + 2; // indentation
// Print header.
- Indent(n0, Header(scope_type_));
+ Indent(n0, Header(scope_type_, is_declaration_scope()));
if (!scope_name_->IsEmpty()) {
PrintF(" ");
PrintName(scope_name_);
// scope and for a function scope that makes an 'eval' call we need a context,
// even if no local variables were statically allocated in the scope.
// Likewise for modules.
- bool must_have_context = is_with_scope() || is_module_scope() ||
- (is_function_scope() && calls_sloppy_eval());
+ bool must_have_context =
+ is_with_scope() || is_module_scope() ||
+ (is_function_scope() && calls_sloppy_eval()) ||
+ (is_block_scope() && is_declaration_scope() && calls_sloppy_eval());
// If we didn't allocate any locals in the local context, then we only
// need the minimal number of slots if we must have a context.
// Information about which scopes calls eval.
bool calls_eval() const { return scope_calls_eval_; }
- bool calls_sloppy_eval() {
+ bool calls_sloppy_eval() const {
return scope_calls_eval_ && is_sloppy(language_mode_);
}
bool outer_scope_calls_sloppy_eval() const {
// Does this scope access "super" property (super.foo).
bool uses_super_property() const { return scope_uses_super_property_; }
+ // Whether this needs to be represented by a runtime context.
+ bool NeedsContext() const { return num_heap_slots() > 0; }
+
bool NeedsHomeObject() const {
return scope_uses_super_property_ ||
(scope_calls_eval_ && (IsConciseMethod(function_kind()) ||
// found in the LICENSE file.
// Flags: --harmony-default-parameters --harmony-arrow-functions
-// Flags: --harmony-rest-parameters
+// Flags: --harmony-rest-parameters --harmony-destructuring
(function TestDefaults() {
(function TestParameterScoping() {
- // TODO(rossberg): Add checks for variable declarations in defaults.
var x = 1;
function f1(a = x) { var x = 2; return a; }
})();
+(function TestSloppyEvalScoping() {
+ var x = 1;
+
+ function f1(y = eval("var x = 2")) { with ({}) { return x; } }
+ assertEquals(1, f1());
+ function f2(y = eval("var x = 2"), z = x) { return z; }
+ assertEquals(1, f2());
+ assertEquals(1, f2(0));
+ function f3(y = eval("var x = 2"), z = eval("x")) { return z; }
+ assertEquals(1, f3());
+ assertEquals(1, f3(0));
+ // TODO(rossberg): eval inside patterns is not recognized yet.
+ // function f41({[eval("var x = 2; 'a'")]: w}, z = x)) { return z; }
+ // assertEquals(1, f41({}));
+ // assertEquals(1, f41({a: 0}));
+ // function f42({[eval("var x = 2; 'a'")]: w}, z = eval("x")) { return z; }
+ // assertEquals(1, f42({}));
+ // assertEquals(1, f42({a: 0}));
+ // function f43({a: w = eval("var x = 2")}, z = x) { return z; }
+ // assertEquals(1, f43({}));
+ // assertEquals(1, f43({a: 0}));
+ // function f44({a: w = eval("var x = 2")}, z = eval("x")) { return z; }
+ // assertEquals(1, f44({}));
+ // assertEquals(1, f44({a: 0}));
+
+ function f5({a = eval("var x = 2"), b = x}) { return b; }
+ assertEquals(2, f5({}));
+ assertEquals(1, f5({a: 0}));
+ function f6({a = eval("var x = 2"), b = eval("x")}) { return b; }
+ assertEquals(2, f6({}));
+ assertEquals(1, f6({a: 0}));
+ function f71({[eval("var x = 2; 'a'")]: w, b = x}) { return b; }
+ assertEquals(2, f71({}));
+ assertEquals(2, f71({a: 0}));
+ function f72({[eval("var x = 2; 'a'")]: w, b = eval("x")}) { return b; }
+ assertEquals(2, f72({}));
+ assertEquals(2, f72({a: 0}));
+ function f73({a: w = eval("var x = 2"), b = x}) { return b; }
+ assertEquals(2, f73({}));
+ assertEquals(1, f73({a: 0}));
+ function f74({a: w = eval("var x = 2"), b = eval("x")}) { return b; }
+ assertEquals(2, f74({}));
+ assertEquals(1, f74({a: 0}));
+ function f8(y = (eval("var x = 2"), x)) { return y; }
+ assertEquals(2, f8());
+ assertEquals(0, f8(0));
+
+ function f11(z = eval("var y = 2")) { return y; }
+ assertThrows(f11, ReferenceError);
+ function f12(z = eval("var y = 2"), b = y) {}
+ assertThrows(f12, ReferenceError);
+ function f13(z = eval("var y = 2"), b = eval("y")) {}
+ assertThrows(f13, ReferenceError);
+
+ function f21(f = () => x) { eval("var x = 2"); return f() }
+ assertEquals(1, f21());
+ assertEquals(3, f21(() => 3));
+ function f22(f = () => eval("x")) { eval("var x = 2"); return f() }
+ assertEquals(1, f22());
+ assertEquals(3, f22(() => 3));
+})();
+
+
+(function TestStrictEvalScoping() {
+ 'use strict';
+ var x = 1;
+
+ function f1(y = eval("var x = 2")) { return x; }
+ assertEquals(1, f1());
+ function f2(y = eval("var x = 2"), z = x) { return z; }
+ assertEquals(1, f2());
+ assertEquals(1, f2(0));
+ function f3(y = eval("var x = 2"), z = eval("x")) { return z; }
+ assertEquals(1, f3());
+ assertEquals(1, f3(0));
+ function f41({[eval("var x = 2; 'a'")]: w}, z = x) { return z; }
+ assertEquals(1, f41({}));
+ assertEquals(1, f41({a: 0}));
+ function f42({[eval("var x = 2; 'a'")]: w}, z = eval("x")) { return z; }
+ assertEquals(1, f42({}));
+ assertEquals(1, f42({a: 0}));
+ function f43({a: w = eval("var x = 2")}, z = x) { return z; }
+ assertEquals(1, f43({}));
+ assertEquals(1, f43({a: 0}));
+ function f44({a: w = eval("var x = 2")}, z = eval("x")) { return z; }
+ assertEquals(1, f44({}));
+ assertEquals(1, f44({a: 0}));
+
+ function f5({a = eval("var x = 2"), b = x}) { return b; }
+ assertEquals(1, f5({}));
+ assertEquals(1, f5({a: 0}));
+ function f6({a = eval("var x = 2"), b = eval("x")}) { return b; }
+ assertEquals(1, f6({}));
+ assertEquals(1, f6({a: 0}));
+ function f71({[eval("var x = 2; 'a'")]: w, b = x}) { return b; }
+ assertEquals(1, f71({}));
+ assertEquals(1, f71({a: 0}));
+ function f72({[eval("var x = 2; 'a'")]: w, b = eval("x")}) { return b; }
+ assertEquals(1, f72({}));
+ assertEquals(1, f72({a: 0}));
+ function f73({a: w = eval("var x = 2"), b = x}) { return b; }
+ assertEquals(1, f73({}));
+ assertEquals(1, f73({a: 0}));
+ function f74({a: w = eval("var x = 2"), b = eval("x")}) { return b; }
+ assertEquals(1, f74({}));
+ assertEquals(1, f74({a: 0}));
+ function f8(y = (eval("var x = 2"), x)) { return y; }
+ assertEquals(1, f8());
+ assertEquals(0, f8(0));
+
+ function f11(z = eval("var y = 2")) { return y; }
+ assertThrows(f11, ReferenceError);
+ function f12(z = eval("var y = 2"), b = y) {}
+ assertThrows(f12, ReferenceError);
+ function f13(z = eval("var y = 2"), b = eval("y")) {}
+ assertThrows(f13, ReferenceError);
+
+ function f21(f = () => x) { eval("var x = 2"); return f() }
+ assertEquals(1, f21());
+ assertEquals(3, f21(() => 3));
+ function f22(f = () => eval("x")) { eval("var x = 2"); return f() }
+ assertEquals(1, f22());
+ assertEquals(3, f22(() => 3));
+})();
+
+
(function TestParameterTDZ() {
function f1(a = x, x) { return a }
assertThrows(() => f1(undefined, 4), ReferenceError);