Allow functions to have custom construct stubs that are called
when the function is instantiated. Review URL: http://codereview.chromium.org/132063 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2222 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
6b03961d4c
commit
5f5fbd4e4a
@ -67,6 +67,24 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
|
|||||||
__ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
|
__ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
|
||||||
__ b(ne, &non_function_call);
|
__ b(ne, &non_function_call);
|
||||||
|
|
||||||
|
// Jump to the function-specific construct stub.
|
||||||
|
__ ldr(r2, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
|
||||||
|
__ ldr(r2, FieldMemOperand(r2, SharedFunctionInfo::kConstructStubOffset));
|
||||||
|
__ add(pc, r2, Operand(Code::kHeaderSize - kHeapObjectTag));
|
||||||
|
|
||||||
|
// r0: number of arguments
|
||||||
|
// r1: called object
|
||||||
|
__ bind(&non_function_call);
|
||||||
|
|
||||||
|
// Set expected number of arguments to zero (not changing r0).
|
||||||
|
__ mov(r2, Operand(0));
|
||||||
|
__ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
|
||||||
|
__ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
|
||||||
|
RelocInfo::CODE_TARGET);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
|
||||||
// Enter a construct frame.
|
// Enter a construct frame.
|
||||||
__ EnterConstructFrame();
|
__ EnterConstructFrame();
|
||||||
|
|
||||||
@ -177,16 +195,6 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
|
|||||||
__ add(sp, sp, Operand(r1, LSL, kPointerSizeLog2 - 1));
|
__ add(sp, sp, Operand(r1, LSL, kPointerSizeLog2 - 1));
|
||||||
__ add(sp, sp, Operand(kPointerSize));
|
__ add(sp, sp, Operand(kPointerSize));
|
||||||
__ Jump(lr);
|
__ Jump(lr);
|
||||||
|
|
||||||
// r0: number of arguments
|
|
||||||
// r1: called object
|
|
||||||
__ bind(&non_function_call);
|
|
||||||
|
|
||||||
// Set expected number of arguments to zero (not changing r0).
|
|
||||||
__ mov(r2, Operand(0));
|
|
||||||
__ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
|
|
||||||
__ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
|
|
||||||
RelocInfo::CODE_TARGET);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -51,6 +51,7 @@ namespace internal {
|
|||||||
#define BUILTIN_LIST_A(V) \
|
#define BUILTIN_LIST_A(V) \
|
||||||
V(ArgumentsAdaptorTrampoline, BUILTIN, UNINITIALIZED) \
|
V(ArgumentsAdaptorTrampoline, BUILTIN, UNINITIALIZED) \
|
||||||
V(JSConstructCall, BUILTIN, UNINITIALIZED) \
|
V(JSConstructCall, BUILTIN, UNINITIALIZED) \
|
||||||
|
V(JSConstructStubGeneric, BUILTIN, UNINITIALIZED) \
|
||||||
V(JSEntryTrampoline, BUILTIN, UNINITIALIZED) \
|
V(JSEntryTrampoline, BUILTIN, UNINITIALIZED) \
|
||||||
V(JSConstructEntryTrampoline, BUILTIN, UNINITIALIZED) \
|
V(JSConstructEntryTrampoline, BUILTIN, UNINITIALIZED) \
|
||||||
\
|
\
|
||||||
@ -210,6 +211,7 @@ class Builtins : public AllStatic {
|
|||||||
|
|
||||||
static void Generate_Adaptor(MacroAssembler* masm, CFunctionId id);
|
static void Generate_Adaptor(MacroAssembler* masm, CFunctionId id);
|
||||||
static void Generate_JSConstructCall(MacroAssembler* masm);
|
static void Generate_JSConstructCall(MacroAssembler* masm);
|
||||||
|
static void Generate_JSConstructStubGeneric(MacroAssembler* masm);
|
||||||
static void Generate_JSEntryTrampoline(MacroAssembler* masm);
|
static void Generate_JSEntryTrampoline(MacroAssembler* masm);
|
||||||
static void Generate_JSConstructEntryTrampoline(MacroAssembler* masm);
|
static void Generate_JSConstructEntryTrampoline(MacroAssembler* masm);
|
||||||
static void Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm);
|
static void Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm);
|
||||||
|
@ -1510,6 +1510,8 @@ Object* Heap::AllocateSharedFunctionInfo(Object* name) {
|
|||||||
share->set_name(name);
|
share->set_name(name);
|
||||||
Code* illegal = Builtins::builtin(Builtins::Illegal);
|
Code* illegal = Builtins::builtin(Builtins::Illegal);
|
||||||
share->set_code(illegal);
|
share->set_code(illegal);
|
||||||
|
Code* construct_stub = Builtins::builtin(Builtins::JSConstructStubGeneric);
|
||||||
|
share->set_construct_stub(construct_stub);
|
||||||
share->set_expected_nof_properties(0);
|
share->set_expected_nof_properties(0);
|
||||||
share->set_length(0);
|
share->set_length(0);
|
||||||
share->set_formal_parameter_count(0);
|
share->set_formal_parameter_count(0);
|
||||||
|
@ -63,6 +63,25 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
|
|||||||
__ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
|
__ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
|
||||||
__ j(not_equal, &non_function_call);
|
__ j(not_equal, &non_function_call);
|
||||||
|
|
||||||
|
// Jump to the function-specific construct stub.
|
||||||
|
__ mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
|
||||||
|
__ mov(ebx, FieldOperand(ebx, SharedFunctionInfo::kConstructStubOffset));
|
||||||
|
__ lea(ebx, FieldOperand(ebx, Code::kHeaderSize));
|
||||||
|
__ jmp(Operand(ebx));
|
||||||
|
|
||||||
|
// edi: called object
|
||||||
|
// eax: number of arguments
|
||||||
|
__ bind(&non_function_call);
|
||||||
|
|
||||||
|
// Set expected number of arguments to zero (not changing eax).
|
||||||
|
__ Set(ebx, Immediate(0));
|
||||||
|
__ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
|
||||||
|
__ jmp(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
|
||||||
|
RelocInfo::CODE_TARGET);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
|
||||||
// Enter a construct frame.
|
// Enter a construct frame.
|
||||||
__ EnterConstructFrame();
|
__ EnterConstructFrame();
|
||||||
|
|
||||||
@ -305,16 +324,6 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
|
|||||||
__ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); // 1 ~ receiver
|
__ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); // 1 ~ receiver
|
||||||
__ push(ecx);
|
__ push(ecx);
|
||||||
__ ret(0);
|
__ ret(0);
|
||||||
|
|
||||||
// edi: called object
|
|
||||||
// eax: number of arguments
|
|
||||||
__ bind(&non_function_call);
|
|
||||||
|
|
||||||
// Set expected number of arguments to zero (not changing eax).
|
|
||||||
__ Set(ebx, Immediate(0));
|
|
||||||
__ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
|
|
||||||
__ jmp(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
|
|
||||||
RelocInfo::CODE_TARGET);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2130,6 +2130,7 @@ ACCESSORS(BreakPointInfo, statement_position, Smi, kStatementPositionIndex)
|
|||||||
ACCESSORS(BreakPointInfo, break_point_objects, Object, kBreakPointObjectsIndex)
|
ACCESSORS(BreakPointInfo, break_point_objects, Object, kBreakPointObjectsIndex)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
ACCESSORS(SharedFunctionInfo, construct_stub, Code, kConstructStubOffset)
|
||||||
ACCESSORS(SharedFunctionInfo, name, Object, kNameOffset)
|
ACCESSORS(SharedFunctionInfo, name, Object, kNameOffset)
|
||||||
ACCESSORS(SharedFunctionInfo, instance_class_name, Object,
|
ACCESSORS(SharedFunctionInfo, instance_class_name, Object,
|
||||||
kInstanceClassNameOffset)
|
kInstanceClassNameOffset)
|
||||||
|
@ -4632,7 +4632,7 @@ void SharedFunctionInfo::SourceCodePrint(StringStream* accumulator,
|
|||||||
|
|
||||||
|
|
||||||
void SharedFunctionInfo::SharedFunctionInfoIterateBody(ObjectVisitor* v) {
|
void SharedFunctionInfo::SharedFunctionInfoIterateBody(ObjectVisitor* v) {
|
||||||
IteratePointers(v, kNameOffset, kCodeOffset + kPointerSize);
|
IteratePointers(v, kNameOffset, kConstructStubOffset + kPointerSize);
|
||||||
IteratePointers(v, kInstanceClassNameOffset, kScriptOffset + kPointerSize);
|
IteratePointers(v, kInstanceClassNameOffset, kScriptOffset + kPointerSize);
|
||||||
IteratePointers(v, kDebugInfoOffset, kInferredNameOffset + kPointerSize);
|
IteratePointers(v, kDebugInfoOffset, kInferredNameOffset + kPointerSize);
|
||||||
}
|
}
|
||||||
|
@ -2743,6 +2743,9 @@ class SharedFunctionInfo: public HeapObject {
|
|||||||
// [code]: Function code.
|
// [code]: Function code.
|
||||||
DECL_ACCESSORS(code, Code)
|
DECL_ACCESSORS(code, Code)
|
||||||
|
|
||||||
|
// [construct stub]: Code stub for constructing instances of this function.
|
||||||
|
DECL_ACCESSORS(construct_stub, Code)
|
||||||
|
|
||||||
// Returns if this function has been compiled to native code yet.
|
// Returns if this function has been compiled to native code yet.
|
||||||
inline bool is_compiled();
|
inline bool is_compiled();
|
||||||
|
|
||||||
@ -2838,7 +2841,8 @@ class SharedFunctionInfo: public HeapObject {
|
|||||||
// (An even number of integers has a size that is a multiple of a pointer.)
|
// (An even number of integers has a size that is a multiple of a pointer.)
|
||||||
static const int kNameOffset = HeapObject::kHeaderSize;
|
static const int kNameOffset = HeapObject::kHeaderSize;
|
||||||
static const int kCodeOffset = kNameOffset + kPointerSize;
|
static const int kCodeOffset = kNameOffset + kPointerSize;
|
||||||
static const int kLengthOffset = kCodeOffset + kPointerSize;
|
static const int kConstructStubOffset = kCodeOffset + kPointerSize;
|
||||||
|
static const int kLengthOffset = kConstructStubOffset + kPointerSize;
|
||||||
static const int kFormalParameterCountOffset = kLengthOffset + kIntSize;
|
static const int kFormalParameterCountOffset = kLengthOffset + kIntSize;
|
||||||
static const int kExpectedNofPropertiesOffset =
|
static const int kExpectedNofPropertiesOffset =
|
||||||
kFormalParameterCountOffset + kIntSize;
|
kFormalParameterCountOffset + kIntSize;
|
||||||
|
@ -4328,45 +4328,61 @@ static Object* Runtime_NewClosure(Arguments args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Handle<Code> ComputeConstructStub(Handle<Map> map) {
|
||||||
|
// TODO(385): Change this to create a construct stub specialized for
|
||||||
|
// the given map to make allocation of simple objects - and maybe
|
||||||
|
// arrays - much faster.
|
||||||
|
return Handle<Code>(Builtins::builtin(Builtins::JSConstructStubGeneric));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static Object* Runtime_NewObject(Arguments args) {
|
static Object* Runtime_NewObject(Arguments args) {
|
||||||
NoHandleAllocation ha;
|
HandleScope scope;
|
||||||
ASSERT(args.length() == 1);
|
ASSERT(args.length() == 1);
|
||||||
|
|
||||||
Object* constructor = args[0];
|
Handle<Object> constructor = args.at<Object>(0);
|
||||||
if (constructor->IsJSFunction()) {
|
|
||||||
JSFunction* function = JSFunction::cast(constructor);
|
|
||||||
|
|
||||||
// Handle stepping into constructors if step into is active.
|
// If the constructor isn't a proper function we throw a type error.
|
||||||
|
if (!constructor->IsJSFunction()) {
|
||||||
|
Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
|
||||||
|
Handle<Object> type_error =
|
||||||
|
Factory::NewTypeError("not_constructor", arguments);
|
||||||
|
return Top::Throw(*type_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
|
||||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||||
if (Debug::StepInActive()) {
|
// Handle stepping into constructors if step into is active.
|
||||||
HandleScope scope;
|
if (Debug::StepInActive()) {
|
||||||
Debug::HandleStepIn(Handle<JSFunction>(function), 0, true);
|
Debug::HandleStepIn(function, 0, true);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (function->has_initial_map() &&
|
if (function->has_initial_map()) {
|
||||||
function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
|
if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
|
||||||
// The 'Function' function ignores the receiver object when
|
// The 'Function' function ignores the receiver object when
|
||||||
// called using 'new' and creates a new JSFunction object that
|
// called using 'new' and creates a new JSFunction object that
|
||||||
// is returned. The receiver object is only used for error
|
// is returned. The receiver object is only used for error
|
||||||
// reporting if an error occurs when constructing the new
|
// reporting if an error occurs when constructing the new
|
||||||
// JSFunction. AllocateJSObject should not be used to allocate
|
// JSFunction. Factory::NewJSObject() should not be used to
|
||||||
// JSFunctions since it does not properly initialize the shared
|
// allocate JSFunctions since it does not properly initialize
|
||||||
// part of the function. Since the receiver is ignored anyway,
|
// the shared part of the function. Since the receiver is
|
||||||
// we use the global object as the receiver instead of a new
|
// ignored anyway, we use the global object as the receiver
|
||||||
// JSFunction object. This way, errors are reported the same
|
// instead of a new JSFunction object. This way, errors are
|
||||||
// way whether or not 'Function' is called using 'new'.
|
// reported the same way whether or not 'Function' is called
|
||||||
|
// using 'new'.
|
||||||
return Top::context()->global();
|
return Top::context()->global();
|
||||||
}
|
}
|
||||||
return Heap::AllocateJSObject(function);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HandleScope scope;
|
bool first_allocation = !function->has_initial_map();
|
||||||
Handle<Object> cons(constructor);
|
Handle<JSObject> result = Factory::NewJSObject(function);
|
||||||
// The constructor is not a function; throw a type error.
|
if (first_allocation) {
|
||||||
Handle<Object> type_error =
|
Handle<Map> map = Handle<Map>(function->initial_map());
|
||||||
Factory::NewTypeError("not_constructor", HandleVector(&cons, 1));
|
Handle<Code> stub = ComputeConstructStub(map);
|
||||||
return Top::Throw(*type_error);
|
function->shared()->set_construct_stub(*stub);
|
||||||
|
}
|
||||||
|
return *result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -171,6 +171,10 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
|
|||||||
masm->int3(); // UNIMPLEMENTED.
|
masm->int3(); // UNIMPLEMENTED.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
|
||||||
|
masm->int3(); // UNIMPLEMENTED.
|
||||||
|
}
|
||||||
|
|
||||||
static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
|
static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
|
||||||
bool is_construct) {
|
bool is_construct) {
|
||||||
// Expects five C++ function parameters.
|
// Expects five C++ function parameters.
|
||||||
|
Loading…
Reference in New Issue
Block a user