Add script context with context-allocated "const this"

This is a reapplication of https://codereview.chromium.org/1173333004.

R=rossberg@chromium.org
LOG=N
BUG=498811

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

Cr-Commit-Position: refs/heads/master@{#28998}
This commit is contained in:
wingo 2015-06-12 05:34:10 -07:00 committed by Commit bot
parent c487aba74c
commit 103fcfaa40
21 changed files with 270 additions and 205 deletions

View File

@ -3104,20 +3104,15 @@ void FullCodeGenerator::EmitCall(Call* expr, CallICState::CallType call_type) {
void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
// r5: copy of the first argument or undefined if it doesn't exist.
// r4: copy of the first argument or undefined if it doesn't exist.
if (arg_count > 0) {
__ ldr(r5, MemOperand(sp, arg_count * kPointerSize));
__ ldr(r4, MemOperand(sp, arg_count * kPointerSize));
} else {
__ LoadRoot(r5, Heap::kUndefinedValueRootIndex);
__ LoadRoot(r4, Heap::kUndefinedValueRootIndex);
}
// r4: the receiver of the enclosing function.
__ ldr(r4, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
// r3: the receiver of the enclosing function.
Variable* this_var = scope()->LookupThis();
DCHECK_NOT_NULL(this_var);
__ ldr(r3, VarOperand(this_var, r3));
__ ldr(r3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
// r2: language mode.
__ mov(r2, Operand(Smi::FromInt(language_mode())));
@ -3126,9 +3121,8 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
__ mov(r1, Operand(Smi::FromInt(scope()->start_position())));
// Do the runtime call.
__ Push(r5);
__ Push(r4, r3, r2, r1);
__ CallRuntime(Runtime::kResolvePossiblyDirectEval, 6);
__ CallRuntime(Runtime::kResolvePossiblyDirectEval, 5);
}
@ -3160,10 +3154,9 @@ void FullCodeGenerator::VisitCall(Call* expr) {
Call::CallType call_type = expr->GetCallType(isolate());
if (call_type == Call::POSSIBLY_EVAL_CALL) {
// In a call to eval, we first call RuntimeHidden_ResolvePossiblyDirectEval
// to resolve the function we need to call and the receiver of the
// call. Then we call the resolved function using the given
// arguments.
// In a call to eval, we first call
// RuntimeHidden_asResolvePossiblyDirectEval to resolve the function we need
// to call. Then we call the resolved function using the given arguments.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
@ -3183,10 +3176,8 @@ void FullCodeGenerator::VisitCall(Call* expr) {
__ push(r1);
EmitResolvePossiblyDirectEval(arg_count);
// The runtime call returns a pair of values in r0 (function) and
// r1 (receiver). Touch up the stack with the right values.
// Touch up the stack with the resolved function.
__ str(r0, MemOperand(sp, (arg_count + 1) * kPointerSize));
__ str(r1, MemOperand(sp, arg_count * kPointerSize));
PrepareForBailoutForId(expr->EvalOrLookupId(), NO_REGISTERS);
}
@ -4783,9 +4774,10 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
context()->Plug(r0);
} else if (proxy != NULL) {
Variable* var = proxy->var();
// Delete of an unqualified identifier is disallowed in strict mode
// but "delete this" is allowed.
DCHECK(is_sloppy(language_mode()) || var->is_this());
// Delete of an unqualified identifier is disallowed in strict mode but
// "delete this" is allowed.
bool is_this = var->HasThisName(isolate());
DCHECK(is_sloppy(language_mode()) || is_this);
if (var->IsUnallocated()) {
__ ldr(r2, GlobalObjectOperand());
__ mov(r1, Operand(var->name()));
@ -4796,7 +4788,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
} else if (var->IsStackAllocated() || var->IsContextSlot()) {
// Result of deleting non-global, non-dynamic variables is false.
// The subexpression does not have side effects.
context()->Plug(var->is_this());
context()->Plug(is_this);
} else {
// Non-global variable. Call the runtime to try to delete from the
// context where the variable was introduced.

View File

@ -2799,21 +2799,17 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
}
__ Ldr(x10, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
// Prepare to push the receiver of the enclosing function.
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()));
__ Mov(x11, Smi::FromInt(language_mode()));
// Prepare to push the start position of the scope the calls resides in.
__ Mov(x13, Smi::FromInt(scope()->start_position()));
__ Mov(x12, Smi::FromInt(scope()->start_position()));
// Push.
__ Push(x9, x10, x11, x12, x13);
__ Push(x9, x10, x11, x12);
// Do the runtime call.
__ CallRuntime(Runtime::kResolvePossiblyDirectEval, 6);
__ CallRuntime(Runtime::kResolvePossiblyDirectEval, 5);
}
@ -2845,9 +2841,8 @@ void FullCodeGenerator::VisitCall(Call* expr) {
if (call_type == Call::POSSIBLY_EVAL_CALL) {
// In a call to eval, we first call RuntimeHidden_ResolvePossiblyDirectEval
// to resolve the function we need to call and the receiver of the
// call. Then we call the resolved function using the given
// arguments.
// to resolve the function we need to call. Then we call the resolved
// function using the given arguments.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
@ -2868,9 +2863,8 @@ void FullCodeGenerator::VisitCall(Call* expr) {
__ Push(x10);
EmitResolvePossiblyDirectEval(arg_count);
// The runtime call returns a pair of values in x0 (function) and
// x1 (receiver). Touch up the stack with the right values.
__ PokePair(x1, x0, arg_count * kPointerSize);
// Touch up the stack with the resolved function.
__ Poke(x0, (arg_count + 1) * kPointerSize);
PrepareForBailoutForId(expr->EvalOrLookupId(), NO_REGISTERS);
}
@ -4464,9 +4458,10 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
context()->Plug(x0);
} else if (proxy != NULL) {
Variable* var = proxy->var();
// Delete of an unqualified identifier is disallowed in strict mode
// but "delete this" is allowed.
DCHECK(is_sloppy(language_mode()) || var->is_this());
// Delete of an unqualified identifier is disallowed in strict mode but
// "delete this" is allowed.
bool is_this = var->HasThisName(isolate());
DCHECK(is_sloppy(language_mode()) || is_this);
if (var->IsUnallocated()) {
__ Ldr(x12, GlobalObjectMemOperand());
__ Mov(x11, Operand(var->name()));
@ -4477,7 +4472,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
} else if (var->IsStackAllocated() || var->IsContextSlot()) {
// Result of deleting non-global, non-dynamic variables is false.
// The subexpression does not have side effects.
context()->Plug(var->is_this());
context()->Plug(is_this);
} else {
// Non-global variable. Call the runtime to try to delete from the
// context where the variable was introduced.

View File

@ -197,6 +197,11 @@ class Genesis BASE_EMBEDDED {
// other objects in the snapshot.
void HookUpGlobalObject(Handle<GlobalObject> global_object,
Handle<FixedArray> outdated_contexts);
// The native context has a ScriptContextTable that store declarative bindings
// made in script scopes. Add a "this" binding to that table pointing to the
// global proxy.
void InstallGlobalThisBinding();
void HookUpGlobalThisBinding(Handle<FixedArray> outdated_contexts);
// New context initialization. Used for creating a context from scratch.
void InitializeGlobal(Handle<GlobalObject> global_object,
Handle<JSFunction> empty_function);
@ -814,6 +819,41 @@ void Genesis::CreateRoots() {
}
void Genesis::InstallGlobalThisBinding() {
Handle<ScriptContextTable> script_contexts(
native_context()->script_context_table());
Handle<ScopeInfo> scope_info = ScopeInfo::CreateGlobalThisBinding(isolate());
Handle<JSFunction> closure(native_context()->closure());
Handle<Context> context = factory()->NewScriptContext(closure, scope_info);
// Go ahead and hook it up while we're at it.
int slot = scope_info->ReceiverContextSlotIndex();
DCHECK_EQ(slot, Context::MIN_CONTEXT_SLOTS);
context->set(slot, native_context()->global_proxy());
Handle<ScriptContextTable> new_script_contexts =
ScriptContextTable::Extend(script_contexts, context);
native_context()->set_script_context_table(*new_script_contexts);
}
void Genesis::HookUpGlobalThisBinding(Handle<FixedArray> outdated_contexts) {
// One of these contexts should be the one that declares the global "this"
// binding.
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());
int slot = scope_info->ReceiverContextSlotIndex();
if (slot >= 0) {
DCHECK_EQ(slot, Context::MIN_CONTEXT_SLOTS);
context->set(slot, native_context()->global_proxy());
}
}
}
}
Handle<GlobalObject> Genesis::CreateNewGlobals(
v8::Handle<v8::ObjectTemplate> global_proxy_template,
Handle<JSGlobalProxy> global_proxy) {
@ -972,6 +1012,7 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> global_object,
Handle<ScriptContextTable> script_context_table =
factory->NewScriptContextTable();
native_context()->set_script_context_table(*script_context_table);
InstallGlobalThisBinding();
Handle<String> object_name = factory->Object_string();
JSObject::AddProperty(
@ -3086,6 +3127,7 @@ Genesis::Genesis(Isolate* isolate,
HookUpGlobalObject(global_object, outdated_contexts);
native_context()->builtins()->set_global_proxy(
native_context()->global_proxy());
HookUpGlobalThisBinding(outdated_contexts);
if (!ConfigureGlobalObjects(global_proxy_template)) return;
} else {

View File

@ -2420,37 +2420,17 @@ 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();
// 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(current_scope()->start_position());
const Operator* op =
javascript()->CallRuntime(Runtime::kResolvePossiblyDirectEval, 6);
Node* pair =
NewNode(op, callee, source, function, receiver, language, position);
PrepareFrameState(pair, expr->EvalOrLookupId(),
javascript()->CallRuntime(Runtime::kResolvePossiblyDirectEval, 5);
Node* new_callee =
NewNode(op, callee, source, function, language, position);
PrepareFrameState(new_callee, expr->EvalOrLookupId(),
OutputFrameStateCombine::PokeAt(arg_count + 1));
Node* new_callee = NewNode(common()->Projection(0), pair);
Node* new_receiver = NewNode(common()->Projection(1), pair);
// Patch callee and receiver on the environment.
// Patch callee on the environment.
environment()->Poke(arg_count + 1, new_callee);
environment()->Poke(arg_count + 0, new_receiver);
}
// Create node to perform the function call.
@ -2873,7 +2853,9 @@ void AstGraphBuilder::VisitDelete(UnaryOperation* expr) {
// Delete of an unqualified identifier is only allowed in classic mode but
// deleting "this" is allowed in all language modes.
Variable* variable = expr->expression()->AsVariableProxy()->var();
DCHECK(is_sloppy(language_mode()) || variable->is_this());
// Delete of an unqualified identifier is disallowed in strict mode but
// "delete this" is allowed.
DCHECK(is_sloppy(language_mode()) || variable->HasThisName(isolate()));
value = BuildVariableDelete(variable, expr->id(),
ast_context()->GetStateCombine());
} else if (expr->expression()->IsProperty()) {
@ -3321,9 +3303,10 @@ Node* AstGraphBuilder::BuildVariableDelete(Variable* variable,
}
case Variable::PARAMETER:
case Variable::LOCAL:
case Variable::CONTEXT:
case Variable::CONTEXT: {
// Local var, const, or let variable or context variable.
return jsgraph()->BooleanConstant(variable->is_this());
return jsgraph()->BooleanConstant(variable->HasThisName(isolate()));
}
case Variable::LOOKUP: {
// Dynamic lookup of context variable (anywhere in the chain).
Node* name = jsgraph()->Constant(variable->name());

View File

@ -10197,12 +10197,11 @@ void HOptimizedGraphBuilder::VisitDelete(UnaryOperation* expr) {
if (var->IsUnallocated()) {
Bailout(kDeleteWithGlobalVariable);
} else if (var->IsStackAllocated() || var->IsContextSlot()) {
// Result of deleting non-global variables is false. 'this' is not
// really a variable, though we implement it as one. The
// subexpression does not have side effects.
HValue* value = var->is_this()
? graph()->GetConstantTrue()
: graph()->GetConstantFalse();
// Result of deleting non-global variables is false. 'this' is not really
// a variable, though we implement it as one. The subexpression does not
// have side effects.
HValue* value = var->HasThisName(isolate()) ? graph()->GetConstantTrue()
: graph()->GetConstantFalse();
return ast_context()->ReturnValue(value);
} else {
Bailout(kDeleteWithNonGlobalVariable);

View File

@ -3006,10 +3006,7 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
// Push the enclosing function.
__ push(Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
// Push the receiver of the enclosing function.
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())));
@ -3017,7 +3014,7 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
__ push(Immediate(Smi::FromInt(scope()->start_position())));
// Do the runtime call.
__ CallRuntime(Runtime::kResolvePossiblyDirectEval, 6);
__ CallRuntime(Runtime::kResolvePossiblyDirectEval, 5);
}
@ -3049,8 +3046,8 @@ void FullCodeGenerator::VisitCall(Call* expr) {
if (call_type == Call::POSSIBLY_EVAL_CALL) {
// In a call to eval, we first call RuntimeHidden_ResolvePossiblyDirectEval
// to resolve the function we need to call and the receiver of the call.
// Then we call the resolved function using the given arguments.
// to resolve the function we need to call. Then we call the resolved
// function using the given arguments.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
{ PreservePositionScope pos_scope(masm()->positions_recorder());
@ -3067,9 +3064,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
__ push(Operand(esp, (arg_count + 1) * kPointerSize));
EmitResolvePossiblyDirectEval(arg_count);
// The runtime call returns a pair of values in eax (function) and
// edx (receiver). Touch up the stack with the right values.
__ mov(Operand(esp, (arg_count + 0) * kPointerSize), edx);
// Touch up the stack with the resolved function.
__ mov(Operand(esp, (arg_count + 1) * kPointerSize), eax);
PrepareForBailoutForId(expr->EvalOrLookupId(), NO_REGISTERS);
@ -4703,9 +4698,10 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
context()->Plug(eax);
} else if (proxy != NULL) {
Variable* var = proxy->var();
// Delete of an unqualified identifier is disallowed in strict mode
// but "delete this" is allowed.
DCHECK(is_sloppy(language_mode()) || var->is_this());
// Delete of an unqualified identifier is disallowed in strict mode but
// "delete this" is allowed.
bool is_this = var->HasThisName(isolate());
DCHECK(is_sloppy(language_mode()) || is_this);
if (var->IsUnallocated()) {
__ push(GlobalObjectOperand());
__ push(Immediate(var->name()));
@ -4716,7 +4712,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
// Result of deleting non-global variables is false. 'this' is
// not really a variable, though we implement it as one. The
// subexpression does not have side effects.
context()->Plug(var->is_this());
context()->Plug(is_this);
} else {
// Non-global variable. Call the runtime to try to delete from the
// context where the variable was introduced.

View File

@ -3089,21 +3089,15 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
// t2: the receiver of the enclosing function.
__ lw(t2, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
// t1: the receiver of the enclosing function.
Variable* this_var = scope()->LookupThis();
DCHECK_NOT_NULL(this_var);
__ lw(t1, VarOperand(this_var, t1));
// t1: the language mode.
__ li(t1, Operand(Smi::FromInt(language_mode())));
// t0: the language mode.
__ li(t0, Operand(Smi::FromInt(language_mode())));
// a1: the start position of the scope the calls resides in.
__ li(a1, Operand(Smi::FromInt(scope()->start_position())));
// t0: the start position of the scope the calls resides in.
__ li(t0, Operand(Smi::FromInt(scope()->start_position())));
// Do the runtime call.
__ Push(t3);
__ Push(t2, t1, t0, a1);
__ CallRuntime(Runtime::kResolvePossiblyDirectEval, 6);
__ Push(t3, t2, t1, t0);
__ CallRuntime(Runtime::kResolvePossiblyDirectEval, 5);
}
@ -3136,9 +3130,8 @@ void FullCodeGenerator::VisitCall(Call* expr) {
if (call_type == Call::POSSIBLY_EVAL_CALL) {
// In a call to eval, we first call RuntimeHidden_ResolvePossiblyDirectEval
// to resolve the function we need to call and the receiver of the
// call. Then we call the resolved function using the given
// arguments.
// to resolve the function we need to call. Then we call the resolved
// function using the given arguments.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
@ -3158,10 +3151,8 @@ void FullCodeGenerator::VisitCall(Call* expr) {
__ push(a1);
EmitResolvePossiblyDirectEval(arg_count);
// The runtime call returns a pair of values in v0 (function) and
// v1 (receiver). Touch up the stack with the right values.
// Touch up the stack with the resolved function.
__ sw(v0, MemOperand(sp, (arg_count + 1) * kPointerSize));
__ sw(v1, MemOperand(sp, arg_count * kPointerSize));
PrepareForBailoutForId(expr->EvalOrLookupId(), NO_REGISTERS);
}
@ -4790,9 +4781,10 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
context()->Plug(v0);
} else if (proxy != NULL) {
Variable* var = proxy->var();
// Delete of an unqualified identifier is disallowed in strict mode
// but "delete this" is allowed.
DCHECK(is_sloppy(language_mode()) || var->is_this());
// Delete of an unqualified identifier is disallowed in strict mode but
// "delete this" is allowed.
bool is_this = var->HasThisName(isolate());
DCHECK(is_sloppy(language_mode()) || is_this);
if (var->IsUnallocated()) {
__ lw(a2, GlobalObjectOperand());
__ li(a1, Operand(var->name()));
@ -4803,7 +4795,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
} else if (var->IsStackAllocated() || var->IsContextSlot()) {
// Result of deleting non-global, non-dynamic variables is false.
// The subexpression does not have side effects.
context()->Plug(var->is_this());
context()->Plug(is_this);
} else {
// Non-global variable. Call the runtime to try to delete from the
// context where the variable was introduced.

View File

@ -3081,20 +3081,15 @@ void FullCodeGenerator::EmitCall(Call* expr, CallICState::CallType call_type) {
void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
// a7: copy of the first argument or undefined if it doesn't exist.
// a6: copy of the first argument or undefined if it doesn't exist.
if (arg_count > 0) {
__ ld(a7, MemOperand(sp, arg_count * kPointerSize));
__ ld(a6, MemOperand(sp, arg_count * kPointerSize));
} else {
__ LoadRoot(a7, Heap::kUndefinedValueRootIndex);
__ LoadRoot(a6, Heap::kUndefinedValueRootIndex);
}
// a6: the receiver of the enclosing function.
__ ld(a6, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
// a5: the receiver of the enclosing function.
Variable* this_var = scope()->LookupThis();
DCHECK_NOT_NULL(this_var);
__ ld(a5, VarOperand(this_var, a5));
__ ld(a5, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
// a4: the language mode.
__ li(a4, Operand(Smi::FromInt(language_mode())));
@ -3103,9 +3098,8 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
__ li(a1, Operand(Smi::FromInt(scope()->start_position())));
// Do the runtime call.
__ Push(a7);
__ Push(a6, a5, a4, a1);
__ CallRuntime(Runtime::kResolvePossiblyDirectEval, 6);
__ CallRuntime(Runtime::kResolvePossiblyDirectEval, 5);
}
@ -3138,9 +3132,8 @@ void FullCodeGenerator::VisitCall(Call* expr) {
if (call_type == Call::POSSIBLY_EVAL_CALL) {
// In a call to eval, we first call RuntimeHidden_ResolvePossiblyDirectEval
// to resolve the function we need to call and the receiver of the
// call. Then we call the resolved function using the given
// arguments.
// to resolve the function we need to call. Then we call the resolved
// function using the given arguments.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
@ -3160,10 +3153,8 @@ void FullCodeGenerator::VisitCall(Call* expr) {
__ push(a1);
EmitResolvePossiblyDirectEval(arg_count);
// The runtime call returns a pair of values in v0 (function) and
// v1 (receiver). Touch up the stack with the right values.
// Touch up the stack with the resolved function.
__ sd(v0, MemOperand(sp, (arg_count + 1) * kPointerSize));
__ sd(v1, MemOperand(sp, arg_count * kPointerSize));
PrepareForBailoutForId(expr->EvalOrLookupId(), NO_REGISTERS);
}
@ -4793,9 +4784,10 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
context()->Plug(v0);
} else if (proxy != NULL) {
Variable* var = proxy->var();
// Delete of an unqualified identifier is disallowed in strict mode
// but "delete this" is allowed.
DCHECK(is_sloppy(language_mode()) || var->is_this());
// Delete of an unqualified identifier is disallowed in strict mode but
// "delete this" is allowed.
bool is_this = var->HasThisName(isolate());
DCHECK(is_sloppy(language_mode()) || is_this);
if (var->IsUnallocated()) {
__ ld(a2, GlobalObjectOperand());
__ li(a1, Operand(var->name()));
@ -4806,7 +4798,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
} else if (var->IsStackAllocated() || var->IsContextSlot()) {
// Result of deleting non-global, non-dynamic variables is false.
// The subexpression does not have side effects.
context()->Plug(var->is_this());
context()->Plug(is_this);
} else {
// Non-global variable. Call the runtime to try to delete from the
// context where the variable was introduced.

View File

@ -4055,6 +4055,7 @@ class ScopeInfo : public FixedArray {
static Handle<ScopeInfo> Create(Isolate* isolate, Zone* zone, Scope* scope);
static Handle<ScopeInfo> CreateGlobalThisBinding(Isolate* isolate);
// Serializes empty scope info.
static ScopeInfo* Empty(Isolate* isolate);

View File

@ -3094,20 +3094,15 @@ void FullCodeGenerator::EmitCall(Call* expr, CallICState::CallType call_type) {
void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
// r8: copy of the first argument or undefined if it doesn't exist.
// r7: copy of the first argument or undefined if it doesn't exist.
if (arg_count > 0) {
__ LoadP(r8, MemOperand(sp, arg_count * kPointerSize), r0);
__ LoadP(r7, MemOperand(sp, arg_count * kPointerSize), r0);
} else {
__ LoadRoot(r8, Heap::kUndefinedValueRootIndex);
__ LoadRoot(r7, Heap::kUndefinedValueRootIndex);
}
// r7: the receiver of the enclosing function.
__ LoadP(r7, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
// r6: the receiver of the enclosing function.
Variable* this_var = scope()->LookupThis();
DCHECK_NOT_NULL(this_var);
GetVar(r6, this_var);
__ LoadP(r6, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
// r5: language mode.
__ LoadSmiLiteral(r5, Smi::FromInt(language_mode()));
@ -3116,8 +3111,8 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
__ LoadSmiLiteral(r4, Smi::FromInt(scope()->start_position()));
// Do the runtime call.
__ Push(r8, r7, r6, r5, r4);
__ CallRuntime(Runtime::kResolvePossiblyDirectEval, 6);
__ Push(r7, r6, r5, r4);
__ CallRuntime(Runtime::kResolvePossiblyDirectEval, 5);
}
@ -3150,9 +3145,8 @@ void FullCodeGenerator::VisitCall(Call* expr) {
if (call_type == Call::POSSIBLY_EVAL_CALL) {
// In a call to eval, we first call RuntimeHidden_ResolvePossiblyDirectEval
// to resolve the function we need to call and the receiver of the
// call. Then we call the resolved function using the given
// arguments.
// to resolve the function we need to call. Then we call the resolved
// function using the given arguments.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
@ -3173,10 +3167,8 @@ void FullCodeGenerator::VisitCall(Call* expr) {
__ push(r4);
EmitResolvePossiblyDirectEval(arg_count);
// The runtime call returns a pair of values in r3 (function) and
// r4 (receiver). Touch up the stack with the right values.
// Touch up the stack with the resolved function.
__ StoreP(r3, MemOperand(sp, (arg_count + 1) * kPointerSize), r0);
__ StoreP(r4, MemOperand(sp, arg_count * kPointerSize), r0);
PrepareForBailoutForId(expr->EvalOrLookupId(), NO_REGISTERS);
}
@ -4805,9 +4797,10 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
context()->Plug(r3);
} else if (proxy != NULL) {
Variable* var = proxy->var();
// Delete of an unqualified identifier is disallowed in strict mode
// but "delete this" is allowed.
DCHECK(is_sloppy(language_mode()) || var->is_this());
// Delete of an unqualified identifier is disallowed in strict mode but
// "delete this" is allowed.
bool is_this = var->HasThisName(isolate());
DCHECK(is_sloppy(language_mode()) || is_this);
if (var->IsUnallocated()) {
__ LoadP(r5, GlobalObjectOperand());
__ mov(r4, Operand(var->name()));
@ -4818,7 +4811,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
} else if (var->IsStackAllocated() || var->IsContextSlot()) {
// Result of deleting non-global, non-dynamic variables is false.
// The subexpression does not have side effects.
context()->Plug(var->is_this());
context()->Plug(is_this);
} else {
// Non-global variable. Call the runtime to try to delete from the
// context where the variable was introduced.

View File

@ -381,11 +381,10 @@ RUNTIME_FUNCTION(Runtime_CompileString) {
}
static ObjectPair CompileGlobalEval(Isolate* isolate, Handle<String> source,
Handle<SharedFunctionInfo> outer_info,
Handle<Object> receiver,
LanguageMode language_mode,
int scope_position) {
static Object* CompileGlobalEval(Isolate* isolate, Handle<String> source,
Handle<SharedFunctionInfo> outer_info,
LanguageMode language_mode,
int scope_position) {
Handle<Context> context = Handle<Context>(isolate->context());
Handle<Context> native_context = Handle<Context>(context->native_context());
@ -399,7 +398,7 @@ static ObjectPair CompileGlobalEval(Isolate* isolate, Handle<String> source,
MaybeHandle<Object> maybe_error = isolate->factory()->NewEvalError(
MessageTemplate::kCodeGenFromStrings, error_message);
if (maybe_error.ToHandle(&error)) isolate->Throw(*error);
return MakePair(isolate->heap()->exception(), NULL);
return isolate->heap()->exception();
}
// Deal with a normal eval call with a string argument. Compile it
@ -410,14 +409,14 @@ static ObjectPair CompileGlobalEval(Isolate* isolate, Handle<String> source,
isolate, compiled,
Compiler::GetFunctionFromEval(source, outer_info, context, language_mode,
restriction, scope_position),
MakePair(isolate->heap()->exception(), NULL));
return MakePair(*compiled, *receiver);
isolate->heap()->exception());
return *compiled;
}
RUNTIME_FUNCTION_RETURN_PAIR(Runtime_ResolvePossiblyDirectEval) {
RUNTIME_FUNCTION(Runtime_ResolvePossiblyDirectEval) {
HandleScope scope(isolate);
DCHECK(args.length() == 6);
DCHECK(args.length() == 5);
Handle<Object> callee = args.at<Object>(0);
@ -428,17 +427,17 @@ RUNTIME_FUNCTION_RETURN_PAIR(Runtime_ResolvePossiblyDirectEval) {
// the first argument without doing anything).
if (*callee != isolate->native_context()->global_eval_fun() ||
!args[1]->IsString()) {
return MakePair(*callee, isolate->heap()->undefined_value());
return *callee;
}
DCHECK(args[3]->IsSmi());
DCHECK(is_valid_language_mode(args.smi_at(3)));
LanguageMode language_mode = static_cast<LanguageMode>(args.smi_at(3));
DCHECK(args[4]->IsSmi());
DCHECK(is_valid_language_mode(args.smi_at(4)));
LanguageMode language_mode = static_cast<LanguageMode>(args.smi_at(4));
DCHECK(args[5]->IsSmi());
Handle<SharedFunctionInfo> outer_info(args.at<JSFunction>(2)->shared(),
isolate);
return CompileGlobalEval(isolate, args.at<String>(1), outer_info,
args.at<Object>(3), language_mode, args.smi_at(5));
language_mode, args.smi_at(4));
}
} // namespace internal
} // namespace v8

View File

@ -127,7 +127,8 @@ namespace internal {
F(NotifyDeoptimized, 1, 1) \
F(CompileForOnStackReplacement, 1, 1) \
F(TryInstallOptimizedCode, 1, 1) \
F(CompileString, 2, 1)
F(CompileString, 2, 1) \
F(ResolvePossiblyDirectEval, 5, 1)
#define FOR_EACH_INTRINSIC_DATE(F) \
@ -697,8 +698,7 @@ namespace internal {
#define FOR_EACH_INTRINSIC_RETURN_PAIR(F) \
F(LoadLookupSlot, 2, 2) \
F(LoadLookupSlotNoReferenceError, 2, 2) \
F(ResolvePossiblyDirectEval, 6, 2)
F(LoadLookupSlotNoReferenceError, 2, 2)
#define FOR_EACH_INTRINSIC_RETURN_OBJECT(F) \

View File

@ -6,6 +6,7 @@
#include "src/v8.h"
#include "src/bootstrapper.h"
#include "src/scopeinfo.h"
#include "src/scopes.h"
@ -192,6 +193,77 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
}
Handle<ScopeInfo> ScopeInfo::CreateGlobalThisBinding(Isolate* isolate) {
DCHECK(isolate->bootstrapper()->IsActive());
const int stack_local_count = 0;
const int context_local_count = 1;
const int strong_mode_free_variable_count = 0;
const bool simple_parameter_list = true;
const VariableAllocationInfo receiver_info = CONTEXT;
const VariableAllocationInfo function_name_info = NONE;
const VariableMode function_variable_mode = VAR;
const bool has_function_name = false;
const bool has_receiver = true;
const int parameter_count = 0;
const int length = kVariablePartIndex + parameter_count +
(1 + stack_local_count) + 2 * context_local_count +
3 * strong_mode_free_variable_count +
(has_receiver ? 1 : 0) + (has_function_name ? 2 : 0);
Factory* factory = isolate->factory();
Handle<ScopeInfo> scope_info = factory->NewScopeInfo(length);
// Encode the flags.
int flags = ScopeTypeField::encode(SCRIPT_SCOPE) |
CallsEvalField::encode(false) |
LanguageModeField::encode(SLOPPY) |
ReceiverVariableField::encode(receiver_info) |
FunctionVariableField::encode(function_name_info) |
FunctionVariableMode::encode(function_variable_mode) |
AsmModuleField::encode(false) | AsmFunctionField::encode(false) |
IsSimpleParameterListField::encode(simple_parameter_list) |
BlockScopeIsClassScopeField::encode(false) |
FunctionKindField::encode(FunctionKind::kNormalFunction);
scope_info->SetFlags(flags);
scope_info->SetParameterCount(parameter_count);
scope_info->SetStackLocalCount(stack_local_count);
scope_info->SetContextLocalCount(context_local_count);
scope_info->SetStrongModeFreeVariableCount(strong_mode_free_variable_count);
int index = kVariablePartIndex;
const int first_slot_index = 0;
DCHECK(index == scope_info->StackLocalFirstSlotIndex());
scope_info->set(index++, Smi::FromInt(first_slot_index));
DCHECK(index == scope_info->StackLocalEntriesIndex());
// Here we add info for context-allocated "this".
DCHECK(index == scope_info->ContextLocalNameEntriesIndex());
scope_info->set(index++, *isolate->factory()->this_string());
DCHECK(index == scope_info->ContextLocalInfoEntriesIndex());
const uint32_t value = ContextLocalMode::encode(CONST) |
ContextLocalInitFlag::encode(kCreatedInitialized) |
ContextLocalMaybeAssignedFlag::encode(kNotAssigned);
scope_info->set(index++, Smi::FromInt(value));
DCHECK(index == scope_info->StrongModeFreeVariableNameEntriesIndex());
DCHECK(index == scope_info->StrongModeFreeVariablePositionEntriesIndex());
// And here we record that this scopeinfo binds a receiver.
DCHECK(index == scope_info->ReceiverEntryIndex());
const int receiver_index = Context::MIN_CONTEXT_SLOTS + 0;
scope_info->set(index++, Smi::FromInt(receiver_index));
DCHECK(index == scope_info->FunctionNameEntryIndex());
DCHECK_EQ(index, scope_info->length());
DCHECK_EQ(scope_info->ParameterCount(), 0);
DCHECK_EQ(scope_info->ContextLength(), Context::MIN_CONTEXT_SLOTS + 1);
return scope_info;
}
ScopeInfo* ScopeInfo::Empty(Isolate* isolate) {
return reinterpret_cast<ScopeInfo*>(isolate->heap()->empty_fixed_array());
}

View File

@ -355,8 +355,7 @@ class Scope: public ZoneObject {
// "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();
return (is_function_scope() && !is_arrow_scope()) || is_module_scope();
}
// The variable corresponding to the 'new.target' value.

View File

@ -77,8 +77,9 @@ MaybeHandle<Context> Snapshot::NewContextFromSnapshot(
if (!maybe_context.ToHandle(&result)) return MaybeHandle<Context>();
CHECK(result->IsContext());
// If the snapshot does not contain a custom script, we need to update
// the global object for exactly one context.
CHECK(EmbedsScript(isolate) || (*outdated_contexts_out)->length() == 1);
// the global object for exactly two contexts: the builtins context and the
// script context that has the global "this" binding.
CHECK(EmbedsScript(isolate) || (*outdated_contexts_out)->length() == 2);
if (FLAG_profile_deserialization) {
double ms = timer.Elapsed().InMillisecondsF();
int bytes = context_data.length();

View File

@ -105,6 +105,16 @@ class Variable: public ZoneObject {
bool is_this() const { return kind_ == THIS; }
bool is_arguments() const { return kind_ == ARGUMENTS; }
// For script scopes, the "this" binding is provided by a ScriptContext added
// to the global's ScriptContextTable. This binding might not statically
// resolve to a Variable::THIS binding, instead being DYNAMIC_LOCAL. However
// any variable named "this" does indeed refer to a Variable::THIS binding;
// the grammar ensures this to be the case. So wherever a "this" binding
// might be provided by the global, use HasThisName instead of is_this().
bool HasThisName(Isolate* isolate) const {
return is_this() || *name() == *isolate->factory()->this_string();
}
ClassVariable* AsClassVariable() {
DCHECK(is_class());
return reinterpret_cast<ClassVariable*>(this);

View File

@ -3004,11 +3004,6 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
// Push the enclosing function.
__ Push(Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
// Push the receiver of the enclosing function and do runtime call.
Variable* this_var = scope()->LookupThis();
DCHECK_NOT_NULL(this_var);
__ Push(VarOperand(this_var, rcx));
// Push the language mode.
__ Push(Smi::FromInt(language_mode()));
@ -3016,7 +3011,7 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
__ Push(Smi::FromInt(scope()->start_position()));
// Do the runtime call.
__ CallRuntime(Runtime::kResolvePossiblyDirectEval, 6);
__ CallRuntime(Runtime::kResolvePossiblyDirectEval, 5);
}
@ -3048,8 +3043,8 @@ void FullCodeGenerator::VisitCall(Call* expr) {
if (call_type == Call::POSSIBLY_EVAL_CALL) {
// In a call to eval, we first call RuntimeHidden_ResolvePossiblyDirectEval
// to resolve the function we need to call and the receiver of the call.
// Then we call the resolved function using the given arguments.
// to resolve the function we need to call. Then we call the resolved
// function using the given arguments.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
{ PreservePositionScope pos_scope(masm()->positions_recorder());
@ -3066,9 +3061,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
__ Push(Operand(rsp, (arg_count + 1) * kPointerSize));
EmitResolvePossiblyDirectEval(arg_count);
// The runtime call returns a pair of values in rax (function) and
// rdx (receiver). Touch up the stack with the right values.
__ movp(Operand(rsp, (arg_count + 0) * kPointerSize), rdx);
// Touch up the callee.
__ movp(Operand(rsp, (arg_count + 1) * kPointerSize), rax);
PrepareForBailoutForId(expr->EvalOrLookupId(), NO_REGISTERS);
@ -4728,9 +4721,10 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
context()->Plug(rax);
} else if (proxy != NULL) {
Variable* var = proxy->var();
// Delete of an unqualified identifier is disallowed in strict mode
// but "delete this" is allowed.
DCHECK(is_sloppy(language_mode()) || var->is_this());
// Delete of an unqualified identifier is disallowed in strict mode but
// "delete this" is allowed.
bool is_this = var->HasThisName(isolate());
DCHECK(is_sloppy(language_mode()) || is_this);
if (var->IsUnallocated()) {
__ Push(GlobalObjectOperand());
__ Push(var->name());
@ -4741,7 +4735,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
// Result of deleting non-global variables is false. 'this' is
// not really a variable, though we implement it as one. The
// subexpression does not have side effects.
context()->Plug(var->is_this());
context()->Plug(is_this);
} else {
// Non-global variable. Call the runtime to try to delete from the
// context where the variable was introduced.

View File

@ -2997,10 +2997,7 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
// Push the enclosing function.
__ push(Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
// Push the receiver of the enclosing function.
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())));
@ -3008,7 +3005,7 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
__ push(Immediate(Smi::FromInt(scope()->start_position())));
// Do the runtime call.
__ CallRuntime(Runtime::kResolvePossiblyDirectEval, 6);
__ CallRuntime(Runtime::kResolvePossiblyDirectEval, 5);
}
@ -3040,8 +3037,8 @@ void FullCodeGenerator::VisitCall(Call* expr) {
if (call_type == Call::POSSIBLY_EVAL_CALL) {
// In a call to eval, we first call RuntimeHidden_ResolvePossiblyDirectEval
// to resolve the function we need to call and the receiver of the call.
// Then we call the resolved function using the given arguments.
// to resolve the function we need to call. Then we call the resolved
// function using the given arguments.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
{ PreservePositionScope pos_scope(masm()->positions_recorder());
@ -3058,9 +3055,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
__ push(Operand(esp, (arg_count + 1) * kPointerSize));
EmitResolvePossiblyDirectEval(arg_count);
// The runtime call returns a pair of values in eax (function) and
// edx (receiver). Touch up the stack with the right values.
__ mov(Operand(esp, (arg_count + 0) * kPointerSize), edx);
// Touch up the stack with the resolved function.
__ mov(Operand(esp, (arg_count + 1) * kPointerSize), eax);
PrepareForBailoutForId(expr->EvalOrLookupId(), NO_REGISTERS);
@ -4693,9 +4688,10 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
context()->Plug(eax);
} else if (proxy != NULL) {
Variable* var = proxy->var();
// Delete of an unqualified identifier is disallowed in strict mode
// but "delete this" is allowed.
DCHECK(is_sloppy(language_mode()) || var->is_this());
// Delete of an unqualified identifier is disallowed in strict mode but
// "delete this" is allowed.
bool is_this = var->HasThisName(isolate());
DCHECK(is_sloppy(language_mode()) || is_this);
if (var->IsUnallocated()) {
__ push(GlobalObjectOperand());
__ push(Immediate(var->name()));
@ -4706,7 +4702,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
// Result of deleting non-global variables is false. 'this' is
// not really a variable, though we implement it as one. The
// subexpression does not have side effects.
context()->Plug(var->is_this());
context()->Plug(is_this);
} else {
// Non-global variable. Call the runtime to try to delete from the
// context where the variable was introduced.

View File

@ -493,7 +493,7 @@ UNINITIALIZED_DEPENDENT_TEST(ContextDeserialization, ContextSerialization) {
&outdated_contexts).ToHandleChecked();
CHECK(root->IsContext());
CHECK(Handle<Context>::cast(root)->global_proxy() == *global_proxy);
CHECK_EQ(1, outdated_contexts->length());
CHECK_EQ(2, outdated_contexts->length());
}
Handle<Object> root2;

View File

@ -0,0 +1,9 @@
// 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.
// NO HARNESS
"use strict";
let l = 0;
eval("eval('this')");

View File

@ -83,7 +83,7 @@ class JSTypeFeedbackTest : public TypedGraphTest {
Node* context = UndefinedConstant();
Unique<Name> name = Unique<Name>::CreateUninitialized(
isolate()->factory()->NewStringFromAsciiChecked(string));
isolate()->factory()->InternalizeUtf8String(string));
const Operator* op = javascript()->LoadNamed(name, feedback);
Node* load = graph()->NewNode(op, global, vector, context);
if (mode == JSTypeFeedbackSpecializer::kDeoptimizationEnabled) {