зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1066878 - Inline Object.create(x) in Ion. r=bhackett
This commit is contained in:
Родитель
8fb96ecee4
Коммит
540219d598
|
@ -622,6 +622,42 @@ obj_isPrototypeOf(JSContext *cx, unsigned argc, Value *vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
PlainObject *
|
||||
js::ObjectCreateImpl(JSContext *cx, HandleObject proto, NewObjectKind newKind, HandleTypeObject type)
|
||||
{
|
||||
// Give the new object a small number of fixed slots, like we do for empty
|
||||
// object literals ({}).
|
||||
gc::AllocKind allocKind = GuessObjectGCKind(0);
|
||||
|
||||
if (!proto) {
|
||||
// Object.create(null) is common, optimize it by using an allocation
|
||||
// site specific TypeObject. Because GetTypeCallerInitObject is pretty
|
||||
// slow, the caller can pass in the type if it's known and we use that
|
||||
// instead.
|
||||
RootedTypeObject ntype(cx, type);
|
||||
if (!ntype) {
|
||||
ntype = GetTypeCallerInitObject(cx, JSProto_Null);
|
||||
if (!ntype)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!ntype->proto().toObjectOrNull());
|
||||
|
||||
return NewObjectWithType<PlainObject>(cx, ntype, cx->global(), allocKind,
|
||||
newKind);
|
||||
}
|
||||
|
||||
return NewObjectWithGivenProto<PlainObject>(cx, proto, cx->global(), allocKind, newKind);
|
||||
}
|
||||
|
||||
PlainObject *
|
||||
js::ObjectCreateWithTemplate(JSContext *cx, HandlePlainObject templateObj)
|
||||
{
|
||||
RootedObject proto(cx, templateObj->getProto());
|
||||
RootedTypeObject type(cx, templateObj->type());
|
||||
return ObjectCreateImpl(cx, proto, GenericObject, type);
|
||||
}
|
||||
|
||||
/* ES5 15.2.3.5: Object.create(O [, Properties]) */
|
||||
bool
|
||||
js::obj_create(JSContext *cx, unsigned argc, Value *vp)
|
||||
|
@ -633,8 +669,8 @@ js::obj_create(JSContext *cx, unsigned argc, Value *vp)
|
|||
return false;
|
||||
}
|
||||
|
||||
RootedValue v(cx, args[0]);
|
||||
if (!v.isObjectOrNull()) {
|
||||
if (!args[0].isObjectOrNull()) {
|
||||
RootedValue v(cx, args[0]);
|
||||
char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NullPtr());
|
||||
if (!bytes)
|
||||
return false;
|
||||
|
@ -644,13 +680,8 @@ js::obj_create(JSContext *cx, unsigned argc, Value *vp)
|
|||
return false;
|
||||
}
|
||||
|
||||
RootedObject proto(cx, v.toObjectOrNull());
|
||||
|
||||
/*
|
||||
* Use the callee's global as the parent of the new object to avoid dynamic
|
||||
* scoping (i.e., using the caller's global).
|
||||
*/
|
||||
RootedObject obj(cx, NewObjectWithGivenProto<PlainObject>(cx, proto, &args.callee().global()));
|
||||
RootedObject proto(cx, args[0].toObjectOrNull());
|
||||
RootedPlainObject obj(cx, ObjectCreateImpl(cx, proto));
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
|
|
|
@ -23,6 +23,13 @@ obj_construct(JSContext *cx, unsigned argc, JS::Value *vp);
|
|||
bool
|
||||
obj_valueOf(JSContext *cx, unsigned argc, JS::Value *vp);
|
||||
|
||||
PlainObject *
|
||||
ObjectCreateImpl(JSContext *cx, HandleObject proto, NewObjectKind newKind = GenericObject,
|
||||
HandleTypeObject type = js::NullPtr());
|
||||
|
||||
PlainObject *
|
||||
ObjectCreateWithTemplate(JSContext *cx, HandlePlainObject templateObj);
|
||||
|
||||
// Object methods exposed so they can be installed in the self-hosting global.
|
||||
bool
|
||||
obj_create(JSContext *cx, unsigned argc, JS::Value *vp);
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
// Ensure Ion inlining of Object.create(x) tests the type of x
|
||||
// matches the template object.
|
||||
|
||||
var P1 = {};
|
||||
var P2 = {};
|
||||
minorgc();
|
||||
|
||||
function f1() {
|
||||
for (var i=0; i<100; i++) {
|
||||
var P = (i & 1) ? P1 : P2;
|
||||
var o = Object.create(P);
|
||||
assertEq(Object.getPrototypeOf(o), P);
|
||||
}
|
||||
}
|
||||
f1();
|
||||
|
||||
function f2() {
|
||||
var arr = [null, Array];
|
||||
for (var i=0; i<99; i++) {
|
||||
var p = arr[(i / 50)|0];
|
||||
var o = Object.create(p);
|
||||
assertEq(Object.getPrototypeOf(o), p);
|
||||
}
|
||||
}
|
||||
f2();
|
|
@ -8909,6 +8909,14 @@ GetTemplateObjectForNative(JSContext *cx, HandleScript script, jsbytecode *pc,
|
|||
return true;
|
||||
}
|
||||
|
||||
if (native == obj_create && args.length() == 1 && args[0].isObjectOrNull()) {
|
||||
RootedObject proto(cx, args[0].toObjectOrNull());
|
||||
res.set(ObjectCreateImpl(cx, proto, TenuredObject));
|
||||
if (!res)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -4038,9 +4038,9 @@ class OutOfLineNewObject : public OutOfLineCodeBase<CodeGenerator>
|
|||
typedef JSObject *(*NewInitObjectFn)(JSContext *, HandlePlainObject);
|
||||
static const VMFunction NewInitObjectInfo = FunctionInfo<NewInitObjectFn>(NewInitObject);
|
||||
|
||||
typedef JSObject *(*NewInitObjectWithClassPrototypeFn)(JSContext *, HandlePlainObject);
|
||||
static const VMFunction NewInitObjectWithClassPrototypeInfo =
|
||||
FunctionInfo<NewInitObjectWithClassPrototypeFn>(NewInitObjectWithClassPrototype);
|
||||
typedef PlainObject *(*ObjectCreateWithTemplateFn)(JSContext *, HandlePlainObject);
|
||||
static const VMFunction ObjectCreateWithTemplateInfo =
|
||||
FunctionInfo<ObjectCreateWithTemplateFn>(ObjectCreateWithTemplate);
|
||||
|
||||
void
|
||||
CodeGenerator::visitNewObjectVMCall(LNewObject *lir)
|
||||
|
@ -4056,10 +4056,12 @@ CodeGenerator::visitNewObjectVMCall(LNewObject *lir)
|
|||
// that derives its class from its prototype instead of being
|
||||
// JSObject::class_'d) from self-hosted code, we need a different init
|
||||
// function.
|
||||
if (lir->mir()->templateObjectIsClassPrototype())
|
||||
callVM(NewInitObjectWithClassPrototypeInfo, lir);
|
||||
else
|
||||
if (lir->mir()->mode() == MNewObject::ObjectLiteral) {
|
||||
callVM(NewInitObjectInfo, lir);
|
||||
} else {
|
||||
MOZ_ASSERT(lir->mir()->mode() == MNewObject::ObjectCreate);
|
||||
callVM(ObjectCreateWithTemplateInfo, lir);
|
||||
}
|
||||
|
||||
if (ReturnReg != objReg)
|
||||
masm.movePtr(ReturnReg, objReg);
|
||||
|
|
|
@ -6120,7 +6120,7 @@ IonBuilder::jsop_newobject()
|
|||
templateObject->hasSingletonType()
|
||||
? gc::TenuredHeap
|
||||
: templateObject->type()->initialHeap(constraints()),
|
||||
/* templateObjectIsClassPrototype = */ false);
|
||||
MNewObject::ObjectLiteral);
|
||||
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
|
|
|
@ -755,6 +755,9 @@ class IonBuilder
|
|||
InliningStatus inlineRegExpExec(CallInfo &callInfo);
|
||||
InliningStatus inlineRegExpTest(CallInfo &callInfo);
|
||||
|
||||
// Object natives.
|
||||
InliningStatus inlineObjectCreate(CallInfo &callInfo);
|
||||
|
||||
// Atomics natives.
|
||||
InliningStatus inlineAtomicsCompareExchange(CallInfo &callInfo);
|
||||
InliningStatus inlineAtomicsLoad(CallInfo &callInfo);
|
||||
|
|
|
@ -163,6 +163,10 @@ IonBuilder::inlineNativeCall(CallInfo &callInfo, JSFunction *target)
|
|||
if (native == regexp_test)
|
||||
return inlineRegExpTest(callInfo);
|
||||
|
||||
// Object natives.
|
||||
if (native == obj_create)
|
||||
return inlineObjectCreate(callInfo);
|
||||
|
||||
// Array intrinsics.
|
||||
if (native == intrinsic_UnsafePutElements)
|
||||
return inlineUnsafePutElements(callInfo);
|
||||
|
@ -1601,6 +1605,50 @@ IonBuilder::inlineSubstringKernel(CallInfo &callInfo)
|
|||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineObjectCreate(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 1 || callInfo.constructing())
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
NativeObject *templateObject = inspector->getTemplateObjectForNative(pc, obj_create);
|
||||
if (!templateObject)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
MOZ_ASSERT(templateObject->is<PlainObject>());
|
||||
MOZ_ASSERT(!templateObject->hasSingletonType());
|
||||
|
||||
// Ensure the argument matches the template object's prototype.
|
||||
MDefinition *arg = callInfo.getArg(0);
|
||||
if (JSObject *proto = templateObject->getProto()) {
|
||||
if (IsInsideNursery(proto))
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
types::TemporaryTypeSet *types = arg->resultTypeSet();
|
||||
if (!types || types->getSingleton() != proto)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
MOZ_ASSERT(types->getKnownMIRType() == MIRType_Object);
|
||||
} else {
|
||||
if (arg->type() != MIRType_Null)
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
|
||||
MConstant *templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
|
||||
current->add(templateConst);
|
||||
MNewObject *ins = MNewObject::New(alloc(), constraints(), templateConst,
|
||||
templateObject->type()->initialHeap(constraints()),
|
||||
MNewObject::ObjectCreate);
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
if (!resumeAfter(ins))
|
||||
return InliningStatus_Error;
|
||||
|
||||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineUnsafePutElements(CallInfo &callInfo)
|
||||
{
|
||||
|
|
|
@ -2793,17 +2793,21 @@ class MNewObject
|
|||
: public MUnaryInstruction,
|
||||
public NoTypePolicy::Data
|
||||
{
|
||||
public:
|
||||
enum Mode { ObjectLiteral, ObjectCreate };
|
||||
|
||||
private:
|
||||
gc::InitialHeap initialHeap_;
|
||||
bool templateObjectIsClassPrototype_;
|
||||
Mode mode_;
|
||||
|
||||
MNewObject(types::CompilerConstraintList *constraints, MConstant *templateConst,
|
||||
gc::InitialHeap initialHeap, bool templateObjectIsClassPrototype)
|
||||
gc::InitialHeap initialHeap, Mode mode)
|
||||
: MUnaryInstruction(templateConst),
|
||||
initialHeap_(initialHeap),
|
||||
templateObjectIsClassPrototype_(templateObjectIsClassPrototype)
|
||||
mode_(mode)
|
||||
{
|
||||
PlainObject *obj = templateObject();
|
||||
MOZ_ASSERT_IF(templateObjectIsClassPrototype, !shouldUseVM());
|
||||
MOZ_ASSERT_IF(mode != ObjectLiteral, !shouldUseVM());
|
||||
setResultType(MIRType_Object);
|
||||
if (!obj->hasSingletonType())
|
||||
setResultTypeSet(MakeSingletonTypeSet(constraints, obj));
|
||||
|
@ -2821,18 +2825,17 @@ class MNewObject
|
|||
|
||||
static MNewObject *New(TempAllocator &alloc, types::CompilerConstraintList *constraints,
|
||||
MConstant *templateConst, gc::InitialHeap initialHeap,
|
||||
bool templateObjectIsClassPrototype)
|
||||
Mode mode)
|
||||
{
|
||||
return new(alloc) MNewObject(constraints, templateConst, initialHeap,
|
||||
templateObjectIsClassPrototype);
|
||||
return new(alloc) MNewObject(constraints, templateConst, initialHeap, mode);
|
||||
}
|
||||
|
||||
// Returns true if the code generator should call through to the
|
||||
// VM rather than the fast path.
|
||||
bool shouldUseVM() const;
|
||||
|
||||
bool templateObjectIsClassPrototype() const {
|
||||
return templateObjectIsClassPrototype_;
|
||||
Mode mode() const {
|
||||
return mode_;
|
||||
}
|
||||
|
||||
PlainObject *templateObject() const {
|
||||
|
|
|
@ -1156,13 +1156,14 @@ MNewObject::writeRecoverData(CompactBufferWriter &writer) const
|
|||
{
|
||||
MOZ_ASSERT(canRecoverOnBailout());
|
||||
writer.writeUnsigned(uint32_t(RInstruction::Recover_NewObject));
|
||||
writer.writeByte(templateObjectIsClassPrototype_);
|
||||
MOZ_ASSERT(Mode(uint8_t(mode_)) == mode_);
|
||||
writer.writeByte(uint8_t(mode_));
|
||||
return true;
|
||||
}
|
||||
|
||||
RNewObject::RNewObject(CompactBufferReader &reader)
|
||||
{
|
||||
templateObjectIsClassPrototype_ = reader.readByte();
|
||||
mode_ = MNewObject::Mode(reader.readByte());
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -1173,10 +1174,12 @@ RNewObject::recover(JSContext *cx, SnapshotIterator &iter) const
|
|||
JSObject *resultObject = nullptr;
|
||||
|
||||
// See CodeGenerator::visitNewObjectVMCall
|
||||
if (templateObjectIsClassPrototype_)
|
||||
resultObject = NewInitObjectWithClassPrototype(cx, templateObject);
|
||||
else
|
||||
if (mode_ == MNewObject::ObjectLiteral) {
|
||||
resultObject = NewInitObject(cx, templateObject);
|
||||
} else {
|
||||
MOZ_ASSERT(mode_ == MNewObject::ObjectCreate);
|
||||
resultObject = ObjectCreateWithTemplate(cx, templateObject);
|
||||
}
|
||||
|
||||
if (!resultObject)
|
||||
return false;
|
||||
|
|
|
@ -613,7 +613,7 @@ class RTruncateToInt32 MOZ_FINAL : public RInstruction
|
|||
class RNewObject MOZ_FINAL : public RInstruction
|
||||
{
|
||||
private:
|
||||
bool templateObjectIsClassPrototype_;
|
||||
MNewObject::Mode mode_;
|
||||
|
||||
public:
|
||||
RINSTRUCTION_HEADER_(NewObject)
|
||||
|
|
|
@ -301,27 +301,6 @@ NewInitObject(JSContext *cx, HandlePlainObject templateObject)
|
|||
return obj;
|
||||
}
|
||||
|
||||
JSObject *
|
||||
NewInitObjectWithClassPrototype(JSContext *cx, HandlePlainObject templateObject)
|
||||
{
|
||||
MOZ_ASSERT(!templateObject->hasSingletonType());
|
||||
MOZ_ASSERT(!templateObject->hasLazyType());
|
||||
|
||||
NewObjectKind newKind = templateObject->type()->shouldPreTenure()
|
||||
? TenuredObject
|
||||
: GenericObject;
|
||||
PlainObject *obj = NewObjectWithGivenProto<PlainObject>(cx,
|
||||
templateObject->getProto(),
|
||||
cx->global(),
|
||||
newKind);
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
||||
obj->setType(templateObject->type());
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
bool
|
||||
ArraySpliceDense(JSContext *cx, HandleObject obj, uint32_t start, uint32_t deleteCount)
|
||||
{
|
||||
|
|
|
@ -270,6 +270,7 @@ template <class> struct TypeToDataType { /* Unexpected return type for a VMFunct
|
|||
template <> struct TypeToDataType<bool> { static const DataType result = Type_Bool; };
|
||||
template <> struct TypeToDataType<JSObject *> { static const DataType result = Type_Object; };
|
||||
template <> struct TypeToDataType<NativeObject *> { static const DataType result = Type_Object; };
|
||||
template <> struct TypeToDataType<PlainObject *> { static const DataType result = Type_Object; };
|
||||
template <> struct TypeToDataType<InlineTypedObject *> { static const DataType result = Type_Object; };
|
||||
template <> struct TypeToDataType<DeclEnvObject *> { static const DataType result = Type_Object; };
|
||||
template <> struct TypeToDataType<ArrayObject *> { static const DataType result = Type_Object; };
|
||||
|
@ -657,7 +658,6 @@ template<bool Equal>
|
|||
bool StringsEqual(JSContext *cx, HandleString left, HandleString right, bool *res);
|
||||
|
||||
JSObject *NewInitObject(JSContext *cx, HandlePlainObject templateObject);
|
||||
JSObject *NewInitObjectWithClassPrototype(JSContext *cx, HandlePlainObject templateObject);
|
||||
|
||||
bool ArrayPopDense(JSContext *cx, HandleObject obj, MutableHandleValue rval);
|
||||
bool ArrayPushDense(JSContext *cx, HandleArrayObject obj, HandleValue v, uint32_t *length);
|
||||
|
|
|
@ -2299,7 +2299,7 @@ TypeCompartment::addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey ke
|
|||
|
||||
if (!res) {
|
||||
RootedObject proto(cx);
|
||||
if (!GetBuiltinPrototype(cx, key.kind, &proto))
|
||||
if (key.kind != JSProto_Null && !GetBuiltinPrototype(cx, key.kind, &proto))
|
||||
return nullptr;
|
||||
|
||||
Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
|
||||
|
|
|
@ -309,6 +309,7 @@ inline const Class *
|
|||
GetClassForProtoKey(JSProtoKey key)
|
||||
{
|
||||
switch (key) {
|
||||
case JSProto_Null:
|
||||
case JSProto_Object:
|
||||
return &PlainObject::class_;
|
||||
case JSProto_Array:
|
||||
|
@ -369,7 +370,7 @@ inline TypeObject *
|
|||
GetTypeNewObject(JSContext *cx, JSProtoKey key)
|
||||
{
|
||||
RootedObject proto(cx);
|
||||
if (!GetBuiltinPrototype(cx, key, &proto))
|
||||
if (key != JSProto_Null && !GetBuiltinPrototype(cx, key, &proto))
|
||||
return nullptr;
|
||||
return cx->getNewType(GetClassForProtoKey(key), TaggedProto(proto.get()));
|
||||
}
|
||||
|
|
|
@ -1422,7 +1422,8 @@ js::NewObjectWithTypeCommon(JSContext *cx, HandleTypeObject type, JSObject *pare
|
|||
NewObjectCache &cache = cx->runtime()->newObjectCache;
|
||||
|
||||
NewObjectCache::EntryIndex entry = -1;
|
||||
if (parent == type->proto().toObject()->getParent() &&
|
||||
if (type->proto().isObject() &&
|
||||
parent == type->proto().toObject()->getParent() &&
|
||||
newKind == GenericObject &&
|
||||
type->clasp()->isNative() &&
|
||||
!cx->compartment()->hasObjectMetadataCallback())
|
||||
|
|
|
@ -545,6 +545,15 @@ NewObjectWithGivenProto(ExclusiveContext *cx, JSObject *proto, JSObject *parent,
|
|||
return NewObjectWithGivenProto<T>(cx, TaggedProto(proto), parent, newKind);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T *
|
||||
NewObjectWithGivenProto(ExclusiveContext *cx, JSObject *proto, JSObject *parent,
|
||||
gc::AllocKind allocKind, NewObjectKind newKind = GenericObject)
|
||||
{
|
||||
JSObject *obj = NewObjectWithGivenProto(cx, &T::class_, TaggedProto(proto), parent, newKind);
|
||||
return obj ? &obj->as<T>() : nullptr;
|
||||
}
|
||||
|
||||
inline bool
|
||||
FindProto(ExclusiveContext *cx, const js::Class *clasp, MutableHandleObject proto)
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче