Generalize the Function.prototype.call hooks in the
arguments adaptor code to allow builtins to work without argument adaptor frames. Get rid of unused JavaScript implementation of call and apply and the associated code generation hooks. Review URL: http://codereview.chromium.org/2850 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@311 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
cf1a6a0bbc
commit
64923a6a83
@ -956,33 +956,37 @@ bool Genesis::InstallNatives() {
|
||||
|
||||
InstallNativeFunctions();
|
||||
|
||||
// TODO(1240778): Get rid of the JS implementation of
|
||||
// Function.prototype.call and simply create a function with the
|
||||
// faked formal parameter count (-1) and use the illegal builtin as
|
||||
// the code for it.
|
||||
|
||||
// Find Function.prototype.call and set it's number of formal
|
||||
// parameters to -1 to let the arguments adaptor handle it
|
||||
// specially.
|
||||
{ Handle<JSFunction> function =
|
||||
Handle<JSFunction>::cast(GetProperty(Top::global(),
|
||||
Factory::function_class_symbol()));
|
||||
// Install Function.prototype.call and apply.
|
||||
{ Handle<String> key = Factory::function_class_symbol();
|
||||
Handle<JSFunction> function =
|
||||
Handle<JSFunction>::cast(GetProperty(Top::global(), key));
|
||||
Handle<JSObject> proto =
|
||||
Handle<JSObject>(JSObject::cast(function->instance_prototype()));
|
||||
|
||||
// Install the call and the apply functions.
|
||||
Handle<JSFunction> call =
|
||||
Handle<JSFunction>::cast(GetProperty(proto, Factory::call_symbol()));
|
||||
call->shared()->set_formal_parameter_count(-1);
|
||||
InstallFunction(proto, "call", JS_OBJECT_TYPE, JSObject::kHeaderSize,
|
||||
Factory::NewJSObject(Top::object_function(), TENURED),
|
||||
Builtins::FunctionCall,
|
||||
false);
|
||||
Handle<JSFunction> apply =
|
||||
InstallFunction(proto, "apply", JS_OBJECT_TYPE, JSObject::kHeaderSize,
|
||||
Factory::NewJSObject(Top::object_function(), TENURED),
|
||||
Builtins::FunctionApply,
|
||||
false);
|
||||
|
||||
// Make sure that Function.prototype.call appears to be compiled.
|
||||
// The code will never be called, but inline caching for call will
|
||||
// only work if it appears to be compiled.
|
||||
call->shared()->set_code(Builtins::builtin(Builtins::Illegal));
|
||||
call->shared()->DontAdaptArguments();
|
||||
ASSERT(call->is_compiled());
|
||||
|
||||
// Use the specialized builtin for Function.prototype.apply.
|
||||
Handle<JSFunction> apply =
|
||||
Handle<JSFunction>::cast(GetProperty(proto, Factory::apply_symbol()));
|
||||
apply->shared()->set_code(Builtins::builtin(Builtins::FunctionApply));
|
||||
// Set the expected paramters for apply to 2; required by builtin.
|
||||
apply->shared()->set_formal_parameter_count(2);
|
||||
|
||||
// Set the lengths for the functions to satisfy ECMA-262.
|
||||
call->shared()->set_length(1);
|
||||
apply->shared()->set_length(2);
|
||||
}
|
||||
|
||||
// Make sure that the builtins object has fast properties.
|
||||
|
@ -258,6 +258,154 @@ 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
|
||||
{ Label done;
|
||||
__ tst(r0, Operand(r0));
|
||||
__ b(ne, &done);
|
||||
__ mov(r2, Operand(Factory::undefined_value()));
|
||||
__ push(r2);
|
||||
__ add(r0, r0, Operand(1));
|
||||
__ bind(&done);
|
||||
}
|
||||
|
||||
// 2. Get the function to call. Already in r1.
|
||||
// r0: actual number of argument
|
||||
{ Label done, non_function, function;
|
||||
__ ldr(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2));
|
||||
__ tst(r1, Operand(kSmiTagMask));
|
||||
__ b(eq, &non_function);
|
||||
__ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
|
||||
__ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset));
|
||||
__ cmp(r2, Operand(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);
|
||||
}
|
||||
|
||||
// 3. Make sure first argument is an object; convert if necessary.
|
||||
// r0: actual number of arguments
|
||||
// r1: function
|
||||
{ Label call_to_object, use_global_receiver, patch_receiver, done;
|
||||
__ 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);
|
||||
|
||||
__ mov(r3, Operand(Factory::null_value()));
|
||||
__ cmp(r2, r3);
|
||||
__ b(eq, &use_global_receiver);
|
||||
__ mov(r3, Operand(Factory::undefined_value()));
|
||||
__ cmp(r2, r3);
|
||||
__ b(eq, &use_global_receiver);
|
||||
|
||||
__ ldr(r3, FieldMemOperand(r2, HeapObject::kMapOffset));
|
||||
__ ldrb(r3, FieldMemOperand(r3, Map::kInstanceTypeOffset));
|
||||
__ cmp(r3, Operand(FIRST_JS_OBJECT_TYPE));
|
||||
__ b(lt, &call_to_object);
|
||||
__ cmp(r3, Operand(LAST_JS_OBJECT_TYPE));
|
||||
__ b(le, &done);
|
||||
|
||||
__ bind(&call_to_object);
|
||||
__ EnterInternalFrame();
|
||||
|
||||
// Store number of arguments and function across the call into the runtime.
|
||||
__ mov(r0, Operand(r0, LSL, kSmiTagSize));
|
||||
__ 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));
|
||||
|
||||
__ ExitInternalFrame();
|
||||
__ b(&patch_receiver);
|
||||
|
||||
// Use the global object from the called function as the receiver.
|
||||
__ bind(&use_global_receiver);
|
||||
const int kGlobalIndex =
|
||||
Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
|
||||
__ ldr(r2, FieldMemOperand(cp, kGlobalIndex));
|
||||
|
||||
__ bind(&patch_receiver);
|
||||
__ add(r3, sp, Operand(r0, LSL, kPointerSizeLog2));
|
||||
__ str(r2, MemOperand(r3, -kPointerSize));
|
||||
|
||||
__ bind(&done);
|
||||
}
|
||||
|
||||
// 4. Shift stuff one slot down the stack
|
||||
// r0: actual number of arguments (including call() receiver)
|
||||
// r1: function
|
||||
{ Label loop;
|
||||
// Calculate the copy start address (destination). Copy end address is sp.
|
||||
__ add(r2, sp, Operand(r0, LSL, kPointerSizeLog2));
|
||||
|
||||
__ bind(&loop);
|
||||
__ ldr(ip, MemOperand(r2, -kPointerSize));
|
||||
__ str(ip, MemOperand(r2));
|
||||
__ sub(r2, r2, Operand(kPointerSize));
|
||||
__ cmp(r2, sp);
|
||||
__ b(ne, &loop);
|
||||
}
|
||||
|
||||
// 5. Adjust the actual number of arguments and remove the top element.
|
||||
// r0: actual number of arguments (including call() receiver)
|
||||
// r1: function
|
||||
__ sub(r0, r0, Operand(1));
|
||||
__ add(sp, sp, Operand(kPointerSize));
|
||||
|
||||
// 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.
|
||||
// r0: actual number of arguments
|
||||
// r1: function
|
||||
{ Label invoke;
|
||||
__ tst(r1, r1);
|
||||
__ b(ne, &invoke);
|
||||
// __ stop("Generate_ArgumentsAdaptorTrampoline - non-function call");
|
||||
__ mov(r2, Operand(0)); // expected arguments is 0 for CALL_NON_FUNCTION
|
||||
__ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION);
|
||||
__ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)), code_target);
|
||||
|
||||
__ bind(&invoke);
|
||||
__ ldr(r3, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ ldr(r2,
|
||||
FieldMemOperand(r3,
|
||||
SharedFunctionInfo::kFormalParameterCountOffset));
|
||||
__ ldr(r3,
|
||||
MemOperand(r3, SharedFunctionInfo::kCodeOffset - kHeapObjectTag));
|
||||
__ add(r3, r3, Operand(Code::kHeaderSize - kHeapObjectTag));
|
||||
__ cmp(r2, r0); // Check formal and actual parameter counts.
|
||||
__ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)), code_target, ne);
|
||||
|
||||
// 7. Jump to the code in r3 without checking arguments.
|
||||
ParameterCount expected(0);
|
||||
__ InvokeCode(r3, expected, expected, JUMP_FUNCTION);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
|
||||
const int kIndexOffset = -5 * kPointerSize;
|
||||
const int kLimitOffset = -4 * kPointerSize;
|
||||
@ -418,14 +566,13 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
|
||||
// -- r3 : code entry to call
|
||||
// -----------------------------------
|
||||
|
||||
Label entry, invoke, function_prototype_call;
|
||||
__ bind(&entry);
|
||||
Label invoke, dont_adapt_arguments;
|
||||
|
||||
Label enough, too_few;
|
||||
__ cmp(r0, Operand(r2));
|
||||
__ b(lt, &too_few);
|
||||
__ cmp(r2, Operand(-1));
|
||||
__ b(eq, &function_prototype_call);
|
||||
__ cmp(r2, Operand(SharedFunctionInfo::kDontAdaptArgumentsSentinel));
|
||||
__ b(eq, &dont_adapt_arguments);
|
||||
|
||||
{ // Enough parameters: actual >= excpected
|
||||
__ bind(&enough);
|
||||
@ -515,155 +662,10 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
|
||||
|
||||
|
||||
// -------------------------------------------
|
||||
// Function.prototype.call implementation.
|
||||
// Dont adapt arguments.
|
||||
// -------------------------------------------
|
||||
// r0: actual number of argument
|
||||
__ bind(&function_prototype_call);
|
||||
|
||||
// 1. Make sure we have at least one argument.
|
||||
// r0: actual number of argument
|
||||
{ Label done;
|
||||
__ tst(r0, Operand(r0));
|
||||
__ b(ne, &done);
|
||||
__ mov(r2, Operand(Factory::undefined_value()));
|
||||
__ push(r2);
|
||||
__ add(r0, r0, Operand(1));
|
||||
__ bind(&done);
|
||||
}
|
||||
|
||||
// 2. Get the function to call. Already in r1.
|
||||
// r0: actual number of argument
|
||||
{ Label done, non_function, function;
|
||||
__ ldr(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2));
|
||||
__ tst(r1, Operand(kSmiTagMask));
|
||||
__ b(eq, &non_function);
|
||||
__ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
|
||||
__ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset));
|
||||
__ cmp(r2, Operand(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);
|
||||
}
|
||||
|
||||
// 3. Make sure first argument is an object; convert if necessary.
|
||||
// r0: actual number of arguments
|
||||
// r1: function
|
||||
{ Label call_to_object, use_global_receiver, patch_receiver, done;
|
||||
__ 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);
|
||||
|
||||
__ mov(r3, Operand(Factory::null_value()));
|
||||
__ cmp(r2, r3);
|
||||
__ b(eq, &use_global_receiver);
|
||||
__ mov(r3, Operand(Factory::undefined_value()));
|
||||
__ cmp(r2, r3);
|
||||
__ b(eq, &use_global_receiver);
|
||||
|
||||
__ ldr(r3, FieldMemOperand(r2, HeapObject::kMapOffset));
|
||||
__ ldrb(r3, FieldMemOperand(r3, Map::kInstanceTypeOffset));
|
||||
__ cmp(r3, Operand(FIRST_JS_OBJECT_TYPE));
|
||||
__ b(lt, &call_to_object);
|
||||
__ cmp(r3, Operand(LAST_JS_OBJECT_TYPE));
|
||||
__ b(le, &done);
|
||||
|
||||
__ bind(&call_to_object);
|
||||
__ EnterInternalFrame();
|
||||
|
||||
// Store number of arguments and function across the call into the runtime.
|
||||
__ mov(r0, Operand(r0, LSL, kSmiTagSize));
|
||||
__ 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));
|
||||
|
||||
__ ExitInternalFrame();
|
||||
__ b(&patch_receiver);
|
||||
|
||||
// Use the global object from the called function as the receiver.
|
||||
__ bind(&use_global_receiver);
|
||||
const int kGlobalIndex =
|
||||
Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
|
||||
__ ldr(r2, FieldMemOperand(cp, kGlobalIndex));
|
||||
|
||||
__ bind(&patch_receiver);
|
||||
__ add(r3, sp, Operand(r0, LSL, kPointerSizeLog2));
|
||||
__ str(r2, MemOperand(r3, -kPointerSize));
|
||||
|
||||
__ bind(&done);
|
||||
}
|
||||
|
||||
// 4. Shift stuff one slot down the stack
|
||||
// r0: actual number of arguments (including call() receiver)
|
||||
// r1: function
|
||||
{ Label loop;
|
||||
// Calculate the copy start address (destination). Copy end address is sp.
|
||||
__ add(r2, sp, Operand(r0, LSL, kPointerSizeLog2));
|
||||
|
||||
__ bind(&loop);
|
||||
__ ldr(ip, MemOperand(r2, -kPointerSize));
|
||||
__ str(ip, MemOperand(r2));
|
||||
__ sub(r2, r2, Operand(kPointerSize));
|
||||
__ cmp(r2, sp);
|
||||
__ b(ne, &loop);
|
||||
}
|
||||
|
||||
// 5. Adjust the actual number of arguments and remove the top element.
|
||||
// r0: actual number of arguments (including call() receiver)
|
||||
// r1: function
|
||||
__ sub(r0, r0, Operand(1));
|
||||
__ add(sp, sp, Operand(kPointerSize));
|
||||
|
||||
// 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.
|
||||
// r0: actual number of arguments
|
||||
// r1: function
|
||||
{ Label invoke;
|
||||
__ tst(r1, r1);
|
||||
__ b(ne, &invoke);
|
||||
// __ stop("Generate_ArgumentsAdaptorTrampoline - non-function call");
|
||||
__ mov(r2, Operand(0)); // expected arguments is 0 for CALL_NON_FUNCTION
|
||||
__ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION);
|
||||
__ b(&enough);
|
||||
|
||||
__ bind(&invoke);
|
||||
__ ldr(r3, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ ldr(r2,
|
||||
FieldMemOperand(r3,
|
||||
SharedFunctionInfo::kFormalParameterCountOffset));
|
||||
__ ldr(r3,
|
||||
MemOperand(r3, SharedFunctionInfo::kCodeOffset - kHeapObjectTag));
|
||||
__ add(r3, r3, Operand(Code::kHeaderSize - kHeapObjectTag));
|
||||
__ cmp(r2, r0); // Check formal and actual parameter counts.
|
||||
__ b(ne, &entry);
|
||||
|
||||
// 7. Jump to the code in r3 without checking arguments.
|
||||
ParameterCount expected(0);
|
||||
__ InvokeCode(r3, expected, expected, JUMP_FUNCTION);
|
||||
}
|
||||
__ bind(&dont_adapt_arguments);
|
||||
__ mov(pc, r3);
|
||||
}
|
||||
|
||||
|
||||
|
@ -379,6 +379,135 @@ void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
|
||||
// 1. Make sure we have at least one argument.
|
||||
{ Label done;
|
||||
__ test(eax, Operand(eax));
|
||||
__ j(not_zero, &done, taken);
|
||||
__ pop(ebx);
|
||||
__ push(Immediate(Factory::undefined_value()));
|
||||
__ push(ebx);
|
||||
__ inc(eax);
|
||||
__ 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);
|
||||
__ mov(ecx, FieldOperand(edi, HeapObject::kMapOffset)); // get the map
|
||||
__ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
|
||||
__ cmp(ecx, JS_FUNCTION_TYPE);
|
||||
__ 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));
|
||||
|
||||
__ 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));
|
||||
|
||||
__ test(ebx, Immediate(kSmiTagMask));
|
||||
__ j(zero, &call_to_object);
|
||||
|
||||
__ cmp(ebx, Factory::null_value());
|
||||
__ j(equal, &use_global_receiver);
|
||||
__ cmp(ebx, Factory::undefined_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);
|
||||
__ cmp(ecx, LAST_JS_OBJECT_TYPE);
|
||||
__ j(less_equal, &done);
|
||||
|
||||
__ bind(&call_to_object);
|
||||
__ EnterInternalFrame(); // preserves eax, ebx, edi
|
||||
|
||||
// Store the arguments count on the stack (smi tagged).
|
||||
ASSERT(kSmiTag == 0);
|
||||
__ shl(eax, kSmiTagSize);
|
||||
__ push(eax);
|
||||
|
||||
__ push(edi); // save edi across the call
|
||||
__ push(ebx);
|
||||
__ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
|
||||
__ mov(Operand(ebx), eax);
|
||||
__ pop(edi); // restore edi after the call
|
||||
|
||||
// Get the arguments count and untag it.
|
||||
__ pop(eax);
|
||||
__ shr(eax, kSmiTagSize);
|
||||
|
||||
__ ExitInternalFrame();
|
||||
__ jmp(&patch_receiver);
|
||||
|
||||
// Use the global object from the called function as the receiver.
|
||||
__ bind(&use_global_receiver);
|
||||
const int kGlobalIndex =
|
||||
Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
|
||||
__ mov(ebx, FieldOperand(esi, kGlobalIndex));
|
||||
|
||||
__ bind(&patch_receiver);
|
||||
__ mov(Operand(esp, eax, times_4, 0), ebx);
|
||||
|
||||
__ bind(&done);
|
||||
}
|
||||
|
||||
// 4. Shift stuff one slot down the stack.
|
||||
{ Label loop;
|
||||
__ lea(ecx, Operand(eax, +1)); // +1 ~ copy receiver too
|
||||
__ bind(&loop);
|
||||
__ mov(ebx, Operand(esp, ecx, times_4, 0));
|
||||
__ mov(Operand(esp, ecx, times_4, kPointerSize), ebx);
|
||||
__ dec(ecx);
|
||||
__ j(not_zero, &loop);
|
||||
}
|
||||
|
||||
// 5. Remove TOS (copy of last arguments), but keep return address.
|
||||
__ pop(ebx);
|
||||
__ pop(ecx);
|
||||
__ push(ebx);
|
||||
__ dec(eax);
|
||||
|
||||
// 6. Check that function really was a function and get the code to
|
||||
// call from the function and check that the number of expected
|
||||
// arguments matches what we're providing.
|
||||
{ Label invoke;
|
||||
__ test(edi, Operand(edi));
|
||||
__ j(not_zero, &invoke, taken);
|
||||
__ xor_(ebx, Operand(ebx));
|
||||
__ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
|
||||
__ jmp(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)), code_target);
|
||||
|
||||
__ bind(&invoke);
|
||||
__ 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)));
|
||||
}
|
||||
|
||||
// 7. Jump (tail-call) to the code in register edx without checking arguments.
|
||||
ParameterCount expected(0);
|
||||
__ InvokeCode(Operand(edx), expected, expected, JUMP_FUNCTION);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
|
||||
__ EnterInternalFrame();
|
||||
|
||||
@ -530,15 +659,14 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
|
||||
// -- edx : code entry to call
|
||||
// -----------------------------------
|
||||
|
||||
Label entry, invoke, function_prototype_call;
|
||||
__ bind(&entry);
|
||||
Label invoke, dont_adapt_arguments;
|
||||
__ IncrementCounter(&Counters::arguments_adaptors, 1);
|
||||
|
||||
Label enough, too_few;
|
||||
__ cmp(eax, Operand(ebx));
|
||||
__ j(less, &too_few);
|
||||
__ cmp(ebx, -1);
|
||||
__ j(equal, &function_prototype_call);
|
||||
__ cmp(ebx, SharedFunctionInfo::kDontAdaptArgumentsSentinel);
|
||||
__ j(equal, &dont_adapt_arguments);
|
||||
|
||||
{ // Enough parameters: Actual >= expected.
|
||||
__ bind(&enough);
|
||||
@ -604,135 +732,10 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
|
||||
|
||||
|
||||
// -------------------------------------------
|
||||
// Function.prototype.call implementation.
|
||||
// Dont adapt arguments.
|
||||
// -------------------------------------------
|
||||
__ bind(&function_prototype_call);
|
||||
|
||||
// 1. Make sure we have at least one argument.
|
||||
{ Label done;
|
||||
__ test(eax, Operand(eax));
|
||||
__ j(not_zero, &done, taken);
|
||||
__ pop(ebx);
|
||||
__ push(Immediate(Factory::undefined_value()));
|
||||
__ push(ebx);
|
||||
__ inc(eax);
|
||||
__ 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);
|
||||
__ mov(ecx, FieldOperand(edi, HeapObject::kMapOffset)); // get the map
|
||||
__ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
|
||||
__ cmp(ecx, JS_FUNCTION_TYPE);
|
||||
__ 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));
|
||||
|
||||
__ 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));
|
||||
|
||||
__ test(ebx, Immediate(kSmiTagMask));
|
||||
__ j(zero, &call_to_object);
|
||||
|
||||
__ cmp(ebx, Factory::null_value());
|
||||
__ j(equal, &use_global_receiver);
|
||||
__ cmp(ebx, Factory::undefined_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);
|
||||
__ cmp(ecx, LAST_JS_OBJECT_TYPE);
|
||||
__ j(less_equal, &done);
|
||||
|
||||
__ bind(&call_to_object);
|
||||
__ EnterInternalFrame(); // preserves eax, ebx, edi
|
||||
|
||||
// Store the arguments count on the stack (smi tagged).
|
||||
ASSERT(kSmiTag == 0);
|
||||
__ shl(eax, kSmiTagSize);
|
||||
__ push(eax);
|
||||
|
||||
__ push(edi); // save edi across the call
|
||||
__ push(ebx);
|
||||
__ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
|
||||
__ mov(Operand(ebx), eax);
|
||||
__ pop(edi); // restore edi after the call
|
||||
|
||||
// Get the arguments count and untag it.
|
||||
__ pop(eax);
|
||||
__ shr(eax, kSmiTagSize);
|
||||
|
||||
__ ExitInternalFrame();
|
||||
__ jmp(&patch_receiver);
|
||||
|
||||
// Use the global object from the called function as the receiver.
|
||||
__ bind(&use_global_receiver);
|
||||
const int kGlobalIndex =
|
||||
Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
|
||||
__ mov(ebx, FieldOperand(esi, kGlobalIndex));
|
||||
|
||||
__ bind(&patch_receiver);
|
||||
__ mov(Operand(esp, eax, times_4, 0), ebx);
|
||||
|
||||
__ bind(&done);
|
||||
}
|
||||
|
||||
// 4. Shift stuff one slot down the stack.
|
||||
{ Label loop;
|
||||
__ lea(ecx, Operand(eax, +1)); // +1 ~ copy receiver too
|
||||
__ bind(&loop);
|
||||
__ mov(ebx, Operand(esp, ecx, times_4, 0));
|
||||
__ mov(Operand(esp, ecx, times_4, kPointerSize), ebx);
|
||||
__ dec(ecx);
|
||||
__ j(not_zero, &loop);
|
||||
}
|
||||
|
||||
// 5. Remove TOS (copy of last arguments), but keep return address.
|
||||
__ pop(ebx);
|
||||
__ pop(ecx);
|
||||
__ push(ebx);
|
||||
__ dec(eax);
|
||||
|
||||
// 6. Check that function really was a function and get the code to
|
||||
// call from the function and check that the number of expected
|
||||
// arguments matches what we're providing.
|
||||
{ Label invoke;
|
||||
__ test(edi, Operand(edi));
|
||||
__ j(not_zero, &invoke, taken);
|
||||
__ xor_(ebx, Operand(ebx));
|
||||
__ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
|
||||
__ jmp(&enough);
|
||||
|
||||
__ bind(&invoke);
|
||||
__ 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, &entry);
|
||||
}
|
||||
|
||||
// 7. Jump (tail-call) to the code in register edx without checking arguments.
|
||||
ParameterCount expected(0);
|
||||
__ InvokeCode(Operand(edx), expected, expected, JUMP_FUNCTION);
|
||||
__ bind(&dont_adapt_arguments);
|
||||
__ jmp(Operand(edx));
|
||||
}
|
||||
|
||||
|
||||
|
@ -28,11 +28,8 @@
|
||||
#ifndef V8_BUILTINS_H_
|
||||
#define V8_BUILTINS_H_
|
||||
|
||||
|
||||
|
||||
namespace v8 { namespace internal {
|
||||
|
||||
|
||||
// Define list of builtins implemented in C.
|
||||
#define BUILTIN_LIST_C(V) \
|
||||
V(Illegal, 0) \
|
||||
@ -90,6 +87,7 @@ namespace v8 { namespace internal {
|
||||
V(KeyedStoreIC_DebugBreak, KEYED_STORE_IC, DEBUG_BREAK) \
|
||||
\
|
||||
/* Uses KeyedLoadIC_Initialize; must be after in list. */ \
|
||||
V(FunctionCall, BUILTIN, UNINITIALIZED) \
|
||||
V(FunctionApply, BUILTIN, UNINITIALIZED)
|
||||
|
||||
|
||||
@ -219,6 +217,8 @@ class Builtins : public AllStatic {
|
||||
static void Generate_JSEntryTrampoline(MacroAssembler* masm);
|
||||
static void Generate_JSConstructEntryTrampoline(MacroAssembler* masm);
|
||||
static void Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm);
|
||||
|
||||
static void Generate_FunctionCall(MacroAssembler* masm);
|
||||
static void Generate_FunctionApply(MacroAssembler* masm);
|
||||
|
||||
static void Generate_LoadIC_DebugBreak(MacroAssembler* masm);
|
||||
@ -231,7 +231,6 @@ class Builtins : public AllStatic {
|
||||
static void Generate_StubNoRegisters_DebugBreak(MacroAssembler* masm);
|
||||
};
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_BUILTINS_H_
|
||||
|
@ -305,16 +305,6 @@ class ArmCodeGenerator: public CodeGenerator {
|
||||
void EnterJSFrame();
|
||||
void ExitJSFrame();
|
||||
|
||||
virtual void GenerateShiftDownAndTailCall(ZoneList<Expression*>* args);
|
||||
virtual void GenerateSetThisFunction(ZoneList<Expression*>* args);
|
||||
virtual void GenerateGetThisFunction(ZoneList<Expression*>* args);
|
||||
virtual void GenerateSetThis(ZoneList<Expression*>* args);
|
||||
virtual void GenerateGetArgumentsLength(ZoneList<Expression*>* args);
|
||||
virtual void GenerateSetArgumentsLength(ZoneList<Expression*>* args);
|
||||
virtual void GenerateTailCallWithArguments(ZoneList<Expression*>* args);
|
||||
virtual void GenerateSetArgument(ZoneList<Expression*>* args);
|
||||
virtual void GenerateSquashFrame(ZoneList<Expression*>* args);
|
||||
virtual void GenerateExpandFrame(ZoneList<Expression*>* args);
|
||||
virtual void GenerateIsSmi(ZoneList<Expression*>* args);
|
||||
virtual void GenerateIsNonNegativeSmi(ZoneList<Expression*>* args);
|
||||
virtual void GenerateIsArray(ZoneList<Expression*>* args);
|
||||
@ -3834,31 +3824,6 @@ void ArmCodeGenerator::VisitCallNew(CallNew* node) {
|
||||
}
|
||||
|
||||
|
||||
void ArmCodeGenerator::GenerateSetThisFunction(ZoneList<Expression*>* args) {
|
||||
__ stop("ArmCodeGenerator::GenerateSetThisFunction - unreachable");
|
||||
}
|
||||
|
||||
|
||||
void ArmCodeGenerator::GenerateGetThisFunction(ZoneList<Expression*>* args) {
|
||||
__ stop("ArmCodeGenerator::GenerateGetThisFunction - unreachable");
|
||||
}
|
||||
|
||||
|
||||
void ArmCodeGenerator::GenerateSetThis(ZoneList<Expression*>* args) {
|
||||
__ stop("ArmCodeGenerator::GenerateSetThis - unreachable");
|
||||
}
|
||||
|
||||
|
||||
void ArmCodeGenerator::GenerateSetArgumentsLength(ZoneList<Expression*>* args) {
|
||||
__ stop("ArmCodeGenerator::GenerateSetArgumentsLength - unreachable");
|
||||
}
|
||||
|
||||
|
||||
void ArmCodeGenerator::GenerateGetArgumentsLength(ZoneList<Expression*>* args) {
|
||||
__ stop("ArmCodeGenerator::GenerateGetArgumentsLength - unreachable");
|
||||
}
|
||||
|
||||
|
||||
void ArmCodeGenerator::GenerateValueOf(ZoneList<Expression*>* args) {
|
||||
ASSERT(args->length() == 1);
|
||||
Label leave;
|
||||
@ -3907,27 +3872,6 @@ void ArmCodeGenerator::GenerateSetValueOf(ZoneList<Expression*>* args) {
|
||||
}
|
||||
|
||||
|
||||
void ArmCodeGenerator::GenerateTailCallWithArguments(
|
||||
ZoneList<Expression*>* args) {
|
||||
__ stop("ArmCodeGenerator::GenerateTailCallWithArguments - unreachable");
|
||||
}
|
||||
|
||||
|
||||
void ArmCodeGenerator::GenerateSetArgument(ZoneList<Expression*>* args) {
|
||||
__ stop("ArmCodeGenerator::GenerateSetArgument - unreachable");
|
||||
}
|
||||
|
||||
|
||||
void ArmCodeGenerator::GenerateSquashFrame(ZoneList<Expression*>* args) {
|
||||
__ stop("ArmCodeGenerator::GenerateSquashFrame - unreachable");
|
||||
}
|
||||
|
||||
|
||||
void ArmCodeGenerator::GenerateExpandFrame(ZoneList<Expression*>* args) {
|
||||
__ stop("ArmCodeGenerator::GenerateExpandFrame - unreachable");
|
||||
}
|
||||
|
||||
|
||||
void ArmCodeGenerator::GenerateIsSmi(ZoneList<Expression*>* args) {
|
||||
ASSERT(args->length() == 1);
|
||||
Load(args->at(0));
|
||||
@ -4020,12 +3964,6 @@ void ArmCodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
|
||||
}
|
||||
|
||||
|
||||
void ArmCodeGenerator::GenerateShiftDownAndTailCall(
|
||||
ZoneList<Expression*>* args) {
|
||||
__ stop("ArmCodeGenerator::GenerateShiftDownAndTailCall - unreachable");
|
||||
}
|
||||
|
||||
|
||||
void ArmCodeGenerator::VisitCallRuntime(CallRuntime* node) {
|
||||
if (CheckForInlineRuntimeCall(node)) return;
|
||||
|
||||
|
@ -320,16 +320,6 @@ class Ia32CodeGenerator: public CodeGenerator {
|
||||
void EnterJSFrame();
|
||||
void ExitJSFrame();
|
||||
|
||||
virtual void GenerateShiftDownAndTailCall(ZoneList<Expression*>* args);
|
||||
virtual void GenerateSetThisFunction(ZoneList<Expression*>* args);
|
||||
virtual void GenerateGetThisFunction(ZoneList<Expression*>* args);
|
||||
virtual void GenerateSetThis(ZoneList<Expression*>* args);
|
||||
virtual void GenerateGetArgumentsLength(ZoneList<Expression*>* args);
|
||||
virtual void GenerateSetArgumentsLength(ZoneList<Expression*>* args);
|
||||
virtual void GenerateTailCallWithArguments(ZoneList<Expression*>* args);
|
||||
virtual void GenerateSetArgument(ZoneList<Expression*>* args);
|
||||
virtual void GenerateSquashFrame(ZoneList<Expression*>* args);
|
||||
virtual void GenerateExpandFrame(ZoneList<Expression*>* args);
|
||||
virtual void GenerateIsSmi(ZoneList<Expression*>* args);
|
||||
virtual void GenerateIsNonNegativeSmi(ZoneList<Expression*>* args);
|
||||
virtual void GenerateIsArray(ZoneList<Expression*>* args);
|
||||
@ -4035,70 +4025,6 @@ void Ia32CodeGenerator::VisitCallNew(CallNew* node) {
|
||||
}
|
||||
|
||||
|
||||
void Ia32CodeGenerator::GenerateSetThisFunction(ZoneList<Expression*>* args) {
|
||||
// Not used on IA-32 anymore. Should go away soon.
|
||||
__ int3();
|
||||
}
|
||||
|
||||
|
||||
void Ia32CodeGenerator::GenerateGetThisFunction(ZoneList<Expression*>* args) {
|
||||
// Not used on IA-32 anymore. Should go away soon.
|
||||
__ int3();
|
||||
}
|
||||
|
||||
|
||||
void Ia32CodeGenerator::GenerateSetThis(ZoneList<Expression*>* args) {
|
||||
// Not used on IA-32 anymore. Should go away soon.
|
||||
__ int3();
|
||||
}
|
||||
|
||||
|
||||
void Ia32CodeGenerator::GenerateSetArgumentsLength(
|
||||
ZoneList<Expression*>* args) {
|
||||
// Not used on IA-32 anymore. Should go away soon.
|
||||
__ int3();
|
||||
}
|
||||
|
||||
|
||||
void Ia32CodeGenerator::GenerateGetArgumentsLength(
|
||||
ZoneList<Expression*>* args) {
|
||||
// Not used on IA-32 anymore. Should go away soon.
|
||||
__ int3();
|
||||
}
|
||||
|
||||
|
||||
void Ia32CodeGenerator::GenerateTailCallWithArguments(
|
||||
ZoneList<Expression*>* args) {
|
||||
// Not used on IA-32 anymore. Should go away soon.
|
||||
__ int3();
|
||||
}
|
||||
|
||||
|
||||
void Ia32CodeGenerator::GenerateSetArgument(ZoneList<Expression*>* args) {
|
||||
// Not used on IA-32 anymore. Should go away soon.
|
||||
__ int3();
|
||||
}
|
||||
|
||||
|
||||
void Ia32CodeGenerator::GenerateSquashFrame(ZoneList<Expression*>* args) {
|
||||
// Not used on IA-32 anymore. Should go away soon.
|
||||
__ int3();
|
||||
}
|
||||
|
||||
|
||||
void Ia32CodeGenerator::GenerateExpandFrame(ZoneList<Expression*>* args) {
|
||||
// Not used on IA-32 anymore. Should go away soon.
|
||||
__ int3();
|
||||
}
|
||||
|
||||
|
||||
void Ia32CodeGenerator::GenerateShiftDownAndTailCall(
|
||||
ZoneList<Expression*>* args) {
|
||||
// Not used on IA-32 anymore. Should go away soon.
|
||||
__ int3();
|
||||
}
|
||||
|
||||
|
||||
void Ia32CodeGenerator::GenerateIsSmi(ZoneList<Expression*>* args) {
|
||||
ASSERT(args->length() == 1);
|
||||
Load(args->at(0));
|
||||
|
@ -216,26 +216,6 @@ bool CodeGenerator::CheckForInlineRuntimeCall(CallRuntime* node) {
|
||||
// for implementing Function.prototype.call() and
|
||||
// Function.prototype.apply().
|
||||
static const InlineRuntimeLUT kInlineRuntimeLUT[] = {
|
||||
{&v8::internal::CodeGenerator::GenerateShiftDownAndTailCall,
|
||||
"_ShiftDownAndTailCall"},
|
||||
{&v8::internal::CodeGenerator::GenerateSetThisFunction,
|
||||
"_SetThisFunction"},
|
||||
{&v8::internal::CodeGenerator::GenerateGetThisFunction,
|
||||
"_GetThisFunction"},
|
||||
{&v8::internal::CodeGenerator::GenerateSetThis,
|
||||
"_SetThis"},
|
||||
{&v8::internal::CodeGenerator::GenerateGetArgumentsLength,
|
||||
"_GetArgumentsLength"},
|
||||
{&v8::internal::CodeGenerator::GenerateSetArgumentsLength,
|
||||
"_SetArgumentsLength"},
|
||||
{&v8::internal::CodeGenerator::GenerateTailCallWithArguments,
|
||||
"_TailCallWithArguments"},
|
||||
{&v8::internal::CodeGenerator::GenerateSetArgument,
|
||||
"_SetArgument"},
|
||||
{&v8::internal::CodeGenerator::GenerateSquashFrame,
|
||||
"_SquashFrame"},
|
||||
{&v8::internal::CodeGenerator::GenerateExpandFrame,
|
||||
"_ExpandFrame"},
|
||||
{&v8::internal::CodeGenerator::GenerateIsSmi,
|
||||
"_IsSmi"},
|
||||
{&v8::internal::CodeGenerator::GenerateIsNonNegativeSmi,
|
||||
|
@ -147,16 +147,7 @@ class CodeGenerator: public Visitor {
|
||||
// name/value pairs.
|
||||
virtual void DeclareGlobals(Handle<FixedArray> pairs) = 0;
|
||||
|
||||
virtual void GenerateShiftDownAndTailCall(ZoneList<Expression*>* args) = 0;
|
||||
virtual void GenerateSetThisFunction(ZoneList<Expression*>* args) = 0;
|
||||
virtual void GenerateGetThisFunction(ZoneList<Expression*>* args) = 0;
|
||||
virtual void GenerateSetThis(ZoneList<Expression*>* args) = 0;
|
||||
virtual void GenerateGetArgumentsLength(ZoneList<Expression*>* args) = 0;
|
||||
virtual void GenerateSetArgumentsLength(ZoneList<Expression*>* args) = 0;
|
||||
virtual void GenerateTailCallWithArguments(ZoneList<Expression*>* args) = 0;
|
||||
virtual void GenerateSetArgument(ZoneList<Expression*>* args) = 0;
|
||||
virtual void GenerateSquashFrame(ZoneList<Expression*>* args) = 0;
|
||||
virtual void GenerateExpandFrame(ZoneList<Expression*>* args) = 0;
|
||||
// Support for type checks.
|
||||
virtual void GenerateIsSmi(ZoneList<Expression*>* args) = 0;
|
||||
virtual void GenerateIsNonNegativeSmi(ZoneList<Expression*>* args) = 0;
|
||||
virtual void GenerateIsArray(ZoneList<Expression*>* args) = 0;
|
||||
|
@ -1782,6 +1782,11 @@ INT_ACCESSORS(SharedFunctionInfo, function_token_position,
|
||||
kFunctionTokenPositionOffset)
|
||||
|
||||
|
||||
void SharedFunctionInfo::DontAdaptArguments() {
|
||||
set_formal_parameter_count(kDontAdaptArgumentsSentinel);
|
||||
}
|
||||
|
||||
|
||||
int SharedFunctionInfo::start_position() {
|
||||
return start_position_and_type() >> kStartPositionShift;
|
||||
}
|
||||
|
@ -2464,6 +2464,10 @@ class SharedFunctionInfo: public HeapObject {
|
||||
inline int formal_parameter_count();
|
||||
inline void set_formal_parameter_count(int value);
|
||||
|
||||
// Set the formal parameter count so the function code will be
|
||||
// called without using argument adaptor frames.
|
||||
inline void DontAdaptArguments();
|
||||
|
||||
// [expected_nof_properties]: Expected number of properties for the function.
|
||||
inline int expected_nof_properties();
|
||||
inline void set_expected_nof_properties(int value);
|
||||
@ -2533,6 +2537,9 @@ class SharedFunctionInfo: public HeapObject {
|
||||
// Casting.
|
||||
static inline SharedFunctionInfo* cast(Object* obj);
|
||||
|
||||
// Constants.
|
||||
static const int kDontAdaptArgumentsSentinel = -1;
|
||||
|
||||
// Layout description.
|
||||
static const int kNameOffset = HeapObject::kSize;
|
||||
static const int kCodeOffset = kNameOffset + kPointerSize;
|
||||
|
104
src/v8natives.js
104
src/v8natives.js
@ -414,107 +414,3 @@ function NewFunction(arg1) { // length == 1
|
||||
};
|
||||
|
||||
%SetCode($Function, NewFunction);
|
||||
|
||||
|
||||
// NOTE: The following functions (call and apply) are only used in this
|
||||
// form on the ARM platform. On IA-32 they are handled through specialized
|
||||
// builtins; see builtins-ia32.cc.
|
||||
|
||||
%AddProperty($Function.prototype, "call", function(receiver) {
|
||||
// Make sure the receiver of this call is a function. If it isn't
|
||||
// we "fake" a call of it (without the right arguments) to force
|
||||
// an exception to be thrown.
|
||||
if (!IS_FUNCTION(this)) this();
|
||||
|
||||
// If receiver is null or undefined set the receiver to the global
|
||||
// object. If the receiver isn't an object, we convert the
|
||||
// receiver to an object.
|
||||
if (receiver == null) receiver = global;
|
||||
else if (!IS_OBJECT(receiver)) receiver = ToObject(receiver);
|
||||
|
||||
%_SetThisFunction(this);
|
||||
%_SetThis(receiver);
|
||||
|
||||
var len = %_GetArgumentsLength(1);
|
||||
return %_ShiftDownAndTailCall(len ? len - 1 : 0);
|
||||
}, DONT_ENUM);
|
||||
|
||||
|
||||
// This implementation of Function.prototype.apply replaces the stack frame
|
||||
// of the apply call with the new stack frame containing the arguments from
|
||||
// the args array.
|
||||
%AddProperty($Function.prototype, "apply", function(receiver, args) {
|
||||
var length = (args == null) ? 0 : ToUint32(args.length);
|
||||
|
||||
// We can handle any number of apply arguments if the stack is
|
||||
// big enough, but sanity check the value to avoid overflow when
|
||||
// multiplying with pointer size.
|
||||
if (length > 0x800000) {
|
||||
throw new $RangeError(
|
||||
"Function.prototype.apply cannot support " + length + " arguments.");
|
||||
}
|
||||
|
||||
if (!IS_FUNCTION(this)) {
|
||||
throw new $TypeError('Function.prototype.apply was called on ' + this.toString() + ', which is a ' + (typeof this) + ' and not a function');
|
||||
}
|
||||
|
||||
// Make sure args has the right type.
|
||||
if (args != null && %ClassOf(args) !== 'Array' && %ClassOf(args) !== 'Arguments') {
|
||||
throw new $TypeError('Function.prototype.apply: args has wrong type');
|
||||
}
|
||||
|
||||
// If receiver is null or undefined set the receiver to the global
|
||||
// object. If the receiver isn't an object, we convert the
|
||||
// receiver to an object.
|
||||
if (receiver == null) receiver = global;
|
||||
else if (!IS_OBJECT(receiver)) receiver = ToObject(receiver);
|
||||
|
||||
%_SetThisFunction(this);
|
||||
%_SetThis(receiver);
|
||||
|
||||
var arguments_length = %_GetArgumentsLength(2);
|
||||
|
||||
// This method has 2 formal arguments so if less are passed, then space has
|
||||
// been made.
|
||||
if (arguments_length < 2)
|
||||
arguments_length = 2;
|
||||
|
||||
// Move some stuff to locals so they don't get overwritten when we start
|
||||
// expanding the args array.
|
||||
var saved_args = args;
|
||||
|
||||
if (arguments_length > length) {
|
||||
// We have too many arguments - we need to squash the frame.
|
||||
%_SquashFrame(arguments_length, length);
|
||||
} else if (arguments_length != length) {
|
||||
// We have too few spaces for arguments - we need to expand the frame.
|
||||
if (!%_ExpandFrame(arguments_length, length)) {
|
||||
throw new $RangeError(
|
||||
"Function.prototype.apply cannot find stack space for " + length + " arguments.");
|
||||
}
|
||||
// GC doesn't like junk in the arguments!
|
||||
for (var i = 0; i < length; i++) {
|
||||
%_SetArgument(i, 0, length);
|
||||
}
|
||||
}
|
||||
|
||||
// Update-number-of-arguments field to keep things looking consistent for
|
||||
// stack traces, and uses of arguments or arguments.length.
|
||||
%_SetArgumentsLength(length);
|
||||
|
||||
// NOTE: For the fast case this should be implemented in assembler,
|
||||
// which would allow us to omit bounds and class checks galore. The
|
||||
// assembler version could fall back to this implementation if
|
||||
// tricky stuff is found, like arrays implemented as dictionaries or
|
||||
// holes in arrays.
|
||||
for (var i = 0; i < length; i++) {
|
||||
%_SetArgument(i, saved_args[i], length);
|
||||
}
|
||||
|
||||
// Replaces the current frame with the new call. This has the added effect
|
||||
// of removing apply from the stack trace entirely, which matches the
|
||||
// behaviour of Firefox.
|
||||
return %_TailCallWithArguments(length);
|
||||
}, DONT_ENUM);
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user