mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-08 04:27:37 +00:00
Bug 931984 - Use baseline cache information for calling common getter/setters in Ion code, r=efaust.
This commit is contained in:
parent
9f18bd8517
commit
3d6cfa9861
@ -3321,7 +3321,7 @@ IsCacheableGetPropReadSlot(JSObject *obj, JSObject *holder, Shape *shape, bool i
|
||||
}
|
||||
|
||||
static bool
|
||||
IsCacheableGetPropCall(JSObject *obj, JSObject *holder, Shape *shape, bool *isScripted,
|
||||
IsCacheableGetPropCall(JSContext *cx, JSObject *obj, JSObject *holder, Shape *shape, bool *isScripted,
|
||||
bool isDOMProxy=false)
|
||||
{
|
||||
JS_ASSERT(isScripted);
|
||||
@ -3343,6 +3343,14 @@ IsCacheableGetPropCall(JSObject *obj, JSObject *holder, Shape *shape, bool *isSc
|
||||
return false;
|
||||
|
||||
JSFunction *func = &shape->getterObject()->as<JSFunction>();
|
||||
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
// Information from get prop call ICs may be used directly from Ion code,
|
||||
// and should not be nursery allocated.
|
||||
if (cx->runtime()->gcNursery.isInside(holder) || cx->runtime()->gcNursery.isInside(func))
|
||||
return false;
|
||||
#endif
|
||||
|
||||
if (func->isNative()) {
|
||||
*isScripted = false;
|
||||
return true;
|
||||
@ -3433,7 +3441,7 @@ IsCacheableSetPropAddSlot(JSContext *cx, HandleObject obj, HandleShape oldShape,
|
||||
}
|
||||
|
||||
static bool
|
||||
IsCacheableSetPropCall(JSObject *obj, JSObject *holder, Shape *shape, bool *isScripted)
|
||||
IsCacheableSetPropCall(JSContext *cx, JSObject *obj, JSObject *holder, Shape *shape, bool *isScripted)
|
||||
{
|
||||
JS_ASSERT(isScripted);
|
||||
|
||||
@ -3454,6 +3462,14 @@ IsCacheableSetPropCall(JSObject *obj, JSObject *holder, Shape *shape, bool *isSc
|
||||
return false;
|
||||
|
||||
JSFunction *func = &shape->setterObject()->as<JSFunction>();
|
||||
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
// Information from set prop call ICs may be used directly from Ion code,
|
||||
// and should not be nursery allocated.
|
||||
if (cx->runtime()->gcNursery.isInside(holder) || cx->runtime()->gcNursery.isInside(func))
|
||||
return false;
|
||||
#endif
|
||||
|
||||
if (func->isNative()) {
|
||||
*isScripted = false;
|
||||
return true;
|
||||
@ -3688,7 +3704,7 @@ static bool TryAttachNativeGetElemStub(JSContext *cx, HandleScript script, jsbyt
|
||||
}
|
||||
|
||||
bool getterIsScripted = false;
|
||||
if (IsCacheableGetPropCall(obj, holder, shape, &getterIsScripted, /*isDOMProxy=*/false)) {
|
||||
if (IsCacheableGetPropCall(cx, obj, holder, shape, &getterIsScripted, /*isDOMProxy=*/false)) {
|
||||
RootedFunction getter(cx, &shape->getterObject()->as<JSFunction>());
|
||||
|
||||
// If a suitable stub already exists, nothing else to do.
|
||||
@ -5822,7 +5838,7 @@ TryAttachNativeGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc,
|
||||
}
|
||||
|
||||
bool isScripted = false;
|
||||
bool cacheableCall = IsCacheableGetPropCall(obj, holder, shape, &isScripted, isDOMProxy);
|
||||
bool cacheableCall = IsCacheableGetPropCall(cx, obj, holder, shape, &isScripted, isDOMProxy);
|
||||
|
||||
// Try handling scripted getters.
|
||||
if (cacheableCall && isScripted && !isDOMProxy) {
|
||||
@ -6765,7 +6781,7 @@ TryAttachSetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICSetPr
|
||||
}
|
||||
|
||||
bool isScripted = false;
|
||||
bool cacheableCall = IsCacheableSetPropCall(obj, holder, shape, &isScripted);
|
||||
bool cacheableCall = IsCacheableSetPropCall(cx, obj, holder, shape, &isScripted);
|
||||
|
||||
// Try handling scripted setters.
|
||||
if (cacheableCall && isScripted) {
|
||||
|
@ -441,3 +441,33 @@ BaselineInspector::templateCallObject()
|
||||
|
||||
return &res->as<CallObject>();
|
||||
}
|
||||
|
||||
JSObject *
|
||||
BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonGetter)
|
||||
{
|
||||
const ICEntry &entry = icEntryFromPC(pc);
|
||||
for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) {
|
||||
if (stub->isGetProp_CallScripted() || stub->isGetProp_CallNative()) {
|
||||
ICGetPropCallGetter *nstub = static_cast<ICGetPropCallGetter *>(stub);
|
||||
*lastProperty = nstub->holderShape();
|
||||
*commonGetter = nstub->getter();
|
||||
return nstub->holder();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSObject *
|
||||
BaselineInspector::commonSetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonSetter)
|
||||
{
|
||||
const ICEntry &entry = icEntryFromPC(pc);
|
||||
for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) {
|
||||
if (stub->isSetProp_CallScripted() || stub->isSetProp_CallNative()) {
|
||||
ICSetPropCallSetter *nstub = static_cast<ICSetPropCallSetter *>(stub);
|
||||
*lastProperty = nstub->holderShape();
|
||||
*commonSetter = nstub->setter();
|
||||
return nstub->holder();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -116,6 +116,9 @@ class BaselineInspector
|
||||
|
||||
DeclEnvObject *templateDeclEnvObject();
|
||||
CallObject *templateCallObject();
|
||||
|
||||
JSObject *commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonGetter);
|
||||
JSObject *commonSetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonSetter);
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
|
@ -5494,24 +5494,6 @@ IonBuilder::jsop_initelem_array()
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
CanEffectlesslyCallLookupGenericOnObject(JSContext *cx, JSObject *obj, PropertyName *name)
|
||||
{
|
||||
while (obj) {
|
||||
if (!obj->isNative())
|
||||
return false;
|
||||
if (obj->getClass()->ops.lookupGeneric)
|
||||
return false;
|
||||
if (obj->nativeLookup(cx, NameToId(name)))
|
||||
return true;
|
||||
if (obj->getClass()->resolve != JS_ResolveStub &&
|
||||
obj->getClass()->resolve != (JSResolveOp)fun_resolve)
|
||||
return false;
|
||||
obj = obj->getProto();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_initprop(PropertyName *name)
|
||||
{
|
||||
@ -5950,6 +5932,22 @@ IonBuilder::maybeInsertResume()
|
||||
return resumeAfter(ins);
|
||||
}
|
||||
|
||||
static bool
|
||||
ClassHasEffectlessLookup(JSCompartment *comp, const Class *clasp, PropertyName *name)
|
||||
{
|
||||
if (!clasp->isNative() || clasp->ops.lookupGeneric)
|
||||
return false;
|
||||
if (clasp->resolve != JS_ResolveStub &&
|
||||
// Note: str_resolve only resolves integers, not names.
|
||||
clasp->resolve != (JSResolveOp)str_resolve &&
|
||||
(clasp->resolve != (JSResolveOp)fun_resolve ||
|
||||
FunctionHasResolveHook(comp->runtimeFromAnyThread(), name)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::testSingletonProperty(JSObject *obj, JSObject *singleton, PropertyName *name)
|
||||
{
|
||||
@ -5968,8 +5966,7 @@ IonBuilder::testSingletonProperty(JSObject *obj, JSObject *singleton, PropertyNa
|
||||
// property will change and trigger invalidation.
|
||||
|
||||
while (obj) {
|
||||
const Class *clasp = obj->getClass();
|
||||
if (!clasp->isNative() || clasp->ops.lookupGeneric)
|
||||
if (!ClassHasEffectlessLookup(compartment, obj->getClass(), name))
|
||||
return false;
|
||||
|
||||
types::TypeObjectKey *objType = types::TypeObjectKey::get(obj);
|
||||
@ -5983,9 +5980,6 @@ IonBuilder::testSingletonProperty(JSObject *obj, JSObject *singleton, PropertyNa
|
||||
return false;
|
||||
}
|
||||
|
||||
if (clasp->resolve != JS_ResolveStub && clasp->resolve != (JSResolveOp)fun_resolve)
|
||||
return false;
|
||||
|
||||
obj = obj->getProto();
|
||||
}
|
||||
|
||||
@ -7574,177 +7568,70 @@ IonBuilder::jsop_not()
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
TestClassHasAccessorHook(const Class *clasp, bool isGetter)
|
||||
{
|
||||
if (isGetter && clasp->ops.getGeneric)
|
||||
return true;
|
||||
if (!isGetter && clasp->ops.setGeneric)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
TestTypeHasOwnProperty(types::TypeObjectKey *typeObj, PropertyName *name, bool &cont)
|
||||
{
|
||||
cont = true;
|
||||
types::HeapTypeSetKey propSet = typeObj->property(NameToId(name));
|
||||
if (propSet.maybeTypes() && !propSet.maybeTypes()->empty())
|
||||
cont = false;
|
||||
if (JSObject *obj = typeObj->singleton()) {
|
||||
// Global objects may have undefined singleton properties without associated
|
||||
// type information.
|
||||
if (types::CanHaveEmptyPropertyTypesForOwnProperty(obj))
|
||||
cont = false;
|
||||
}
|
||||
// Note: Callers must explicitly freeze the property type set later on if optimizing.
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
TestCommonAccessorProtoChain(JSContext *cx, PropertyName *name,
|
||||
bool isGetter, JSObject *foundProto,
|
||||
JSObject *obj, bool &cont)
|
||||
{
|
||||
cont = false;
|
||||
JSObject *curObj = obj;
|
||||
JSObject *stopAt = foundProto->getProto();
|
||||
while (curObj != stopAt) {
|
||||
// Don't optimize if we have a hook that would have to be called.
|
||||
if (TestClassHasAccessorHook(curObj->getClass(), isGetter))
|
||||
return true;
|
||||
|
||||
// Check here to make sure that everyone has Type Objects with known
|
||||
// properties between them and the proto we found the accessor on. We
|
||||
// need those to add freezes safely. NOTE: We do not do this above, as
|
||||
// we may be able to freeze all the types up to where we found the
|
||||
// property, even if there are unknown types higher in the prototype
|
||||
// chain.
|
||||
if (curObj != foundProto) {
|
||||
types::TypeObjectKey *typeObj = types::TypeObjectKey::get(curObj);
|
||||
if (typeObj->unknownProperties())
|
||||
return true;
|
||||
|
||||
// Check here to make sure that nobody on the prototype chain is
|
||||
// marked as having the property as an "own property". This can
|
||||
// happen in cases of |delete| having been used, or cases with
|
||||
// watched objects. If TI ever decides to be more accurate about
|
||||
// |delete| handling, this should go back to curObj->watched().
|
||||
|
||||
// Even though we are not directly accessing the properties on the whole
|
||||
// prototype chain, we need to fault in the sets anyway, as we need
|
||||
// to freeze on them.
|
||||
bool lcont;
|
||||
if (!TestTypeHasOwnProperty(typeObj, name, lcont))
|
||||
return false;
|
||||
if (!lcont)
|
||||
return true;
|
||||
}
|
||||
|
||||
curObj = curObj->getProto();
|
||||
}
|
||||
cont = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
SearchCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types,
|
||||
PropertyName *name, bool isGetter,
|
||||
JSObject *&found, JSObject *&foundProto, bool &cont)
|
||||
{
|
||||
cont = false;
|
||||
for (unsigned i = 0; i < types->getObjectCount(); i++) {
|
||||
RootedObject curObj(cx, types->getSingleObject(i));
|
||||
|
||||
// Non-Singleton type
|
||||
if (!curObj) {
|
||||
types::TypeObjectKey *typeObj = types->getObject(i);
|
||||
if (!typeObj)
|
||||
continue;
|
||||
|
||||
if (typeObj->unknownProperties())
|
||||
return true;
|
||||
|
||||
// If the class of the object has a hook, we can't
|
||||
// inline, as we would need to call the hook.
|
||||
if (TestClassHasAccessorHook(typeObj->clasp(), isGetter))
|
||||
return true;
|
||||
|
||||
// If the type has an own property, we can't be sure we don't shadow
|
||||
// the chain.
|
||||
bool lcont;
|
||||
if (!TestTypeHasOwnProperty(typeObj, name, lcont))
|
||||
return false;
|
||||
if (!lcont)
|
||||
return true;
|
||||
|
||||
// Otherwise try using the prototype.
|
||||
curObj = typeObj->proto().toObjectOrNull();
|
||||
} else {
|
||||
// We can't optimize setters on watched singleton objects. A getter
|
||||
// on an own property can be protected with the prototype
|
||||
// shapeguard, though.
|
||||
if (!isGetter && curObj->watched())
|
||||
return true;
|
||||
}
|
||||
|
||||
// Turns out that we need to check for a property lookup op, else we
|
||||
// will end up calling it mid-compilation.
|
||||
if (!CanEffectlesslyCallLookupGenericOnObject(cx, curObj, name))
|
||||
return true;
|
||||
|
||||
RootedId idRoot(cx, NameToId(name));
|
||||
RootedObject proto(cx);
|
||||
RootedShape shape(cx);
|
||||
if (!JSObject::lookupGeneric(cx, curObj, idRoot, &proto, &shape))
|
||||
return false;
|
||||
|
||||
if (!shape)
|
||||
return true;
|
||||
|
||||
// We want to optimize specialized getters/setters. The defaults will
|
||||
// hit the slot optimization.
|
||||
if (isGetter) {
|
||||
if (shape->hasDefaultGetter() || !shape->hasGetterValue())
|
||||
return true;
|
||||
} else {
|
||||
if (shape->hasDefaultSetter() || !shape->hasSetterValue())
|
||||
return true;
|
||||
}
|
||||
|
||||
JSObject * curFound = isGetter ? shape->getterObject():
|
||||
shape->setterObject();
|
||||
|
||||
// Save the first seen, or verify uniqueness.
|
||||
if (!found) {
|
||||
if (!curFound->is<JSFunction>())
|
||||
return true;
|
||||
found = curFound;
|
||||
} else if (found != curFound) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// We only support cases with a single prototype shared. This is
|
||||
// overwhelmingly more likely than having multiple different prototype
|
||||
// chains with the same custom property function.
|
||||
if (!foundProto)
|
||||
foundProto = proto;
|
||||
else if (foundProto != proto)
|
||||
return true;
|
||||
|
||||
bool lcont;
|
||||
if (!TestCommonAccessorProtoChain(cx, name, isGetter, foundProto, curObj, lcont))
|
||||
return false;
|
||||
if (!lcont)
|
||||
return true;
|
||||
}
|
||||
cont = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::freezePropTypeSets(types::TemporaryTypeSet *types,
|
||||
JSObject *foundProto, PropertyName *name)
|
||||
IonBuilder::objectsHaveCommonPrototype(types::TemporaryTypeSet *types, PropertyName *name,
|
||||
bool isGetter, JSObject *foundProto)
|
||||
{
|
||||
// With foundProto a prototype with a getter or setter for name, return
|
||||
// whether looking up name on any object in |types| will go through
|
||||
// foundProto, i.e. all the objects have foundProto on their prototype
|
||||
// chain and do not have a property for name before reaching foundProto.
|
||||
|
||||
// No sense looking if we don't know what's going on.
|
||||
if (!types || types->unknownObject())
|
||||
return false;
|
||||
|
||||
for (unsigned i = 0; i < types->getObjectCount(); i++) {
|
||||
if (types->getSingleObject(i) == foundProto)
|
||||
continue;
|
||||
|
||||
types::TypeObjectKey *type = types->getObject(i);
|
||||
if (!type)
|
||||
continue;
|
||||
|
||||
while (type) {
|
||||
if (type->unknownProperties())
|
||||
return false;
|
||||
|
||||
const Class *clasp = type->clasp();
|
||||
if (!ClassHasEffectlessLookup(compartment, clasp, name))
|
||||
return false;
|
||||
|
||||
// Look for a getter/setter on the class itself which may need
|
||||
// to be called.
|
||||
if (isGetter && clasp->ops.getGeneric)
|
||||
return false;
|
||||
if (!isGetter && clasp->ops.setGeneric)
|
||||
return false;
|
||||
|
||||
// Note: freezePropertiesForCommonPropFunc will freeze the property
|
||||
// type sets later on if optimizing.
|
||||
types::HeapTypeSetKey property = type->property(NameToId(name));
|
||||
if (property.maybeTypes() && !property.maybeTypes()->empty())
|
||||
return false;
|
||||
if (JSObject *obj = type->singleton()) {
|
||||
if (types::CanHaveEmptyPropertyTypesForOwnProperty(obj))
|
||||
return false;
|
||||
}
|
||||
|
||||
JSObject *proto = type->proto().toObjectOrNull();
|
||||
if (proto == foundProto)
|
||||
break;
|
||||
if (!proto) {
|
||||
// The foundProto being searched for did not show up on the
|
||||
// object's prototype chain.
|
||||
return false;
|
||||
}
|
||||
type = types::TypeObjectKey::get(type->proto().toObjectOrNull());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
IonBuilder::freezePropertiesForCommonPrototype(types::TemporaryTypeSet *types, PropertyName *name,
|
||||
JSObject *foundProto)
|
||||
{
|
||||
for (unsigned i = 0; i < types->getObjectCount(); i++) {
|
||||
// If we found a Singleton object's own-property, there's nothing to
|
||||
@ -7756,8 +7643,6 @@ IonBuilder::freezePropTypeSets(types::TemporaryTypeSet *types,
|
||||
if (!type)
|
||||
continue;
|
||||
|
||||
// Walk the prototype chain. Everyone has to have the property, since we
|
||||
// just checked, so propSet cannot be nullptr.
|
||||
while (true) {
|
||||
types::HeapTypeSetKey property = type->property(NameToId(name));
|
||||
JS_ALWAYS_TRUE(!property.isOwnProperty(constraints()));
|
||||
@ -7770,37 +7655,20 @@ IonBuilder::freezePropTypeSets(types::TemporaryTypeSet *types,
|
||||
type = types::TypeObjectKey::get(type->proto().toObjectOrNull());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
IonBuilder::testCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types, PropertyName *name,
|
||||
JSFunction **funcp, bool isGetter, bool *isDOM,
|
||||
MDefinition **guardOut)
|
||||
inline MDefinition *
|
||||
IonBuilder::testCommonGetterSetter(types::TemporaryTypeSet *types, PropertyName *name,
|
||||
bool isGetter, JSObject *foundProto, Shape *lastProperty)
|
||||
{
|
||||
JSObject *found = nullptr;
|
||||
JSObject *foundProto = nullptr;
|
||||
// Check if all objects being accessed will lookup the name through foundProto.
|
||||
if (!objectsHaveCommonPrototype(types, name, isGetter, foundProto))
|
||||
return nullptr;
|
||||
|
||||
*funcp = nullptr;
|
||||
*isDOM = false;
|
||||
|
||||
// No sense looking if we don't know what's going on.
|
||||
if (!types || types->unknownObject())
|
||||
return true;
|
||||
|
||||
// Iterate down all the types to see if they all have the same getter or
|
||||
// setter.
|
||||
bool cont;
|
||||
if (!SearchCommonPropFunc(cx, types, name, isGetter, found, foundProto, cont))
|
||||
return false;
|
||||
if (!cont)
|
||||
return true;
|
||||
|
||||
// No need to add a freeze if we didn't find anything
|
||||
if (!found)
|
||||
return true;
|
||||
|
||||
JS_ASSERT(foundProto);
|
||||
// We can optimize the getter/setter, so freeze all involved properties to
|
||||
// ensure there isn't a lower shadowing getter or setter installed in the
|
||||
// future.
|
||||
freezePropertiesForCommonPrototype(types, name, foundProto);
|
||||
|
||||
// Add a shape guard on the prototype we found the property on. The rest of
|
||||
// the prototype chain is guarded by TI freezes. Note that a shape guard is
|
||||
@ -7808,23 +7676,8 @@ IonBuilder::testCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types, Pr
|
||||
// are no lookup hooks for this property.
|
||||
MInstruction *wrapper = MConstant::New(ObjectValue(*foundProto));
|
||||
current->add(wrapper);
|
||||
wrapper = addShapeGuard(wrapper, foundProto->lastProperty(), Bailout_ShapeGuard);
|
||||
|
||||
// Pass the guard back so it can be an operand.
|
||||
if (guardOut) {
|
||||
JS_ASSERT(wrapper->isGuardShape());
|
||||
*guardOut = wrapper;
|
||||
}
|
||||
|
||||
// Now we have to freeze all the property typesets to ensure there isn't a
|
||||
// lower shadowing getter or setter installed in the future.
|
||||
if (!freezePropTypeSets(types, foundProto, name))
|
||||
return false;
|
||||
|
||||
*funcp = &found->as<JSFunction>();
|
||||
*isDOM = types->isDOMClass();
|
||||
|
||||
return true;
|
||||
return addShapeGuard(wrapper, lastProperty, Bailout_ShapeGuard);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -8283,21 +8136,20 @@ IonBuilder::getPropTryCommonGetter(bool *emitted, PropertyName *name,
|
||||
types::TemporaryTypeSet *types)
|
||||
{
|
||||
JS_ASSERT(*emitted == false);
|
||||
JSFunction *commonGetter;
|
||||
bool isDOM;
|
||||
MDefinition *guard;
|
||||
|
||||
Shape *lastProperty = NULL;
|
||||
JSFunction *commonGetter = NULL;
|
||||
JSObject *foundProto = inspector->commonGetPropFunction(pc, &lastProperty, &commonGetter);
|
||||
if (!foundProto)
|
||||
return true;
|
||||
|
||||
types::TemporaryTypeSet *objTypes = current->peek(-1)->resultTypeSet();
|
||||
|
||||
if (!testCommonPropFunc(cx, objTypes, name, &commonGetter, true, &isDOM, &guard))
|
||||
return false;
|
||||
if (!commonGetter)
|
||||
MDefinition *guard = testCommonGetterSetter(objTypes, name, /* isGetter = */ true,
|
||||
foundProto, lastProperty);
|
||||
if (!guard)
|
||||
return true;
|
||||
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
if (GetIonContext()->runtime->gcNursery.isInside(commonGetter))
|
||||
return true;
|
||||
#endif
|
||||
bool isDOM = objTypes->isDOMClass();
|
||||
|
||||
MDefinition *obj = current->pop();
|
||||
|
||||
@ -8564,16 +8416,20 @@ IonBuilder::setPropTryCommonSetter(bool *emitted, MDefinition *obj,
|
||||
{
|
||||
JS_ASSERT(*emitted == false);
|
||||
|
||||
JSFunction *commonSetter;
|
||||
bool isDOM;
|
||||
Shape *lastProperty = NULL;
|
||||
JSFunction *commonSetter = NULL;
|
||||
JSObject *foundProto = inspector->commonSetPropFunction(pc, &lastProperty, &commonSetter);
|
||||
if (!foundProto)
|
||||
return true;
|
||||
|
||||
types::TemporaryTypeSet *objTypes = obj->resultTypeSet();
|
||||
if (!testCommonPropFunc(cx, objTypes, name, &commonSetter, false, &isDOM, nullptr))
|
||||
return false;
|
||||
|
||||
if (!commonSetter)
|
||||
MDefinition *guard = testCommonGetterSetter(objTypes, name, /* isGetter = */ false,
|
||||
foundProto, lastProperty);
|
||||
if (!guard)
|
||||
return true;
|
||||
|
||||
bool isDOM = objTypes->isDOMClass();
|
||||
|
||||
// Emit common setter.
|
||||
|
||||
// Setters can be called even if the property write needs a type
|
||||
|
@ -626,10 +626,12 @@ class IonBuilder : public MIRGenerator
|
||||
MDefinition *patchInlinedReturn(CallInfo &callInfo, MBasicBlock *exit, MBasicBlock *bottom);
|
||||
MDefinition *patchInlinedReturns(CallInfo &callInfo, MIRGraphExits &exits, MBasicBlock *bottom);
|
||||
|
||||
inline bool testCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types,
|
||||
PropertyName *name, JSFunction **funcp,
|
||||
bool isGetter, bool *isDOM,
|
||||
MDefinition **guardOut);
|
||||
bool objectsHaveCommonPrototype(types::TemporaryTypeSet *types, PropertyName *name,
|
||||
bool isGetter, JSObject *foundProto);
|
||||
void freezePropertiesForCommonPrototype(types::TemporaryTypeSet *types, PropertyName *name,
|
||||
JSObject *foundProto);
|
||||
MDefinition *testCommonGetterSetter(types::TemporaryTypeSet *types, PropertyName *name,
|
||||
bool isGetter, JSObject *foundProto, Shape *lastProperty);
|
||||
bool testShouldDOMCall(types::TypeSet *inTypes,
|
||||
JSFunction *func, JSJitInfo::OpType opType);
|
||||
|
||||
|
@ -247,6 +247,22 @@ ResolveInterpretedFunctionPrototype(JSContext *cx, HandleObject obj)
|
||||
return proto;
|
||||
}
|
||||
|
||||
bool
|
||||
js::FunctionHasResolveHook(JSRuntime *rt, PropertyName *name)
|
||||
{
|
||||
if (name == rt->atomState.prototype || name == rt->atomState.length || name == rt->atomState.name)
|
||||
return true;
|
||||
|
||||
for (unsigned i = 0; i < ArrayLength(poisonPillProps); i++) {
|
||||
const uint16_t offset = poisonPillProps[i];
|
||||
|
||||
if (name == OFFSET_TO_NAME(rt, offset))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
js::fun_resolve(JSContext *cx, HandleObject obj, HandleId id, unsigned flags,
|
||||
MutableHandleObject objp)
|
||||
|
@ -489,9 +489,12 @@ DefineFunction(JSContext *cx, HandleObject obj, HandleId id, JSNative native,
|
||||
gc::AllocKind allocKind = JSFunction::FinalizeKind,
|
||||
NewObjectKind newKind = GenericObject);
|
||||
|
||||
bool
|
||||
FunctionHasResolveHook(JSRuntime *rt, PropertyName *name);
|
||||
|
||||
extern bool
|
||||
fun_resolve(JSContext *cx, js::HandleObject obj, js::HandleId id,
|
||||
unsigned flags, js::MutableHandleObject objp);
|
||||
fun_resolve(JSContext *cx, HandleObject obj, HandleId id,
|
||||
unsigned flags, MutableHandleObject objp);
|
||||
|
||||
// ES6 9.2.5 IsConstructor
|
||||
bool IsConstructor(const Value &v);
|
||||
|
@ -382,9 +382,9 @@ str_enumerate(JSContext *cx, HandleObject obj)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
str_resolve(JSContext *cx, HandleObject obj, HandleId id, unsigned flags,
|
||||
MutableHandleObject objp)
|
||||
bool
|
||||
js::str_resolve(JSContext *cx, HandleObject obj, HandleId id, unsigned flags,
|
||||
MutableHandleObject objp)
|
||||
{
|
||||
if (!JSID_IS_INT(id))
|
||||
return true;
|
||||
|
@ -355,6 +355,10 @@ str_search(JSContext *cx, unsigned argc, Value *vp);
|
||||
bool
|
||||
str_split(JSContext *cx, unsigned argc, Value *vp);
|
||||
|
||||
bool
|
||||
str_resolve(JSContext *cx, HandleObject obj, HandleId id, unsigned flags,
|
||||
MutableHandleObject objp);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
extern bool
|
||||
|
Loading…
Reference in New Issue
Block a user