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:
dslomov 2015-01-22 10:39:34 -08:00 committed by Commit bot
parent 6f9d60cc6f
commit 22ce08ade6
18 changed files with 461 additions and 126 deletions

View File

@ -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

View File

@ -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));

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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,

View File

@ -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);

View File

@ -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);

View File

@ -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));

View File

@ -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);

View File

@ -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);
}

View File

@ -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) \

View File

@ -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

View File

@ -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));

View File

@ -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);

View 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"));