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) { ...@@ -499,7 +499,10 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
// r0: number of arguments // r0: number of arguments
// r1: called object // r1: called object
__ bind(&non_function_call); __ 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). // Set expected number of arguments to zero (not changing r0).
__ mov(r2, Operand(0)); __ mov(r2, Operand(0));
__ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR); __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
...@@ -904,7 +907,7 @@ void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) { ...@@ -904,7 +907,7 @@ void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
void Builtins::Generate_FunctionCall(MacroAssembler* masm) { void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
// 1. Make sure we have at least one argument. // 1. Make sure we have at least one argument.
// r0: actual number of argument // r0: actual number of arguments
{ Label done; { Label done;
__ tst(r0, Operand(r0)); __ tst(r0, Operand(r0));
__ b(ne, &done); __ b(ne, &done);
...@@ -914,40 +917,31 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { ...@@ -914,40 +917,31 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ bind(&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 // r0: actual number of arguments
{ Label done, non_function, function; Label non_function;
__ ldr(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2)); __ ldr(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2));
__ tst(r1, Operand(kSmiTagMask)); __ tst(r1, Operand(kSmiTagMask));
__ b(eq, &non_function); __ b(eq, &non_function);
__ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE); __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
__ b(eq, &function); __ b(ne, &non_function);
// Non-function called: Clear the function to force exception. // 3a. Patch the first argument if necessary when calling a function.
__ 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);
}
// 3. Make sure first argument is an object; convert if necessary.
// r0: actual number of arguments // r0: actual number of arguments
// r1: function // 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)); __ add(r2, sp, Operand(r0, LSL, kPointerSizeLog2));
__ ldr(r2, MemOperand(r2, -kPointerSize)); __ ldr(r2, MemOperand(r2, -kPointerSize));
// r0: actual number of arguments // r0: actual number of arguments
// r1: function // r1: function
// r2: first argument // r2: first argument
__ tst(r2, Operand(kSmiTagMask)); __ tst(r2, Operand(kSmiTagMask));
__ b(eq, &call_to_object); __ b(eq, &convert_to_object);
__ LoadRoot(r3, Heap::kNullValueRootIndex); __ LoadRoot(r3, Heap::kNullValueRootIndex);
__ cmp(r2, r3); __ cmp(r2, r3);
...@@ -957,31 +951,28 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { ...@@ -957,31 +951,28 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ b(eq, &use_global_receiver); __ b(eq, &use_global_receiver);
__ CompareObjectType(r2, r3, r3, FIRST_JS_OBJECT_TYPE); __ 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)); __ cmp(r3, Operand(LAST_JS_OBJECT_TYPE));
__ b(le, &done); __ b(le, &shift_arguments);
__ bind(&call_to_object);
__ EnterInternalFrame();
// Store number of arguments and function across the call into the runtime. __ bind(&convert_to_object);
__ mov(r0, Operand(r0, LSL, kSmiTagSize)); __ EnterInternalFrame(); // In order to preserve argument count.
__ mov(r0, Operand(r0, LSL, kSmiTagSize)); // Smi-tagged.
__ push(r0); __ push(r0);
__ push(r1);
__ push(r2); __ push(r2);
__ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS); __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS);
__ mov(r2, r0); __ mov(r2, r0);
// Restore number of arguments and function.
__ pop(r1);
__ pop(r0); __ pop(r0);
__ mov(r0, Operand(r0, ASR, kSmiTagSize)); __ mov(r0, Operand(r0, ASR, kSmiTagSize));
__ LeaveInternalFrame(); __ 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); __ bind(&use_global_receiver);
const int kGlobalIndex = const int kGlobalIndex =
Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize; Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
...@@ -994,27 +985,27 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { ...@@ -994,27 +985,27 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ add(r3, sp, Operand(r0, LSL, kPointerSizeLog2)); __ add(r3, sp, Operand(r0, LSL, kPointerSizeLog2));
__ str(r2, MemOperand(r3, -kPointerSize)); __ str(r2, MemOperand(r3, -kPointerSize));
__ bind(&done); __ jmp(&shift_arguments);
} }
// 4. Handle non-functions. // 3b. Patch the first argument when calling a non-function. The
// r0: actual number of arguments (including call() receiver) // 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 // r1: function
{ Label done; __ bind(&non_function);
__ tst(r1, r1); __ add(r2, sp, Operand(r0, LSL, kPointerSizeLog2));
__ b(ne, &done); __ str(r1, MemOperand(r2, -kPointerSize));
__ mov(r2, Operand(0)); // expected arguments is 0 for CALL_NON_FUNCTION // Clear r1 to indicate a non-function being called.
// Transfer the receiver from the first argument to the top of the __ mov(r1, Operand(0));
// 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 // 4. Shift arguments and return address one slot down on the stack
// stack, overwriting the receiver. // (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; { Label loop;
// Calculate the copy start address (destination). Copy end address is sp. // Calculate the copy start address (destination). Copy end address is sp.
__ add(r2, sp, Operand(r0, LSL, kPointerSizeLog2)); __ add(r2, sp, Operand(r0, LSL, kPointerSizeLog2));
...@@ -1025,14 +1016,28 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { ...@@ -1025,14 +1016,28 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ sub(r2, r2, Operand(kPointerSize)); __ sub(r2, r2, Operand(kPointerSize));
__ cmp(r2, sp); __ cmp(r2, sp);
__ b(ne, &loop); __ 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)); __ sub(r0, r0, Operand(1));
__ pop(); __ pop();
} }
// 6. Get the code for the function or the non-function builtin. // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin.
// If number of expected arguments matches, then call. Otherwise restart // r0: actual number of arguments
// the arguments adaptor stub. // 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 // r0: actual number of arguments
// r1: function // r1: function
__ ldr(r3, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); __ ldr(r3, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
...@@ -1044,7 +1049,6 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { ...@@ -1044,7 +1049,6 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)), __ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
RelocInfo::CODE_TARGET, ne); RelocInfo::CODE_TARGET, ne);
// 7. Jump (tail-call) to the code in r3 without checking arguments.
ParameterCount expected(0); ParameterCount expected(0);
__ InvokeCode(r3, expected, expected, JUMP_FUNCTION); __ InvokeCode(r3, expected, expected, JUMP_FUNCTION);
} }
......
...@@ -6917,6 +6917,9 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { ...@@ -6917,6 +6917,9 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
// Slow-case: Non-function called. // Slow-case: Non-function called.
__ bind(&slow); __ 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(r0, Operand(argc_)); // Setup the number of arguments.
__ mov(r2, Operand(0)); __ mov(r2, Operand(0));
__ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION); __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION);
......
...@@ -93,7 +93,10 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { ...@@ -93,7 +93,10 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
// edi: called object // edi: called object
// eax: number of arguments // eax: number of arguments
__ bind(&non_function_call); __ 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 expected number of arguments to zero (not changing eax).
__ Set(ebx, Immediate(0)); __ Set(ebx, Immediate(0));
__ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR); __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
...@@ -437,33 +440,26 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { ...@@ -437,33 +440,26 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ bind(&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
{ Label done, non_function, function; // if it is a function.
// +1 ~ return address. Label non_function;
__ mov(edi, Operand(esp, eax, times_4, +1 * kPointerSize)); // 1 ~ return address.
__ mov(edi, Operand(esp, eax, times_4, 1 * kPointerSize));
__ test(edi, Immediate(kSmiTagMask)); __ test(edi, Immediate(kSmiTagMask));
__ j(zero, &non_function, not_taken); __ j(zero, &non_function, not_taken);
__ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
__ j(equal, &function, taken); __ j(not_equal, &non_function, not_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. // 3a. Patch the first argument if necessary when calling a function.
__ bind(&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(esi, FieldOperand(edi, JSFunction::kContextOffset));
__ bind(&done); __ mov(ebx, Operand(esp, eax, times_4, 0)); // First argument.
}
// 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));
__ test(ebx, Immediate(kSmiTagMask)); __ test(ebx, Immediate(kSmiTagMask));
__ j(zero, &call_to_object); __ j(zero, &convert_to_object);
__ cmp(ebx, Factory::null_value()); __ cmp(ebx, Factory::null_value());
__ j(equal, &use_global_receiver); __ j(equal, &use_global_receiver);
...@@ -473,31 +469,28 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { ...@@ -473,31 +469,28 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ mov(ecx, FieldOperand(ebx, HeapObject::kMapOffset)); __ mov(ecx, FieldOperand(ebx, HeapObject::kMapOffset));
__ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
__ cmp(ecx, FIRST_JS_OBJECT_TYPE); __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
__ j(less, &call_to_object); __ j(below, &convert_to_object);
__ cmp(ecx, LAST_JS_OBJECT_TYPE); __ 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); __ SmiTag(eax);
__ push(eax); __ push(eax);
__ push(edi); // save edi across the call
__ push(ebx); __ push(ebx);
__ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
__ mov(ebx, eax); __ mov(ebx, eax);
__ pop(edi); // restore edi after the call
// Get the arguments count and untag it.
__ pop(eax); __ pop(eax);
__ SmiUntag(eax); __ SmiUntag(eax);
__ LeaveInternalFrame(); __ LeaveInternalFrame();
// Restore the function to edi.
__ mov(edi, Operand(esp, eax, times_4, 1 * kPointerSize));
__ jmp(&patch_receiver); __ 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); __ bind(&use_global_receiver);
const int kGlobalIndex = const int kGlobalIndex =
Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize; Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
...@@ -509,50 +502,55 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { ...@@ -509,50 +502,55 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ bind(&patch_receiver); __ bind(&patch_receiver);
__ mov(Operand(esp, eax, times_4, 0), ebx); __ mov(Operand(esp, eax, times_4, 0), ebx);
__ bind(&done); __ jmp(&shift_arguments);
} }
// 4. Check that the function really is a function. // 3b. Patch the first argument when calling a non-function. The
{ Label done; // CALL_NON_FUNCTION builtin expects the non-function callee as
__ test(edi, Operand(edi)); // receiver, so overwrite the first argument which will ultimately
__ j(not_zero, &done, taken); // become the receiver.
__ xor_(ebx, Operand(ebx)); __ bind(&non_function);
// CALL_NON_FUNCTION will expect to find the non-function callee on the __ mov(Operand(esp, eax, times_4, 0), edi);
// expression stack of the caller. Transfer it from receiver to the // Clear edi to indicate a non-function being called.
// caller's expression stack (and make the first argument the receiver __ xor_(edi, Operand(edi));
// 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 // 4. Shift arguments and return address one slot down on the stack
// (overwriting the receiver). // (overwriting the original receiver). Adjust argument count to make
// the original first argument the new receiver.
__ bind(&shift_arguments);
{ Label loop; { Label loop;
__ mov(ecx, eax); __ mov(ecx, eax);
__ bind(&loop); __ bind(&loop);
__ mov(ebx, Operand(esp, ecx, times_4, 0)); __ mov(ebx, Operand(esp, ecx, times_4, 0));
__ mov(Operand(esp, ecx, times_4, kPointerSize), ebx); __ mov(Operand(esp, ecx, times_4, kPointerSize), ebx);
__ dec(ecx); __ dec(ecx);
__ j(not_sign, &loop); __ j(not_sign, &loop); // While non-negative (to copy return address).
__ pop(ebx); // Discard copy of return address. __ pop(ebx); // Discard copy of return address.
__ dec(eax); // One fewer argument (first argument is new receiver). __ 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 // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin.
// expected arguments matches what we're providing. { Label function;
{ __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); __ 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);
}
// 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, __ mov(ebx,
FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset)); FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset));
__ mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset)); __ mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset));
__ lea(edx, FieldOperand(edx, Code::kHeaderSize)); __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
__ cmp(eax, Operand(ebx)); __ cmp(eax, Operand(ebx));
__ j(not_equal, Handle<Code>(builtin(ArgumentsAdaptorTrampoline))); __ j(not_equal, Handle<Code>(builtin(ArgumentsAdaptorTrampoline)));
}
// 7. Jump (tail-call) to the code in register edx without checking arguments.
ParameterCount expected(0); ParameterCount expected(0);
__ InvokeCode(Operand(edx), expected, expected, JUMP_FUNCTION); __ InvokeCode(Operand(edx), expected, expected, JUMP_FUNCTION);
} }
......
...@@ -9356,6 +9356,9 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { ...@@ -9356,6 +9356,9 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
// Slow-case: Non-function called. // Slow-case: Non-function called.
__ bind(&slow); __ 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(eax, Immediate(argc_));
__ Set(ebx, Immediate(0)); __ Set(ebx, Immediate(0));
__ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION); __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
......
...@@ -4768,41 +4768,6 @@ static Object* Runtime_Math_tan(Arguments args) { ...@@ -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) { static Object* Runtime_NewArgumentsFast(Arguments args) {
NoHandleAllocation ha; NoHandleAllocation ha;
ASSERT(args.length() == 3); ASSERT(args.length() == 3);
...@@ -4953,28 +4918,6 @@ static Object* Runtime_LazyCompile(Arguments args) { ...@@ -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) { static Object* Runtime_GetFunctionDelegate(Arguments args) {
HandleScope scope; HandleScope scope;
ASSERT(args.length() == 1); ASSERT(args.length() == 1);
......
...@@ -71,10 +71,8 @@ namespace internal { ...@@ -71,10 +71,8 @@ namespace internal {
F(IsExtensible, 1, 1) \ F(IsExtensible, 1, 1) \
\ \
/* Utilities */ \ /* Utilities */ \
F(GetCalledFunction, 0, 1) \
F(GetFunctionDelegate, 1, 1) \ F(GetFunctionDelegate, 1, 1) \
F(GetConstructorDelegate, 1, 1) \ F(GetConstructorDelegate, 1, 1) \
F(NewArguments, 1, 1) \
F(NewArgumentsFast, 3, 1) \ F(NewArgumentsFast, 3, 1) \
F(LazyCompile, 1, 1) \ F(LazyCompile, 1, 1) \
F(SetNewFunctionAttributes, 1, 1) \ F(SetNewFunctionAttributes, 1, 1) \
......
...@@ -395,26 +395,20 @@ function FILTER_KEY(key) { ...@@ -395,26 +395,20 @@ function FILTER_KEY(key) {
function CALL_NON_FUNCTION() { function CALL_NON_FUNCTION() {
var callee = %GetCalledFunction(); var delegate = %GetFunctionDelegate(this);
var delegate = %GetFunctionDelegate(callee);
if (!IS_FUNCTION(delegate)) { if (!IS_FUNCTION(delegate)) {
throw %MakeTypeError('called_non_callable', [typeof callee]); throw %MakeTypeError('called_non_callable', [typeof this]);
} }
return delegate.apply(this, arguments);
var parameters = %NewArguments(delegate);
return delegate.apply(callee, parameters);
} }
function CALL_NON_FUNCTION_AS_CONSTRUCTOR() { function CALL_NON_FUNCTION_AS_CONSTRUCTOR() {
var callee = %GetCalledFunction(); var delegate = %GetConstructorDelegate(this);
var delegate = %GetConstructorDelegate(callee);
if (!IS_FUNCTION(delegate)) { if (!IS_FUNCTION(delegate)) {
throw %MakeTypeError('called_non_callable', [typeof callee]); throw %MakeTypeError('called_non_callable', [typeof this]);
} }
return delegate.apply(this, arguments);
var parameters = %NewArguments(delegate);
return delegate.apply(callee, parameters);
} }
......
...@@ -185,14 +185,14 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { ...@@ -185,14 +185,14 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
void Builtins::Generate_FunctionCall(MacroAssembler* masm) { void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
// Stack Layout: // Stack Layout:
// rsp: return address // rsp[0]: Return address
// +1: Argument n // rsp[1]: Argument n
// +2: Argument n-1 // rsp[2]: Argument n-1
// ... // ...
// +n: Argument 1 = receiver // rsp[n]: Argument 1
// +n+1: Argument 0 = function to call // 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. // 1. Make sure we have at least one argument.
{ Label done; { Label done;
...@@ -205,31 +205,23 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { ...@@ -205,31 +205,23 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ bind(&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
{ Label done, non_function, function; // if it is a function.
Label non_function;
// The function to call is at position n+1 on the stack. // The function to call is at position n+1 on the stack.
__ movq(rdi, Operand(rsp, rax, times_pointer_size, +1 * kPointerSize)); __ movq(rdi, Operand(rsp, rax, times_pointer_size, 1 * kPointerSize));
__ JumpIfSmi(rdi, &non_function); __ JumpIfSmi(rdi, &non_function);
__ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx); __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
__ j(equal, &function); __ j(not_equal, &non_function);
// Non-function called: Clear the function to force exception.
__ bind(&non_function);
__ xor_(rdi, rdi);
__ jmp(&done);
// Function called: Change context eagerly to get the right global object. // 3a. Patch the first argument if necessary when calling a function.
__ bind(&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)); __ 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)); __ movq(rbx, Operand(rsp, rax, times_pointer_size, 0));
__ JumpIfSmi(rbx, &convert_to_object);
__ JumpIfSmi(rbx, &call_to_object);
__ CompareRoot(rbx, Heap::kNullValueRootIndex); __ CompareRoot(rbx, Heap::kNullValueRootIndex);
__ j(equal, &use_global_receiver); __ j(equal, &use_global_receiver);
...@@ -237,31 +229,28 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { ...@@ -237,31 +229,28 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ j(equal, &use_global_receiver); __ j(equal, &use_global_receiver);
__ CmpObjectType(rbx, FIRST_JS_OBJECT_TYPE, rcx); __ CmpObjectType(rbx, FIRST_JS_OBJECT_TYPE, rcx);
__ j(below, &call_to_object); __ j(below, &convert_to_object);
__ CmpInstanceType(rcx, LAST_JS_OBJECT_TYPE); __ CmpInstanceType(rcx, LAST_JS_OBJECT_TYPE);
__ j(below_equal, &done); __ j(below_equal, &shift_arguments);
__ bind(&call_to_object);
__ EnterInternalFrame(); // preserves rax, rbx, rdi
// Store the arguments count on the stack (smi tagged). __ bind(&convert_to_object);
__ EnterInternalFrame(); // In order to preserve argument count.
__ Integer32ToSmi(rax, rax); __ Integer32ToSmi(rax, rax);
__ push(rax); __ push(rax);
__ push(rdi); // save edi across the call
__ push(rbx); __ push(rbx);
__ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
__ movq(rbx, rax); __ movq(rbx, rax);
__ pop(rdi); // restore edi after the call
// Get the arguments count and untag it.
__ pop(rax); __ pop(rax);
__ SmiToInteger32(rax, rax); __ SmiToInteger32(rax, rax);
__ LeaveInternalFrame(); __ LeaveInternalFrame();
// Restore the function to rdi.
__ movq(rdi, Operand(rsp, rax, times_pointer_size, 1 * kPointerSize));
__ jmp(&patch_receiver); __ 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); __ bind(&use_global_receiver);
const int kGlobalIndex = const int kGlobalIndex =
Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize; Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
...@@ -273,41 +262,47 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { ...@@ -273,41 +262,47 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ bind(&patch_receiver); __ bind(&patch_receiver);
__ movq(Operand(rsp, rax, times_pointer_size, 0), rbx); __ 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 // 4. Shift arguments and return address one slot down on the stack
// (overwriting the receiver). // (overwriting the original receiver). Adjust argument count to make
// the original first argument the new receiver.
__ bind(&shift_arguments);
{ Label loop; { Label loop;
__ movq(rcx, rax); __ movq(rcx, rax);
__ bind(&loop); __ bind(&loop);
__ movq(rbx, Operand(rsp, rcx, times_pointer_size, 0)); __ movq(rbx, Operand(rsp, rcx, times_pointer_size, 0));
__ movq(Operand(rsp, rcx, times_pointer_size, 1 * kPointerSize), rbx); __ movq(Operand(rsp, rcx, times_pointer_size, 1 * kPointerSize), rbx);
__ decq(rcx); __ decq(rcx);
__ j(not_sign, &loop); __ j(not_sign, &loop); // While non-negative (to copy return address).
__ pop(rbx); // Discard copy of return address. __ pop(rbx); // Discard copy of return address.
__ decq(rax); // One fewer argument (first argument is new receiver). __ 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 // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin.
// expected arguments matches what we're providing. { 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)); __ movq(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
__ movsxlq(rbx, __ movsxlq(rbx,
FieldOperand(rdx, SharedFunctionInfo::kFormalParameterCountOffset)); FieldOperand(rdx, SharedFunctionInfo::kFormalParameterCountOffset));
...@@ -318,7 +313,6 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { ...@@ -318,7 +313,6 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
Handle<Code>(builtin(ArgumentsAdaptorTrampoline)), Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
RelocInfo::CODE_TARGET); RelocInfo::CODE_TARGET);
// 7. Jump (tail-call) to the code in register edx without checking arguments.
ParameterCount expected(0); ParameterCount expected(0);
__ InvokeCode(rdx, expected, expected, JUMP_FUNCTION); __ InvokeCode(rdx, expected, expected, JUMP_FUNCTION);
} }
...@@ -905,7 +899,11 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { ...@@ -905,7 +899,11 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
// edi: called object // edi: called object
// eax: number of arguments // eax: number of arguments
__ bind(&non_function_call); __ 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)); __ movq(rbx, Immediate(0));
__ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR); __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
__ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)), __ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
......
...@@ -7856,6 +7856,9 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { ...@@ -7856,6 +7856,9 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
// Slow-case: Non-function called. // Slow-case: Non-function called.
__ bind(&slow); __ 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(rax, argc_);
__ Set(rbx, 0); __ Set(rbx, 0);
__ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION); __ 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