}
+Object* ConstructStubCompiler::CompileConstructStub(
+ SharedFunctionInfo* shared) {
+ // Not implemented yet - just jump to generic stub.
+ Code* code = Builtins::builtin(Builtins::JSConstructStubGeneric);
+ Handle<Code> generic_construct_stub(code);
+ __ Jump(generic_construct_stub, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ return GetCode();
+}
+
+
#undef __
} } // namespace v8::internal
return %FunctionGetSourceCode(f);
};
-Debug.assembler = function(f) {
+Debug.disassemble = function(f) {
if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
- return %FunctionGetAssemblerCode(f);
+ return %DebugDisassembleFunction(f);
+};
+
+Debug.disassembleConstructor = function(f) {
+ if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
+ return %DebugDisassembleConstructor(f);
};
Debug.sourcePosition = function(f) {
// Make sure that the maximum heap object size will never cause us
// problem here, because it is always greater than the maximum
// instance size that can be represented in a byte.
- ASSERT(Heap::MaxObjectSizeInPagedSpace() >= (1 << kBitsPerByte));
+ ASSERT(Heap::MaxObjectSizeInPagedSpace() >= JSObject::kMaxInstanceSize);
ExternalReference new_space_allocation_top =
ExternalReference::new_space_allocation_top_address();
__ mov(ebx, Operand::StaticVariable(new_space_allocation_top));
}
+// Specialized stub for constructing objects from functions which only have only
+// simple assignments of the form this.x = ...; in their body.
+Object* ConstructStubCompiler::CompileConstructStub(
+ SharedFunctionInfo* shared) {
+ // ----------- S t a t e -------------
+ // -- eax : argc
+ // -- edi : constructor
+ // -- esp[0] : return address
+ // -- esp[4] : last argument
+ // -----------------------------------
+ Label generic_stub_call;
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // Check to see whether there are any break points in the function code. If
+ // there are jump to the generic constructor stub which calls the actual
+ // code for the function thereby hitting the break points.
+ __ mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
+ __ mov(ebx, FieldOperand(ebx, SharedFunctionInfo::kDebugInfoOffset));
+ __ cmp(ebx, Factory::undefined_value());
+ __ j(not_equal, &generic_stub_call, not_taken);
+#endif
+
+ // Load the initial map and verify that it is in fact a map.
+ __ mov(ebx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
+ // Will both indicate a NULL and a Smi.
+ __ test(ebx, Immediate(kSmiTagMask));
+ __ j(zero, &generic_stub_call);
+ __ CmpObjectType(ebx, MAP_TYPE, ecx);
+ __ j(not_equal, &generic_stub_call);
+
+#ifdef DEBUG
+ // Cannot construct functions this way.
+ // edi: constructor
+ // ebx: initial map
+ __ CmpInstanceType(ebx, JS_FUNCTION_TYPE);
+ __ Assert(not_equal, "Function constructed by construct stub.");
+#endif
+
+ // Now allocate the JSObject on the heap by moving the new space allocation
+ // top forward.
+ // edi: constructor
+ // ebx: initial map
+ __ movzx_b(ecx, FieldOperand(ebx, Map::kInstanceSizeOffset));
+ __ shl(ecx, kPointerSizeLog2);
+ // Make sure that the maximum heap object size will never cause us
+ // problems here.
+ ASSERT(Heap::MaxObjectSizeInPagedSpace() >= JSObject::kMaxInstanceSize);
+ ExternalReference new_space_allocation_top =
+ ExternalReference::new_space_allocation_top_address();
+ __ mov(edx, Operand::StaticVariable(new_space_allocation_top));
+ __ add(ecx, Operand(edx)); // Calculate new top.
+ ExternalReference new_space_allocation_limit =
+ ExternalReference::new_space_allocation_limit_address();
+ __ cmp(ecx, Operand::StaticVariable(new_space_allocation_limit));
+ __ j(above_equal, &generic_stub_call);
+
+ // Update new space top.
+ __ mov(Operand::StaticVariable(new_space_allocation_top), ecx);
+
+ // Allocated the JSObject, now initialize the fields and add the heap tag.
+ // ebx: initial map
+ // edx: JSObject
+ __ mov(Operand(edx, JSObject::kMapOffset), ebx);
+ __ mov(ebx, Factory::empty_fixed_array());
+ __ mov(Operand(edx, JSObject::kPropertiesOffset), ebx);
+ __ mov(Operand(edx, JSObject::kElementsOffset), ebx);
+ __ or_(Operand(edx), Immediate(kHeapObjectTag));
+
+ // Push the allocated object to the stack. This is the object that will be
+ // returned.
+ __ push(edx);
+
+ // eax: argc
+ // edx: JSObject
+ // Load the address of the first in-object property into edx.
+ __ lea(edx, Operand(edx, JSObject::kHeaderSize));
+ __ xor_(Operand(edx), Immediate(kHeapObjectTag)); // Clear heap object tag.
+ // Calculate the location of the first argument. The stack contains the
+ // allocated object and the return address on top of the argc arguments.
+ __ lea(ecx, Operand(esp, eax, times_4, 1 * kPointerSize));
+
+ // Use edi for holding undefined which is used in several places below.
+ __ mov(edi, Factory::undefined_value());
+
+ // eax: argc
+ // ecx: first argument
+ // edx: first in-object property of the JSObject
+ // edi: undefined
+ // Fill the initialized properties with a constant value or a passed argument
+ // depending on the this.x = ...; assignment in the function.
+ for (int i = 0; i < shared->this_property_assignments_count(); i++) {
+ if (shared->IsThisPropertyAssignmentArgument(i)) {
+ Label not_passed;
+ // Set the property to undefined.
+ __ mov(Operand(edx, i * kPointerSize), edi);
+ // Check if the argument assigned to the property is actually passed.
+ int arg_number = shared->GetThisPropertyAssignmentArgument(i);
+ __ cmp(eax, arg_number);
+ __ j(below_equal, ¬_passed);
+ // Argument passed - find it on the stack.
+ __ mov(ebx, Operand(ecx, arg_number * -kPointerSize));
+ __ mov(Operand(edx, i * kPointerSize), ebx);
+ __ bind(¬_passed);
+ } else {
+ // Set the property to the constant value.
+ Handle<Object> constant(shared->GetThisPropertyAssignmentConstant(i));
+ __ mov(Operand(edx, i * kPointerSize), Immediate(constant));
+ }
+ }
+
+ // Fill the unused in-object property fields with undefined.
+ for (int i = shared->this_property_assignments_count();
+ i < shared->CalculateInObjectProperties();
+ i++) {
+ __ mov(Operand(edx, i * kPointerSize), edi);
+ }
+
+ // Move argc to ebx and retreive the JSObject to return.
+ __ mov(ebx, eax);
+ __ pop(eax);
+
+ // Remove caller arguments from the stack and return.
+ __ pop(ecx);
+ __ lea(esp, Operand(esp, ebx, times_4, 1 * kPointerSize)); // 1 ~ receiver
+ __ push(ecx);
+ __ IncrementCounter(&Counters::constructed_objects, 1);
+ __ IncrementCounter(&Counters::constructed_objects_stub, 1);
+ __ ret(0);
+
+ // Jump to the generic stub in case the specialized code cannot handle the
+ // construction.
+ __ bind(&generic_stub_call);
+ Code* code = Builtins::builtin(Builtins::JSConstructStubGeneric);
+ Handle<Code> generic_construct_stub(code);
+ __ jmp(generic_construct_stub, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ return GetCode();
+}
+
+
#undef __
} } // namespace v8::internal
}
+void SharedFunctionInfo::ClearThisPropertyAssignmentsInfo() {
+ set_compiler_hints(BooleanBit::set(compiler_hints(),
+ kHasOnlyThisPropertyAssignments,
+ false));
+ set_compiler_hints(BooleanBit::set(compiler_hints(),
+ kHasOnlySimpleThisPropertyAssignments,
+ false));
+ set_this_property_assignments(Heap::undefined_value());
+ set_this_property_assignments_count(0);
+}
+
+
String* SharedFunctionInfo::GetThisPropertyAssignmentName(int index) {
Object* obj = this_property_assignments();
ASSERT(obj->IsFixedArray());
}
+bool SharedFunctionInfo::IsThisPropertyAssignmentArgument(int index) {
+ Object* obj = this_property_assignments();
+ ASSERT(obj->IsFixedArray());
+ ASSERT(index < this_property_assignments_count());
+ obj = FixedArray::cast(obj)->get(index * 3 + 1);
+ return Smi::cast(obj)->value() != -1;
+}
+
+
+int SharedFunctionInfo::GetThisPropertyAssignmentArgument(int index) {
+ ASSERT(IsThisPropertyAssignmentArgument(index));
+ Object* obj =
+ FixedArray::cast(this_property_assignments())->get(index * 3 + 1);
+ return Smi::cast(obj)->value();
+}
+
+
+Object* SharedFunctionInfo::GetThisPropertyAssignmentConstant(int index) {
+ ASSERT(!IsThisPropertyAssignmentArgument(index));
+ Object* obj =
+ FixedArray::cast(this_property_assignments())->get(index * 3 + 2);
+ return obj;
+}
+
+
+
// Support function for printing the source code to a StringStream
// without any allocation in the heap.
void SharedFunctionInfo::SourceCodePrint(StringStream* accumulator,
bool has_only_simple_this_property_assignments,
FixedArray* this_property_assignments);
+ // Clear information on assignments of the form this.x = ...;
+ void ClearThisPropertyAssignmentsInfo();
+
// Indicate that this function only consists of assignments of the form
// this.x = ...;.
inline bool has_only_this_property_assignments();
inline int this_property_assignments_count();
inline void set_this_property_assignments_count(int value);
String* GetThisPropertyAssignmentName(int index);
+ bool IsThisPropertyAssignmentArgument(int index);
+ int GetThisPropertyAssignmentArgument(int index);
+ Object* GetThisPropertyAssignmentConstant(int index);
// [source code]: Source code for the function.
bool HasSourceCode();
#include "v8threads.h"
#include "smart-pointer.h"
#include "parser.h"
+#include "stub-cache.h"
namespace v8 {
namespace internal {
// Array, and Object, and some web code
// doesn't like seeing source code for constructors.
target->shared()->set_script(Heap::undefined_value());
+ // Clear the optimization hints related to the compiled code as these are no
+ // longer valid when the code is overwritten.
+ target->shared()->ClearThisPropertyAssignmentsInfo();
context = Handle<Context>(fun->context());
// Make sure we get a fresh copy of the literal vector to avoid
}
-static Handle<Code> ComputeConstructStub(Handle<Map> map) {
+static Code* ComputeConstructStub(Handle<SharedFunctionInfo> shared) {
// 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));
+ if (FLAG_inline_new
+ && shared->has_only_simple_this_property_assignments()) {
+ ConstructStubCompiler compiler;
+ Object* code = compiler.CompileConstructStub(*shared);
+ if (code->IsFailure()) {
+ return Builtins::builtin(Builtins::JSConstructStubGeneric);
+ }
+ return Code::cast(code);
+ }
+
+ return Builtins::builtin(Builtins::JSConstructStubGeneric);
}
// The function should be compiled for the optimization hints to be available.
if (!function->shared()->is_compiled()) {
- CompileLazyShared(Handle<SharedFunctionInfo>(function->shared()),
- CLEAR_EXCEPTION,
- 0);
+ CompileLazyShared(Handle<SharedFunctionInfo>(function->shared()),
+ CLEAR_EXCEPTION,
+ 0);
}
bool first_allocation = !function->has_initial_map();
Handle<JSObject> result = Factory::NewJSObject(function);
if (first_allocation) {
Handle<Map> map = Handle<Map>(function->initial_map());
- Handle<Code> stub = ComputeConstructStub(map);
+ Handle<Code> stub = Handle<Code>(
+ ComputeConstructStub(Handle<SharedFunctionInfo>(function->shared())));
function->shared()->set_construct_stub(*stub);
}
+
Counters::constructed_objects.Increment();
Counters::constructed_objects_runtime.Increment();
+
return *result;
}
}
-static Object* Runtime_FunctionGetAssemblerCode(Arguments args) {
+static Object* Runtime_DebugDisassembleFunction(Arguments args) {
#ifdef DEBUG
HandleScope scope;
ASSERT(args.length() == 1);
}
+static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
+#ifdef DEBUG
+ HandleScope scope;
+ ASSERT(args.length() == 1);
+ // Get the function and make sure it is compiled.
+ CONVERT_ARG_CHECKED(JSFunction, func, 0);
+ if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
+ return Failure::Exception();
+ }
+ func->shared()->construct_stub()->PrintLn();
+#endif // DEBUG
+ return Heap::undefined_value();
+}
+
+
static Object* Runtime_FunctionGetInferredName(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
F(DebugConstructedBy, 2) \
F(DebugGetPrototype, 1) \
F(SystemBreak, 0) \
- F(FunctionGetAssemblerCode, 1) \
+ F(DebugDisassembleFunction, 1) \
+ F(DebugDisassembleConstructor, 1) \
F(FunctionGetInferredName, 1)
#else
#define RUNTIME_FUNCTION_LIST_DEBUGGER_SUPPORT(F)
}
+Object* ConstructStubCompiler::GetCode() {
+ Code::Flags flags = Code::ComputeFlags(Code::STUB);
+ return GetCodeWithFlags(flags, "ConstructStub");
+}
+
+
+
} } // namespace v8::internal
};
+class ConstructStubCompiler: public StubCompiler {
+ public:
+ explicit ConstructStubCompiler() {}
+
+ Object* CompileConstructStub(SharedFunctionInfo* shared);
+
+ private:
+ Object* GetCode();
+};
+
+
} } // namespace v8::internal
#endif // V8_STUB_CACHE_H_
SC(call_global_inline_miss, V8.CallGlobalInlineMiss) \
SC(constructed_objects, V8.ConstructedObjects) \
SC(constructed_objects_runtime, V8.ConstructedObjectsRuntime) \
+ SC(constructed_objects_stub, V8.ConstructedObjectsStub) \
SC(for_in, V8.ForIn) \
SC(enum_cache_hits, V8.EnumCacheHits) \
SC(enum_cache_misses, V8.EnumCacheMisses) \
}
+Object* ConstructStubCompiler::CompileConstructStub(
+ SharedFunctionInfo* shared) {
+ // Not implemented yet - just jump to generic stub.
+ Code* code = Builtins::builtin(Builtins::JSConstructStubGeneric);
+ Handle<Code> generic_construct_stub(code);
+ __ Jump(generic_construct_stub, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ return GetCode();
+}
+
+
#undef __
} } // namespace v8::internal
break_break_point_hit_count = 0;
f();
assertEquals(5, break_break_point_hit_count);
+f();
+assertEquals(10, break_break_point_hit_count);
+f();
+assertEquals(15, break_break_point_hit_count);
// Test step into constructor with builtin constructor.
function g() {
}
o1_1 = new f1();
+assertEquals(1, o1_1.x, "1");
o1_2 = new f1();
-assertArrayEquals(["x"], props(o1_1));
-assertArrayEquals(["x"], props(o1_2));
+assertEquals(1, o1_1.x, "2");
+assertArrayEquals(["x"], props(o1_1), "3");
+assertArrayEquals(["x"], props(o1_2), "4");
o2_1 = new f2(0);
o2_2 = new f2(0);
assertArrayEquals(["x", "y"], props(o4_1_1));
assertArrayEquals(["x", "y"], props(o4_1_2));
+function f5(x, y) {
+ this.x = x;
+ this.y = y;
+}
+
+function f6(x, y) {
+ this.y = y;
+ this.x = x;
+}
+
+function f7(x, y, z) {
+ this.x = x;
+ this.y = y;
+}
+
+function testArgs(fun) {
+ obj = new fun();
+ assertArrayEquals(["x", "y"], props(obj));
+ assertEquals(void 0, obj.x);
+ assertEquals(void 0, obj.y);
+
+ obj = new fun("x");
+ assertArrayEquals(["x", "y"], props(obj));
+ assertEquals("x", obj.x);
+ assertEquals(void 0, obj.y);
+
+ obj = new fun("x", "y");
+ assertArrayEquals(["x", "y"], props(obj));
+ assertEquals("x", obj.x);
+ assertEquals("y", obj.y);
+
+ obj = new fun("x", "y", "z");
+ assertArrayEquals(["x", "y"], props(obj));
+ assertEquals("x", obj.x);
+ assertEquals("y", obj.y);
+}
+
+for (var i = 0; i < 10; i++) {
+ testArgs(f5);
+ testArgs(f6);
+ testArgs(f7);
+}
+
function g(){
this.x=1
}