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:
parent
c487aba74c
commit
103fcfaa40
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -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());
|
||||
|
@ -10197,11 +10197,10 @@ 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()
|
||||
// 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 {
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -381,9 +381,8 @@ RUNTIME_FUNCTION(Runtime_CompileString) {
|
||||
}
|
||||
|
||||
|
||||
static ObjectPair CompileGlobalEval(Isolate* isolate, Handle<String> source,
|
||||
static Object* CompileGlobalEval(Isolate* isolate, Handle<String> source,
|
||||
Handle<SharedFunctionInfo> outer_info,
|
||||
Handle<Object> receiver,
|
||||
LanguageMode language_mode,
|
||||
int scope_position) {
|
||||
Handle<Context> context = Handle<Context>(isolate->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
|
||||
|
@ -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) \
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
9
test/mjsunit/regress/regress-crbug-498811.js
Normal file
9
test/mjsunit/regress/regress-crbug-498811.js
Normal 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')");
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user