Resolve references to "this" the same way as normal variables

Make the parser handle references to "this" as unresolved variables, so the
same logic as for the rest of function parameters is used for the receiver.
Minor additions to the code generation handle copying the receiver to the
context, along with the rest of the function parameters.

Based on work by Adrian Perez de Castro <aperez@igalia.com>.

BUG=v8:2700
LOG=N

Review URL: https://codereview.chromium.org/1130733003

Cr-Commit-Position: refs/heads/master@{#28263}
This commit is contained in:
wingo 2015-05-06 07:18:03 -07:00 committed by Commit bot
parent fe0e49dfc5
commit 06a792b7cc
26 changed files with 313 additions and 120 deletions

View File

@ -127,7 +127,7 @@ void FullCodeGenerator::Generate() {
// global proxy when called as functions (without an explicit receiver
// object).
if (is_sloppy(info->language_mode()) && !info->is_native() &&
info->MayUseThis()) {
info->MayUseThis() && info->scope()->has_this_declaration()) {
Label ok;
int receiver_offset = info->scope()->num_parameters() * kPointerSize;
__ ldr(r2, MemOperand(sp, receiver_offset));
@ -216,8 +216,9 @@ void FullCodeGenerator::Generate() {
__ str(r0, MemOperand(fp, StandardFrameConstants::kContextOffset));
// Copy any necessary parameters into the context.
int num_parameters = info->scope()->num_parameters();
for (int i = 0; i < num_parameters; i++) {
Variable* var = scope()->parameter(i);
int first_parameter = info->scope()->has_this_declaration() ? -1 : 0;
for (int i = first_parameter; i < num_parameters; i++) {
Variable* var = (i == -1) ? scope()->receiver() : scope()->parameter(i);
if (var->IsContextSlot()) {
int parameter_offset = StandardFrameConstants::kCallerSPOffset +
(num_parameters - 1 - i) * kPointerSize;
@ -3066,8 +3067,9 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
__ ldr(r4, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
// r3: the receiver of the enclosing function.
int receiver_offset = 2 + info_->scope()->num_parameters();
__ ldr(r3, MemOperand(fp, receiver_offset * kPointerSize));
Variable* this_var = scope()->LookupThis();
DCHECK_NOT_NULL(this_var);
__ ldr(r3, VarOperand(this_var, r3));
// r2: language mode.
__ mov(r2, Operand(Smi::FromInt(language_mode())));

View File

@ -121,7 +121,7 @@ bool LCodeGen::GeneratePrologue() {
// global proxy when called as functions (without an explicit receiver
// object).
if (is_sloppy(info_->language_mode()) && info()->MayUseThis() &&
!info_->is_native()) {
!info_->is_native() && info_->scope()->has_this_declaration()) {
Label ok;
int receiver_offset = info_->scope()->num_parameters() * kPointerSize;
__ ldr(r2, MemOperand(sp, receiver_offset));
@ -197,8 +197,9 @@ bool LCodeGen::GeneratePrologue() {
__ str(r0, MemOperand(fp, StandardFrameConstants::kContextOffset));
// Copy any necessary parameters into the context.
int num_parameters = scope()->num_parameters();
for (int i = 0; i < num_parameters; i++) {
Variable* var = scope()->parameter(i);
int first_parameter = scope()->has_this_declaration() ? -1 : 0;
for (int i = first_parameter; i < num_parameters; i++) {
Variable* var = (i == -1) ? scope()->receiver() : scope()->parameter(i);
if (var->IsContextSlot()) {
int parameter_offset = StandardFrameConstants::kCallerSPOffset +
(num_parameters - 1 - i) * kPointerSize;

View File

@ -125,7 +125,7 @@ void FullCodeGenerator::Generate() {
// global proxy when called as functions (without an explicit receiver
// object).
if (is_sloppy(info->language_mode()) && !info->is_native() &&
info->MayUseThis()) {
info->MayUseThis() && info->scope()->has_this_declaration()) {
Label ok;
int receiver_offset = info->scope()->num_parameters() * kXRegSize;
__ Peek(x10, receiver_offset);
@ -217,8 +217,9 @@ void FullCodeGenerator::Generate() {
__ Str(x0, MemOperand(fp, StandardFrameConstants::kContextOffset));
// Copy any necessary parameters into the context.
int num_parameters = info->scope()->num_parameters();
for (int i = 0; i < num_parameters; i++) {
Variable* var = scope()->parameter(i);
int first_parameter = info->scope()->has_this_declaration() ? -1 : 0;
for (int i = first_parameter; i < num_parameters; i++) {
Variable* var = (i == -1) ? scope()->receiver() : scope()->parameter(i);
if (var->IsContextSlot()) {
int parameter_offset = StandardFrameConstants::kCallerSPOffset +
(num_parameters - 1 - i) * kPointerSize;
@ -2751,8 +2752,9 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
__ Ldr(x10, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
// Prepare to push the receiver of the enclosing function.
int receiver_offset = 2 + info_->scope()->num_parameters();
__ Ldr(x11, MemOperand(fp, receiver_offset * kPointerSize));
Variable* this_var = scope()->LookupThis();
DCHECK_NOT_NULL(this_var);
__ Ldr(x11, VarOperand(this_var, x11));
// Prepare to push the language mode.
__ Mov(x12, Smi::FromInt(language_mode()));

View File

@ -669,7 +669,7 @@ bool LCodeGen::GeneratePrologue() {
// global proxy when called as functions (without an explicit receiver
// object).
if (is_sloppy(info_->language_mode()) && info()->MayUseThis() &&
!info_->is_native()) {
!info()->is_native() && info()->scope()->has_this_declaration()) {
Label ok;
int receiver_offset = info_->scope()->num_parameters() * kXRegSize;
__ Peek(x10, receiver_offset);
@ -728,8 +728,9 @@ bool LCodeGen::GeneratePrologue() {
__ Str(x0, MemOperand(fp, StandardFrameConstants::kContextOffset));
// Copy any necessary parameters into the context.
int num_parameters = scope()->num_parameters();
for (int i = 0; i < num_parameters; i++) {
Variable* var = scope()->parameter(i);
int first_parameter = scope()->has_this_declaration() ? -1 : 0;
for (int i = first_parameter; i < num_parameters; i++) {
Variable* var = (i == -1) ? scope()->receiver() : scope()->parameter(i);
if (var->IsContextSlot()) {
Register value = x0;
Register scratch = x3;

View File

@ -470,15 +470,21 @@ bool AstGraphBuilder::CreateGraph(bool constant_context, bool stack_check) {
// Build receiver check for sloppy mode if necessary.
// TODO(mstarzinger/verwaest): Should this be moved back into the CallIC?
Node* original_receiver = env.Lookup(scope->receiver());
Node* patched_receiver = BuildPatchReceiverToGlobalProxy(original_receiver);
env.Bind(scope->receiver(), patched_receiver);
Node* patched_receiver = nullptr;
if (scope->has_this_declaration()) {
Node* original_receiver = NewNode(common()->Parameter(0), graph()->start());
patched_receiver = BuildPatchReceiverToGlobalProxy(original_receiver);
if (scope->receiver()->IsStackAllocated()) {
env.Bind(scope->receiver(), patched_receiver);
}
}
// Build function context only if there are context allocated variables.
int heap_slots = info()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
if (heap_slots > 0) {
// Push a new inner context scope for the function.
Node* inner_context = BuildLocalFunctionContext(function_context_.get());
Node* inner_context =
BuildLocalFunctionContext(function_context_.get(), patched_receiver);
ContextScope top_context(this, scope, inner_context);
CreateGraphBody(stack_check);
} else {
@ -2167,7 +2173,23 @@ void AstGraphBuilder::VisitCall(Call* expr) {
// Create node to ask for help resolving potential eval call. This will
// provide a fully resolved callee and the corresponding receiver.
Node* function = GetFunctionClosure();
Node* receiver = environment()->Lookup(info()->scope()->receiver());
// TODO(wingo): ResolvePossibleDirectEval doesn't really need a receiver,
// now that eval scopes don't have "this" declarations. Remove this hack
// once ResolvePossibleDirectEval changes.
Node* receiver;
{
Variable* variable = info()->scope()->LookupThis();
if (variable->IsStackAllocated()) {
receiver = environment()->Lookup(variable);
} else {
DCHECK(variable->IsContextSlot());
int depth = current_scope()->ContextChainLength(variable->scope());
bool immutable = variable->maybe_assigned() == kNotAssigned;
const Operator* op =
javascript()->LoadContext(depth, variable->index(), immutable);
receiver = NewNode(op, current_context());
}
}
Node* language = jsgraph()->Constant(language_mode());
Node* position = jsgraph()->Constant(info()->scope()->start_position());
const Operator* op =
@ -2658,25 +2680,36 @@ Node* AstGraphBuilder::BuildPatchReceiverToGlobalProxy(Node* receiver) {
}
Node* AstGraphBuilder::BuildLocalFunctionContext(Node* context) {
Node* AstGraphBuilder::BuildLocalFunctionContext(Node* context,
Node* patched_receiver) {
Scope* scope = info()->scope();
Node* closure = GetFunctionClosure();
// Allocate a new local context.
Node* local_context =
info()->scope()->is_script_scope()
? BuildLocalScriptContext(info()->scope())
scope->is_script_scope()
? BuildLocalScriptContext(scope)
: NewNode(javascript()->CreateFunctionContext(), closure);
if (scope->has_this_declaration() && scope->receiver()->IsContextSlot()) {
DCHECK_NOT_NULL(patched_receiver);
// Context variable (at bottom of the context chain).
Variable* variable = scope->receiver();
DCHECK_EQ(0, scope->ContextChainLength(variable->scope()));
const Operator* op = javascript()->StoreContext(0, variable->index());
NewNode(op, local_context, patched_receiver);
}
// Copy parameters into context if necessary.
int num_parameters = info()->scope()->num_parameters();
int num_parameters = scope->num_parameters();
for (int i = 0; i < num_parameters; i++) {
Variable* variable = info()->scope()->parameter(i);
Variable* variable = scope->parameter(i);
if (!variable->IsContextSlot()) continue;
// Temporary parameter node. The parameter indices are shifted by 1
// (receiver is parameter index -1 but environment index 0).
Node* parameter = NewNode(common()->Parameter(i + 1), graph()->start());
// Context variable (at bottom of the context chain).
DCHECK_EQ(0, info()->scope()->ContextChainLength(variable->scope()));
DCHECK_EQ(0, scope->ContextChainLength(variable->scope()));
const Operator* op = javascript()->StoreContext(0, variable->index());
NewNode(op, local_context, parameter);
}

View File

@ -246,7 +246,7 @@ class AstGraphBuilder : public AstVisitor {
Node* BuildPatchReceiverToGlobalProxy(Node* receiver);
// Builders to create local function, script and block contexts.
Node* BuildLocalFunctionContext(Node* context);
Node* BuildLocalFunctionContext(Node* context, Node* patched_receiver);
Node* BuildLocalScriptContext(Scope* scope);
Node* BuildLocalBlockContext(Scope* scope);

View File

@ -258,8 +258,13 @@ Handle<Object> Context::Lookup(Handle<String> name,
object->IsJSContextExtensionObject()) {
maybe = JSReceiver::GetOwnPropertyAttributes(object, name);
} else if (context->IsWithContext()) {
LookupIterator it(object, name);
maybe = UnscopableLookup(&it);
// A with context will never bind "this".
if (name->Equals(*isolate->factory()->this_string())) {
maybe = Just(ABSENT);
} else {
LookupIterator it(object, name);
maybe = UnscopableLookup(&it);
}
} else {
maybe = JSReceiver::GetPropertyAttributes(object, name);
}

View File

@ -231,6 +231,7 @@ namespace internal {
V(source_string, "source") \
V(source_url_string, "source_url") \
V(source_mapping_url_string, "source_mapping_url") \
V(this_string, "this") \
V(global_string, "global") \
V(ignore_case_string, "ignoreCase") \
V(multiline_string, "multiline") \

View File

@ -210,8 +210,9 @@ void FullCodeGenerator::Generate() {
// Copy parameters into context if necessary.
int num_parameters = info->scope()->num_parameters();
for (int i = 0; i < num_parameters; i++) {
Variable* var = scope()->parameter(i);
int first_parameter = info->scope()->has_this_declaration() ? -1 : 0;
for (int i = first_parameter; i < num_parameters; i++) {
Variable* var = (i == -1) ? scope()->receiver() : scope()->parameter(i);
if (var->IsContextSlot()) {
int parameter_offset = StandardFrameConstants::kCallerSPOffset +
(num_parameters - 1 - i) * kPointerSize;
@ -2959,7 +2960,9 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
// Push the enclosing function.
__ push(Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
// Push the receiver of the enclosing function.
__ push(Operand(ebp, (2 + info_->scope()->num_parameters()) * kPointerSize));
Variable* this_var = scope()->LookupThis();
DCHECK_NOT_NULL(this_var);
__ push(VarOperand(this_var, ecx));
// Push the language mode.
__ push(Immediate(Smi::FromInt(language_mode())));

View File

@ -141,8 +141,8 @@ bool LCodeGen::GeneratePrologue() {
// Sloppy mode functions and builtins need to replace the receiver with the
// global proxy when called as functions (without an explicit receiver
// object).
if (is_sloppy(info_->language_mode()) && info()->MayUseThis() &&
!info_->is_native()) {
if (is_sloppy(info()->language_mode()) && info()->MayUseThis() &&
!info()->is_native() && info()->scope()->has_this_declaration()) {
Label ok;
// +1 for return address.
int receiver_offset = (scope()->num_parameters() + 1) * kPointerSize;
@ -275,8 +275,9 @@ bool LCodeGen::GeneratePrologue() {
// Copy parameters into context if necessary.
int num_parameters = scope()->num_parameters();
for (int i = 0; i < num_parameters; i++) {
Variable* var = scope()->parameter(i);
int first_parameter = scope()->has_this_declaration() ? -1 : 0;
for (int i = first_parameter; i < num_parameters; i++) {
Variable* var = (i == -1) ? scope()->receiver() : scope()->parameter(i);
if (var->IsContextSlot()) {
int parameter_offset = StandardFrameConstants::kCallerSPOffset +
(num_parameters - 1 - i) * kPointerSize;

View File

@ -135,7 +135,7 @@ void FullCodeGenerator::Generate() {
// global proxy when called as functions (without an explicit receiver
// object).
if (is_sloppy(info->language_mode()) && !info->is_native() &&
info->MayUseThis()) {
info->MayUseThis() && info->scope()->has_this_declaration()) {
Label ok;
int receiver_offset = info->scope()->num_parameters() * kPointerSize;
__ lw(at, MemOperand(sp, receiver_offset));
@ -225,8 +225,9 @@ void FullCodeGenerator::Generate() {
__ sw(v0, MemOperand(fp, StandardFrameConstants::kContextOffset));
// Copy any necessary parameters into the context.
int num_parameters = info->scope()->num_parameters();
for (int i = 0; i < num_parameters; i++) {
Variable* var = scope()->parameter(i);
int first_parameter = info->scope()->has_this_declaration() ? -1 : 0;
for (int i = first_parameter; i < num_parameters; i++) {
Variable* var = (i == -1) ? scope()->receiver() : scope()->parameter(i);
if (var->IsContextSlot()) {
int parameter_offset = StandardFrameConstants::kCallerSPOffset +
(num_parameters - 1 - i) * kPointerSize;
@ -3037,8 +3038,9 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
__ lw(t2, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
// t1: the receiver of the enclosing function.
int receiver_offset = 2 + info_->scope()->num_parameters();
__ lw(t1, MemOperand(fp, receiver_offset * kPointerSize));
Variable* this_var = scope()->LookupThis();
DCHECK_NOT_NULL(this_var);
__ lw(t1, VarOperand(this_var, t1));
// t0: the language mode.
__ li(t0, Operand(Smi::FromInt(language_mode())));

View File

@ -143,8 +143,8 @@ bool LCodeGen::GeneratePrologue() {
// Sloppy mode functions and builtins need to replace the receiver with the
// global proxy when called as functions (without an explicit receiver
// object).
if (is_sloppy(info_->language_mode()) && info()->MayUseThis() &&
!info_->is_native()) {
if (is_sloppy(info()->language_mode()) && info()->MayUseThis() &&
!info()->is_native() && info()->scope()->has_this_declaration()) {
Label ok;
int receiver_offset = info_->scope()->num_parameters() * kPointerSize;
__ LoadRoot(at, Heap::kUndefinedValueRootIndex);
@ -217,8 +217,9 @@ bool LCodeGen::GeneratePrologue() {
__ sw(v0, MemOperand(fp, StandardFrameConstants::kContextOffset));
// Copy any necessary parameters into the context.
int num_parameters = scope()->num_parameters();
for (int i = 0; i < num_parameters; i++) {
Variable* var = scope()->parameter(i);
int first_parameter = scope()->has_this_declaration() ? -1 : 0;
for (int i = first_parameter; i < num_parameters; i++) {
Variable* var = (i == -1) ? scope()->receiver() : scope()->parameter(i);
if (var->IsContextSlot()) {
int parameter_offset = StandardFrameConstants::kCallerSPOffset +
(num_parameters - 1 - i) * kPointerSize;

View File

@ -135,7 +135,7 @@ void FullCodeGenerator::Generate() {
// global proxy when called as functions (without an explicit receiver
// object).
if (is_sloppy(info->language_mode()) && !info->is_native() &&
info->MayUseThis()) {
info->MayUseThis() && info->scope()->has_this_declaration()) {
Label ok;
int receiver_offset = info->scope()->num_parameters() * kPointerSize;
__ ld(at, MemOperand(sp, receiver_offset));
@ -222,8 +222,9 @@ void FullCodeGenerator::Generate() {
__ sd(v0, MemOperand(fp, StandardFrameConstants::kContextOffset));
// Copy any necessary parameters into the context.
int num_parameters = info->scope()->num_parameters();
for (int i = 0; i < num_parameters; i++) {
Variable* var = scope()->parameter(i);
int first_parameter = info->scope()->has_this_declaration() ? -1 : 0;
for (int i = first_parameter; i < num_parameters; i++) {
Variable* var = (i == -1) ? scope()->receiver() : scope()->parameter(i);
if (var->IsContextSlot()) {
int parameter_offset = StandardFrameConstants::kCallerSPOffset +
(num_parameters - 1 - i) * kPointerSize;
@ -3039,8 +3040,9 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
__ ld(a6, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
// a5: the receiver of the enclosing function.
int receiver_offset = 2 + info_->scope()->num_parameters();
__ ld(a5, MemOperand(fp, receiver_offset * kPointerSize));
Variable* this_var = scope()->LookupThis();
DCHECK_NOT_NULL(this_var);
__ ld(a5, VarOperand(this_var, a5));
// a4: the language mode.
__ li(a4, Operand(Smi::FromInt(language_mode())));

View File

@ -118,8 +118,8 @@ bool LCodeGen::GeneratePrologue() {
// Sloppy mode functions and builtins need to replace the receiver with the
// global proxy when called as functions (without an explicit receiver
// object).
if (is_sloppy(info_->language_mode()) && info()->MayUseThis() &&
!info_->is_native()) {
if (is_sloppy(info()->language_mode()) && info()->MayUseThis() &&
!info()->is_native() && info()->scope()->has_this_declaration()) {
Label ok;
int receiver_offset = info_->scope()->num_parameters() * kPointerSize;
__ LoadRoot(at, Heap::kUndefinedValueRootIndex);
@ -192,8 +192,9 @@ bool LCodeGen::GeneratePrologue() {
__ sd(v0, MemOperand(fp, StandardFrameConstants::kContextOffset));
// Copy any necessary parameters into the context.
int num_parameters = scope()->num_parameters();
for (int i = 0; i < num_parameters; i++) {
Variable* var = scope()->parameter(i);
int first_parameter = scope()->has_this_declaration() ? -1 : 0;
for (int i = first_parameter; i < num_parameters; i++) {
Variable* var = (i == -1) ? scope()->receiver() : scope()->parameter(i);
if (var->IsContextSlot()) {
int parameter_offset = StandardFrameConstants::kCallerSPOffset +
(num_parameters - 1 - i) * kPointerSize;

View File

@ -740,7 +740,9 @@ const AstRawString* ParserTraits::GetNextSymbol(Scanner* scanner) {
Expression* ParserTraits::ThisExpression(Scope* scope, AstNodeFactory* factory,
int pos) {
return factory->NewVariableProxy(scope->receiver(), pos);
return scope->NewUnresolved(factory,
parser_->ast_value_factory()->this_string(),
Variable::THIS, pos, pos + 4);
}
Expression* ParserTraits::SuperReference(Scope* scope, AstNodeFactory* factory,
@ -788,7 +790,8 @@ Expression* ParserTraits::ExpressionFromIdentifier(const AstRawString* name,
Scope* scope,
AstNodeFactory* factory) {
if (parser_->fni_ != NULL) parser_->fni_->PushVariableName(name);
return scope->NewUnresolved(factory, name, start_position, end_position);
return scope->NewUnresolved(factory, name, Variable::NORMAL, start_position,
end_position);
}
@ -967,6 +970,8 @@ FunctionLiteral* Parser::DoParseProgram(ParseInfo* info) {
FunctionLiteral* result = NULL;
{
// TODO(wingo): Add an outer GLOBAL_SCOPE corresponding to the native
// context, which will have the "this" binding for script scopes.
Scope* scope = NewScope(scope_, SCRIPT_SCOPE);
info->set_script_scope(scope);
if (!info->context().is_null() && !info->context()->IsNativeContext()) {
@ -1919,9 +1924,9 @@ VariableProxy* Parser::NewUnresolved(const AstRawString* name,
// scope.
// Let/const variables in harmony mode are always added to the immediately
// enclosing scope.
return DeclarationScope(mode)->NewUnresolved(factory(), name,
scanner()->location().beg_pos,
scanner()->location().end_pos);
return DeclarationScope(mode)->NewUnresolved(
factory(), name, Variable::NORMAL, scanner()->location().beg_pos,
scanner()->location().end_pos);
}
@ -3585,8 +3590,8 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
Expression* enumerable = ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
VariableProxy* each =
scope_->NewUnresolved(factory(), name, each_beg_pos, each_end_pos);
VariableProxy* each = scope_->NewUnresolved(
factory(), name, Variable::NORMAL, each_beg_pos, each_end_pos);
Statement* body = ParseSubStatement(NULL, CHECK_OK);
InitializeForEachStatement(loop, each, enumerable, body);
Block* result =
@ -3667,8 +3672,8 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
scope_ = for_scope;
Expect(Token::RPAREN, CHECK_OK);
VariableProxy* each =
scope_->NewUnresolved(factory(), name, each_beg_pos, each_end_pos);
VariableProxy* each = scope_->NewUnresolved(
factory(), name, Variable::NORMAL, each_beg_pos, each_end_pos);
Statement* body = ParseSubStatement(NULL, CHECK_OK);
Block* body_block =
factory()->NewBlock(NULL, 3, false, RelocInfo::kNoPosition);

View File

@ -1298,16 +1298,21 @@ class ScopeIterator {
context_ = Handle<Context>();
return;
}
if (scope_type == ScopeTypeScript) seen_script_scope_ = true;
if (nested_scope_chain_.is_empty()) {
if (scope_type == ScopeTypeScript) {
if (context_->IsScriptContext()) {
context_ = Handle<Context>(context_->previous(), isolate_);
}
CHECK(context_->IsNativeContext());
} else {
if (scope_type == ScopeTypeScript) {
seen_script_scope_ = true;
if (context_->IsScriptContext()) {
context_ = Handle<Context>(context_->previous(), isolate_);
}
if (!nested_scope_chain_.is_empty()) {
DCHECK_EQ(nested_scope_chain_.last()->scope_type(), SCRIPT_SCOPE);
nested_scope_chain_.RemoveLast();
DCHECK(nested_scope_chain_.is_empty());
}
CHECK(context_->IsNativeContext());
return;
}
if (nested_scope_chain_.is_empty()) {
context_ = Handle<Context>(context_->previous(), isolate_);
} else {
if (nested_scope_chain_.last()->HasContext()) {
DCHECK(context_->previous() != NULL);

View File

@ -250,6 +250,12 @@ RUNTIME_FUNCTION(Runtime_DeclareLookupSlot) {
JSGlobalObject::cast(context_arg->extension()), isolate);
return DeclareGlobals(isolate, global, name, value, attr, is_var, is_const,
is_function);
} else if (context->IsScriptContext()) {
DCHECK(context->global_object()->IsJSGlobalObject());
Handle<JSGlobalObject> global(
JSGlobalObject::cast(context->global_object()), isolate);
return DeclareGlobals(isolate, global, name, value, attr, is_var, is_const,
is_function);
}
if (attributes != ABSENT) {
@ -325,8 +331,12 @@ RUNTIME_FUNCTION(Runtime_InitializeLegacyConstLookupSlot) {
// meanwhile. If so, re-introduce the variable in the context extension.
if (attributes == ABSENT) {
Handle<Context> declaration_context(context_arg->declaration_context());
DCHECK(declaration_context->has_extension());
holder = handle(declaration_context->extension(), isolate);
if (declaration_context->IsScriptContext()) {
holder = handle(declaration_context->global_object(), isolate);
} else {
DCHECK(declaration_context->has_extension());
holder = handle(declaration_context->extension(), isolate);
}
CHECK(holder->IsJSObject());
} else {
// For JSContextExtensionObjects, the initializer can be run multiple times
@ -630,8 +640,12 @@ RUNTIME_FUNCTION(Runtime_NewScriptContext) {
FindNameClash(scope_info, global_object, script_context_table);
if (isolate->has_pending_exception()) return name_clash_result;
// Script contexts have a canonical empty function as their closure, not the
// anonymous closure containing the global code. See
// FullCodeGenerator::PushFunctionArgumentForContextAllocation.
Handle<JSFunction> closure(native_context->closure());
Handle<Context> result =
isolate->factory()->NewScriptContext(function, scope_info);
isolate->factory()->NewScriptContext(closure, scope_info);
DCHECK(function->context() == isolate->context());
DCHECK(function->context()->global_object() == result->global_object());

View File

@ -317,7 +317,8 @@ bool ScopeInfo::LocalIsSynthetic(int var) {
// with user declarations, the current temporaries like .generator_object and
// .result start with a dot, so we can use that as a flag. It's a hack!
Handle<String> name(LocalName(var));
return name->length() > 0 && name->Get(0) == '.';
return (name->length() > 0 && name->Get(0) == '.') ||
name->Equals(*GetIsolate()->factory()->this_string());
}

View File

@ -309,22 +309,16 @@ void Scope::Initialize() {
scope_inside_with_ = is_with_scope();
}
// Declare convenience variables.
// Declare and allocate receiver (even for the script scope, and even
// if naccesses_ == 0).
// NOTE: When loading parameters in the script scope, we must take
// care not to access them as properties of the global object, but
// instead load them directly from the stack. Currently, the only
// such parameter is 'this' which is passed on the stack when
// invoking scripts
// Declare convenience variables and the receiver.
if (is_declaration_scope()) {
DCHECK(!subclass_constructor || is_function_scope());
Variable* var = variables_.Declare(
this, ast_value_factory_->this_string(),
subclass_constructor ? CONST : VAR, Variable::THIS,
subclass_constructor ? kNeedsInitialization : kCreatedInitialized);
var->AllocateTo(Variable::PARAMETER, -1);
receiver_ = var;
if (has_this_declaration()) {
Variable* var = variables_.Declare(
this, ast_value_factory_->this_string(),
subclass_constructor ? CONST : VAR, Variable::THIS,
subclass_constructor ? kNeedsInitialization : kCreatedInitialized);
receiver_ = var;
}
if (subclass_constructor) {
new_target_ =
@ -333,9 +327,6 @@ void Scope::Initialize() {
new_target_->AllocateTo(Variable::PARAMETER, -2);
new_target_->set_is_used();
}
} else {
DCHECK(outer_scope() != NULL);
receiver_ = outer_scope()->receiver();
}
if (is_function_scope() && !is_arrow_scope()) {
@ -1049,7 +1040,7 @@ Variable* Scope::LookupRecursive(VariableProxy* proxy,
DCHECK(is_script_scope());
}
if (is_with_scope()) {
if (is_with_scope() && (var == nullptr || !var->is_this())) {
DCHECK(!already_resolved());
// The current scope is a with scope, so the variable binding can not be
// statically resolved. However, note that it was necessary to do a lookup
@ -1415,24 +1406,40 @@ void Scope::AllocateParameterLocals(Isolate* isolate) {
// Force context allocation of the parameter.
var->ForceContextAllocation();
}
AllocateParameter(var, i);
}
}
if (MustAllocate(var)) {
if (MustAllocateInContext(var)) {
DCHECK(var->IsUnallocated() || var->IsContextSlot());
if (var->IsUnallocated()) {
AllocateHeapSlot(var);
}
} else {
DCHECK(var->IsUnallocated() || var->IsParameter());
if (var->IsUnallocated()) {
var->AllocateTo(Variable::PARAMETER, i);
}
void Scope::AllocateParameter(Variable* var, int index) {
if (MustAllocate(var)) {
if (MustAllocateInContext(var)) {
DCHECK(var->IsUnallocated() || var->IsContextSlot());
if (var->IsUnallocated()) {
AllocateHeapSlot(var);
}
} else {
DCHECK(var->IsUnallocated() || var->IsParameter());
if (var->IsUnallocated()) {
var->AllocateTo(Variable::PARAMETER, index);
}
}
}
}
void Scope::AllocateReceiver() {
DCHECK_NOT_NULL(receiver());
DCHECK_EQ(receiver()->scope(), this);
if (has_forced_context_allocation()) {
// Force context allocation of the receiver.
receiver()->ForceContextAllocation();
}
AllocateParameter(receiver(), -1);
}
void Scope::AllocateNonParameterLocal(Isolate* isolate, Variable* var) {
DCHECK(var->scope() == this);
DCHECK(!var->IsVariable(isolate->factory()->dot_result_string()) ||
@ -1502,6 +1509,7 @@ void Scope::AllocateVariablesRecursively(Isolate* isolate) {
// Allocate variables for this scope.
// Parameters must be allocated first, if any.
if (is_function_scope()) AllocateParameterLocals(isolate);
if (has_this_declaration()) AllocateReceiver();
AllocateNonParameterLocals(isolate);
// Force allocation of a context for this scope if necessary. For a 'with'

View File

@ -144,14 +144,15 @@ class Scope: public ZoneObject {
// Create a new unresolved variable.
VariableProxy* NewUnresolved(AstNodeFactory* factory,
const AstRawString* name,
Variable::Kind kind = Variable::NORMAL,
int start_position = RelocInfo::kNoPosition,
int end_position = RelocInfo::kNoPosition) {
// Note that we must not share the unresolved variables with
// the same name because they may be removed selectively via
// RemoveUnresolved().
DCHECK(!already_resolved());
VariableProxy* proxy = factory->NewVariableProxy(
name, Variable::NORMAL, start_position, end_position);
VariableProxy* proxy =
factory->NewVariableProxy(name, kind, start_position, end_position);
unresolved_.Add(proxy, zone_);
return proxy;
}
@ -346,7 +347,21 @@ class Scope: public ZoneObject {
LanguageMode language_mode() const { return language_mode_; }
// The variable corresponding to the 'this' value.
Variable* receiver() { return receiver_; }
Variable* receiver() {
DCHECK(has_this_declaration());
DCHECK_NOT_NULL(receiver_);
return receiver_;
}
Variable* LookupThis() { return Lookup(ast_value_factory_->this_string()); }
// TODO(wingo): Add a GLOBAL_SCOPE scope type which will lexically allocate
// "this" (and no other variable) on the native context. Script scopes then
// will not have a "this" declaration.
bool has_this_declaration() const {
return (is_function_scope() && !is_arrow_scope()) || is_module_scope() ||
is_script_scope();
}
// The variable corresponding to the 'new.target' value.
Variable* new_target_var() { return new_target_; }
@ -706,6 +721,8 @@ class Scope: public ZoneObject {
void AllocateNonParameterLocal(Isolate* isolate, Variable* var);
void AllocateNonParameterLocals(Isolate* isolate);
void AllocateVariablesRecursively(Isolate* isolate);
void AllocateParameter(Variable* var, int index);
void AllocateReceiver();
void AllocateModules();
// Resolve and fill in the allocation information for all variables

View File

@ -59,8 +59,8 @@ bool Variable::IsGlobalObjectProperty() const {
// Temporaries are never global, they must always be allocated in the
// activation frame.
return (IsDynamicVariableMode(mode_) ||
(IsDeclaredVariableMode(mode_) && !IsLexicalVariableMode(mode_)))
&& scope_ != NULL && scope_->is_script_scope();
(IsDeclaredVariableMode(mode_) && !IsLexicalVariableMode(mode_))) &&
scope_ != NULL && scope_->is_script_scope() && !is_this();
}

View File

@ -115,7 +115,7 @@ void FullCodeGenerator::Generate() {
// global proxy when called as functions (without an explicit receiver
// object).
if (is_sloppy(info->language_mode()) && !info->is_native() &&
info->MayUseThis()) {
info->MayUseThis() && info->scope()->has_this_declaration()) {
Label ok;
// +1 for return address.
StackArgumentsAccessor args(rsp, info->scope()->num_parameters());
@ -209,8 +209,9 @@ void FullCodeGenerator::Generate() {
// Copy any necessary parameters into the context.
int num_parameters = info->scope()->num_parameters();
for (int i = 0; i < num_parameters; i++) {
Variable* var = scope()->parameter(i);
int first_parameter = info->scope()->has_this_declaration() ? -1 : 0;
for (int i = first_parameter; i < num_parameters; i++) {
Variable* var = (i == -1) ? scope()->receiver() : scope()->parameter(i);
if (var->IsContextSlot()) {
int parameter_offset = StandardFrameConstants::kCallerSPOffset +
(num_parameters - 1 - i) * kPointerSize;
@ -2960,8 +2961,9 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
__ Push(Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
// Push the receiver of the enclosing function and do runtime call.
StackArgumentsAccessor args(rbp, info_->scope()->num_parameters());
__ Push(args.GetReceiverOperand());
Variable* this_var = scope()->LookupThis();
DCHECK_NOT_NULL(this_var);
__ Push(VarOperand(this_var, rcx));
// Push the language mode.
__ Push(Smi::FromInt(language_mode()));

View File

@ -129,8 +129,8 @@ bool LCodeGen::GeneratePrologue() {
// Sloppy mode functions need to replace the receiver with the global proxy
// when called as functions (without an explicit receiver object).
if (is_sloppy(info_->language_mode()) && info()->MayUseThis() &&
!info_->is_native()) {
if (is_sloppy(info()->language_mode()) && info()->MayUseThis() &&
!info()->is_native() && info()->scope()->has_this_declaration()) {
Label ok;
StackArgumentsAccessor args(rsp, scope()->num_parameters());
__ movp(rcx, args.GetReceiverOperand());
@ -213,8 +213,9 @@ bool LCodeGen::GeneratePrologue() {
// Copy any necessary parameters into the context.
int num_parameters = scope()->num_parameters();
for (int i = 0; i < num_parameters; i++) {
Variable* var = scope()->parameter(i);
int first_parameter = scope()->has_this_declaration() ? -1 : 0;
for (int i = first_parameter; i < num_parameters; i++) {
Variable* var = (i == -1) ? scope()->receiver() : scope()->parameter(i);
if (var->IsContextSlot()) {
int parameter_offset = StandardFrameConstants::kCallerSPOffset +
(num_parameters - 1 - i) * kPointerSize;

View File

@ -628,7 +628,7 @@ UNINITIALIZED_DEPENDENT_TEST(CustomContextDeserialization,
root =
deserializer.DeserializePartial(isolate, global_proxy,
&outdated_contexts).ToHandleChecked();
CHECK_EQ(2, outdated_contexts->length());
CHECK_EQ(3, outdated_contexts->length());
CHECK(root->IsContext());
Handle<Context> context = Handle<Context>::cast(root);
CHECK(context->global_proxy() == *global_proxy);

View File

@ -167,6 +167,10 @@ function CheckScopeContent(content, number, exec_state) {
if (!scope.scopeObject().property('arguments').isUndefined()) {
scope_size--;
}
// Ditto for 'this'.
if (!scope.scopeObject().property('this').isUndefined()) {
scope_size--;
}
// Skip property with empty name.
if (!scope.scopeObject().property('').isUndefined()) {
scope_size--;

View File

@ -0,0 +1,81 @@
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-arrow-functions
var object = {};
var global = this;
var call = Function.call.bind(Function.call);
var globalSloppyArrow = () => this;
var globalStrictArrow = () => { "use strict"; return this; };
var globalSloppyArrowEval = (s) => eval(s);
var globalStrictArrowEval = (s) => { "use strict"; return eval(s); };
var sloppyFunctionArrow = function() {
return (() => this)();
};
var strictFunctionArrow = function() {
"use strict";
return (() => this)();
};
var sloppyFunctionEvalArrow = function() {
return eval("(() => this)()");
};
var strictFunctionEvalArrow = function() {
"use strict";
return eval("(() => this)()");
};
var sloppyFunctionArrowEval = function(s) {
return (() => eval(s))();
};
var strictFunctionArrowEval = function(s) {
"use strict";
return (() => eval(s))();
};
var withObject = { 'this': object }
var arrowInsideWith, arrowInsideWithEval;
with (withObject) {
arrowInsideWith = () => this;
arrowInsideWithEval = (s) => eval(s);
}
assertEquals(global, call(globalSloppyArrow, object));
assertEquals(global, call(globalStrictArrow, object));
assertEquals(global, call(globalSloppyArrowEval, object, "this"));
assertEquals(global, call(globalStrictArrowEval, object, "this"));
assertEquals(global, call(globalSloppyArrowEval, object, "(() => this)()"));
assertEquals(global, call(globalStrictArrowEval, object, "(() => this)()"));
assertEquals(object, call(sloppyFunctionArrow, object));
assertEquals(global, call(sloppyFunctionArrow, undefined));
assertEquals(object, call(strictFunctionArrow, object));
assertEquals(undefined, call(strictFunctionArrow, undefined));
assertEquals(object, call(sloppyFunctionEvalArrow, object));
assertEquals(global, call(sloppyFunctionEvalArrow, undefined));
assertEquals(object, call(strictFunctionEvalArrow, object));
assertEquals(undefined, call(strictFunctionEvalArrow, undefined));
assertEquals(object, call(sloppyFunctionArrowEval, object, "this"));
assertEquals(global, call(sloppyFunctionArrowEval, undefined, "this"));
assertEquals(object, call(strictFunctionArrowEval, object, "this"));
assertEquals(undefined, call(strictFunctionArrowEval, undefined, "this"));
assertEquals(object,
call(sloppyFunctionArrowEval, object, "(() => this)()"));
assertEquals(global,
call(sloppyFunctionArrowEval, undefined, "(() => this)()"));
assertEquals(object,
call(strictFunctionArrowEval, object, "(() => this)()"));
assertEquals(undefined,
call(strictFunctionArrowEval, undefined, "(() => this)()"));
assertEquals(global, call(arrowInsideWith, undefined));
assertEquals(global, call(arrowInsideWith, object));
assertEquals(global, call(arrowInsideWithEval, undefined, "this"));
assertEquals(global, call(arrowInsideWithEval, object, "this"));
assertEquals(global, call(arrowInsideWithEval, undefined, "(() => this)()"));
assertEquals(global, call(arrowInsideWithEval, object, "(() => this)()"));