new classes: change semantics of super(...) call and add new.target to construct stub.
R=arv@chromium.org,rossberg@chromium.org BUG=v8:3834 LOG=N Review URL: https://codereview.chromium.org/803933008 Cr-Commit-Position: refs/heads/master@{#26227}
This commit is contained in:
parent
6f9d60cc6f
commit
22ce08ade6
@ -310,6 +310,36 @@ void Builtins::Generate_InOptimizationQueue(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
static void Generate_Runtime_NewObject(MacroAssembler* masm,
|
||||
bool create_memento,
|
||||
Register original_constructor,
|
||||
Label* count_incremented,
|
||||
Label* allocated) {
|
||||
if (create_memento) {
|
||||
// Get the cell or allocation site.
|
||||
__ ldr(r2, MemOperand(sp, 2 * kPointerSize));
|
||||
__ push(r2);
|
||||
}
|
||||
|
||||
__ push(r1); // argument for Runtime_NewObject
|
||||
__ push(original_constructor); // original constructor
|
||||
if (create_memento) {
|
||||
__ CallRuntime(Runtime::kNewObjectWithAllocationSite, 3);
|
||||
} else {
|
||||
__ CallRuntime(Runtime::kNewObject, 2);
|
||||
}
|
||||
__ mov(r4, r0);
|
||||
|
||||
// Runtime_NewObjectWithAllocationSite increments allocation count.
|
||||
// Skip the increment.
|
||||
if (create_memento) {
|
||||
__ jmp(count_incremented);
|
||||
} else {
|
||||
__ jmp(allocated);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void Generate_JSConstructStubHelper(MacroAssembler* masm,
|
||||
bool is_api_function,
|
||||
bool create_memento) {
|
||||
@ -317,6 +347,7 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
|
||||
// -- r0 : number of arguments
|
||||
// -- r1 : constructor function
|
||||
// -- r2 : allocation site or undefined
|
||||
// -- r3 : original constructor
|
||||
// -- lr : return address
|
||||
// -- sp[...]: constructor arguments
|
||||
// -----------------------------------
|
||||
@ -331,7 +362,7 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
|
||||
FrameAndConstantPoolScope scope(masm, StackFrame::CONSTRUCT);
|
||||
|
||||
if (create_memento) {
|
||||
__ AssertUndefinedOrAllocationSite(r2, r3);
|
||||
__ AssertUndefinedOrAllocationSite(r2, r4);
|
||||
__ push(r2);
|
||||
}
|
||||
|
||||
@ -340,9 +371,17 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
|
||||
__ push(r0); // Smi-tagged arguments count.
|
||||
__ push(r1); // Constructor function.
|
||||
|
||||
Label rt_call, allocated, normal_new, count_incremented;
|
||||
__ cmp(r1, r3);
|
||||
__ b(eq, &normal_new);
|
||||
|
||||
// Original constructor and function are different.
|
||||
Generate_Runtime_NewObject(masm, create_memento, r3, &count_incremented,
|
||||
&allocated);
|
||||
__ bind(&normal_new);
|
||||
|
||||
// Try to allocate the object without transitioning into C code. If any of
|
||||
// the preconditions is not met, the code bails out to the runtime call.
|
||||
Label rt_call, allocated;
|
||||
if (FLAG_inline_new) {
|
||||
Label undo_allocation;
|
||||
ExternalReference debug_step_in_fp =
|
||||
@ -569,27 +608,8 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
|
||||
// Allocate the new receiver object using the runtime call.
|
||||
// r1: constructor function
|
||||
__ bind(&rt_call);
|
||||
if (create_memento) {
|
||||
// Get the cell or allocation site.
|
||||
__ ldr(r2, MemOperand(sp, 2 * kPointerSize));
|
||||
__ push(r2);
|
||||
}
|
||||
|
||||
__ push(r1); // argument for Runtime_NewObject
|
||||
if (create_memento) {
|
||||
__ CallRuntime(Runtime::kNewObjectWithAllocationSite, 2);
|
||||
} else {
|
||||
__ CallRuntime(Runtime::kNewObject, 1);
|
||||
}
|
||||
__ mov(r4, r0);
|
||||
|
||||
// If we ended up using the runtime, and we want a memento, then the
|
||||
// runtime call made it for us, and we shouldn't do create count
|
||||
// increment.
|
||||
Label count_incremented;
|
||||
if (create_memento) {
|
||||
__ jmp(&count_incremented);
|
||||
}
|
||||
Generate_Runtime_NewObject(masm, create_memento, r1, &count_incremented,
|
||||
&allocated);
|
||||
|
||||
// Receiver for constructor call allocated.
|
||||
// r4: JSObject
|
||||
|
@ -2562,6 +2562,9 @@ void CallConstructStub::Generate(MacroAssembler* masm) {
|
||||
__ AssertUndefinedOrAllocationSite(r2, r5);
|
||||
}
|
||||
|
||||
// Pass function as original constructor.
|
||||
__ mov(r3, r1);
|
||||
|
||||
// Jump to the function-specific construct stub.
|
||||
Register jmp_reg = r4;
|
||||
__ ldr(jmp_reg, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
|
||||
|
@ -3173,11 +3173,15 @@ void FullCodeGenerator::VisitCall(Call* expr) {
|
||||
}
|
||||
}
|
||||
} else if (call_type == Call::SUPER_CALL) {
|
||||
SuperReference* super_ref = callee->AsSuperReference();
|
||||
EmitLoadSuperConstructor(super_ref);
|
||||
__ Push(result_register());
|
||||
VisitForStackValue(super_ref->this_var());
|
||||
EmitCall(expr, CallICState::METHOD);
|
||||
if (FLAG_experimental_classes) {
|
||||
EmitSuperConstructorCall(expr);
|
||||
} else {
|
||||
SuperReference* super_ref = callee->AsSuperReference();
|
||||
EmitLoadSuperConstructor(super_ref);
|
||||
__ Push(result_register());
|
||||
VisitForStackValue(super_ref->this_var());
|
||||
EmitCall(expr, CallICState::METHOD);
|
||||
}
|
||||
} else {
|
||||
DCHECK(call_type == Call::OTHER_CALL);
|
||||
// Call to an arbitrary expression not handled specially above.
|
||||
@ -3245,6 +3249,51 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
|
||||
SuperReference* super_ref = expr->expression()->AsSuperReference();
|
||||
EmitLoadSuperConstructor(super_ref);
|
||||
__ push(result_register());
|
||||
|
||||
// Push the arguments ("left-to-right") on the stack.
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
int arg_count = args->length();
|
||||
for (int i = 0; i < arg_count; i++) {
|
||||
VisitForStackValue(args->at(i));
|
||||
}
|
||||
|
||||
// Call the construct call builtin that handles allocation and
|
||||
// constructor invocation.
|
||||
SetSourcePosition(expr->position());
|
||||
|
||||
// Load function and argument count into r1 and r0.
|
||||
__ mov(r0, Operand(arg_count));
|
||||
__ ldr(r1, MemOperand(sp, arg_count * kPointerSize));
|
||||
|
||||
// Record call targets in unoptimized code.
|
||||
if (FLAG_pretenuring_call_new) {
|
||||
UNREACHABLE();
|
||||
/* TODO(dslomov): support pretenuring.
|
||||
EnsureSlotContainsAllocationSite(expr->AllocationSiteFeedbackSlot());
|
||||
DCHECK(expr->AllocationSiteFeedbackSlot().ToInt() ==
|
||||
expr->CallNewFeedbackSlot().ToInt() + 1);
|
||||
*/
|
||||
}
|
||||
|
||||
__ Move(r2, FeedbackVector());
|
||||
__ mov(r3, Operand(SmiFromSlot(expr->CallFeedbackSlot())));
|
||||
|
||||
// TODO(dslomov): use a different stub and propagate new.target.
|
||||
CallConstructStub stub(isolate(), RECORD_CONSTRUCTOR_TARGET);
|
||||
__ Call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
|
||||
|
||||
RecordJSReturnSite(expr);
|
||||
|
||||
// TODO(dslomov): implement TDZ for `this`.
|
||||
EmitVariableAssignment(super_ref->this_var()->var(), Token::ASSIGN);
|
||||
context()->Plug(r0);
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitIsSmi(CallRuntime* expr) {
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
DCHECK(args->length() == 1);
|
||||
|
@ -301,6 +301,33 @@ void Builtins::Generate_InOptimizationQueue(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
static void Generate_Runtime_NewObject(MacroAssembler* masm,
|
||||
bool create_memento,
|
||||
Register original_constructor,
|
||||
Label* count_incremented,
|
||||
Label* allocated) {
|
||||
if (create_memento) {
|
||||
// Get the cell or allocation site.
|
||||
__ Peek(x4, 2 * kXRegSize);
|
||||
__ Push(x4);
|
||||
__ Push(x1); // Argument for Runtime_NewObject.
|
||||
__ Push(original_constructor);
|
||||
__ CallRuntime(Runtime::kNewObjectWithAllocationSite, 3);
|
||||
__ Mov(x4, x0);
|
||||
// If we ended up using the runtime, and we want a memento, then the
|
||||
// runtime call made it for us, and we shouldn't do create count
|
||||
// increment.
|
||||
__ jmp(count_incremented);
|
||||
} else {
|
||||
__ Push(x1); // Argument for Runtime_NewObject.
|
||||
__ Push(original_constructor);
|
||||
__ CallRuntime(Runtime::kNewObject, 2);
|
||||
__ Mov(x4, x0);
|
||||
__ jmp(allocated);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void Generate_JSConstructStubHelper(MacroAssembler* masm,
|
||||
bool is_api_function,
|
||||
bool create_memento) {
|
||||
@ -308,6 +335,7 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
|
||||
// -- x0 : number of arguments
|
||||
// -- x1 : constructor function
|
||||
// -- x2 : allocation site or undefined
|
||||
// -- x3 : original constructor
|
||||
// -- lr : return address
|
||||
// -- sp[...]: constructor arguments
|
||||
// -----------------------------------
|
||||
@ -330,15 +358,23 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
|
||||
|
||||
Register argc = x0;
|
||||
Register constructor = x1;
|
||||
Register original_constructor = x3;
|
||||
// x1: constructor function
|
||||
__ SmiTag(argc);
|
||||
__ Push(argc, constructor);
|
||||
// sp[0] : Constructor function.
|
||||
// sp[1]: number of arguments (smi-tagged)
|
||||
|
||||
Label rt_call, count_incremented, allocated, normal_new;
|
||||
__ Cmp(constructor, original_constructor);
|
||||
__ B(eq, &normal_new);
|
||||
Generate_Runtime_NewObject(masm, create_memento, original_constructor,
|
||||
&count_incremented, &allocated);
|
||||
|
||||
__ Bind(&normal_new);
|
||||
|
||||
// Try to allocate the object without transitioning into C code. If any of
|
||||
// the preconditions is not met, the code bails out to the runtime call.
|
||||
Label rt_call, allocated;
|
||||
if (FLAG_inline_new) {
|
||||
Label undo_allocation;
|
||||
ExternalReference debug_step_in_fp =
|
||||
@ -535,23 +571,8 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
|
||||
|
||||
// Allocate the new receiver object using the runtime call.
|
||||
__ Bind(&rt_call);
|
||||
Label count_incremented;
|
||||
if (create_memento) {
|
||||
// Get the cell or allocation site.
|
||||
__ Peek(x4, 2 * kXRegSize);
|
||||
__ Push(x4);
|
||||
__ Push(constructor); // Argument for Runtime_NewObject.
|
||||
__ CallRuntime(Runtime::kNewObjectWithAllocationSite, 2);
|
||||
__ Mov(x4, x0);
|
||||
// If we ended up using the runtime, and we want a memento, then the
|
||||
// runtime call made it for us, and we shouldn't do create count
|
||||
// increment.
|
||||
__ jmp(&count_incremented);
|
||||
} else {
|
||||
__ Push(constructor); // Argument for Runtime_NewObject.
|
||||
__ CallRuntime(Runtime::kNewObject, 1);
|
||||
__ Mov(x4, x0);
|
||||
}
|
||||
Generate_Runtime_NewObject(masm, create_memento, constructor,
|
||||
&count_incremented, &allocated);
|
||||
|
||||
// Receiver for constructor call allocated.
|
||||
// x4: JSObject
|
||||
|
@ -2938,6 +2938,8 @@ void CallConstructStub::Generate(MacroAssembler* masm) {
|
||||
__ AssertUndefinedOrAllocationSite(x2, x5);
|
||||
}
|
||||
|
||||
__ Mov(x3, function);
|
||||
|
||||
// Jump to the function-specific construct stub.
|
||||
Register jump_reg = x4;
|
||||
Register shared_func_info = jump_reg;
|
||||
|
@ -2862,11 +2862,15 @@ void FullCodeGenerator::VisitCall(Call* expr) {
|
||||
}
|
||||
}
|
||||
} else if (call_type == Call::SUPER_CALL) {
|
||||
SuperReference* super_ref = callee->AsSuperReference();
|
||||
EmitLoadSuperConstructor(super_ref);
|
||||
__ Push(result_register());
|
||||
VisitForStackValue(super_ref->this_var());
|
||||
EmitCall(expr, CallICState::METHOD);
|
||||
if (FLAG_experimental_classes) {
|
||||
EmitSuperConstructorCall(expr);
|
||||
} else {
|
||||
SuperReference* super_ref = callee->AsSuperReference();
|
||||
EmitLoadSuperConstructor(super_ref);
|
||||
__ Push(result_register());
|
||||
VisitForStackValue(super_ref->this_var());
|
||||
EmitCall(expr, CallICState::METHOD);
|
||||
}
|
||||
} else {
|
||||
DCHECK(call_type == Call::OTHER_CALL);
|
||||
// Call to an arbitrary expression not handled specially above.
|
||||
@ -2934,6 +2938,51 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
|
||||
SuperReference* super_ref = expr->expression()->AsSuperReference();
|
||||
EmitLoadSuperConstructor(super_ref);
|
||||
__ push(result_register());
|
||||
|
||||
// Push the arguments ("left-to-right") on the stack.
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
int arg_count = args->length();
|
||||
for (int i = 0; i < arg_count; i++) {
|
||||
VisitForStackValue(args->at(i));
|
||||
}
|
||||
|
||||
// Call the construct call builtin that handles allocation and
|
||||
// constructor invocation.
|
||||
SetSourcePosition(expr->position());
|
||||
|
||||
// Load function and argument count into x1 and x0.
|
||||
__ Mov(x0, arg_count);
|
||||
__ Peek(x1, arg_count * kXRegSize);
|
||||
|
||||
// Record call targets in unoptimized code.
|
||||
if (FLAG_pretenuring_call_new) {
|
||||
UNREACHABLE();
|
||||
/* TODO(dslomov): support pretenuring.
|
||||
EnsureSlotContainsAllocationSite(expr->AllocationSiteFeedbackSlot());
|
||||
DCHECK(expr->AllocationSiteFeedbackSlot().ToInt() ==
|
||||
expr->CallNewFeedbackSlot().ToInt() + 1);
|
||||
*/
|
||||
}
|
||||
|
||||
__ LoadObject(x2, FeedbackVector());
|
||||
__ Mov(x3, SmiFromSlot(expr->CallFeedbackSlot()));
|
||||
|
||||
// TODO(dslomov): use a different stub and propagate new.target.
|
||||
CallConstructStub stub(isolate(), RECORD_CONSTRUCTOR_TARGET);
|
||||
__ Call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
|
||||
|
||||
RecordJSReturnSite(expr);
|
||||
|
||||
// TODO(dslomov): implement TDZ for `this`.
|
||||
EmitVariableAssignment(super_ref->this_var()->var(), Token::ASSIGN);
|
||||
context()->Plug(x0);
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitIsSmi(CallRuntime* expr) {
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
DCHECK(args->length() == 1);
|
||||
|
@ -786,8 +786,8 @@ static void ThrowSuperConstructorCheckError(CompilationInfo* info,
|
||||
|
||||
static bool CheckSuperConstructorCall(CompilationInfo* info) {
|
||||
FunctionLiteral* function = info->function();
|
||||
if (FLAG_experimental_classes) return true;
|
||||
if (!function->uses_super_constructor_call()) return true;
|
||||
|
||||
if (function->is_default_constructor()) return true;
|
||||
|
||||
ZoneList<Statement*>* body = function->body();
|
||||
@ -837,7 +837,6 @@ static bool CheckSuperConstructorCall(CompilationInfo* info) {
|
||||
ThrowSuperConstructorCheckError(info, stmt);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -256,6 +256,10 @@ DEFINE_IMPLICATION(track_field_types, track_fields)
|
||||
DEFINE_IMPLICATION(track_field_types, track_heap_object_fields)
|
||||
DEFINE_BOOL(smi_binop, true, "support smi representation in binary operations")
|
||||
DEFINE_BOOL(vector_ics, false, "support vector-based ics")
|
||||
DEFINE_BOOL(experimental_classes, false,
|
||||
"experimental new semantics for super() calls")
|
||||
DEFINE_IMPLICATION(experimental_classes, harmony_classes)
|
||||
DEFINE_IMPLICATION(experimental_classes, harmony_object_literals)
|
||||
|
||||
// Flags for optimization types.
|
||||
DEFINE_BOOL(optimize_for_size, false,
|
||||
|
@ -496,6 +496,7 @@ class FullCodeGenerator: public AstVisitor {
|
||||
|
||||
// Platform-specific code sequences for calls
|
||||
void EmitCall(Call* expr, CallICState::CallType = CallICState::FUNCTION);
|
||||
void EmitSuperConstructorCall(Call* expr);
|
||||
void EmitCallWithLoadIC(Call* expr);
|
||||
void EmitSuperCallWithLoadIC(Call* expr);
|
||||
void EmitKeyedCallWithLoadIC(Call* expr, Expression* key);
|
||||
|
@ -100,6 +100,42 @@ void Builtins::Generate_InOptimizationQueue(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
static void Generate_Runtime_NewObject(MacroAssembler* masm,
|
||||
bool create_memento,
|
||||
Register original_constructor,
|
||||
Label* count_incremented,
|
||||
Label* allocated) {
|
||||
int offset = 0;
|
||||
if (create_memento) {
|
||||
// Get the cell or allocation site.
|
||||
__ mov(edi, Operand(esp, kPointerSize * 2));
|
||||
__ push(edi);
|
||||
offset = kPointerSize;
|
||||
}
|
||||
|
||||
// Must restore esi (context) and edi (constructor) before calling
|
||||
// runtime.
|
||||
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
|
||||
__ mov(edi, Operand(esp, offset));
|
||||
__ push(edi);
|
||||
__ push(original_constructor);
|
||||
if (create_memento) {
|
||||
__ CallRuntime(Runtime::kNewObjectWithAllocationSite, 3);
|
||||
} else {
|
||||
__ CallRuntime(Runtime::kNewObject, 2);
|
||||
}
|
||||
__ mov(ebx, eax); // store result in ebx
|
||||
|
||||
// Runtime_NewObjectWithAllocationSite increments allocation count.
|
||||
// Skip the increment.
|
||||
if (create_memento) {
|
||||
__ jmp(count_incremented);
|
||||
} else {
|
||||
__ jmp(allocated);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void Generate_JSConstructStubHelper(MacroAssembler* masm,
|
||||
bool is_api_function,
|
||||
bool create_memento) {
|
||||
@ -107,6 +143,7 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
|
||||
// -- eax: number of arguments
|
||||
// -- edi: constructor function
|
||||
// -- ebx: allocation site or undefined
|
||||
// -- edx: original constructor
|
||||
// -----------------------------------
|
||||
|
||||
// Should never create mementos for api functions.
|
||||
@ -128,9 +165,20 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
|
||||
// Push the function to invoke on the stack.
|
||||
__ push(edi);
|
||||
|
||||
__ cmp(edx, edi);
|
||||
Label normal_new;
|
||||
Label count_incremented;
|
||||
Label allocated;
|
||||
__ j(equal, &normal_new);
|
||||
|
||||
// Original constructor and function are different.
|
||||
Generate_Runtime_NewObject(masm, create_memento, edx, &count_incremented,
|
||||
&allocated);
|
||||
__ bind(&normal_new);
|
||||
|
||||
// Try to allocate the object without transitioning into C code. If any of
|
||||
// the preconditions is not met, the code bails out to the runtime call.
|
||||
Label rt_call, allocated;
|
||||
Label rt_call;
|
||||
if (FLAG_inline_new) {
|
||||
Label undo_allocation;
|
||||
ExternalReference debug_step_in_fp =
|
||||
@ -344,34 +392,8 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
|
||||
|
||||
// Allocate the new receiver object using the runtime call.
|
||||
__ bind(&rt_call);
|
||||
int offset = 0;
|
||||
if (create_memento) {
|
||||
// Get the cell or allocation site.
|
||||
__ mov(edi, Operand(esp, kPointerSize * 2));
|
||||
__ push(edi);
|
||||
offset = kPointerSize;
|
||||
}
|
||||
|
||||
// Must restore esi (context) and edi (constructor) before calling runtime.
|
||||
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
|
||||
__ mov(edi, Operand(esp, offset));
|
||||
// edi: function (constructor)
|
||||
__ push(edi);
|
||||
if (create_memento) {
|
||||
__ CallRuntime(Runtime::kNewObjectWithAllocationSite, 2);
|
||||
} else {
|
||||
__ CallRuntime(Runtime::kNewObject, 1);
|
||||
}
|
||||
__ mov(ebx, eax); // store result in ebx
|
||||
|
||||
// If we ended up using the runtime, and we want a memento, then the
|
||||
// runtime call made it for us, and we shouldn't do create count
|
||||
// increment.
|
||||
Label count_incremented;
|
||||
if (create_memento) {
|
||||
__ jmp(&count_incremented);
|
||||
}
|
||||
|
||||
Generate_Runtime_NewObject(masm, create_memento, edi, &count_incremented,
|
||||
&allocated);
|
||||
// New object allocated.
|
||||
// ebx: newly allocated object
|
||||
__ bind(&allocated);
|
||||
|
@ -2149,6 +2149,9 @@ void CallConstructStub::Generate(MacroAssembler* masm) {
|
||||
__ AssertUndefinedOrAllocationSite(ebx);
|
||||
}
|
||||
|
||||
// Pass original constructor to construct stub.
|
||||
__ mov(edx, edi);
|
||||
|
||||
// Jump to the function-specific construct stub.
|
||||
Register jmp_reg = ecx;
|
||||
__ mov(jmp_reg, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
|
||||
|
@ -3001,7 +3001,6 @@ void FullCodeGenerator::VisitCall(Call* expr) {
|
||||
|
||||
} else if (call_type == Call::GLOBAL_CALL) {
|
||||
EmitCallWithLoadIC(expr);
|
||||
|
||||
} else if (call_type == Call::LOOKUP_SLOT_CALL) {
|
||||
// Call to a lookup slot (dynamically introduced variable).
|
||||
VariableProxy* proxy = callee->AsVariableProxy();
|
||||
@ -3060,11 +3059,15 @@ void FullCodeGenerator::VisitCall(Call* expr) {
|
||||
}
|
||||
}
|
||||
} else if (call_type == Call::SUPER_CALL) {
|
||||
SuperReference* super_ref = callee->AsSuperReference();
|
||||
EmitLoadSuperConstructor(super_ref);
|
||||
__ push(result_register());
|
||||
VisitForStackValue(super_ref->this_var());
|
||||
EmitCall(expr, CallICState::METHOD);
|
||||
if (FLAG_experimental_classes) {
|
||||
EmitSuperConstructorCall(expr);
|
||||
} else {
|
||||
SuperReference* super_ref = callee->AsSuperReference();
|
||||
EmitLoadSuperConstructor(super_ref);
|
||||
__ push(result_register());
|
||||
VisitForStackValue(super_ref->this_var());
|
||||
EmitCall(expr, CallICState::METHOD);
|
||||
}
|
||||
} else {
|
||||
DCHECK(call_type == Call::OTHER_CALL);
|
||||
// Call to an arbitrary expression not handled specially above.
|
||||
@ -3131,6 +3134,51 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
|
||||
SuperReference* super_ref = expr->expression()->AsSuperReference();
|
||||
EmitLoadSuperConstructor(super_ref);
|
||||
__ push(result_register());
|
||||
|
||||
// Push the arguments ("left-to-right") on the stack.
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
int arg_count = args->length();
|
||||
for (int i = 0; i < arg_count; i++) {
|
||||
VisitForStackValue(args->at(i));
|
||||
}
|
||||
|
||||
// Call the construct call builtin that handles allocation and
|
||||
// constructor invocation.
|
||||
SetSourcePosition(expr->position());
|
||||
|
||||
// Load function and argument count into edi and eax.
|
||||
__ Move(eax, Immediate(arg_count));
|
||||
__ mov(edi, Operand(esp, arg_count * kPointerSize));
|
||||
|
||||
// Record call targets in unoptimized code.
|
||||
if (FLAG_pretenuring_call_new) {
|
||||
UNREACHABLE();
|
||||
/* TODO(dslomov): support pretenuring.
|
||||
EnsureSlotContainsAllocationSite(expr->AllocationSiteFeedbackSlot());
|
||||
DCHECK(expr->AllocationSiteFeedbackSlot().ToInt() ==
|
||||
expr->CallNewFeedbackSlot().ToInt() + 1);
|
||||
*/
|
||||
}
|
||||
|
||||
__ LoadHeapObject(ebx, FeedbackVector());
|
||||
__ mov(edx, Immediate(SmiFromSlot(expr->CallFeedbackSlot())));
|
||||
|
||||
// TODO(dslomov): use a different stub and propagate new.target.
|
||||
CallConstructStub stub(isolate(), RECORD_CONSTRUCTOR_TARGET);
|
||||
__ call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
|
||||
|
||||
RecordJSReturnSite(expr);
|
||||
|
||||
// TODO(dslomov): implement TDZ for `this`.
|
||||
EmitVariableAssignment(super_ref->this_var()->var(), Token::ASSIGN);
|
||||
context()->Plug(eax);
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitIsSmi(CallRuntime* expr) {
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
DCHECK(args->length() == 1);
|
||||
|
@ -1279,7 +1279,12 @@ RUNTIME_FUNCTION(Runtime_AllocateHeapNumber) {
|
||||
|
||||
static Object* Runtime_NewObjectHelper(Isolate* isolate,
|
||||
Handle<Object> constructor,
|
||||
Handle<Object> original_constructor,
|
||||
Handle<AllocationSite> site) {
|
||||
// TODO(dslomov): implement prototype rewiring.
|
||||
// The check below is a sanity check.
|
||||
CHECK(*constructor == *original_constructor);
|
||||
|
||||
// If the constructor isn't a proper function we throw a type error.
|
||||
if (!constructor->IsJSFunction()) {
|
||||
Vector<Handle<Object> > arguments = HandleVector(&constructor, 1);
|
||||
@ -1340,16 +1345,18 @@ static Object* Runtime_NewObjectHelper(Isolate* isolate,
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NewObject) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
DCHECK(args.length() == 2);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, constructor, 0);
|
||||
return Runtime_NewObjectHelper(isolate, constructor,
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, original_constructor, 1);
|
||||
return Runtime_NewObjectHelper(isolate, constructor, original_constructor,
|
||||
Handle<AllocationSite>::null());
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_NewObjectWithAllocationSite) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
DCHECK(args.length() == 3);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, original_constructor, 2);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, constructor, 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, feedback, 0);
|
||||
Handle<AllocationSite> site;
|
||||
@ -1357,7 +1364,8 @@ RUNTIME_FUNCTION(Runtime_NewObjectWithAllocationSite) {
|
||||
// The feedback can be an AllocationSite or undefined.
|
||||
site = Handle<AllocationSite>::cast(feedback);
|
||||
}
|
||||
return Runtime_NewObjectHelper(isolate, constructor, site);
|
||||
return Runtime_NewObjectHelper(isolate, constructor, original_constructor,
|
||||
site);
|
||||
}
|
||||
|
||||
|
||||
|
@ -477,8 +477,8 @@ namespace internal {
|
||||
/* Statements */ \
|
||||
F(NewClosure, 3, 1) \
|
||||
F(NewClosureFromStubFailure, 1, 1) \
|
||||
F(NewObject, 1, 1) \
|
||||
F(NewObjectWithAllocationSite, 2, 1) \
|
||||
F(NewObject, 2, 1) \
|
||||
F(NewObjectWithAllocationSite, 3, 1) \
|
||||
F(FinalizeInstanceSize, 1, 1) \
|
||||
F(Throw, 1, 1) \
|
||||
F(ReThrow, 1, 1) \
|
||||
|
@ -99,6 +99,41 @@ void Builtins::Generate_InOptimizationQueue(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
static void Generate_Runtime_NewObject(MacroAssembler* masm,
|
||||
bool create_memento,
|
||||
Register original_constructor,
|
||||
Label* count_incremented,
|
||||
Label* allocated) {
|
||||
int offset = 0;
|
||||
if (create_memento) {
|
||||
// Get the cell or allocation site.
|
||||
__ movp(rdi, Operand(rsp, kPointerSize * 2));
|
||||
__ Push(rdi);
|
||||
offset = kPointerSize;
|
||||
}
|
||||
|
||||
// Must restore rsi (context) and rdi (constructor) before calling runtime.
|
||||
__ movp(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
|
||||
__ movp(rdi, Operand(rsp, offset));
|
||||
__ Push(rdi);
|
||||
__ Push(original_constructor);
|
||||
if (create_memento) {
|
||||
__ CallRuntime(Runtime::kNewObjectWithAllocationSite, 3);
|
||||
} else {
|
||||
__ CallRuntime(Runtime::kNewObject, 2);
|
||||
}
|
||||
__ movp(rbx, rax); // store result in rbx
|
||||
|
||||
// Runtime_NewObjectWithAllocationSite increments allocation count.
|
||||
// Skip the increment.
|
||||
if (create_memento) {
|
||||
__ jmp(count_incremented);
|
||||
} else {
|
||||
__ jmp(allocated);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void Generate_JSConstructStubHelper(MacroAssembler* masm,
|
||||
bool is_api_function,
|
||||
bool create_memento) {
|
||||
@ -106,6 +141,7 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
|
||||
// -- rax: number of arguments
|
||||
// -- rdi: constructor function
|
||||
// -- rbx: allocation site or undefined
|
||||
// -- rdx: original constructor
|
||||
// -----------------------------------
|
||||
|
||||
// Should never create mementos for api functions.
|
||||
@ -127,9 +163,16 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
|
||||
// Push the function to invoke on the stack.
|
||||
__ Push(rdi);
|
||||
|
||||
Label rt_call, normal_new, allocated, count_incremented;
|
||||
__ cmpp(rdx, rdi);
|
||||
__ j(equal, &normal_new);
|
||||
|
||||
Generate_Runtime_NewObject(masm, create_memento, rdx, &count_incremented,
|
||||
&allocated);
|
||||
|
||||
__ bind(&normal_new);
|
||||
// Try to allocate the object without transitioning into C code. If any of
|
||||
// the preconditions is not met, the code bails out to the runtime call.
|
||||
Label rt_call, allocated;
|
||||
if (FLAG_inline_new) {
|
||||
Label undo_allocation;
|
||||
|
||||
@ -345,32 +388,8 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
|
||||
// Allocate the new receiver object using the runtime call.
|
||||
// rdi: function (constructor)
|
||||
__ bind(&rt_call);
|
||||
int offset = 0;
|
||||
if (create_memento) {
|
||||
// Get the cell or allocation site.
|
||||
__ movp(rdi, Operand(rsp, kPointerSize*2));
|
||||
__ Push(rdi);
|
||||
offset = kPointerSize;
|
||||
}
|
||||
|
||||
// Must restore rsi (context) and rdi (constructor) before calling runtime.
|
||||
__ movp(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
|
||||
__ movp(rdi, Operand(rsp, offset));
|
||||
__ Push(rdi);
|
||||
if (create_memento) {
|
||||
__ CallRuntime(Runtime::kNewObjectWithAllocationSite, 2);
|
||||
} else {
|
||||
__ CallRuntime(Runtime::kNewObject, 1);
|
||||
}
|
||||
__ movp(rbx, rax); // store result in rbx
|
||||
|
||||
// If we ended up using the runtime, and we want a memento, then the
|
||||
// runtime call made it for us, and we shouldn't do create count
|
||||
// increment.
|
||||
Label count_incremented;
|
||||
if (create_memento) {
|
||||
__ jmp(&count_incremented);
|
||||
}
|
||||
Generate_Runtime_NewObject(masm, create_memento, rdi, &count_incremented,
|
||||
&allocated);
|
||||
|
||||
// New object allocated.
|
||||
// rbx: newly allocated object
|
||||
|
@ -2017,6 +2017,9 @@ void CallConstructStub::Generate(MacroAssembler* masm) {
|
||||
__ AssertUndefinedOrAllocationSite(rbx);
|
||||
}
|
||||
|
||||
// Pass original constructor to construct stub.
|
||||
__ movp(rdx, rdi);
|
||||
|
||||
// Jump to the function-specific construct stub.
|
||||
Register jmp_reg = rcx;
|
||||
__ movp(jmp_reg, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
|
||||
|
@ -3063,11 +3063,15 @@ void FullCodeGenerator::VisitCall(Call* expr) {
|
||||
}
|
||||
}
|
||||
} else if (call_type == Call::SUPER_CALL) {
|
||||
SuperReference* super_ref = callee->AsSuperReference();
|
||||
EmitLoadSuperConstructor(super_ref);
|
||||
__ Push(result_register());
|
||||
VisitForStackValue(super_ref->this_var());
|
||||
EmitCall(expr, CallICState::METHOD);
|
||||
if (FLAG_experimental_classes) {
|
||||
EmitSuperConstructorCall(expr);
|
||||
} else {
|
||||
SuperReference* super_ref = callee->AsSuperReference();
|
||||
EmitLoadSuperConstructor(super_ref);
|
||||
__ Push(result_register());
|
||||
VisitForStackValue(super_ref->this_var());
|
||||
EmitCall(expr, CallICState::METHOD);
|
||||
}
|
||||
} else {
|
||||
DCHECK(call_type == Call::OTHER_CALL);
|
||||
// Call to an arbitrary expression not handled specially above.
|
||||
@ -3134,6 +3138,51 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
|
||||
SuperReference* super_ref = expr->expression()->AsSuperReference();
|
||||
EmitLoadSuperConstructor(super_ref);
|
||||
__ Push(result_register());
|
||||
|
||||
// Push the arguments ("left-to-right") on the stack.
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
int arg_count = args->length();
|
||||
for (int i = 0; i < arg_count; i++) {
|
||||
VisitForStackValue(args->at(i));
|
||||
}
|
||||
|
||||
// Call the construct call builtin that handles allocation and
|
||||
// constructor invocation.
|
||||
SetSourcePosition(expr->position());
|
||||
|
||||
// Load function and argument count into edi and eax.
|
||||
__ Set(rax, arg_count);
|
||||
__ movp(rdi, Operand(rsp, arg_count * kPointerSize));
|
||||
|
||||
// Record call targets in unoptimized code.
|
||||
if (FLAG_pretenuring_call_new) {
|
||||
UNREACHABLE();
|
||||
/* TODO(dslomov): support pretenuring.
|
||||
EnsureSlotContainsAllocationSite(expr->AllocationSiteFeedbackSlot());
|
||||
DCHECK(expr->AllocationSiteFeedbackSlot().ToInt() ==
|
||||
expr->CallNewFeedbackSlot().ToInt() + 1);
|
||||
*/
|
||||
}
|
||||
|
||||
__ Move(rbx, FeedbackVector());
|
||||
__ Move(rdx, SmiFromSlot(expr->CallFeedbackSlot()));
|
||||
|
||||
// TODO(dslomov): use a different stub and propagate new.target.
|
||||
CallConstructStub stub(isolate(), RECORD_CONSTRUCTOR_TARGET);
|
||||
__ call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
|
||||
|
||||
RecordJSReturnSite(expr);
|
||||
|
||||
// TODO(dslomov): implement TDZ for `this`.
|
||||
EmitVariableAssignment(super_ref->this_var()->var(), Token::ASSIGN);
|
||||
context()->Plug(rax);
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitIsSmi(CallRuntime* expr) {
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
DCHECK(args->length() == 1);
|
||||
|
35
test/mjsunit/harmony/classes-experimental.js
Normal file
35
test/mjsunit/harmony/classes-experimental.js
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright 2014 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: --experimental-classes --harmony-classes
|
||||
|
||||
'use strict';
|
||||
|
||||
class Base {
|
||||
constructor() {
|
||||
let o = new Object();
|
||||
o.prp = 1;
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
class Subclass extends Base {
|
||||
constructor() {
|
||||
try {
|
||||
this.prp1 = 3;
|
||||
} catch (e) {
|
||||
// TODO(dslomov): actually test the exception once TDZ is implemented.
|
||||
}
|
||||
super();
|
||||
assertSame(1, this.prp);
|
||||
assertSame(undefined, this.prp1);
|
||||
assertFalse(this.hasOwnProperty("prp1"));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
let s = new Subclass();
|
||||
assertSame(1, s.prp);
|
||||
assertSame(undefined, s.prp1);
|
||||
assertFalse(s.hasOwnProperty("prp1"));
|
Loading…
Reference in New Issue
Block a user