From 5f5fbd4e4a0bad7f9cccba3b04ea2efdca082111 Mon Sep 17 00:00:00 2001 From: "kasperl@chromium.org" Date: Fri, 19 Jun 2009 07:36:16 +0000 Subject: [PATCH] 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 --- src/arm/builtins-arm.cc | 28 +++++++++++------ src/builtins.h | 2 ++ src/heap.cc | 2 ++ src/ia32/builtins-ia32.cc | 29 ++++++++++++------ src/objects-inl.h | 1 + src/objects.cc | 2 +- src/objects.h | 6 +++- src/runtime.cc | 64 ++++++++++++++++++++++++--------------- src/x64/builtins-x64.cc | 4 +++ 9 files changed, 92 insertions(+), 46 deletions(-) diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc index 6d23a19bb2..b5332ece4c 100644 --- a/src/arm/builtins-arm.cc +++ b/src/arm/builtins-arm.cc @@ -67,6 +67,24 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE); __ 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(builtin(ArgumentsAdaptorTrampoline)), + RelocInfo::CODE_TARGET); +} + + +void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { // Enter a construct frame. __ EnterConstructFrame(); @@ -177,16 +195,6 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { __ add(sp, sp, Operand(r1, LSL, kPointerSizeLog2 - 1)); __ add(sp, sp, Operand(kPointerSize)); __ 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(builtin(ArgumentsAdaptorTrampoline)), - RelocInfo::CODE_TARGET); } diff --git a/src/builtins.h b/src/builtins.h index 6e0f832565..0f4a610b83 100644 --- a/src/builtins.h +++ b/src/builtins.h @@ -51,6 +51,7 @@ namespace internal { #define BUILTIN_LIST_A(V) \ V(ArgumentsAdaptorTrampoline, BUILTIN, UNINITIALIZED) \ V(JSConstructCall, BUILTIN, UNINITIALIZED) \ + V(JSConstructStubGeneric, BUILTIN, UNINITIALIZED) \ V(JSEntryTrampoline, 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_JSConstructCall(MacroAssembler* masm); + static void Generate_JSConstructStubGeneric(MacroAssembler* masm); static void Generate_JSEntryTrampoline(MacroAssembler* masm); static void Generate_JSConstructEntryTrampoline(MacroAssembler* masm); static void Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm); diff --git a/src/heap.cc b/src/heap.cc index 19434f8f90..caf1274932 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -1510,6 +1510,8 @@ Object* Heap::AllocateSharedFunctionInfo(Object* name) { share->set_name(name); Code* illegal = Builtins::builtin(Builtins::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_length(0); share->set_formal_parameter_count(0); diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc index 6230f2c953..3cafd904b5 100644 --- a/src/ia32/builtins-ia32.cc +++ b/src/ia32/builtins-ia32.cc @@ -63,6 +63,25 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); __ 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(builtin(ArgumentsAdaptorTrampoline)), + RelocInfo::CODE_TARGET); +} + + +void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { // Enter a construct frame. __ EnterConstructFrame(); @@ -305,16 +324,6 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { __ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); // 1 ~ receiver __ push(ecx); __ 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(builtin(ArgumentsAdaptorTrampoline)), - RelocInfo::CODE_TARGET); } diff --git a/src/objects-inl.h b/src/objects-inl.h index 8ed114c3b1..2b996529ea 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -2130,6 +2130,7 @@ ACCESSORS(BreakPointInfo, statement_position, Smi, kStatementPositionIndex) ACCESSORS(BreakPointInfo, break_point_objects, Object, kBreakPointObjectsIndex) #endif +ACCESSORS(SharedFunctionInfo, construct_stub, Code, kConstructStubOffset) ACCESSORS(SharedFunctionInfo, name, Object, kNameOffset) ACCESSORS(SharedFunctionInfo, instance_class_name, Object, kInstanceClassNameOffset) diff --git a/src/objects.cc b/src/objects.cc index 341c70ddfb..c69dc376cb 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -4632,7 +4632,7 @@ void SharedFunctionInfo::SourceCodePrint(StringStream* accumulator, void SharedFunctionInfo::SharedFunctionInfoIterateBody(ObjectVisitor* v) { - IteratePointers(v, kNameOffset, kCodeOffset + kPointerSize); + IteratePointers(v, kNameOffset, kConstructStubOffset + kPointerSize); IteratePointers(v, kInstanceClassNameOffset, kScriptOffset + kPointerSize); IteratePointers(v, kDebugInfoOffset, kInferredNameOffset + kPointerSize); } diff --git a/src/objects.h b/src/objects.h index 16c100c6b4..8a0099fd87 100644 --- a/src/objects.h +++ b/src/objects.h @@ -2743,6 +2743,9 @@ class SharedFunctionInfo: public HeapObject { // [code]: Function 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. 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.) static const int kNameOffset = HeapObject::kHeaderSize; 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 kExpectedNofPropertiesOffset = kFormalParameterCountOffset + kIntSize; diff --git a/src/runtime.cc b/src/runtime.cc index f6b604ab2a..807abab9c2 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -4328,45 +4328,61 @@ static Object* Runtime_NewClosure(Arguments args) { } +static Handle ComputeConstructStub(Handle 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(Builtins::builtin(Builtins::JSConstructStubGeneric)); +} + + static Object* Runtime_NewObject(Arguments args) { - NoHandleAllocation ha; + HandleScope scope; ASSERT(args.length() == 1); - Object* constructor = args[0]; - if (constructor->IsJSFunction()) { - JSFunction* function = JSFunction::cast(constructor); + Handle constructor = args.at(0); - // 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 > arguments = HandleVector(&constructor, 1); + Handle type_error = + Factory::NewTypeError("not_constructor", arguments); + return Top::Throw(*type_error); + } + + Handle function = Handle::cast(constructor); #ifdef ENABLE_DEBUGGER_SUPPORT - if (Debug::StepInActive()) { - HandleScope scope; - Debug::HandleStepIn(Handle(function), 0, true); - } + // Handle stepping into constructors if step into is active. + if (Debug::StepInActive()) { + Debug::HandleStepIn(function, 0, true); + } #endif - if (function->has_initial_map() && - function->initial_map()->instance_type() == JS_FUNCTION_TYPE) { + if (function->has_initial_map()) { + if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) { // The 'Function' function ignores the receiver object when // called using 'new' and creates a new JSFunction object that // is returned. The receiver object is only used for error // reporting if an error occurs when constructing the new - // JSFunction. AllocateJSObject should not be used to allocate - // JSFunctions since it does not properly initialize the shared - // part of the function. Since the receiver is ignored anyway, - // we use the global object as the receiver instead of a new - // JSFunction object. This way, errors are reported the same - // way whether or not 'Function' is called using 'new'. + // JSFunction. Factory::NewJSObject() should not be used to + // allocate JSFunctions since it does not properly initialize + // the shared part of the function. Since the receiver is + // ignored anyway, we use the global object as the receiver + // instead of a new JSFunction object. This way, errors are + // reported the same way whether or not 'Function' is called + // using 'new'. return Top::context()->global(); } - return Heap::AllocateJSObject(function); } - HandleScope scope; - Handle cons(constructor); - // The constructor is not a function; throw a type error. - Handle type_error = - Factory::NewTypeError("not_constructor", HandleVector(&cons, 1)); - return Top::Throw(*type_error); + bool first_allocation = !function->has_initial_map(); + Handle result = Factory::NewJSObject(function); + if (first_allocation) { + Handle map = Handle(function->initial_map()); + Handle stub = ComputeConstructStub(map); + function->shared()->set_construct_stub(*stub); + } + return *result; } diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc index eb9c43f788..9f8d20cf63 100644 --- a/src/x64/builtins-x64.cc +++ b/src/x64/builtins-x64.cc @@ -171,6 +171,10 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { masm->int3(); // UNIMPLEMENTED. } +void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED. +} + static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, bool is_construct) { // Expects five C++ function parameters.