Commit 7a63bf77 authored by arv's avatar arv Committed by Commit bot

[es6] Make new.target work in functions

This makes new.target work in [[Call]] and [[Construct]] of ordinary
functions.

We achieve this by introducing a new construct stub for functions that
uses the new.target variable. The construct stub pushes the original
constructor just above the receiver in the construct frame.

BUG=v8:3887
LOG=N
R=adamk@chromium.org, dslomov@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#29358}
parent 473badf5
......@@ -343,6 +343,7 @@ static void Generate_Runtime_NewObject(MacroAssembler* masm,
static void Generate_JSConstructStubHelper(MacroAssembler* masm,
bool is_api_function,
bool use_new_target,
bool create_memento) {
// ----------- S t a t e -------------
// -- r0 : number of arguments
......@@ -367,10 +368,13 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ push(r2);
}
// Preserve the two incoming parameters on the stack.
// Preserve the incoming parameters on the stack.
__ SmiTag(r0);
__ push(r0); // Smi-tagged arguments count.
__ push(r1); // Constructor function.
__ push(r0);
__ push(r1);
if (use_new_target) {
__ push(r3);
}
Label rt_call, allocated, normal_new, count_incremented;
__ cmp(r1, r3);
......@@ -610,7 +614,8 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ bind(&allocated);
if (create_memento) {
__ ldr(r2, MemOperand(sp, kPointerSize * 2));
int offset = (use_new_target ? 3 : 2) * kPointerSize;
__ ldr(r2, MemOperand(sp, offset));
__ LoadRoot(r5, Heap::kUndefinedValueRootIndex);
__ cmp(r2, r5);
__ b(eq, &count_incremented);
......@@ -624,23 +629,27 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ bind(&count_incremented);
}
__ pop(r1); // Constructor function.
// Restore the parameters.
if (use_new_target) {
__ pop(r3);
}
__ pop(r1);
// Retrieve smi-tagged arguments count from the stack.
__ ldr(r0, MemOperand(sp));
__ SmiUntag(r0);
// Push new.target onto the construct frame. This is stored just below the
// receiver on the stack.
if (use_new_target) {
__ push(r3);
}
__ push(r4);
__ push(r4);
// Reload the number of arguments from the stack.
// sp[0]: receiver
// sp[1]: receiver
// sp[2]: number of arguments (smi-tagged)
__ ldr(r3, MemOperand(sp, 2 * kPointerSize));
// Set up pointer to last argument.
__ add(r2, fp, Operand(StandardFrameConstants::kCallerSPOffset));
// Set up number of arguments for function call below
__ SmiUntag(r0, r3);
// Copy arguments and receiver to the expression stack.
// r0: number of arguments
// r1: constructor function
......@@ -648,8 +657,10 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
// r3: number of arguments (smi-tagged)
// sp[0]: receiver
// sp[1]: receiver
// sp[2]: number of arguments (smi-tagged)
// sp[2]: new.target (if used)
// sp[2/3]: number of arguments (smi-tagged)
Label loop, entry;
__ SmiTag(r3, r0);
__ b(&entry);
__ bind(&loop);
__ ldr(ip, MemOperand(r2, r3, LSL, kPointerSizeLog2 - 1));
......@@ -672,14 +683,17 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
}
// Store offset of return address for deoptimizer.
if (!is_api_function) {
// TODO(arv): Remove the "!use_new_target" before supporting optimization
// of functions that reference new.target
if (!is_api_function && !use_new_target) {
masm->isolate()->heap()->SetConstructStubDeoptPCOffset(masm->pc_offset());
}
// Restore context from the frame.
// r0: result
// sp[0]: receiver
// sp[1]: number of arguments (smi-tagged)
// sp[1]: new.target (if used)
// sp[1/2]: number of arguments (smi-tagged)
__ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
// If the result is an object (in the ECMA sense), we should get rid
......@@ -690,7 +704,8 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
// If the result is a smi, it is *not* an object in the ECMA sense.
// r0: result
// sp[0]: receiver (newly allocated object)
// sp[1]: number of arguments (smi-tagged)
// sp[1]: new.target (if used)
// sp[1/2]: number of arguments (smi-tagged)
__ JumpIfSmi(r0, &use_receiver);
// If the type of the result (stored in its map) is less than
......@@ -708,8 +723,10 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ bind(&exit);
// r0: result
// sp[0]: receiver (newly allocated object)
// sp[1]: number of arguments (smi-tagged)
__ ldr(r1, MemOperand(sp, kPointerSize));
// sp[1]: new.target (if used)
// sp[1/2]: number of arguments (smi-tagged)
int offset = (use_new_target ? 2 : 1) * kPointerSize;
__ ldr(r1, MemOperand(sp, offset));
// Leave construct frame.
}
......@@ -722,12 +739,17 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, FLAG_pretenuring_call_new);
Generate_JSConstructStubHelper(masm, false, false, FLAG_pretenuring_call_new);
}
void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, true, false);
Generate_JSConstructStubHelper(masm, true, false, false);
}
void Builtins::Generate_JSConstructStubNewTarget(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, true, FLAG_pretenuring_call_new);
}
......
......@@ -331,6 +331,7 @@ static void Generate_Runtime_NewObject(MacroAssembler* masm,
static void Generate_JSConstructStubHelper(MacroAssembler* masm,
bool is_api_function,
bool use_new_target,
bool create_memento) {
// ----------- S t a t e -------------
// -- x0 : number of arguments
......@@ -360,11 +361,17 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
Register argc = x0;
Register constructor = x1;
Register original_constructor = x3;
// x1: constructor function
// Preserve the incoming parameters on the stack.
__ SmiTag(argc);
__ Push(argc, constructor);
// sp[0] : Constructor function.
// sp[1]: number of arguments (smi-tagged)
if (use_new_target) {
__ Push(argc, constructor, original_constructor);
} else {
__ Push(argc, constructor);
}
// sp[0]: new.target (if used)
// sp[0/1]: Constructor function.
// sp[1/2]: number of arguments (smi-tagged)
Label rt_call, count_incremented, allocated, normal_new;
__ Cmp(constructor, original_constructor);
......@@ -580,7 +587,8 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ Bind(&allocated);
if (create_memento) {
__ Peek(x10, 2 * kXRegSize);
int offset = (use_new_target ? 3 : 2) * kXRegSize;
__ Peek(x10, offset);
__ JumpIfRoot(x10, Heap::kUndefinedValueRootIndex, &count_incremented);
// r2 is an AllocationSite. We are creating a memento from it, so we
// need to increment the memento create count.
......@@ -592,18 +600,24 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ bind(&count_incremented);
}
// Restore the parameters.
if (use_new_target) {
__ Pop(original_constructor);
}
__ Pop(constructor);
__ Push(x4, x4);
// Reload the number of arguments from the stack.
// Set it up in x0 for the function call below.
// jssp[0]: receiver
// jssp[1]: receiver
// jssp[2]: number of arguments (smi-tagged)
__ Peek(argc, 2 * kXRegSize); // Load number of arguments.
// jssp[0]: number of arguments (smi-tagged)
__ Peek(argc, 0); // Load number of arguments.
__ SmiUntag(argc);
if (use_new_target) {
__ Push(original_constructor, x4, x4);
} else {
__ Push(x4, x4);
}
// Set up pointer to last argument.
__ Add(x2, fp, StandardFrameConstants::kCallerSPOffset);
......@@ -614,7 +628,8 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
// x2: address of last argument (caller sp)
// jssp[0]: receiver
// jssp[1]: receiver
// jssp[2]: number of arguments (smi-tagged)
// jssp[2]: new.target (if used)
// jssp[2/3]: number of arguments (smi-tagged)
// Compute the start address of the copy in x3.
__ Add(x3, x2, Operand(argc, LSL, kPointerSizeLog2));
Label loop, entry, done_copying_arguments;
......@@ -645,14 +660,17 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
}
// Store offset of return address for deoptimizer.
if (!is_api_function) {
// TODO(arv): Remove the "!use_new_target" before supporting optimization
// of functions that reference new.target
if (!is_api_function && !use_new_target) {
masm->isolate()->heap()->SetConstructStubDeoptPCOffset(masm->pc_offset());
}
// Restore the context from the frame.
// x0: result
// jssp[0]: receiver
// jssp[1]: number of arguments (smi-tagged)
// jssp[1]: new.target (if used)
// jssp[1/2]: number of arguments (smi-tagged)
__ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
// If the result is an object (in the ECMA sense), we should get rid
......@@ -680,8 +698,10 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ Bind(&exit);
// x0: result
// jssp[0]: receiver (newly allocated object)
// jssp[1]: number of arguments (smi-tagged)
__ Peek(x1, kXRegSize);
// jssp[1]: new.target (if used)
// jssp[1/2]: number of arguments (smi-tagged)
int offset = (use_new_target ? 2 : 1) * kXRegSize;
__ Peek(x1, offset);
// Leave construct frame.
}
......@@ -694,12 +714,17 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, FLAG_pretenuring_call_new);
Generate_JSConstructStubHelper(masm, false, false, FLAG_pretenuring_call_new);
}
void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, true, false);
Generate_JSConstructStubHelper(masm, true, false, false);
}
void Builtins::Generate_JSConstructStubNewTarget(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, true, FLAG_pretenuring_call_new);
}
......
......@@ -273,7 +273,7 @@ void FullCodeGenerator::Generate() {
__ LoadRoot(x0, Heap::kUndefinedValueRootIndex);
__ Bind(&done);
// new.target is parameter -2.
SetVar(new_target_var, x0, x2, x3);
}
......
......@@ -70,6 +70,7 @@ enum BuiltinExtraArguments {
V(JSConstructStubGeneric, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(JSConstructStubForDerived, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(JSConstructStubApi, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(JSConstructStubNewTarget, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(JSEntryTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(JSConstructEntryTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(CompileLazy, BUILTIN, UNINITIALIZED, kNoExtraICState) \
......@@ -322,6 +323,7 @@ class Builtins {
static void Generate_JSConstructStubGeneric(MacroAssembler* masm);
static void Generate_JSConstructStubForDerived(MacroAssembler* masm);
static void Generate_JSConstructStubApi(MacroAssembler* masm);
static void Generate_JSConstructStubNewTarget(MacroAssembler* masm);
static void Generate_JSEntryTrampoline(MacroAssembler* masm);
static void Generate_JSConstructEntryTrampoline(MacroAssembler* masm);
static void Generate_NotifyDeoptimized(MacroAssembler* masm);
......
......@@ -1440,6 +1440,11 @@ Handle<SharedFunctionInfo> Compiler::GetSharedFunctionInfo(
// first time. It may have already been compiled previously.
result->set_never_compiled(outer_info->is_first_compile() && lazy);
if (literal->scope()->new_target_var() != nullptr) {
Handle<Code> stub(isolate->builtins()->JSConstructStubNewTarget());
result->set_construct_stub(*stub);
}
RecordFunctionCompilation(Logger::FUNCTION_TAG, &info, result);
result->set_allows_lazy_compilation(literal->AllowsLazyCompilation());
result->set_allows_lazy_compilation_without_context(allow_lazy_without_ctx);
......
......@@ -138,6 +138,7 @@ static void Generate_Runtime_NewObject(MacroAssembler* masm,
static void Generate_JSConstructStubHelper(MacroAssembler* masm,
bool is_api_function,
bool use_new_target,
bool create_memento) {
// ----------- S t a t e -------------
// -- eax: number of arguments
......@@ -158,12 +159,13 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ push(ebx);
}
// Store a smi-tagged arguments count on the stack.
// Preserve the incoming parameters on the stack.
__ SmiTag(eax);
__ push(eax);
// Push the function to invoke on the stack.
__ push(edi);
if (use_new_target) {
__ push(edx);
}
__ cmp(edx, edi);
Label normal_new;
......@@ -391,7 +393,8 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ bind(&allocated);
if (create_memento) {
__ mov(ecx, Operand(esp, kPointerSize * 2));
int offset = (use_new_target ? 3 : 2) * kPointerSize;
__ mov(ecx, Operand(esp, offset));
__ cmp(ecx, masm->isolate()->factory()->undefined_value());
__ j(equal, &count_incremented);
// ecx is an AllocationSite. We are creating a memento from it, so we
......@@ -401,13 +404,22 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ bind(&count_incremented);
}
// Retrieve the function from the stack.
__ pop(edi);
// Restore the parameters.
if (use_new_target) {
__ pop(edx); // new.target
}
__ pop(edi); // Constructor function.
// Retrieve smi-tagged arguments count from the stack.
__ mov(eax, Operand(esp, 0));
__ SmiUntag(eax);
// Push new.target onto the construct frame. This is stored just below the
// receiver on the stack.
if (use_new_target) {
__ push(edx);
}
// Push the allocated receiver to the stack. We need two copies
// because we may have to return the original one and the calling
// conventions dictate that the called function pops the receiver.
......@@ -440,7 +452,9 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
}
// Store offset of return address for deoptimizer.
if (!is_api_function) {
// TODO(arv): Remove the "!use_new_target" before supporting optimization
// of functions that reference new.target
if (!is_api_function && !use_new_target) {
masm->isolate()->heap()->SetConstructStubDeoptPCOffset(masm->pc_offset());
}
......@@ -465,9 +479,11 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ bind(&use_receiver);
__ mov(eax, Operand(esp, 0));
// Restore the arguments count and leave the construct frame.
// Restore the arguments count and leave the construct frame. The arguments
// count is stored below the reciever and the new.target.
__ bind(&exit);
__ mov(ebx, Operand(esp, kPointerSize)); // Get arguments count.
int offset = (use_new_target ? 2 : 1) * kPointerSize;
__ mov(ebx, Operand(esp, offset));
// Leave construct frame.
}
......@@ -483,12 +499,17 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, FLAG_pretenuring_call_new);
Generate_JSConstructStubHelper(masm, false, false, FLAG_pretenuring_call_new);
}
void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, true, false);
Generate_JSConstructStubHelper(masm, true, false, false);
}
void Builtins::Generate_JSConstructStubNewTarget(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, true, FLAG_pretenuring_call_new);
}
......
......@@ -348,6 +348,7 @@ static void Generate_Runtime_NewObject(MacroAssembler* masm,
static void Generate_JSConstructStubHelper(MacroAssembler* masm,
bool is_api_function,
bool use_new_target,
bool create_memento) {
// ----------- S t a t e -------------
// -- a0 : number of arguments
......@@ -379,9 +380,13 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ push(a2);
}
// Preserve the two incoming parameters on the stack.
__ sll(a0, a0, kSmiTagSize); // Tag arguments count.
__ MultiPushReversed(a0.bit() | a1.bit());
// Preserve the incoming parameters on the stack.
__ SmiTag(a0);
if (use_new_target) {
__ Push(a0, a1, a3);
} else {
__ Push(a0, a1);
}
Label rt_call, allocated, normal_new, count_incremented;
__ Branch(&normal_new, eq, a1, Operand(a3));
......@@ -628,7 +633,8 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ bind(&allocated);
if (create_memento) {
__ lw(a2, MemOperand(sp, kPointerSize * 2));
int offset = (use_new_target ? 3 : 2) * kPointerSize;
__ lw(a2, MemOperand(sp, offset));
__ LoadRoot(t5, Heap::kUndefinedValueRootIndex);
__ Branch(&count_incremented, eq, a2, Operand(t5));
// a2 is an AllocationSite. We are creating a memento from it, so we
......@@ -641,22 +647,25 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ bind(&count_incremented);
}
// Restore the parameters.
if (use_new_target) {
__ Pop(a3); // new.target
}
__ Pop(a1);
__ Push(t4, t4);
// Retrieve smi-tagged arguments count from the stack.
__ lw(a0, MemOperand(sp));
__ SmiUntag(a0);
// Reload the number of arguments from the stack.
// sp[0]: receiver
// sp[1]: receiver
// sp[2]: number of arguments (smi-tagged)
__ lw(a3, MemOperand(sp, 2 * kPointerSize));
if (use_new_target) {
__ Push(a3, t4, t4);
} else {
__ Push(t4, t4);
}
// Set up pointer to last argument.
__ Addu(a2, fp, Operand(StandardFrameConstants::kCallerSPOffset));
// Set up number of arguments for function call below.
__ srl(a0, a3, kSmiTagSize);
// Copy arguments and receiver to the expression stack.
// a0: number of arguments
// a1: constructor function
......@@ -664,8 +673,10 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
// a3: number of arguments (smi-tagged)
// sp[0]: receiver
// sp[1]: receiver
// sp[2]: number of arguments (smi-tagged)
// sp[2]: new.target (if used)
// sp[2/3]: number of arguments (smi-tagged)
Label loop, entry;
__ SmiTag(a3, a0);
__ jmp(&entry);
__ bind(&loop);
__ sll(t0, a3, kPointerSizeLog2 - kSmiTagSize);
......@@ -690,7 +701,9 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
}
// Store offset of return address for deoptimizer.
if (!is_api_function) {
// TODO(arv): Remove the "!use_new_target" before supporting optimization
// of functions that reference new.target
if (!is_api_function && !use_new_target) {
masm->isolate()->heap()->SetConstructStubDeoptPCOffset(masm->pc_offset());
}
......@@ -705,7 +718,8 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
// If the result is a smi, it is *not* an object in the ECMA sense.
// v0: result
// sp[0]: receiver (newly allocated object)
// sp[1]: number of arguments (smi-tagged)
// sp[1]: new.target (if used)
// sp[1/2]: number of arguments (smi-tagged)
__ JumpIfSmi(v0, &use_receiver);
// If the type of the result (stored in its map) is less than
......@@ -723,8 +737,10 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ bind(&exit);
// v0: result
// sp[0]: receiver (newly allocated object)
// sp[1]: number of arguments (smi-tagged)
__ lw(a1, MemOperand(sp, kPointerSize));
// sp[1]: new.target (if used)
// sp[1/2]: number of arguments (smi-tagged)
int offset = (use_new_target ? 2 : 1) * kPointerSize;
__ lw(a1, MemOperand(sp, offset));
// Leave construct frame.
}
......@@ -738,12 +754,17 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, FLAG_pretenuring_call_new);
Generate_JSConstructStubHelper(masm, false, false, FLAG_pretenuring_call_new);
}
void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, true, false);
Generate_JSConstructStubHelper(masm, true, false, false);
}
void Builtins::Generate_JSConstructStubNewTarget(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, true, FLAG_pretenuring_call_new);
}
......
......@@ -347,6 +347,7 @@ static void Generate_Runtime_NewObject(MacroAssembler* masm,
static void Generate_JSConstructStubHelper(MacroAssembler* masm,
bool is_api_function,
bool use_new_target,
bool create_memento) {
// ----------- S t a t e -------------
// -- a0 : number of arguments
......@@ -378,10 +379,13 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ push(a2);
}
// Preserve the two incoming parameters on the stack.
// Tag arguments count.
__ dsll32(a0, a0, 0);
__ MultiPushReversed(a0.bit() | a1.bit());
// Preserve the incoming parameters on the stack.
__ SmiTag(a0);
if (use_new_target) {
__ Push(a0, a1, a3);
} else {
__ Push(a0, a1);
}
Label rt_call, allocated, normal_new, count_incremented;
__ Branch(&normal_new, eq, a1, Operand(a3));
......@@ -636,7 +640,8 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ bind(&allocated);
if (create_memento) {
__ ld(a2, MemOperand(sp, kPointerSize * 2));
int offset = (use_new_target ? 3 : 2) * kPointerSize;
__ ld(a2, MemOperand(sp, offset));
__ LoadRoot(t1, Heap::kUndefinedValueRootIndex);
__ Branch(&count_incremented, eq, a2, Operand(t1));
// a2 is an AllocationSite. We are creating a memento from it, so we
......@@ -649,22 +654,24 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ bind(&count_incremented);
}
// Restore the parameters.
if (use_new_target) {
__ Pop(a3); // new.target
}
__ Pop(a1);
__ Push(t0, t0);
__ ld(a0, MemOperand(sp));
__ SmiUntag(a0);
// Reload the number of arguments from the stack.
// sp[0]: receiver
// sp[1]: receiver
// sp[2]: number of arguments (smi-tagged)
__ ld(a3, MemOperand(sp, 2 * kPointerSize));
if (use_new_target) {
__ Push(a3, t0, t0);
} else {
__ Push(t0, t0);
}
// Set up pointer to last argument.
__ Daddu(a2, fp, Operand(StandardFrameConstants::kCallerSPOffset));
// Set up number of arguments for function call below.
__ SmiUntag(a0, a3);
// Copy arguments and receiver to the expression stack.
// a0: number of arguments
// a1: constructor function
......@@ -672,9 +679,10 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
// a3: number of arguments (smi-tagged)
// sp[0]: receiver
// sp[1]: receiver
// sp[2]: number of arguments (smi-tagged)
// sp[2]: new.target (if used)
// sp[2/3]: number of arguments (smi-tagged)
Label loop, entry;
__ SmiUntag(a3);
__ mov(a3, a0);
__ jmp(&entry);
__ bind(&loop);
__ dsll(a4, a3, kPointerSizeLog2);
......@@ -699,7 +707,9 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
}
// Store offset of return address for deoptimizer.
if (!is_api_function) {
// TODO(arv): Remove the "!use_new_target" before supporting optimization
// of functions that reference new.target
if (!is_api_function && !use_new_target) {
masm->isolate()->heap()->SetConstructStubDeoptPCOffset(masm->pc_offset());
}
......@@ -714,7 +724,8 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
// If the result is a smi, it is *not* an object in the ECMA sense.
// v0: result
// sp[0]: receiver (newly allocated object)
// sp[1]: number of arguments (smi-tagged)
// sp[1]: new.target (if used)
// sp[1/2]: number of arguments (smi-tagged)
__ JumpIfSmi(v0, &use_receiver);
// If the type of the result (stored in its map) is less than
......@@ -732,8 +743,10 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ bind(&exit);
// v0: result
// sp[0]: receiver (newly allocated object)
// sp[1]: number of arguments (smi-tagged)
__ ld(a1, MemOperand(sp, kPointerSize));
// sp[1]: new.target (if used)
// sp[1/2]: number of arguments (smi-tagged)
int offset = (use_new_target ? 2 : 1) * kPointerSize;
__ ld(a1, MemOperand(sp, offset));
// Leave construct frame.
}
......@@ -747,12 +760,17 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, FLAG_pretenuring_call_new);
Generate_JSConstructStubHelper(masm, false, false, FLAG_pretenuring_call_new);
}
void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, true, false);
Generate_JSConstructStubHelper(masm, true, false, false);
}
void Builtins::Generate_JSConstructStubNewTarget(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, true, FLAG_pretenuring_call_new);
}
......
......@@ -319,24 +319,20 @@ void Scope::Initialize() {
receiver_ = var;
}
if (is_function_scope()) {
if (!is_arrow_scope()) {
// Declare 'arguments' variable which exists in all non arrow functions.
// Note that it might never be accessed, in which case it won't be
// allocated during variable allocation.
variables_.Declare(this, ast_value_factory_->arguments_string(), VAR,
Variable::ARGUMENTS, kCreatedInitialized);
}
if (subclass_constructor) {
DCHECK(!is_arrow_scope());
if (is_function_scope() && !is_arrow_scope()) {
// Declare 'arguments' variable which exists in all non arrow functions.
// Note that it might never be accessed, in which case it won't be
// allocated during variable allocation.
variables_.Declare(this, ast_value_factory_->arguments_string(), VAR,
Variable::ARGUMENTS, kCreatedInitialized);
if (subclass_constructor || FLAG_harmony_new_target) {
variables_.Declare(this, ast_value_factory_->new_target_string(), CONST,
Variable::NORMAL, kCreatedInitialized);
}
if (IsConciseMethod(function_kind_) || IsConstructor(function_kind_) ||
IsAccessorFunction(function_kind_)) {
DCHECK(!is_arrow_scope());
variables_.Declare(this, ast_value_factory_->this_function_string(),
CONST, Variable::NORMAL, kCreatedInitialized);
}
......
......@@ -136,6 +136,7 @@ static void Generate_Runtime_NewObject(MacroAssembler* masm,
static void Generate_JSConstructStubHelper(MacroAssembler* masm,
bool is_api_function,
bool use_new_target,
bool create_memento) {
// ----------- S t a t e -------------
// -- rax: number of arguments
......@@ -156,12 +157,13 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ Push(rbx);
}
// Store a smi-tagged arguments count on the stack.
// Preserve the incoming parameters on the stack.
__ Integer32ToSmi(rax, rax);
__ Push(rax);
// Push the function to invoke on the stack.
__ Push(rdi);
if (use_new_target) {
__ Push(rdx);
}
Label rt_call, normal_new, allocated, count_incremented;
__ cmpp(rdx, rdi);
......@@ -388,7 +390,8 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ bind(&allocated);
if (create_memento) {
__ movp(rcx, Operand(rsp, kPointerSize*2));
int offset = (use_new_target ? 3 : 2) * kPointerSize;
__ movp(rcx, Operand(rsp, offset));
__ Cmp(rcx, masm->isolate()->factory()->undefined_value());
__ j(equal, &count_incremented);
// rcx is an AllocationSite. We are creating a memento from it, so we
......@@ -399,13 +402,22 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ bind(&count_incremented);
}
// Retrieve the function from the stack.
// Restore the parameters.
if (use_new_target) {
__ Pop(rdx);
}
__ Pop(rdi);
// Retrieve smi-tagged arguments count from the stack.
__ movp(rax, Operand(rsp, 0));
__ SmiToInteger32(rax, rax);
// Push new.target onto the construct frame. This is stored just below the
// receiver on the stack.
if (use_new_target) {
__ Push(rdx);
}
// Push the allocated receiver to the stack. We need two copies
// because we may have to return the original one and the calling
// conventions dictate that the called function pops the receiver.
......@@ -437,7 +449,9 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
}
// Store offset of return address for deoptimizer.
if (!is_api_function) {
// TODO(arv): Remove the "!use_new_target" before supporting optimization
// of functions that reference new.target
if (!is_api_function && !use_new_target) {
masm->isolate()->heap()->SetConstructStubDeoptPCOffset(masm->pc_offset());
}
......@@ -462,9 +476,11 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ bind(&use_receiver);
__ movp(rax, Operand(rsp, 0));
// Restore the arguments count and leave the construct frame.
// Restore the arguments count and leave the construct frame. The arguments
// count is stored below the reciever and the new.target.
__ bind(&exit);
__ movp(rbx, Operand(rsp, kPointerSize)); // Get arguments count.
int offset = (use_new_target ? 2 : 1) * kPointerSize;
__ movp(rbx, Operand(rsp, offset));
// Leave construct frame.
}
......@@ -481,12 +497,17 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, FLAG_pretenuring_call_new);
Generate_JSConstructStubHelper(masm, false, false, FLAG_pretenuring_call_new);
}
void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, true, false);
Generate_JSConstructStubHelper(masm, true, false, false);
}
void Builtins::Generate_JSConstructStubNewTarget(MacroAssembler* masm) {
Generate_JSConstructStubHelper(masm, false, true, FLAG_pretenuring_call_new);
}
......
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-classes --harmony-new-target --harmony-reflect
// Flags: --harmony-rest-parameters --harmony-arrow-functions
(function TestClass() {
'use strict';
var calls = 0;
class Base {
constructor(_) {
assertEquals(Base, new.target);
calls++;
}
}
assertInstanceof(new Base(1), Base);
assertInstanceof(new Base(1, 2), Base);
assertInstanceof(new Base(), Base);
assertEquals(3, calls);
})();
(function TestDerivedClass() {
'use strict';
var calls = 0;
class Base {
constructor(expected) {
assertEquals(expected, new.target);
}
}
class Derived extends Base {
constructor(expected) {
super(expected);
assertEquals(expected, new.target);
calls++;
}
}
new Derived(Derived, 'extra');
new Derived(Derived);
assertEquals(2, calls);
class Derived2 extends Derived {}
calls = 0;
new Derived2(Derived2);
new Derived2(Derived2, 'extra');
assertEquals(2, calls);
})();
(function TestFunctionCall() {
var calls;
function f(expected) {
calls++;
assertEquals(expected, new.target);
}
calls = 0;
f(undefined);
f(undefined, 'extra');
f();
assertEquals(3, calls);
calls = 0;
f.call({}, undefined);
f.call({}, undefined, 'extra');
f.call({});
assertEquals(3, calls);
calls = 0;
f.apply({}, [undefined]);
f.apply({}, [undefined, 'extra']);
f.apply({}, []);
assertEquals(3, calls);
})();
(function TestFunctionConstruct() {
var calls;
function f(expected) {
calls++;
assertEquals(expected, new.target);
}
calls = 0;
new f(f);
new f(f, 'extra');
assertEquals(2, calls);
})();
(function TestClassExtendsFunction() {
'use strict';
var calls = 0;
function f(expected) {
assertEquals(expected, new.target);
}
class Derived extends f {
constructor(expected) {
super(expected);
assertEquals(expected, new.target);
calls++;
}
}
new Derived(Derived);
new Derived(Derived, 'extra');
assertEquals(2, calls);
})();
(function TestFunctionReturnObject() {
function f(expected) {
assertEquals(expected, new.target);
return /abc/;
}
assertInstanceof(new f(f), RegExp);
assertInstanceof(new f(f, 'extra'), RegExp);
assertInstanceof(f(undefined), RegExp);
assertInstanceof(f(), RegExp);
assertInstanceof(f(undefined, 'extra'), RegExp);
})();
(function TestClassReturnObject() {
'use strict';
class Base {
constructor(expected) {
assertEquals(expected, new.target);
return /abc/;
}
}
assertInstanceof(new Base(Base), RegExp);
assertInstanceof(new Base(Base, 'extra'), RegExp);
class Derived extends Base {}
assertInstanceof(new Derived(Derived), RegExp);
assertInstanceof(new Derived(Derived, 'extra'), RegExp);
class Derived2 extends Base {
constructor(expected) {
super(expected);
assertInstanceof(this, RegExp);
}
}
assertInstanceof(new Derived2(Derived2), RegExp);
assertInstanceof(new Derived2(Derived2, 'extra'), RegExp);
})();
/*
// TODO(arv): Reflect.construct does not work correctly with a third argument.
(function TestReflectConstruct() {
var calls = 0;
function f(expected) {
calls++;
assertEquals(expected, new.target);
}
var o = Reflect.construct(f, [f]);
assertEquals(Object.getPrototypeOf(o), f.prototype);
o = Reflect.construct(f, [f, 'extra']);
assertEquals(Object.getPrototypeOf(o), f.prototype);
assertEquals(2, calls);
calls = 0;
o = Reflect.construct(f, [f], f);
assertEquals(Object.getPrototypeOf(o), f.prototype);
o = Reflect.construct(f, [f, 'extra'], f);
assertEquals(Object.getPrototypeOf(o), f.prototype);
assertEquals(2, calls);
function g() {}
calls = 0;
o = Reflect.construct(f, [g], g);
assertEquals(Object.getPrototypeOf(o), g.prototype);
o = Reflect.construct(f, [g, 'extra'], g);
assertEquals(Object.getPrototypeOf(o), g.prototype);
assertEquals(2, calls);
})();
*/
(function TestRestParametersFunction() {
function f(...rest) {
assertEquals(rest[0], new.target);
}
assertInstanceof(new f(f), f);
assertInstanceof(new f(f, 'extra'), f);
})();
(function TestRestParametersClass() {
'use strict';
class Base {
constructor(...rest) {
assertEquals(rest[0], new.target);
}
}
assertInstanceof(new Base(Base), Base);
assertInstanceof(new Base(Base, 'extra'), Base);
class Derived extends Base {}
assertInstanceof(new Derived(Derived), Derived);
assertInstanceof(new Derived(Derived, 'extra'), Derived);
})();
(function TestArrowFunction() {
function f(expected) {
(() => {
assertEquals(expected, new.target);
})();
}
assertInstanceof(new f(f), f);
assertInstanceof(new f(f, 'extra'), f);
})();
(function TestRestParametersClass() {
'use strict';
class Base {
constructor(expected) {
(() => {
assertEquals(expected, new.target);
})();
}
}
assertInstanceof(new Base(Base), Base);
assertInstanceof(new Base(Base, 'extra'), Base);
class Derived extends Base {}
assertInstanceof(new Derived(Derived), Derived);
assertInstanceof(new Derived(Derived, 'extra'), Derived);
})();
(function TestSloppyArguments() {
var length, a0, a1, a2, nt;
function f(x) {
assertEquals(length, arguments.length);
assertEquals(a0, arguments[0]);
assertEquals(a1, arguments[1]);
assertEquals(a2, arguments[2]);
assertEquals(nt, new.target);
if (length > 0) {
x = 42;
assertEquals(42, x);
assertEquals(42, arguments[0]);
arguments[0] = 33;
assertEquals(33, x);
assertEquals(33, arguments[0]);
}
}
nt = f;
length = 0;
new f();
length = 1;
a0 = 1;
new f(1);
length = 2;
a0 = 1;
a1 = 2;
new f(1, 2);
length = 3;
a0 = 1;
a1 = 2;
a2 = 3;
new f(1, 2, 3);
nt = undefined;
a0 = a1 = a2 = undefined;
length = 0;
f();
length = 1;
a0 = 1;
f(1);
length = 2;
a0 = 1;
a1 = 2;
f(1, 2);
length = 3;
a0 = 1;
a1 = 2;
a2 = 3;
f(1, 2, 3);
})();
(function TestStrictArguments() {
var length, a0, a1, a2, nt;
function f(x) {
'use strict';
assertEquals(length, arguments.length);
assertEquals(a0, arguments[0]);
assertEquals(a1, arguments[1]);
assertEquals(a2, arguments[2]);
assertEquals(nt, new.target);
if (length > 0) {
x = 42;
assertEquals(a0, arguments[0]);
arguments[0] = 33;
assertEquals(33, arguments[0]);
}
}
nt = f;
length = 0;
new f();
length = 1;
a0 = 1;
new f(1);
length = 2;
a0 = 1;
a1 = 2;
new f(1, 2);
length = 3;
a0 = 1;
a1 = 2;
a2 = 3;
new f(1, 2, 3);
nt = undefined;
a0 = a1 = a2 = undefined;
length = 0;
f();
length = 1;
a0 = 1;
f(1);
length = 2;
a0 = 1;
a1 = 2;
f(1, 2);
length = 3;
a0 = 1;
a1 = 2;
a2 = 3;
f(1, 2, 3);
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment