// 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));
__ 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;
__ 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())));
// 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));
__ 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;
// 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);
__ 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;
__ 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()));
// 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);
__ 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;
bool CompilationInfo::MayUseThis() const {
- return scope()->uses_this() || scope()->inner_uses_this() ||
- scope()->calls_sloppy_eval();
+ return scope()->has_this_declaration() && scope()->receiver()->is_used();
}
// 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 {
// 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 =
// object). Otherwise there is nothing left to do here.
if (is_strict(language_mode()) || info()->is_native()) return receiver;
- // There is no need to perform patching if the receiver is never used. Note
- // that scope predicates are purely syntactical, a call to eval might still
- // inspect the receiver value.
+ // There is no need to perform patching if the receiver will never be used.
if (!info()->MayUseThis()) return receiver;
IfBuilder receiver_check(this);
}
-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);
}
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);
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);
}
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") \
// 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;
// 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())));
// 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;
// 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;
// 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));
__ 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;
__ 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())));
// 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);
__ 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;
// 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));
__ 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;
__ 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())));
// 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);
__ 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;
// no contexts are allocated for this scope ContextLength returns 0.
int ContextLength();
+ // Does this scope declare a "this" binding?
+ bool HasReceiver();
+
+ // Does this scope declare a "this" binding, and the "this" binding is stack-
+ // or context-allocated?
+ bool HasAllocatedReceiver();
+
// Is this scope the scope of a named function expression?
bool HasFunctionName();
// must be an internalized string.
int FunctionContextSlotIndex(String* name, VariableMode* mode);
+ // Lookup support for serialized scope info. Returns the receiver context
+ // slot index if scope has a "this" binding, and the binding is
+ // context-allocated. Otherwise returns a value < 0.
+ int ReceiverContextSlotIndex();
+
bool block_scope_is_class_scope();
FunctionKind function_kind();
// 7. StrongModeFreeVariablePositionEntries:
// Stores the locations (start and end position) of strong mode free
// variables.
- // 8. FunctionNameEntryIndex:
+ // 8. RecieverEntryIndex:
+ // If the scope binds a "this" value, one slot is reserved to hold the
+ // context or stack slot index for the variable.
+ // 9. FunctionNameEntryIndex:
// If the scope belongs to a named function expression this part contains
// information about the function variable. It always occupies two array
// slots: a. The name of the function variable.
int ContextLocalInfoEntriesIndex();
int StrongModeFreeVariableNameEntriesIndex();
int StrongModeFreeVariablePositionEntriesIndex();
+ int ReceiverEntryIndex();
int FunctionNameEntryIndex();
- // Location of the function variable for named function expressions.
- enum FunctionVariableInfo {
- NONE, // No function name present.
- STACK, // Function
- CONTEXT,
- UNUSED
- };
+ // Used for the function name variable for named function expressions, and for
+ // the receiver.
+ enum VariableAllocationInfo { NONE, STACK, CONTEXT, UNUSED };
// Properties of scopes.
class ScopeTypeField : public BitField<ScopeType, 0, 4> {};
- class CallsEvalField : public BitField<bool, 4, 1> {};
+ class CallsEvalField : public BitField<bool, ScopeTypeField::kNext, 1> {};
STATIC_ASSERT(LANGUAGE_END == 3);
- class LanguageModeField : public BitField<LanguageMode, 5, 2> {};
- class FunctionVariableField : public BitField<FunctionVariableInfo, 7, 2> {};
- class FunctionVariableMode : public BitField<VariableMode, 9, 3> {};
- class AsmModuleField : public BitField<bool, 12, 1> {};
- class AsmFunctionField : public BitField<bool, 13, 1> {};
+ class LanguageModeField
+ : public BitField<LanguageMode, CallsEvalField::kNext, 2> {};
+ class ReceiverVariableField
+ : public BitField<VariableAllocationInfo, LanguageModeField::kNext, 2> {};
+ class FunctionVariableField
+ : public BitField<VariableAllocationInfo, ReceiverVariableField::kNext,
+ 2> {};
+ class FunctionVariableMode
+ : public BitField<VariableMode, FunctionVariableField::kNext, 3> {};
+ class AsmModuleField : public BitField<bool, FunctionVariableMode::kNext, 1> {
+ };
+ class AsmFunctionField : public BitField<bool, AsmModuleField::kNext, 1> {};
class IsSimpleParameterListField
: public BitField<bool, AsmFunctionField::kNext, 1> {};
class BlockScopeIsClassScopeField
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,
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);
}
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()) {
// 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);
}
Expression* enumerable = ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
- VariableProxy* each = scope_->NewUnresolved(
- factory(), parsing_result.SingleName(), each_beg_pos, each_end_pos);
+ VariableProxy* each =
+ scope_->NewUnresolved(factory(), parsing_result.SingleName(),
+ Variable::NORMAL, each_beg_pos, each_end_pos);
Statement* body = ParseSubStatement(NULL, CHECK_OK);
InitializeForEachStatement(loop, each, enumerable, body);
Block* result =
scope_ = for_scope;
Expect(Token::RPAREN, CHECK_OK);
- VariableProxy* each = scope_->NewUnresolved(
- factory(), parsing_result.SingleName(), each_end_pos);
+ VariableProxy* each =
+ scope_->NewUnresolved(factory(), parsing_result.SingleName(),
+ Variable::NORMAL, each_end_pos);
Statement* body = ParseSubStatement(NULL, CHECK_OK);
Block* body_block =
factory()->NewBlock(NULL, 3, false, RelocInfo::kNoPosition);
if (function->IsProperty()) {
// Method calls
if (function->AsProperty()->IsSuperAccess()) {
- VariableProxy* original_home =
- function->AsProperty()->obj()->AsSuperReference()->this_var();
- VariableProxy* home = factory()->NewVariableProxy(original_home->var());
+ Expression* home =
+ ThisExpression(scope_, factory(), RelocInfo::kNoPosition);
args->InsertAt(0, function, zone());
args->InsertAt(1, home, zone());
} else {
// 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;
__ LoadP(r5, MemOperand(sp, receiver_offset), r0);
__ StoreP(r3, 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;
__ LoadP(r7, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
// r6: the receiver of the enclosing function.
- int receiver_offset = 2 + info_->scope()->num_parameters();
- __ LoadP(r6, MemOperand(fp, receiver_offset * kPointerSize), r0);
+ Variable* this_var = scope()->LookupThis();
+ DCHECK_NOT_NULL(this_var);
+ GetVar(r6, this_var);
// r5: language mode.
__ LoadSmiLiteral(r5, Smi::FromInt(language_mode()));
// 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;
__ LoadP(r5, MemOperand(sp, receiver_offset));
__ StoreP(r3, 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;
break;
}
}
- scope_->RecordThisUsage();
result = this->ThisExpression(scope_, factory(), beg_pos);
break;
}
Consume(Token::THIS);
int pos = position();
function_state_->set_this_location(scanner()->location());
- scope_->RecordThisUsage();
ExpressionT this_expr = this->ThisExpression(scope_, factory(), pos);
ExpressionT left = this->EmptyExpression();
}
+MUST_USE_RESULT
+static MaybeHandle<Context> MaterializeReceiver(Isolate* isolate,
+ Handle<Context> target,
+ Handle<JSFunction> function,
+ JavaScriptFrame* frame) {
+ Handle<SharedFunctionInfo> shared(function->shared());
+ Handle<ScopeInfo> scope_info(shared->scope_info());
+ Handle<Object> receiver;
+ switch (scope_info->scope_type()) {
+ case FUNCTION_SCOPE: {
+ VariableMode variable_mode;
+ InitializationFlag init_flag;
+ MaybeAssignedFlag maybe_assigned_flag;
+
+ // Don't bother creating a fake context node if "this" is in the context
+ // already.
+ if (ScopeInfo::ContextSlotIndex(
+ scope_info, isolate->factory()->this_string(), &variable_mode,
+ &init_flag, &maybe_assigned_flag) >= 0) {
+ return target;
+ }
+ receiver = Handle<Object>(frame->receiver(), isolate);
+ break;
+ }
+ case MODULE_SCOPE:
+ receiver = isolate->factory()->undefined_value();
+ break;
+ case SCRIPT_SCOPE:
+ receiver = Handle<Object>(function->global_proxy(), isolate);
+ break;
+ default:
+ // For eval code, arrow functions, and the like, there's no "this" binding
+ // to materialize.
+ return target;
+ }
+
+ return isolate->factory()->NewCatchContext(
+ function, target, isolate->factory()->this_string(), receiver);
+}
+
+
// Create a plain JSObject which materializes the local scope for the specified
// frame.
MUST_USE_RESULT
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);
outer_info_ = handle(function->shared());
Handle<Context> inner_context;
+ // The "this" binding, if any, can't be bound via "with". If we need to,
+ // add another node onto the outer context to bind "this".
+ if (!MaterializeReceiver(isolate, outer_context, function, frame)
+ .ToHandle(&outer_context))
+ return;
+
bool stop = false;
for (ScopeIterator it(isolate, frame, inlined_jsframe_index);
!it.Failed() && !it.Done() && !stop; it.Next()) {
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) {
// 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
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());
bool simple_parameter_list =
scope->is_function_scope() ? scope->is_simple_parameter_list() : true;
+ // Determine use and location of the "this" binding if it is present.
+ VariableAllocationInfo receiver_info;
+ if (scope->has_this_declaration()) {
+ Variable* var = scope->receiver();
+ if (!var->is_used()) {
+ receiver_info = UNUSED;
+ } else if (var->IsContextSlot()) {
+ receiver_info = CONTEXT;
+ } else {
+ DCHECK(var->IsParameter());
+ receiver_info = STACK;
+ }
+ } else {
+ receiver_info = NONE;
+ }
+
// Determine use and location of the function variable if it is present.
- FunctionVariableInfo function_name_info;
+ VariableAllocationInfo function_name_info;
VariableMode function_variable_mode;
if (scope->is_function_scope() && scope->function() != NULL) {
Variable* var = scope->function()->proxy()->var();
}
const bool has_function_name = function_name_info != NONE;
+ const bool has_receiver = receiver_info == STACK || receiver_info == CONTEXT;
const int parameter_count = scope->num_parameters();
const int length = kVariablePartIndex + parameter_count +
(1 + stack_local_count) + 2 * context_local_count +
3 * strong_mode_free_variable_count +
- (has_function_name ? 2 : 0);
+ (has_receiver ? 1 : 0) + (has_function_name ? 2 : 0);
Factory* factory = isolate->factory();
Handle<ScopeInfo> scope_info = factory->NewScopeInfo(length);
int flags = ScopeTypeField::encode(scope->scope_type()) |
CallsEvalField::encode(scope->calls_eval()) |
LanguageModeField::encode(scope->language_mode()) |
+ ReceiverVariableField::encode(receiver_info) |
FunctionVariableField::encode(function_name_info) |
FunctionVariableMode::encode(function_variable_mode) |
AsmModuleField::encode(scope->asm_module()) |
scope_info->set(index++, *end_position);
}
+ // If the receiver is allocated, add its index.
+ DCHECK(index == scope_info->ReceiverEntryIndex());
+ if (has_receiver) {
+ int var_index = scope->receiver()->index();
+ scope_info->set(index++, Smi::FromInt(var_index));
+ // ?? DCHECK(receiver_info != CONTEXT || var_index ==
+ // scope_info->ContextLength() - 1);
+ }
+
// If present, add the function variable name and its index.
DCHECK(index == scope_info->FunctionNameEntryIndex());
if (has_function_name) {
}
+bool ScopeInfo::HasReceiver() {
+ if (length() > 0) {
+ return NONE != ReceiverVariableField::decode(Flags());
+ } else {
+ return false;
+ }
+}
+
+
+bool ScopeInfo::HasAllocatedReceiver() {
+ if (length() > 0) {
+ VariableAllocationInfo allocation = ReceiverVariableField::decode(Flags());
+ return allocation == STACK || allocation == CONTEXT;
+ } else {
+ return false;
+ }
+}
+
+
bool ScopeInfo::HasFunctionName() {
if (length() > 0) {
return NONE != FunctionVariableField::decode(Flags());
// 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());
}
}
+int ScopeInfo::ReceiverContextSlotIndex() {
+ if (length() > 0 && ReceiverVariableField::decode(Flags()) == CONTEXT)
+ return Smi::cast(get(ReceiverEntryIndex()))->value();
+ return -1;
+}
+
+
int ScopeInfo::FunctionContextSlotIndex(String* name, VariableMode* mode) {
DCHECK(name->IsInternalizedString());
DCHECK(mode != NULL);
}
-int ScopeInfo::FunctionNameEntryIndex() {
+int ScopeInfo::ReceiverEntryIndex() {
return StrongModeFreeVariablePositionEntriesIndex() +
2 * StrongModeFreeVariableCount();
}
+int ScopeInfo::FunctionNameEntryIndex() {
+ return ReceiverEntryIndex() + (HasAllocatedReceiver() ? 1 : 0);
+}
+
+
int ContextSlotCache::Hash(Object* data, String* name) {
// Uses only lower 32 bits if pointers are larger.
uintptr_t addr_hash =
scope_calls_eval_ = false;
scope_uses_arguments_ = false;
scope_uses_super_property_ = false;
- scope_uses_this_ = false;
asm_module_ = false;
asm_function_ = outer_scope != NULL && outer_scope->asm_module_;
// Inherit the language mode from the parent scope.
outer_scope_calls_sloppy_eval_ = false;
inner_scope_calls_eval_ = false;
inner_scope_uses_arguments_ = false;
- inner_scope_uses_this_ = false;
inner_scope_uses_super_property_ = false;
force_eager_compilation_ = false;
force_context_allocation_ = (outer_scope != NULL && !is_function_scope())
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_ =
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()) {
// Propagate usage flags to outer scope.
if (uses_arguments()) outer_scope_->RecordArgumentsUsage();
if (uses_super_property()) outer_scope_->RecordSuperPropertyUsage();
- if (uses_this()) outer_scope_->RecordThisUsage();
if (scope_calls_eval_) outer_scope_->RecordEvalCall();
return NULL;
maybe_assigned_flag = kMaybeAssigned;
}
- // TODO(marja, rossberg): Declare variables of the right Kind.
- Variable* var = variables_.Declare(this, name, mode, Variable::NORMAL,
- init_flag, maybe_assigned_flag);
+ Variable::Kind kind = Variable::NORMAL;
+ if (location == Variable::CONTEXT &&
+ index == scope_info_->ReceiverContextSlotIndex()) {
+ kind = Variable::THIS;
+ }
+ // TODO(marja, rossberg): Correctly declare FUNCTION, CLASS, NEW_TARGET, and
+ // ARGUMENTS bindings as their corresponding Variable::Kind.
+
+ Variable* var = variables_.Declare(this, name, mode, kind, init_flag,
+ maybe_assigned_flag);
var->AllocateTo(location, index);
return var;
}
if (scope_uses_arguments_) Indent(n1, "// scope uses 'arguments'\n");
if (scope_uses_super_property_)
Indent(n1, "// scope uses 'super' property\n");
- if (scope_uses_this_) Indent(n1, "// scope uses 'this'\n");
if (inner_scope_uses_arguments_) {
Indent(n1, "// inner scope uses 'arguments'\n");
}
if (inner_scope_uses_super_property_)
Indent(n1, "// inner scope uses 'super' property\n");
- if (inner_scope_uses_this_) Indent(n1, "// inner scope uses 'this'\n");
if (outer_scope_calls_sloppy_eval_) {
Indent(n1, "// outer scope calls 'eval' in sloppy context\n");
}
DCHECK(is_script_scope());
}
- if (is_with_scope()) {
+ // "this" can't be shadowed by "eval"-introduced bindings or by "with" scopes.
+ // TODO(wingo): There are other variables in this category; add them.
+ bool name_can_be_shadowed = var == nullptr || !var->is_this();
+
+ if (is_with_scope() && name_can_be_shadowed) {
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
if (var != NULL && proxy->is_assigned()) var->set_maybe_assigned();
*binding_kind = DYNAMIC_LOOKUP;
return NULL;
- } else if (calls_sloppy_eval()) {
+ } else if (calls_sloppy_eval() && name_can_be_shadowed) {
// A variable binding may have been found in an outer scope, but the current
// scope makes a sloppy 'eval' call, so the found variable may not be
// the correct one (the 'eval' may introduce a binding with the same name).
inner->inner_scope_uses_super_property_) {
inner_scope_uses_super_property_ = true;
}
- if (inner->scope_uses_this_ || inner->inner_scope_uses_this_) {
- inner_scope_uses_this_ = true;
- }
}
if (inner->force_eager_compilation_) {
force_eager_compilation_ = true;
// 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()) ||
// 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'
// 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;
}
// Inform the scope that the corresponding code uses "super".
void RecordSuperPropertyUsage() { scope_uses_super_property_ = true; }
- // Inform the scope that the corresponding code uses "this".
- void RecordThisUsage() { scope_uses_this_ = true; }
-
// Set the language mode flag (unless disabled by a global flag).
void SetLanguageMode(LanguageMode language_mode) {
language_mode_ = language_mode;
bool inner_uses_super_property() const {
return inner_scope_uses_super_property_;
}
- // Does this scope access "this".
- bool uses_this() const { return scope_uses_this_; }
- // Does any inner scope access "this".
- bool inner_uses_this() const { return inner_scope_uses_this_; }
const Scope* NearestOuterEvalScope() const {
if (is_eval_scope()) return this;
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_; }
bool scope_uses_arguments_;
// This scope uses "super" property ('super.foo').
bool scope_uses_super_property_;
- // This scope uses "this".
- bool scope_uses_this_;
// This scope contains an "use asm" annotation.
bool asm_module_;
// This scope's outer context is an asm module.
bool inner_scope_calls_eval_;
bool inner_scope_uses_arguments_;
bool inner_scope_uses_super_property_;
- bool inner_scope_uses_this_;
bool force_eager_compilation_;
bool force_context_allocation_;
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
// 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();
}
// 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());
// 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;
__ 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()));
// 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());
// 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;
THIS = 1 << 2,
INNER_ARGUMENTS = 1 << 3,
INNER_SUPER_PROPERTY = 1 << 4,
- INNER_THIS = 1 << 5
};
static const struct {
{"return this + arguments[0]", ARGUMENTS | THIS},
{"return this + arguments[0] + super.x",
ARGUMENTS | SUPER_PROPERTY | THIS},
- {"return x => this + x", INNER_THIS},
+ {"return x => this + x", THIS},
{"return x => super.f() + x", INNER_SUPER_PROPERTY},
{"this.foo = 42;", THIS},
{"this.foo();", THIS},
{"while (true) { while (true) { while (true) return this } }", THIS},
{"while (true) { while (true) { while (true) return super.f() } }",
SUPER_PROPERTY},
- {"if (1) { return () => { while (true) new this() } }", INNER_THIS},
- // Note that propagation of the inner_uses_this() value does not
- // cross boundaries of normal functions onto parent scopes.
+ {"if (1) { return () => { while (true) new this() } }", THIS},
{"return function (x) { return this + x }", NONE},
{"return { m(x) { return super.m() + x } }", NONE},
{"var x = function () { this.foo = 42 };", NONE},
{"return { m(x) { return () => super.m() } }", NONE},
// Flags must be correctly set when using block scoping.
{"\"use strict\"; while (true) { let x; this, arguments; }",
- INNER_ARGUMENTS | INNER_THIS},
+ INNER_ARGUMENTS | THIS},
{"\"use strict\"; while (true) { let x; this, super.f(), arguments; }",
- INNER_ARGUMENTS | INNER_SUPER_PROPERTY | INNER_THIS},
- {"\"use strict\"; if (foo()) { let x; this.f() }", INNER_THIS},
+ INNER_ARGUMENTS | INNER_SUPER_PROPERTY | THIS},
+ {"\"use strict\"; if (foo()) { let x; this.f() }", THIS},
{"\"use strict\"; if (foo()) { let x; super.f() }",
INNER_SUPER_PROPERTY},
{"\"use strict\"; if (1) {"
scope->uses_arguments());
CHECK_EQ((source_data[i].expected & SUPER_PROPERTY) != 0,
scope->uses_super_property());
- CHECK_EQ((source_data[i].expected & THIS) != 0, scope->uses_this());
+ if ((source_data[i].expected & THIS) != 0) {
+ // Currently the is_used() flag is conservative; all variables in a
+ // script scope are marked as used.
+ CHECK(scope->LookupThis()->is_used());
+ }
CHECK_EQ((source_data[i].expected & INNER_ARGUMENTS) != 0,
scope->inner_uses_arguments());
CHECK_EQ((source_data[i].expected & INNER_SUPER_PROPERTY) != 0,
scope->inner_uses_super_property());
- CHECK_EQ((source_data[i].expected & INNER_THIS) != 0,
- scope->inner_uses_this());
}
}
}
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);
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--;
--- /dev/null
+// 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)()"));
FILES_PATTERN = re.compile(r"//\s+Files:(.*)")
SELF_SCRIPT_PATTERN = re.compile(r"//\s+Env: TEST_FILE_NAME")
MODULE_PATTERN = re.compile(r"^// MODULE$", flags=re.MULTILINE)
+NO_HARNESS_PATTERN = re.compile(r"^// NO HARNESS$", flags=re.MULTILINE)
class MjsunitTestSuite(testsuite.TestSuite):
env = ["-e", "TEST_FILE_NAME=\"%s\"" % testfilename.replace("\\", "\\\\")]
files = env + files
- if not context.no_harness:
+ if not context.no_harness and not NO_HARNESS_PATTERN.search(source):
files.append(os.path.join(self.root, "mjsunit.js"))
if MODULE_PATTERN.search(source):
--- /dev/null
+// 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
+// NO HARNESS
+
+var globalEval = eval;
+globalEval("this; eval('42')");
+globalEval("eval('42'); this");