Fix stack corruption when calling non-function.

Fix for issue 603.

Revision r3484 removed the property name from the call stack for
call ICs.  When a non-function was called via a call IC and
Function.prototype.call, an extra value was left on the stack that the
caller could not know to clean up.

Fix is to change the JS builtin used for calling non-functions.  It
now gets the callee as receiver, rather than iterating stack frames
and finding it on the expression stack of its JS caller.

Review URL: http://codereview.chromium.org/604064

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3882 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent a48a7bf6
......@@ -499,7 +499,10 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
// 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);
......@@ -904,7 +907,7 @@ void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
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);
......@@ -914,40 +917,31 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ 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);
......@@ -957,31 +951,28 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ 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;
......@@ -994,27 +985,27 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ 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));
......@@ -1025,14 +1016,28 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ 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));
......@@ -1044,7 +1049,6 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ 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);
}
......
......@@ -6917,6 +6917,9 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
// 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);
......
......@@ -93,7 +93,10 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
// 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);
......@@ -437,33 +440,26 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ 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);
......@@ -473,31 +469,28 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ 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;
......@@ -509,50 +502,55 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ 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);
}
......
......@@ -9356,6 +9356,9 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
// 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);
......
......@@ -4768,41 +4768,6 @@ static Object* Runtime_Math_tan(Arguments args) {
}
// 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);
......@@ -4953,28 +4918,6 @@ static Object* Runtime_LazyCompile(Arguments args) {
}
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);
......
......@@ -71,10 +71,8 @@ namespace internal {
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) \
......
......@@ -395,26 +395,20 @@ function FILTER_KEY(key) {
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);
}
......
......@@ -185,14 +185,14 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
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;
......@@ -205,31 +205,23 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ 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);
......@@ -237,31 +229,28 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ 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;
......@@ -273,41 +262,47 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ 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));
......@@ -318,7 +313,6 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
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);
}
......@@ -905,7 +899,11 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
// 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)),
......
......@@ -7856,6 +7856,9 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
// 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);
......
// 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);
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