Commit de070ccf authored by antonm@chromium.org's avatar antonm@chromium.org

First take on custom call generators.

Review URL: http://codereview.chromium.org/669061

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4108 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent cb1f817f
......@@ -815,6 +815,55 @@ Object* CallStubCompiler::CompileCallField(JSObject* object,
}
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,
......@@ -824,6 +873,13 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
// -- 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
......
......@@ -1088,15 +1088,6 @@ function ArrayIsArray(obj) {
return IS_ARRAY(obj);
}
// -------------------------------------------------------------------
function UpdateFunctionLengths(lengths) {
for (var key in lengths) {
%FunctionSetLength(this[key], lengths[key]);
}
}
// -------------------------------------------------------------------
function SetupArray() {
......@@ -1109,47 +1100,47 @@ 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);
}
......
......@@ -245,15 +245,6 @@ class Genesis BASE_EMBEDDED {
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,
......@@ -1458,73 +1449,6 @@ void Genesis::MakeFunctionInstancePrototypeWritable() {
}
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) {
......@@ -1548,7 +1472,6 @@ Genesis::Genesis(Handle<Object> global_object,
if (!InstallNatives()) return;
MakeFunctionInstancePrototypeWritable();
InstallSpecialFunctions();
if (!ConfigureGlobalObjects(global_template)) return;
......
......@@ -790,20 +790,19 @@ static Object* HandleApiCallHelper(
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()) {
......@@ -874,8 +873,8 @@ BUILTIN(HandleApiCallConstruct) {
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();
......@@ -959,9 +958,9 @@ static Object* HandleApiCallAsFunctionOrConstructor(
// 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();
......
......@@ -866,6 +866,7 @@ Handle<JSFunction> Factory::CreateApiFunction(
map->set_instance_descriptors(*array);
}
ASSERT(result->shared()->IsApiFunction());
return result;
}
......
......@@ -10217,6 +10217,12 @@ void NumberToStringStub::Generate(MacroAssembler* masm) {
}
void RecordWriteStub::Generate(MacroAssembler* masm) {
masm->RecordWriteHelper(object_, addr_, scratch_);
masm->ret(0);
}
void CompareStub::Generate(MacroAssembler* masm) {
Label call_builtin, done;
......
......@@ -977,6 +977,42 @@ class NumberToStringStub: public CodeStub {
};
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_
......@@ -47,33 +47,32 @@ MacroAssembler::MacroAssembler(void* buffer, int size)
}
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;
......@@ -83,59 +82,40 @@ static void RecordWriteHelper(MacroAssembler* masm,
// 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);
}
......@@ -161,22 +141,7 @@ void MacroAssembler::RecordWrite(Register object, int offset,
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'.
......@@ -209,7 +174,7 @@ void MacroAssembler::RecordWrite(Register object, int offset,
// 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);
......
......@@ -48,6 +48,18 @@ class MacroAssembler: public Assembler {
// ---------------------------------------------------------------------------
// 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
......
......@@ -549,9 +549,8 @@ class CallOptimization BASE_EMBEDDED {
// 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;
......@@ -1210,6 +1209,107 @@ Object* CallStubCompiler::CompileCallField(JSObject* object,
}
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,
......@@ -1222,6 +1322,14 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
// -- ...
// -- 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.
......
......@@ -1346,10 +1346,9 @@ void Logger::LogCompiledFunctions() {
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);
......
......@@ -789,7 +789,7 @@ void SharedFunctionInfo::SharedFunctionInfoVerify() {
VerifyObjectField(kNameOffset);
VerifyObjectField(kCodeOffset);
VerifyObjectField(kInstanceClassNameOffset);
VerifyObjectField(kExternalReferenceDataOffset);
VerifyObjectField(kFunctionDataOffset);
VerifyObjectField(kScriptOffset);
VerifyObjectField(kDebugInfoOffset);
}
......
......@@ -2361,8 +2361,7 @@ ACCESSORS(SharedFunctionInfo, construct_stub, Code, kConstructStubOffset)
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)
......@@ -2453,6 +2452,22 @@ bool SharedFunctionInfo::is_compiled() {
}
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();
}
......
......@@ -480,7 +480,7 @@ bool JSObject::IsDirty() {
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.
......@@ -6433,9 +6433,9 @@ void Dictionary<Shape, Key>::CopyValuesTo(FixedArray* elements) {
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);
}
......@@ -6443,9 +6443,9 @@ InterceptorInfo* JSObject::GetNamedInterceptor() {
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);
}
......
......@@ -3186,12 +3186,18 @@ class SharedFunctionInfo: public HeapObject {
// [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)
......@@ -3299,9 +3305,9 @@ class SharedFunctionInfo: public HeapObject {
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 =
......
......@@ -1964,9 +1964,7 @@ Statement* Parser::ParseNativeDeclaration(bool* ok) {
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);
......
......@@ -1238,6 +1238,60 @@ static Object* Runtime_FinishArrayPrototypeSetup(Arguments args) {
}
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);
......@@ -1372,10 +1426,8 @@ static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
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) {
......
......@@ -61,6 +61,7 @@ namespace internal {
F(ToFastProperties, 1, 1) \
F(ToSlowProperties, 1, 1) \
F(FinishArrayPrototypeSetup, 1, 1) \
F(SpecialArrayFunctions, 1, 1) \
\
F(IsInPrototypeChain, 2, 1) \
F(SetHiddenPrototype, 2, 1) \
......
......@@ -548,7 +548,7 @@ class KeyedStoreStubCompiler: public StubCompiler {
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,
......@@ -569,6 +569,12 @@ class CallStubCompiler: public StubCompiler {
JSFunction* function,
String* name);
Object* CompileArrayPushCall(Object* object,
JSObject* holder,
JSFunction* function,
String* name,
CheckType check);
private:
const ParameterCount arguments_;
const InLoopFlag in_loop_;
......@@ -590,6 +596,14 @@ class ConstructStubCompiler: public StubCompiler {
};
typedef Object* (*CustomCallGenerator)(CallStubCompiler* compiler,
Object* object,
JSObject* holder,
JSFunction* function,
String* name,
StubCompiler::CheckType check);
} } // namespace v8::internal
#endif // V8_STUB_CACHE_H_
......@@ -439,10 +439,9 @@ void Top::ReportFailedAccessCheck(JSObject* receiver, v8::AccessType type) {
// 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;
......@@ -502,10 +501,10 @@ bool Top::MayNamedAccess(JSObject* receiver, Object* key, v8::AccessType type) {
// 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();
......@@ -547,10 +546,10 @@ bool Top::MayIndexedAccess(JSObject* receiver,
// 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();
......
......@@ -655,6 +655,63 @@ class CallInterceptorCompiler BASE_EMBEDDED {
#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,
......@@ -670,6 +727,13 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
// 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.
......
// 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)");
}
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment