// r0: number of arguments
// r1: called object
__ bind(&non_function_call);
-
+ // CALL_NON_FUNCTION expects the non-function constructor as receiver
+ // (instead of the original receiver from the call site). The receiver is
+ // stack element argc.
+ __ str(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2));
// Set expected number of arguments to zero (not changing r0).
__ mov(r2, Operand(0));
__ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
// 1. Make sure we have at least one argument.
- // r0: actual number of argument
+ // r0: actual number of arguments
{ Label done;
__ tst(r0, Operand(r0));
__ b(ne, &done);
__ bind(&done);
}
- // 2. Get the function to call from the stack.
+ // 2. Get the function to call (passed as receiver) from the stack, check
+ // if it is a function.
// r0: actual number of arguments
- { Label done, non_function, function;
- __ ldr(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2));
- __ tst(r1, Operand(kSmiTagMask));
- __ b(eq, &non_function);
- __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
- __ b(eq, &function);
-
- // Non-function called: Clear the function to force exception.
- __ bind(&non_function);
- __ mov(r1, Operand(0));
- __ b(&done);
-
- // Change the context eagerly because it will be used below to get the
- // right global object.
- __ bind(&function);
- __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
-
- __ bind(&done);
- }
+ Label non_function;
+ __ ldr(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2));
+ __ tst(r1, Operand(kSmiTagMask));
+ __ b(eq, &non_function);
+ __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
+ __ b(ne, &non_function);
- // 3. Make sure first argument is an object; convert if necessary.
+ // 3a. Patch the first argument if necessary when calling a function.
// r0: actual number of arguments
// r1: function
- { Label call_to_object, use_global_receiver, patch_receiver, done;
+ Label shift_arguments;
+ { Label convert_to_object, use_global_receiver, patch_receiver;
+ // Change context eagerly in case we need the global receiver.
+ __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
+
__ add(r2, sp, Operand(r0, LSL, kPointerSizeLog2));
__ ldr(r2, MemOperand(r2, -kPointerSize));
-
// r0: actual number of arguments
// r1: function
// r2: first argument
__ tst(r2, Operand(kSmiTagMask));
- __ b(eq, &call_to_object);
+ __ b(eq, &convert_to_object);
__ LoadRoot(r3, Heap::kNullValueRootIndex);
__ cmp(r2, r3);
__ b(eq, &use_global_receiver);
__ CompareObjectType(r2, r3, r3, FIRST_JS_OBJECT_TYPE);
- __ b(lt, &call_to_object);
+ __ b(lt, &convert_to_object);
__ cmp(r3, Operand(LAST_JS_OBJECT_TYPE));
- __ b(le, &done);
-
- __ bind(&call_to_object);
- __ EnterInternalFrame();
+ __ b(le, &shift_arguments);
- // Store number of arguments and function across the call into the runtime.
- __ mov(r0, Operand(r0, LSL, kSmiTagSize));
+ __ bind(&convert_to_object);
+ __ EnterInternalFrame(); // In order to preserve argument count.
+ __ mov(r0, Operand(r0, LSL, kSmiTagSize)); // Smi-tagged.
__ push(r0);
- __ push(r1);
__ push(r2);
__ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS);
__ mov(r2, r0);
- // Restore number of arguments and function.
- __ pop(r1);
__ pop(r0);
__ mov(r0, Operand(r0, ASR, kSmiTagSize));
-
__ LeaveInternalFrame();
- __ b(&patch_receiver);
+ // Restore the function to r1.
+ __ ldr(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2));
+ __ jmp(&patch_receiver);
- // Use the global receiver object from the called function as the receiver.
+ // Use the global receiver object from the called function as the
+ // receiver.
__ bind(&use_global_receiver);
const int kGlobalIndex =
Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
__ add(r3, sp, Operand(r0, LSL, kPointerSizeLog2));
__ str(r2, MemOperand(r3, -kPointerSize));
- __ bind(&done);
+ __ jmp(&shift_arguments);
}
- // 4. Handle non-functions.
- // r0: actual number of arguments (including call() receiver)
+ // 3b. Patch the first argument when calling a non-function. The
+ // CALL_NON_FUNCTION builtin expects the non-function callee as
+ // receiver, so overwrite the first argument which will ultimately
+ // become the receiver.
+ // r0: actual number of arguments
// r1: function
- { Label done;
- __ tst(r1, r1);
- __ b(ne, &done);
- __ mov(r2, Operand(0)); // expected arguments is 0 for CALL_NON_FUNCTION
- // Transfer the receiver from the first argument to the top of the
- // caller's expression stack simply by decrementing argc.
- __ sub(r0, r0, Operand(1));
- __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION);
- __ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
- RelocInfo::CODE_TARGET);
- __ bind(&done);
- }
-
- // 5. Shift arguments one slot toward the bottom of the
- // stack, overwriting the receiver.
+ __ bind(&non_function);
+ __ add(r2, sp, Operand(r0, LSL, kPointerSizeLog2));
+ __ str(r1, MemOperand(r2, -kPointerSize));
+ // Clear r1 to indicate a non-function being called.
+ __ mov(r1, Operand(0));
+
+ // 4. Shift arguments and return address one slot down on the stack
+ // (overwriting the original receiver). Adjust argument count to make
+ // the original first argument the new receiver.
+ // r0: actual number of arguments
+ // r1: function
+ __ bind(&shift_arguments);
{ Label loop;
// Calculate the copy start address (destination). Copy end address is sp.
__ add(r2, sp, Operand(r0, LSL, kPointerSizeLog2));
__ sub(r2, r2, Operand(kPointerSize));
__ cmp(r2, sp);
__ b(ne, &loop);
- // Adjust the actual number of arguments and remove the top element.
+ // Adjust the actual number of arguments and remove the top element
+ // (which is a copy of the last argument).
__ sub(r0, r0, Operand(1));
__ pop();
}
- // 6. Get the code for the function or the non-function builtin.
- // If number of expected arguments matches, then call. Otherwise restart
- // the arguments adaptor stub.
+ // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin.
+ // r0: actual number of arguments
+ // r1: function
+ { Label function;
+ __ tst(r1, r1);
+ __ b(ne, &function);
+ __ mov(r2, Operand(0)); // expected arguments is 0 for CALL_NON_FUNCTION
+ __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION);
+ __ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
+ RelocInfo::CODE_TARGET);
+ __ bind(&function);
+ }
+
+ // 5b. Get the code to call from the function and check that the number of
+ // expected arguments matches what we're providing. If so, jump
+ // (tail-call) to the code in register edx without checking arguments.
// r0: actual number of arguments
// r1: function
__ ldr(r3, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
__ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
RelocInfo::CODE_TARGET, ne);
- // 7. Jump (tail-call) to the code in r3 without checking arguments.
ParameterCount expected(0);
__ InvokeCode(r3, expected, expected, JUMP_FUNCTION);
}
// Slow-case: Non-function called.
__ bind(&slow);
+ // CALL_NON_FUNCTION expects the non-function callee as receiver (instead
+ // of the original receiver from the call site).
+ __ str(r1, MemOperand(sp, argc_ * kPointerSize));
__ mov(r0, Operand(argc_)); // Setup the number of arguments.
__ mov(r2, Operand(0));
__ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION);
// edi: called object
// eax: number of arguments
__ bind(&non_function_call);
-
+ // CALL_NON_FUNCTION expects the non-function constructor as receiver
+ // (instead of the original receiver from the call site). The receiver is
+ // stack element argc+1.
+ __ mov(Operand(esp, eax, times_4, kPointerSize), edi);
// Set expected number of arguments to zero (not changing eax).
__ Set(ebx, Immediate(0));
__ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
__ bind(&done);
}
- // 2. Get the function to call from the stack.
- { Label done, non_function, function;
- // +1 ~ return address.
- __ mov(edi, Operand(esp, eax, times_4, +1 * kPointerSize));
- __ test(edi, Immediate(kSmiTagMask));
- __ j(zero, &non_function, not_taken);
- __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
- __ j(equal, &function, taken);
-
- // Non-function called: Clear the function to force exception.
- __ bind(&non_function);
- __ xor_(edi, Operand(edi));
- __ jmp(&done);
-
- // Function called: Change context eagerly to get the right global object.
- __ bind(&function);
- __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
+ // 2. Get the function to call (passed as receiver) from the stack, check
+ // if it is a function.
+ Label non_function;
+ // 1 ~ return address.
+ __ mov(edi, Operand(esp, eax, times_4, 1 * kPointerSize));
+ __ test(edi, Immediate(kSmiTagMask));
+ __ j(zero, &non_function, not_taken);
+ __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
+ __ j(not_equal, &non_function, not_taken);
- __ bind(&done);
- }
- // 3. Make sure first argument is an object; convert if necessary.
- { Label call_to_object, use_global_receiver, patch_receiver, done;
- __ mov(ebx, Operand(esp, eax, times_4, 0));
+ // 3a. Patch the first argument if necessary when calling a function.
+ Label shift_arguments;
+ { Label convert_to_object, use_global_receiver, patch_receiver;
+ // Change context eagerly in case we need the global receiver.
+ __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
+ __ mov(ebx, Operand(esp, eax, times_4, 0)); // First argument.
__ test(ebx, Immediate(kSmiTagMask));
- __ j(zero, &call_to_object);
+ __ j(zero, &convert_to_object);
__ cmp(ebx, Factory::null_value());
__ j(equal, &use_global_receiver);
__ mov(ecx, FieldOperand(ebx, HeapObject::kMapOffset));
__ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
__ cmp(ecx, FIRST_JS_OBJECT_TYPE);
- __ j(less, &call_to_object);
+ __ j(below, &convert_to_object);
__ cmp(ecx, LAST_JS_OBJECT_TYPE);
- __ j(less_equal, &done);
+ __ j(below_equal, &shift_arguments);
- __ bind(&call_to_object);
- __ EnterInternalFrame(); // preserves eax, ebx, edi
-
- // Store the arguments count on the stack (smi tagged).
+ __ bind(&convert_to_object);
+ __ EnterInternalFrame(); // In order to preserve argument count.
__ SmiTag(eax);
__ push(eax);
- __ push(edi); // save edi across the call
__ push(ebx);
__ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
__ mov(ebx, eax);
- __ pop(edi); // restore edi after the call
- // Get the arguments count and untag it.
__ pop(eax);
__ SmiUntag(eax);
-
__ LeaveInternalFrame();
+ // Restore the function to edi.
+ __ mov(edi, Operand(esp, eax, times_4, 1 * kPointerSize));
__ jmp(&patch_receiver);
- // Use the global receiver object from the called function as the receiver.
+ // Use the global receiver object from the called function as the
+ // receiver.
__ bind(&use_global_receiver);
const int kGlobalIndex =
Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
__ bind(&patch_receiver);
__ mov(Operand(esp, eax, times_4, 0), ebx);
- __ bind(&done);
+ __ jmp(&shift_arguments);
}
- // 4. Check that the function really is a function.
- { Label done;
- __ test(edi, Operand(edi));
- __ j(not_zero, &done, taken);
- __ xor_(ebx, Operand(ebx));
- // CALL_NON_FUNCTION will expect to find the non-function callee on the
- // expression stack of the caller. Transfer it from receiver to the
- // caller's expression stack (and make the first argument the receiver
- // for CALL_NON_FUNCTION) by decrementing the argument count.
- __ dec(eax);
- __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
- __ jmp(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
- RelocInfo::CODE_TARGET);
- __ bind(&done);
- }
-
- // 5. Shift arguments and return address one slot down on the stack
- // (overwriting the receiver).
+ // 3b. Patch the first argument when calling a non-function. The
+ // CALL_NON_FUNCTION builtin expects the non-function callee as
+ // receiver, so overwrite the first argument which will ultimately
+ // become the receiver.
+ __ bind(&non_function);
+ __ mov(Operand(esp, eax, times_4, 0), edi);
+ // Clear edi to indicate a non-function being called.
+ __ xor_(edi, Operand(edi));
+
+ // 4. Shift arguments and return address one slot down on the stack
+ // (overwriting the original receiver). Adjust argument count to make
+ // the original first argument the new receiver.
+ __ bind(&shift_arguments);
{ Label loop;
__ mov(ecx, eax);
__ bind(&loop);
__ mov(ebx, Operand(esp, ecx, times_4, 0));
__ mov(Operand(esp, ecx, times_4, kPointerSize), ebx);
__ dec(ecx);
- __ j(not_sign, &loop);
+ __ j(not_sign, &loop); // While non-negative (to copy return address).
__ pop(ebx); // Discard copy of return address.
__ dec(eax); // One fewer argument (first argument is new receiver).
}
- // 6. Get the code to call from the function and check that the number of
- // expected arguments matches what we're providing.
- { __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
- __ mov(ebx,
- FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset));
- __ mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset));
- __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
- __ cmp(eax, Operand(ebx));
- __ j(not_equal, Handle<Code>(builtin(ArgumentsAdaptorTrampoline)));
+ // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin.
+ { Label function;
+ __ test(edi, Operand(edi));
+ __ j(not_zero, &function, taken);
+ __ xor_(ebx, Operand(ebx));
+ __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
+ __ jmp(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
+ RelocInfo::CODE_TARGET);
+ __ bind(&function);
}
- // 7. Jump (tail-call) to the code in register edx without checking arguments.
+ // 5b. Get the code to call from the function and check that the number of
+ // expected arguments matches what we're providing. If so, jump
+ // (tail-call) to the code in register edx without checking arguments.
+ __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
+ __ mov(ebx,
+ FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset));
+ __ mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset));
+ __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
+ __ cmp(eax, Operand(ebx));
+ __ j(not_equal, Handle<Code>(builtin(ArgumentsAdaptorTrampoline)));
+
ParameterCount expected(0);
__ InvokeCode(Operand(edx), expected, expected, JUMP_FUNCTION);
}
// Slow-case: Non-function called.
__ bind(&slow);
+ // CALL_NON_FUNCTION expects the non-function callee as receiver (instead
+ // of the original receiver from the call site).
+ __ mov(Operand(esp, (argc_ + 1) * kPointerSize), edi);
__ Set(eax, Immediate(argc_));
__ Set(ebx, Immediate(0));
__ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
}
-// The NewArguments function is only used when constructing the
-// arguments array when calling non-functions from JavaScript in
-// runtime.js:CALL_NON_FUNCTION.
-static Object* Runtime_NewArguments(Arguments args) {
- NoHandleAllocation ha;
- ASSERT(args.length() == 1);
-
- // ECMA-262, 3rd., 10.1.8, p.39
- CONVERT_CHECKED(JSFunction, callee, args[0]);
-
- // Compute the frame holding the arguments.
- JavaScriptFrameIterator it;
- it.AdvanceToArgumentsFrame();
- JavaScriptFrame* frame = it.frame();
-
- const int length = frame->GetProvidedParametersCount();
- Object* result = Heap::AllocateArgumentsObject(callee, length);
- if (result->IsFailure()) return result;
- if (length > 0) {
- Object* obj = Heap::AllocateFixedArray(length);
- if (obj->IsFailure()) return obj;
- FixedArray* array = FixedArray::cast(obj);
- ASSERT(array->length() == length);
-
- AssertNoAllocation no_gc;
- WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
- for (int i = 0; i < length; i++) {
- array->set(i, frame->GetParameter(i), mode);
- }
- JSObject::cast(result)->set_elements(array);
- }
- return result;
-}
-
-
static Object* Runtime_NewArgumentsFast(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 3);
}
-static Object* Runtime_GetCalledFunction(Arguments args) {
- HandleScope scope;
- ASSERT(args.length() == 0);
- StackFrameIterator it;
- // Get past the JS-to-C exit frame.
- ASSERT(it.frame()->is_exit());
- it.Advance();
- // Get past the CALL_NON_FUNCTION activation frame.
- ASSERT(it.frame()->is_java_script());
- it.Advance();
- // Argument adaptor frames do not copy the function; we have to skip
- // past them to get to the real calling frame.
- if (it.frame()->is_arguments_adaptor()) it.Advance();
- // Get the function from the top of the expression stack of the
- // calling frame.
- StandardFrame* frame = StandardFrame::cast(it.frame());
- int index = frame->ComputeExpressionsCount() - 1;
- Object* result = frame->GetExpression(index);
- return result;
-}
-
-
static Object* Runtime_GetFunctionDelegate(Arguments args) {
HandleScope scope;
ASSERT(args.length() == 1);
F(IsExtensible, 1, 1) \
\
/* Utilities */ \
- F(GetCalledFunction, 0, 1) \
F(GetFunctionDelegate, 1, 1) \
F(GetConstructorDelegate, 1, 1) \
- F(NewArguments, 1, 1) \
F(NewArgumentsFast, 3, 1) \
F(LazyCompile, 1, 1) \
F(SetNewFunctionAttributes, 1, 1) \
function CALL_NON_FUNCTION() {
- var callee = %GetCalledFunction();
- var delegate = %GetFunctionDelegate(callee);
+ var delegate = %GetFunctionDelegate(this);
if (!IS_FUNCTION(delegate)) {
- throw %MakeTypeError('called_non_callable', [typeof callee]);
+ throw %MakeTypeError('called_non_callable', [typeof this]);
}
-
- var parameters = %NewArguments(delegate);
- return delegate.apply(callee, parameters);
+ return delegate.apply(this, arguments);
}
function CALL_NON_FUNCTION_AS_CONSTRUCTOR() {
- var callee = %GetCalledFunction();
- var delegate = %GetConstructorDelegate(callee);
+ var delegate = %GetConstructorDelegate(this);
if (!IS_FUNCTION(delegate)) {
- throw %MakeTypeError('called_non_callable', [typeof callee]);
+ throw %MakeTypeError('called_non_callable', [typeof this]);
}
-
- var parameters = %NewArguments(delegate);
- return delegate.apply(callee, parameters);
+ return delegate.apply(this, arguments);
}
void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
// Stack Layout:
- // rsp: return address
- // +1: Argument n
- // +2: Argument n-1
+ // rsp[0]: Return address
+ // rsp[1]: Argument n
+ // rsp[2]: Argument n-1
// ...
- // +n: Argument 1 = receiver
- // +n+1: Argument 0 = function to call
+ // rsp[n]: Argument 1
+ // rsp[n+1]: Receiver (function to call)
//
- // rax contains the number of arguments, n, not counting the function.
+ // rax contains the number of arguments, n, not counting the receiver.
//
// 1. Make sure we have at least one argument.
{ Label done;
__ bind(&done);
}
- // 2. Get the function to call from the stack.
- { Label done, non_function, function;
- // The function to call is at position n+1 on the stack.
- __ movq(rdi, Operand(rsp, rax, times_pointer_size, +1 * kPointerSize));
- __ JumpIfSmi(rdi, &non_function);
- __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
- __ j(equal, &function);
-
- // Non-function called: Clear the function to force exception.
- __ bind(&non_function);
- __ xor_(rdi, rdi);
- __ jmp(&done);
+ // 2. Get the function to call (passed as receiver) from the stack, check
+ // if it is a function.
+ Label non_function;
+ // The function to call is at position n+1 on the stack.
+ __ movq(rdi, Operand(rsp, rax, times_pointer_size, 1 * kPointerSize));
+ __ JumpIfSmi(rdi, &non_function);
+ __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
+ __ j(not_equal, &non_function);
- // Function called: Change context eagerly to get the right global object.
- __ bind(&function);
+ // 3a. Patch the first argument if necessary when calling a function.
+ Label shift_arguments;
+ { Label convert_to_object, use_global_receiver, patch_receiver;
+ // Change context eagerly in case we need the global receiver.
__ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
- __ bind(&done);
- }
-
- // 3. Make sure first argument is an object; convert if necessary.
- { Label call_to_object, use_global_receiver, patch_receiver, done;
__ movq(rbx, Operand(rsp, rax, times_pointer_size, 0));
-
- __ JumpIfSmi(rbx, &call_to_object);
+ __ JumpIfSmi(rbx, &convert_to_object);
__ CompareRoot(rbx, Heap::kNullValueRootIndex);
__ j(equal, &use_global_receiver);
__ j(equal, &use_global_receiver);
__ CmpObjectType(rbx, FIRST_JS_OBJECT_TYPE, rcx);
- __ j(below, &call_to_object);
+ __ j(below, &convert_to_object);
__ CmpInstanceType(rcx, LAST_JS_OBJECT_TYPE);
- __ j(below_equal, &done);
-
- __ bind(&call_to_object);
- __ EnterInternalFrame(); // preserves rax, rbx, rdi
+ __ j(below_equal, &shift_arguments);
- // Store the arguments count on the stack (smi tagged).
+ __ bind(&convert_to_object);
+ __ EnterInternalFrame(); // In order to preserve argument count.
__ Integer32ToSmi(rax, rax);
__ push(rax);
- __ push(rdi); // save edi across the call
__ push(rbx);
__ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
__ movq(rbx, rax);
- __ pop(rdi); // restore edi after the call
- // Get the arguments count and untag it.
__ pop(rax);
__ SmiToInteger32(rax, rax);
-
__ LeaveInternalFrame();
+ // Restore the function to rdi.
+ __ movq(rdi, Operand(rsp, rax, times_pointer_size, 1 * kPointerSize));
__ jmp(&patch_receiver);
- // Use the global receiver object from the called function as the receiver.
+ // Use the global receiver object from the called function as the
+ // receiver.
__ bind(&use_global_receiver);
const int kGlobalIndex =
Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
__ bind(&patch_receiver);
__ movq(Operand(rsp, rax, times_pointer_size, 0), rbx);
- __ bind(&done);
+ __ jmp(&shift_arguments);
}
- // 4. Check that the function really is a function.
- { Label real_function;
- __ testq(rdi, rdi);
- __ j(not_zero, &real_function);
- __ xor_(rbx, rbx);
- // CALL_NON_FUNCTION will expect to find the non-function callee on the
- // expression stack of the caller. Transfer it from receiver to the
- // caller's expression stack (and make the first argument the receiver
- // for CALL_NON_FUNCTION) by decrementing the argument count.
- __ decq(rax);
- __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION);
- __ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
- RelocInfo::CODE_TARGET);
- __ bind(&real_function);
- }
+ // 3b. Patch the first argument when calling a non-function. The
+ // CALL_NON_FUNCTION builtin expects the non-function callee as
+ // receiver, so overwrite the first argument which will ultimately
+ // become the receiver.
+ __ bind(&non_function);
+ __ movq(Operand(rsp, rax, times_pointer_size, 0), rdi);
+ __ xor_(rdi, rdi);
- // 5. Shift arguments and return address one slot down on the stack
- // (overwriting the receiver).
+ // 4. Shift arguments and return address one slot down on the stack
+ // (overwriting the original receiver). Adjust argument count to make
+ // the original first argument the new receiver.
+ __ bind(&shift_arguments);
{ Label loop;
__ movq(rcx, rax);
__ bind(&loop);
__ movq(rbx, Operand(rsp, rcx, times_pointer_size, 0));
__ movq(Operand(rsp, rcx, times_pointer_size, 1 * kPointerSize), rbx);
__ decq(rcx);
- __ j(not_sign, &loop);
+ __ j(not_sign, &loop); // While non-negative (to copy return address).
__ pop(rbx); // Discard copy of return address.
__ decq(rax); // One fewer argument (first argument is new receiver).
}
- // 6. Get the code to call from the function and check that the number of
- // expected arguments matches what we're providing.
+ // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin.
+ { Label function;
+ __ testq(rdi, rdi);
+ __ j(not_zero, &function);
+ __ xor_(rbx, rbx);
+ __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION);
+ __ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
+ RelocInfo::CODE_TARGET);
+ __ bind(&function);
+ }
+
+ // 5b. Get the code to call from the function and check that the number of
+ // expected arguments matches what we're providing. If so, jump
+ // (tail-call) to the code in register edx without checking arguments.
__ movq(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
__ movsxlq(rbx,
FieldOperand(rdx, SharedFunctionInfo::kFormalParameterCountOffset));
Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
RelocInfo::CODE_TARGET);
- // 7. Jump (tail-call) to the code in register edx without checking arguments.
ParameterCount expected(0);
__ InvokeCode(rdx, expected, expected, JUMP_FUNCTION);
}
// edi: called object
// eax: number of arguments
__ bind(&non_function_call);
- // Set expected number of arguments to zero (not changing eax).
+ // CALL_NON_FUNCTION expects the non-function constructor as receiver
+ // (instead of the original receiver from the call site). The receiver is
+ // stack element argc+1.
+ __ movq(Operand(rsp, rax, times_pointer_size, kPointerSize), rdi);
+ // Set expected number of arguments to zero (not changing rax).
__ movq(rbx, Immediate(0));
__ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
__ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
// Slow-case: Non-function called.
__ bind(&slow);
+ // CALL_NON_FUNCTION expects the non-function callee as receiver (instead
+ // of the original receiver from the call site).
+ __ movq(Operand(rsp, (argc_ + 1) * kPointerSize), rdi);
__ Set(rax, argc_);
__ Set(rbx, 0);
__ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION);
--- /dev/null
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Calling non-objects directly or via Function.prototype.call should
+// not mess up the stack.
+// http://code.google.com/p/v8/issues/detail?id=603
+
+function test0() {
+ var re = /b../;
+ return re('abcdefghijklm') + 'z';
+}
+assertEquals('bcdz', test0());
+
+var re1 = /c../;
+re1.call = Function.prototype.call;
+var test1 = re1.call(null, 'abcdefghijklm') + 'z';
+assertEquals('cdez', test1);
+
+var re2 = /d../;
+var test2 = Function.prototype.call.call(re2, null, 'abcdefghijklm') + 'z';
+assertEquals('defz', test2);
+
+var re3 = /e../;
+var test3 = Function.prototype.call.apply(re3, [null, 'abcdefghijklm']) + 'z';
+assertEquals('efgz', test3);