}
+Object* CallStubCompiler::CompileArrayPushCall(Object* object,
+ JSObject* holder,
+ JSFunction* function,
+ String* name,
+ CheckType check) {
+ // ----------- S t a t e -------------
+ // -- r2 : name
+ // -- lr : return address
+ // -----------------------------------
+
+ // TODO(639): faster implementation.
+ ASSERT(check == RECEIVER_MAP_CHECK);
+
+ Label miss;
+
+ // Get the receiver from the stack
+ const int argc = arguments().immediate();
+ __ ldr(r1, MemOperand(sp, argc * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ __ tst(r1, Operand(kSmiTagMask));
+ __ b(eq, &miss);
+
+ // Check that the maps haven't changed.
+ CheckPrototypes(JSObject::cast(object), r1, holder, r3, r0, name, &miss);
+
+ if (object->IsGlobalObject()) {
+ __ ldr(r3, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset));
+ __ str(r3, MemOperand(sp, argc * kPointerSize));
+ }
+
+ __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPush),
+ argc + 1,
+ 1);
+
+ // Handle call cache miss.
+ __ bind(&miss);
+ Handle<Code> ic = ComputeCallMiss(arguments().immediate());
+ __ Jump(ic, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ String* function_name = NULL;
+ if (function->shared()->name()->IsString()) {
+ function_name = String::cast(function->shared()->name());
+ }
+ return GetCode(CONSTANT_FUNCTION, function_name);
+}
+
+
Object* CallStubCompiler::CompileCallConstant(Object* object,
JSObject* holder,
JSFunction* function,
// -- r2 : name
// -- lr : return address
// -----------------------------------
+ SharedFunctionInfo* function_info = function->shared();
+ if (function_info->HasCustomCallGenerator()) {
+ CustomCallGenerator generator =
+ ToCData<CustomCallGenerator>(function_info->function_data());
+ return generator(this, object, holder, function, name, check);
+ }
+
Label miss;
// Get the receiver from the stack
return IS_ARRAY(obj);
}
-// -------------------------------------------------------------------
-
-
-function UpdateFunctionLengths(lengths) {
- for (var key in lengths) {
- %FunctionSetLength(this[key], lengths[key]);
- }
-}
-
// -------------------------------------------------------------------
function SetupArray() {
"isArray", ArrayIsArray
));
+ var specialFunctions = %SpecialArrayFunctions({});
+
+ function getFunction(name, jsBuiltin, len) {
+ var f = jsBuiltin;
+ if (specialFunctions.hasOwnProperty(name)) {
+ f = specialFunctions[name];
+ }
+ if (arguments.length == 3) {
+ %FunctionSetLength(f, len);
+ }
+ return f;
+ }
+
// Setup non-enumerable functions of the Array.prototype object and
// set their names.
+ // Manipulate the length of some of the functions to meet
+ // expectations set by ECMA-262 or Mozilla.
InstallFunctionsOnHiddenPrototype($Array.prototype, DONT_ENUM, $Array(
- "toString", ArrayToString,
- "toLocaleString", ArrayToLocaleString,
- "join", ArrayJoin,
- "pop", ArrayPop,
- "push", ArrayPush,
- "concat", ArrayConcat,
- "reverse", ArrayReverse,
- "shift", ArrayShift,
- "unshift", ArrayUnshift,
- "slice", ArraySlice,
- "splice", ArraySplice,
- "sort", ArraySort,
- "filter", ArrayFilter,
- "forEach", ArrayForEach,
- "some", ArraySome,
- "every", ArrayEvery,
- "map", ArrayMap,
- "indexOf", ArrayIndexOf,
- "lastIndexOf", ArrayLastIndexOf,
- "reduce", ArrayReduce,
- "reduceRight", ArrayReduceRight
+ "toString", getFunction("toString", ArrayToString),
+ "toLocaleString", getFunction("toLocaleString", ArrayToLocaleString),
+ "join", getFunction("join", ArrayJoin),
+ "pop", getFunction("pop", ArrayPop),
+ "push", getFunction("push", ArrayPush, 1),
+ "concat", getFunction("concat", ArrayConcat),
+ "reverse", getFunction("reverse", ArrayReverse),
+ "shift", getFunction("shift", ArrayShift),
+ "unshift", getFunction("unshift", ArrayUnshift, 1),
+ "slice", getFunction("slice", ArraySlice, 2),
+ "splice", getFunction("splice", ArraySplice, 2),
+ "sort", getFunction("sort", ArraySort),
+ "filter", getFunction("filter", ArrayFilter, 1),
+ "forEach", getFunction("forEach", ArrayForEach, 1),
+ "some", getFunction("some", ArraySome, 1),
+ "every", getFunction("every", ArrayEvery, 1),
+ "map", getFunction("map", ArrayMap, 1),
+ "indexOf", getFunction("indexOf", ArrayIndexOf, 1),
+ "lastIndexOf", getFunction("lastIndexOf", ArrayLastIndexOf, 1),
+ "reduce", getFunction("reduce", ArrayReduce, 1),
+ "reduceRight", getFunction("reduceRight", ArrayReduceRight, 1)
));
- // Manipulate the length of some of the functions to meet
- // expectations set by ECMA-262 or Mozilla.
- UpdateFunctionLengths({
- ArrayFilter: 1,
- ArrayForEach: 1,
- ArraySome: 1,
- ArrayEvery: 1,
- ArrayMap: 1,
- ArrayIndexOf: 1,
- ArrayLastIndexOf: 1,
- ArrayPush: 1,
- ArrayReduce: 1,
- ArrayReduceRight: 1
- });
-
%FinishArrayPrototypeSetup($Array.prototype);
}
bool make_prototype_enumerable = false);
void MakeFunctionInstancePrototypeWritable();
- Handle<JSFunction> MakeFunctionForBuiltin(Handle<String> name,
- Handle<Code> code);
-
- void OverrideWithSpecialFunction(Handle<JSObject> prototype,
- const char* name,
- Handle<Code> code);
-
- void InstallSpecialFunctions();
-
static bool CompileBuiltin(int index);
static bool CompileNative(Vector<const char> name, Handle<String> source);
static bool CompileScriptCached(Vector<const char> name,
}
-Handle<JSFunction> Genesis::MakeFunctionForBuiltin(Handle<String> name,
- Handle<Code> code) {
- Handle<JSFunction> optimized = Factory::NewFunction(name,
- JS_OBJECT_TYPE,
- JSObject::kHeaderSize,
- code,
- false);
- optimized->shared()->DontAdaptArguments();
- return optimized;
-}
-
-
-void Genesis::OverrideWithSpecialFunction(Handle<JSObject> prototype,
- const char* name,
- Handle<Code> code) {
- Handle<String> key = Factory::LookupAsciiSymbol(name);
- Handle<Object> old_value = GetProperty(prototype, key);
- // Check if the function is present in the first place.
- // For example, FLAG_natives_file could affect if Array functions
- // are installed at all.
- if (!old_value->IsJSFunction()) return;
- int old_length = Handle<JSFunction>::cast(old_value)->shared()->length();
- Handle<JSFunction> optimized = MakeFunctionForBuiltin(key, code);
- optimized->shared()->set_length(old_length);
- SetProperty(prototype, key, optimized, NONE);
-}
-
-
-void Genesis::InstallSpecialFunctions() {
- HandleScope scope;
- Handle<JSObject> global = Handle<JSObject>(global_context()->global());
- // Add special versions for some Array.prototype functions.
- Handle<JSFunction> function =
- Handle<JSFunction>(
- JSFunction::cast(global->GetProperty(Heap::Array_symbol())));
- Handle<JSObject> visible_prototype =
- Handle<JSObject>(JSObject::cast(function->prototype()));
- // Remember to put those specializations on the hidden prototype if present.
- Handle<JSObject> special_prototype;
- Handle<Object> superproto(visible_prototype->GetPrototype());
- if (superproto->IsJSObject() &&
- JSObject::cast(*superproto)->map()->is_hidden_prototype()) {
- special_prototype = Handle<JSObject>::cast(superproto);
- } else {
- special_prototype = visible_prototype;
- }
- OverrideWithSpecialFunction(
- special_prototype, "pop",
- Handle<Code>(Builtins::builtin(Builtins::ArrayPop)));
- OverrideWithSpecialFunction(
- special_prototype, "push",
- Handle<Code>(Builtins::builtin(Builtins::ArrayPush)));
- OverrideWithSpecialFunction(
- special_prototype, "shift",
- Handle<Code>(Builtins::builtin(Builtins::ArrayShift)));
- OverrideWithSpecialFunction(
- special_prototype, "unshift",
- Handle<Code>(Builtins::builtin(Builtins::ArrayUnshift)));
- OverrideWithSpecialFunction(
- special_prototype, "slice",
- Handle<Code>(Builtins::builtin(Builtins::ArraySlice)));
- OverrideWithSpecialFunction(
- special_prototype, "splice",
- Handle<Code>(Builtins::builtin(Builtins::ArraySplice)));
-}
-
-
Genesis::Genesis(Handle<Object> global_object,
v8::Handle<v8::ObjectTemplate> global_template,
v8::ExtensionConfiguration* extensions) {
if (!InstallNatives()) return;
MakeFunctionInstancePrototypeWritable();
- InstallSpecialFunctions();
if (!ConfigureGlobalObjects(global_template)) return;
HandleScope scope;
Handle<JSFunction> function = args.called_function();
+ ASSERT(function->shared()->IsApiFunction());
+ FunctionTemplateInfo* fun_data = function->shared()->get_api_func_data();
if (is_construct) {
- Handle<FunctionTemplateInfo> desc =
- Handle<FunctionTemplateInfo>(
- FunctionTemplateInfo::cast(function->shared()->function_data()));
+ Handle<FunctionTemplateInfo> desc(fun_data);
bool pending_exception = false;
Factory::ConfigureInstance(desc, Handle<JSObject>::cast(args.receiver()),
&pending_exception);
ASSERT(Top::has_pending_exception() == pending_exception);
if (pending_exception) return Failure::Exception();
+ fun_data = *desc;
}
- FunctionTemplateInfo* fun_data =
- FunctionTemplateInfo::cast(function->shared()->function_data());
Object* raw_holder = TypeCheck(args.length(), &args[0], fun_data);
if (raw_holder->IsNull()) {
static void VerifyTypeCheck(Handle<JSObject> object,
Handle<JSFunction> function) {
- FunctionTemplateInfo* info =
- FunctionTemplateInfo::cast(function->shared()->function_data());
+ ASSERT(function->shared()->IsApiFunction());
+ FunctionTemplateInfo* info = function->shared()->get_api_func_data();
if (info->signature()->IsUndefined()) return;
SignatureInfo* signature = SignatureInfo::cast(info->signature());
Object* receiver_type = signature->receiver();
// used to create the called object.
ASSERT(obj->map()->has_instance_call_handler());
JSFunction* constructor = JSFunction::cast(obj->map()->constructor());
- Object* template_info = constructor->shared()->function_data();
+ ASSERT(constructor->shared()->IsApiFunction());
Object* handler =
- FunctionTemplateInfo::cast(template_info)->instance_call_handler();
+ constructor->shared()->get_api_func_data()->instance_call_handler();
ASSERT(!handler->IsUndefined());
CallHandlerInfo* call_data = CallHandlerInfo::cast(handler);
Object* callback_obj = call_data->callback();
map->set_instance_descriptors(*array);
}
+ ASSERT(result->shared()->IsApiFunction());
return result;
}
}
+void RecordWriteStub::Generate(MacroAssembler* masm) {
+ masm->RecordWriteHelper(object_, addr_, scratch_);
+ masm->ret(0);
+}
+
+
void CompareStub::Generate(MacroAssembler* masm) {
Label call_builtin, done;
};
+class RecordWriteStub : public CodeStub {
+ public:
+ RecordWriteStub(Register object, Register addr, Register scratch)
+ : object_(object), addr_(addr), scratch_(scratch) { }
+
+ void Generate(MacroAssembler* masm);
+
+ private:
+ Register object_;
+ Register addr_;
+ Register scratch_;
+
+#ifdef DEBUG
+ void Print() {
+ PrintF("RecordWriteStub (object reg %d), (addr reg %d), (scratch reg %d)\n",
+ object_.code(), addr_.code(), scratch_.code());
+ }
+#endif
+
+ // Minor key encoding in 12 bits of three registers (object, address and
+ // scratch) OOOOAAAASSSS.
+ class ScratchBits: public BitField<uint32_t, 0, 4> {};
+ class AddressBits: public BitField<uint32_t, 4, 4> {};
+ class ObjectBits: public BitField<uint32_t, 8, 4> {};
+
+ Major MajorKey() { return RecordWrite; }
+
+ int MinorKey() {
+ // Encode the registers.
+ return ObjectBits::encode(object_.code()) |
+ AddressBits::encode(addr_.code()) |
+ ScratchBits::encode(scratch_.code());
+ }
+};
+
+
} } // namespace v8::internal
#endif // V8_IA32_CODEGEN_IA32_H_
}
-static void RecordWriteHelper(MacroAssembler* masm,
- Register object,
- Register addr,
- Register scratch) {
+void MacroAssembler::RecordWriteHelper(Register object,
+ Register addr,
+ Register scratch) {
Label fast;
// Compute the page start address from the heap object pointer, and reuse
// the 'object' register for it.
- masm->and_(object, ~Page::kPageAlignmentMask);
+ and_(object, ~Page::kPageAlignmentMask);
Register page_start = object;
// Compute the bit addr in the remembered set/index of the pointer in the
// page. Reuse 'addr' as pointer_offset.
- masm->sub(addr, Operand(page_start));
- masm->shr(addr, kObjectAlignmentBits);
+ sub(addr, Operand(page_start));
+ shr(addr, kObjectAlignmentBits);
Register pointer_offset = addr;
// If the bit offset lies beyond the normal remembered set range, it is in
// the extra remembered set area of a large object.
- masm->cmp(pointer_offset, Page::kPageSize / kPointerSize);
- masm->j(less, &fast);
+ cmp(pointer_offset, Page::kPageSize / kPointerSize);
+ j(less, &fast);
// Adjust 'page_start' so that addressing using 'pointer_offset' hits the
// extra remembered set after the large object.
// Find the length of the large object (FixedArray).
- masm->mov(scratch, Operand(page_start, Page::kObjectStartOffset
+ mov(scratch, Operand(page_start, Page::kObjectStartOffset
+ FixedArray::kLengthOffset));
Register array_length = scratch;
// Add the delta between the end of the normal RSet and the start of the
// extra RSet to 'page_start', so that addressing the bit using
// 'pointer_offset' hits the extra RSet words.
- masm->lea(page_start,
- Operand(page_start, array_length, times_pointer_size,
- Page::kObjectStartOffset + FixedArray::kHeaderSize
- - Page::kRSetEndOffset));
+ lea(page_start,
+ Operand(page_start, array_length, times_pointer_size,
+ Page::kObjectStartOffset + FixedArray::kHeaderSize
+ - Page::kRSetEndOffset));
// NOTE: For now, we use the bit-test-and-set (bts) x86 instruction
// to limit code size. We should probably evaluate this decision by
// measuring the performance of an equivalent implementation using
// "simpler" instructions
- masm->bind(&fast);
- masm->bts(Operand(page_start, Page::kRSetOffset), pointer_offset);
+ bind(&fast);
+ bts(Operand(page_start, Page::kRSetOffset), pointer_offset);
}
-class RecordWriteStub : public CodeStub {
- public:
- RecordWriteStub(Register object, Register addr, Register scratch)
- : object_(object), addr_(addr), scratch_(scratch) { }
-
- void Generate(MacroAssembler* masm);
-
- private:
- Register object_;
- Register addr_;
- Register scratch_;
-
-#ifdef DEBUG
- void Print() {
- PrintF("RecordWriteStub (object reg %d), (addr reg %d), (scratch reg %d)\n",
- object_.code(), addr_.code(), scratch_.code());
- }
-#endif
-
- // Minor key encoding in 12 bits of three registers (object, address and
- // scratch) OOOOAAAASSSS.
- class ScratchBits: public BitField<uint32_t, 0, 4> {};
- class AddressBits: public BitField<uint32_t, 4, 4> {};
- class ObjectBits: public BitField<uint32_t, 8, 4> {};
-
- Major MajorKey() { return RecordWrite; }
-
- int MinorKey() {
- // Encode the registers.
- return ObjectBits::encode(object_.code()) |
- AddressBits::encode(addr_.code()) |
- ScratchBits::encode(scratch_.code());
+void MacroAssembler::InNewSpace(Register object,
+ Register scratch,
+ Condition cc,
+ Label* branch) {
+ if (Serializer::enabled()) {
+ // Can't do arithmetic on external references if it might get serialized.
+ mov(scratch, Operand(object));
+ // The mask isn't really an address. We load it as an external reference in
+ // case the size of the new space is different between the snapshot maker
+ // and the running system.
+ and_(Operand(scratch), Immediate(ExternalReference::new_space_mask()));
+ cmp(Operand(scratch), Immediate(ExternalReference::new_space_start()));
+ j(cc, branch);
+ } else {
+ int32_t new_space_start = reinterpret_cast<int32_t>(
+ ExternalReference::new_space_start().address());
+ lea(scratch, Operand(object, -new_space_start));
+ and_(scratch, Heap::NewSpaceMask());
+ j(cc, branch);
}
-};
-
-
-void RecordWriteStub::Generate(MacroAssembler* masm) {
- RecordWriteHelper(masm, object_, addr_, scratch_);
- masm->ret(0);
}
test(value, Immediate(kSmiTagMask));
j(zero, &done);
- if (Serializer::enabled()) {
- // Can't do arithmetic on external references if it might get serialized.
- mov(value, Operand(object));
- // The mask isn't really an address. We load it as an external reference in
- // case the size of the new space is different between the snapshot maker
- // and the running system.
- and_(Operand(value), Immediate(ExternalReference::new_space_mask()));
- cmp(Operand(value), Immediate(ExternalReference::new_space_start()));
- j(equal, &done);
- } else {
- int32_t new_space_start = reinterpret_cast<int32_t>(
- ExternalReference::new_space_start().address());
- lea(value, Operand(object, -new_space_start));
- and_(value, Heap::NewSpaceMask());
- j(equal, &done);
- }
+ InNewSpace(object, value, equal, &done);
if ((offset > 0) && (offset < Page::kMaxHeapObjectSize)) {
// Compute the bit offset in the remembered set, leave it in 'value'.
// If we are already generating a shared stub, not inlining the
// record write code isn't going to save us any memory.
if (generating_stub()) {
- RecordWriteHelper(this, object, dst, value);
+ RecordWriteHelper(object, dst, value);
} else {
RecordWriteStub stub(object, dst, value);
CallStub(&stub);
// ---------------------------------------------------------------------------
// GC Support
+
+ void RecordWriteHelper(Register object,
+ Register addr,
+ Register scratch);
+
+ // Check if object is in new space.
+ // scratch can be object itself, but it will be clobbered.
+ void InNewSpace(Register object,
+ Register scratch,
+ Condition cc, // equal for new space, not_equal otherwise.
+ Label* branch);
+
// Set the remembered set bit for [object+offset].
// object is the object being stored into, value is the object being stored.
// If offset is zero, then the scratch register contains the array index into
// fast api call builtin.
void AnalyzePossibleApiFunction(JSFunction* function) {
SharedFunctionInfo* sfi = function->shared();
- if (sfi->function_data()->IsUndefined()) return;
- FunctionTemplateInfo* info =
- FunctionTemplateInfo::cast(sfi->function_data());
+ if (!sfi->IsApiFunction()) return;
+ FunctionTemplateInfo* info = sfi->get_api_func_data();
// Require a C++ callback.
if (info->call_code()->IsUndefined()) return;
}
+Object* CallStubCompiler::CompileArrayPushCall(Object* object,
+ JSObject* holder,
+ JSFunction* function,
+ String* name,
+ CheckType check) {
+ // ----------- S t a t e -------------
+ // -- ecx : name
+ // -- esp[0] : return address
+ // -- esp[(argc - n) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- esp[(argc + 1) * 4] : receiver
+ // -----------------------------------
+ Label miss;
+
+ // Get the receiver from the stack.
+ const int argc = arguments().immediate();
+ __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ __ test(edx, Immediate(kSmiTagMask));
+ __ j(zero, &miss, not_taken);
+
+ CheckPrototypes(JSObject::cast(object), edx,
+ holder, ebx,
+ eax, name, &miss);
+
+ if (argc == 0) {
+ // Noop, return the length.
+ __ mov(eax, FieldOperand(edx, JSArray::kLengthOffset));
+ __ ret((argc + 1) * kPointerSize);
+ } else {
+ // Get the elements array of the object.
+ __ mov(ebx, FieldOperand(edx, JSArray::kElementsOffset));
+
+ // Check that the elements are in fast mode (not dictionary).
+ __ cmp(FieldOperand(ebx, HeapObject::kMapOffset),
+ Immediate(Factory::fixed_array_map()));
+ __ j(not_equal, &miss, not_taken);
+
+ if (argc == 1) { // Otherwise fall through to call builtin.
+ Label call_builtin, exit, with_rset_update;
+
+ // Get the array's length into eax and calculate new length.
+ __ mov(eax, FieldOperand(edx, JSArray::kLengthOffset));
+ __ add(Operand(eax), Immediate(argc << 1));
+
+ // Get the element's length into ecx.
+ __ mov(ecx, FieldOperand(ebx, FixedArray::kLengthOffset));
+ __ SmiTag(ecx);
+
+ // Check if we could survive without allocation, go to builtin otherwise.
+ __ cmp(eax, Operand(ecx));
+ __ j(greater, &call_builtin);
+
+ // Save new length.
+ __ mov(FieldOperand(edx, JSArray::kLengthOffset), eax);
+
+ // Push the element.
+ __ lea(edx, FieldOperand(ebx,
+ eax, times_half_pointer_size,
+ FixedArray::kHeaderSize - argc * kPointerSize));
+ __ mov(ecx, Operand(esp, argc * kPointerSize));
+ __ mov(Operand(edx, 0), ecx);
+
+ // Check if wrote not a smi.
+ __ test(ecx, Immediate(kSmiTagMask));
+ __ j(not_zero, &with_rset_update);
+
+ __ bind(&exit);
+ __ ret((argc + 1) * kPointerSize);
+
+ __ bind(&with_rset_update);
+
+ __ InNewSpace(ebx, ecx, equal, &exit);
+
+ RecordWriteStub stub(ebx, edx, ecx);
+ __ CallStub(&stub);
+ __ ret((argc + 1) * kPointerSize);
+
+ __ bind(&call_builtin);
+ }
+
+ __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPush),
+ argc + 1,
+ 1);
+ }
+
+ __ bind(&miss);
+
+ Handle<Code> ic = ComputeCallMiss(arguments().immediate());
+ __ jmp(ic, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ String* function_name = NULL;
+ if (function->shared()->name()->IsString()) {
+ function_name = String::cast(function->shared()->name());
+ }
+ return GetCode(CONSTANT_FUNCTION, function_name);
+}
+
+
Object* CallStubCompiler::CompileCallConstant(Object* object,
JSObject* holder,
JSFunction* function,
// -- ...
// -- esp[(argc + 1) * 4] : receiver
// -----------------------------------
+
+ SharedFunctionInfo* function_info = function->shared();
+ if (function_info->HasCustomCallGenerator()) {
+ CustomCallGenerator generator =
+ ToCData<CustomCallGenerator>(function_info->function_data());
+ return generator(this, object, holder, function, name, check);
+ }
+
Label miss_in_smi_check;
// Get the receiver from the stack.
LOG(CodeCreateEvent(
Logger::LAZY_COMPILE_TAG, shared->code(), *func_name));
}
- } else if (shared->function_data()->IsFunctionTemplateInfo()) {
+ } else if (shared->IsApiFunction()) {
// API function.
- FunctionTemplateInfo* fun_data =
- FunctionTemplateInfo::cast(shared->function_data());
+ FunctionTemplateInfo* fun_data = shared->get_api_func_data();
Object* raw_call_data = fun_data->call_code();
if (!raw_call_data->IsUndefined()) {
CallHandlerInfo* call_data = CallHandlerInfo::cast(raw_call_data);
VerifyObjectField(kNameOffset);
VerifyObjectField(kCodeOffset);
VerifyObjectField(kInstanceClassNameOffset);
- VerifyObjectField(kExternalReferenceDataOffset);
+ VerifyObjectField(kFunctionDataOffset);
VerifyObjectField(kScriptOffset);
VerifyObjectField(kDebugInfoOffset);
}
ACCESSORS(SharedFunctionInfo, name, Object, kNameOffset)
ACCESSORS(SharedFunctionInfo, instance_class_name, Object,
kInstanceClassNameOffset)
-ACCESSORS(SharedFunctionInfo, function_data, Object,
- kExternalReferenceDataOffset)
+ACCESSORS(SharedFunctionInfo, function_data, Object, kFunctionDataOffset)
ACCESSORS(SharedFunctionInfo, script, Object, kScriptOffset)
ACCESSORS(SharedFunctionInfo, debug_info, Object, kDebugInfoOffset)
ACCESSORS(SharedFunctionInfo, inferred_name, String, kInferredNameOffset)
}
+bool SharedFunctionInfo::IsApiFunction() {
+ return function_data()->IsFunctionTemplateInfo();
+}
+
+
+FunctionTemplateInfo* SharedFunctionInfo::get_api_func_data() {
+ ASSERT(IsApiFunction());
+ return FunctionTemplateInfo::cast(function_data());
+}
+
+
+bool SharedFunctionInfo::HasCustomCallGenerator() {
+ return function_data()->IsProxy();
+}
+
+
bool JSFunction::IsBoilerplate() {
return map() == Heap::boilerplate_function_map();
}
if (!cons_obj->IsJSFunction())
return true;
JSFunction* fun = JSFunction::cast(cons_obj);
- if (!fun->shared()->function_data()->IsFunctionTemplateInfo())
+ if (!fun->shared()->IsApiFunction())
return true;
// If the object is fully fast case and has the same map it was
// created with then no changes can have been made to it.
InterceptorInfo* JSObject::GetNamedInterceptor() {
ASSERT(map()->has_named_interceptor());
JSFunction* constructor = JSFunction::cast(map()->constructor());
- Object* template_info = constructor->shared()->function_data();
+ ASSERT(constructor->shared()->IsApiFunction());
Object* result =
- FunctionTemplateInfo::cast(template_info)->named_property_handler();
+ constructor->shared()->get_api_func_data()->named_property_handler();
return InterceptorInfo::cast(result);
}
InterceptorInfo* JSObject::GetIndexedInterceptor() {
ASSERT(map()->has_indexed_interceptor());
JSFunction* constructor = JSFunction::cast(map()->constructor());
- Object* template_info = constructor->shared()->function_data();
+ ASSERT(constructor->shared()->IsApiFunction());
Object* result =
- FunctionTemplateInfo::cast(template_info)->indexed_property_handler();
+ constructor->shared()->get_api_func_data()->indexed_property_handler();
return InterceptorInfo::cast(result);
}
// [instance class name]: class name for instances.
DECL_ACCESSORS(instance_class_name, Object)
- // [function data]: This field has been added for make benefit the API.
+ // [function data]: This field holds some additional data for function.
+ // Currently it either has FunctionTemplateInfo to make benefit the API
+ // or Proxy wrapping CustomCallGenerator.
// In the long run we don't want all functions to have this field but
// we can fix that when we have a better model for storing hidden data
// on objects.
DECL_ACCESSORS(function_data, Object)
+ inline bool IsApiFunction();
+ inline FunctionTemplateInfo* get_api_func_data();
+ inline bool HasCustomCallGenerator();
+
// [script info]: Script from which the function originates.
DECL_ACCESSORS(script, Object)
static const int kConstructStubOffset = kCodeOffset + kPointerSize;
static const int kInstanceClassNameOffset =
kConstructStubOffset + kPointerSize;
- static const int kExternalReferenceDataOffset =
+ static const int kFunctionDataOffset =
kInstanceClassNameOffset + kPointerSize;
- static const int kScriptOffset = kExternalReferenceDataOffset + kPointerSize;
+ static const int kScriptOffset = kFunctionDataOffset + kPointerSize;
static const int kDebugInfoOffset = kScriptOffset + kPointerSize;
static const int kInferredNameOffset = kDebugInfoOffset + kPointerSize;
static const int kThisPropertyAssignmentsOffset =
Factory::NewFunctionBoilerplate(name, literals, code);
boilerplate->shared()->set_construct_stub(*construct_stub);
- // Copy the function data to the boilerplate. Used by
- // builtins.cc:HandleApiCall to perform argument type checks and to
- // find the right native code to call.
+ // Copy the function data to the boilerplate.
boilerplate->shared()->set_function_data(fun->shared()->function_data());
int parameters = fun->shared()->formal_parameter_count();
boilerplate->shared()->set_formal_parameter_count(parameters);
}
+static void SetCustomCallGenerator(Handle<JSFunction> function,
+ CustomCallGenerator generator) {
+ if (function->shared()->function_data()->IsUndefined()) {
+ function->shared()->set_function_data(*FromCData(generator));
+ }
+}
+
+
+static Handle<JSFunction> InstallBuiltin(Handle<JSObject> holder,
+ const char* name,
+ Builtins::Name builtin_name,
+ CustomCallGenerator generator = NULL) {
+ Handle<String> key = Factory::LookupAsciiSymbol(name);
+ Handle<Code> code(Builtins::builtin(builtin_name));
+ Handle<JSFunction> optimized = Factory::NewFunction(key,
+ JS_OBJECT_TYPE,
+ JSObject::kHeaderSize,
+ code,
+ false);
+ optimized->shared()->DontAdaptArguments();
+ if (generator != NULL) {
+ SetCustomCallGenerator(optimized, generator);
+ }
+ SetProperty(holder, key, optimized, NONE);
+ return optimized;
+}
+
+
+static Object* CompileArrayPushCall(CallStubCompiler* compiler,
+ Object* object,
+ JSObject* holder,
+ JSFunction* function,
+ String* name,
+ StubCompiler::CheckType check) {
+ return compiler->CompileArrayPushCall(object, holder, function, name, check);
+}
+
+
+static Object* Runtime_SpecialArrayFunctions(Arguments args) {
+ HandleScope scope;
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSObject, holder, 0);
+
+ InstallBuiltin(holder, "pop", Builtins::ArrayPop);
+ InstallBuiltin(holder, "push", Builtins::ArrayPush, CompileArrayPushCall);
+ InstallBuiltin(holder, "shift", Builtins::ArrayShift);
+ InstallBuiltin(holder, "unshift", Builtins::ArrayUnshift);
+ InstallBuiltin(holder, "slice", Builtins::ArraySlice);
+ InstallBuiltin(holder, "splice", Builtins::ArraySplice);
+
+ return *holder;
+}
+
+
static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
HandleScope scope;
ASSERT(args.length() == 4);
ASSERT(args.length() == 1);
CONVERT_CHECKED(JSFunction, f, args[0]);
- // The function_data field of the shared function info is used exclusively by
- // the API.
- return !f->shared()->function_data()->IsUndefined() ? Heap::true_value()
- : Heap::false_value();
+ return f->shared()->IsApiFunction() ? Heap::true_value()
+ : Heap::false_value();
}
static Object* Runtime_FunctionIsBuiltin(Arguments args) {
F(ToFastProperties, 1, 1) \
F(ToSlowProperties, 1, 1) \
F(FinishArrayPrototypeSetup, 1, 1) \
+ F(SpecialArrayFunctions, 1, 1) \
\
F(IsInPrototypeChain, 2, 1) \
F(SetHiddenPrototype, 2, 1) \
class CallStubCompiler: public StubCompiler {
public:
- explicit CallStubCompiler(int argc, InLoopFlag in_loop)
+ CallStubCompiler(int argc, InLoopFlag in_loop)
: arguments_(argc), in_loop_(in_loop) { }
Object* CompileCallField(JSObject* object,
JSFunction* function,
String* name);
+ Object* CompileArrayPushCall(Object* object,
+ JSObject* holder,
+ JSFunction* function,
+ String* name,
+ CheckType check);
+
private:
const ParameterCount arguments_;
const InLoopFlag in_loop_;
};
+typedef Object* (*CustomCallGenerator)(CallStubCompiler* compiler,
+ Object* object,
+ JSObject* holder,
+ JSFunction* function,
+ String* name,
+ StubCompiler::CheckType check);
+
+
} } // namespace v8::internal
#endif // V8_STUB_CACHE_H_
// Get the data object from access check info.
JSFunction* constructor = JSFunction::cast(receiver->map()->constructor());
- Object* info = constructor->shared()->function_data();
- if (info == Heap::undefined_value()) return;
-
- Object* data_obj = FunctionTemplateInfo::cast(info)->access_check_info();
+ if (!constructor->shared()->IsApiFunction()) return;
+ Object* data_obj =
+ constructor->shared()->get_api_func_data()->access_check_info();
if (data_obj == Heap::undefined_value()) return;
HandleScope scope;
// Get named access check callback
JSFunction* constructor = JSFunction::cast(receiver->map()->constructor());
- Object* info = constructor->shared()->function_data();
- if (info == Heap::undefined_value()) return false;
+ if (!constructor->shared()->IsApiFunction()) return false;
- Object* data_obj = FunctionTemplateInfo::cast(info)->access_check_info();
+ Object* data_obj =
+ constructor->shared()->get_api_func_data()->access_check_info();
if (data_obj == Heap::undefined_value()) return false;
Object* fun_obj = AccessCheckInfo::cast(data_obj)->named_callback();
// Get indexed access check callback
JSFunction* constructor = JSFunction::cast(receiver->map()->constructor());
- Object* info = constructor->shared()->function_data();
- if (info == Heap::undefined_value()) return false;
+ if (!constructor->shared()->IsApiFunction()) return false;
- Object* data_obj = FunctionTemplateInfo::cast(info)->access_check_info();
+ Object* data_obj =
+ constructor->shared()->get_api_func_data()->access_check_info();
if (data_obj == Heap::undefined_value()) return false;
Object* fun_obj = AccessCheckInfo::cast(data_obj)->indexed_callback();
#define __ ACCESS_MASM((masm()))
+Object* CallStubCompiler::CompileArrayPushCall(Object* object,
+ JSObject* holder,
+ JSFunction* function,
+ String* name,
+ CheckType check) {
+ // ----------- S t a t e -------------
+ // rcx : function name
+ // rsp[0] : return address
+ // rsp[8] : argument argc
+ // rsp[16] : argument argc - 1
+ // ...
+ // rsp[argc * 8] : argument 1
+ // rsp[(argc + 1) * 8] : argument 0 = receiver
+ // -----------------------------------
+
+ // TODO(639): faster implementation.
+ ASSERT(check == RECEIVER_MAP_CHECK);
+
+ Label miss;
+
+ // Get the receiver from the stack.
+ const int argc = arguments().immediate();
+ __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(rdx, &miss);
+
+ // Check that the maps haven't changed.
+ CheckPrototypes(JSObject::cast(object), rdx, holder,
+ rbx, rax, name, &miss);
+
+ // Patch the receiver on the stack with the global proxy if
+ // necessary.
+ if (object->IsGlobalObject()) {
+ __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset));
+ __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx);
+ }
+
+ __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPush),
+ argc + 1,
+ 1);
+
+
+ // Handle call cache miss.
+ __ bind(&miss);
+ Handle<Code> ic = ComputeCallMiss(arguments().immediate());
+ __ Jump(ic, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ String* function_name = NULL;
+ if (function->shared()->name()->IsString()) {
+ function_name = String::cast(function->shared()->name());
+ }
+ return GetCode(CONSTANT_FUNCTION, function_name);
+}
+
+
Object* CallStubCompiler::CompileCallConstant(Object* object,
JSObject* holder,
JSFunction* function,
// rsp[(argc + 1) * 8] : argument 0 = receiver
// -----------------------------------
+ SharedFunctionInfo* function_info = function->shared();
+ if (function_info->HasCustomCallGenerator()) {
+ CustomCallGenerator generator =
+ ToCData<CustomCallGenerator>(function_info->function_data());
+ return generator(this, object, holder, function, name, check);
+ }
+
Label miss;
// Get the receiver from the stack.
--- /dev/null
+// 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.
+
+// Check pushes with various number of arguments.
+(function() {
+ var a = [];
+ for (var i = 0; i < 7; i++) {
+ a = [];
+
+ assertEquals(0, a.push());
+ assertEquals([], a, "after .push()");
+
+ assertEquals(1, a.push(1), "length after .push(1)");
+ assertEquals([1], a, "after .push(1)");
+
+ assertEquals(3, a.push(2, 3), "length after .push(2, 3)");
+ assertEquals([1, 2, 3], a, "after .push(2, 3)");
+
+ assertEquals(6, a.push(4, 5, 6),
+ "length after .push(4, 5, 6)");
+ assertEquals([1, 2, 3, 4, 5, 6], a,
+ "after .push(4, 5, 5)");
+
+ assertEquals(10, a.push(7, 8, 9, 10),
+ "length after .push(7, 8, 9, 10)");
+ assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], a,
+ "after .push(7, 8, 9, 10)");
+
+ assertEquals(15, a.push(11, 12, 13, 14, 15),
+ "length after .push(11, 12, 13, 14, 15)");
+ assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], a,
+ "after .push(11, 12, 13, 14, 15)");
+
+ assertEquals(21, a.push(16, 17, 18, 19, 20, 21),
+ "length after .push(16, 17, 18, 19, 20, 21)");
+ assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21], a,
+ "after .push(16, 17, 18, 19, 20, 21)");
+
+ assertEquals(28, a.push(22, 23, 24, 25, 26, 27, 28),
+ "length hafter .push(22, 23, 24, 25, 26, 27, 28)");
+ assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28], a,
+ "after .push(22, 23, 24, 25, 26, 27, 28)");
+ }
+})();