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
This commit is contained in:
parent
cb1f817f77
commit
de070ccfa6
@ -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
|
||||
|
81
src/array.js
81
src/array.js
@ -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,46 +1100,46 @@ 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.
|
||||
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
|
||||
));
|
||||
|
||||
// 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
|
||||
});
|
||||
InstallFunctionsOnHiddenPrototype($Array.prototype, DONT_ENUM, $Array(
|
||||
"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)
|
||||
));
|
||||
|
||||
%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());
|
||||
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);
|
||||
}
|
||||
#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 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_
|
||||
|
19
src/top.cc
19
src/top.cc
@ -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.
|
||||
|
68
test/mjsunit/array-push.js
Normal file
68
test/mjsunit/array-push.js
Normal file
@ -0,0 +1,68 @@
|
||||
// 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)");
|
||||
}
|
||||
})();
|
Loading…
Reference in New Issue
Block a user