static void GeneratePrototypeGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder, ObjOperandId objId) { // The guards here protect against the effects of JSObject::swap(). If the // prototype chain is directly altered, then TI will toss the jitcode, so we // don't have to worry about it, and any other change to the holder, or // adding a shadowing property will result in reshaping the holder, and thus // the failure of the shape guard. MOZ_ASSERT(obj != holder); if (obj->hasUncacheableProto()) { // If the shape does not imply the proto, emit an explicit proto guard. writer.guardProto(objId, obj->getProto()); } JSObject* pobj = IsCacheableDOMProxy(obj) ? obj->getTaggedProto().toObjectOrNull() : obj->getProto(); if (!pobj) return; while (pobj != holder) { if (pobj->hasUncacheableProto()) { ObjOperandId protoId = writer.loadObject(pobj); if (pobj->isSingleton()) { // Singletons can have their group's |proto| mutated directly. writer.guardProto(protoId, pobj->getProto()); } else { writer.guardGroup(protoId, pobj->group()); } } pobj = pobj->getProto(); } }
bool GetPropIRGenerator::tryAttachDenseElementHole(HandleObject obj, ObjOperandId objId, ValOperandId indexId) { MOZ_ASSERT(idVal_.isInt32()); if (idVal_.toInt32() < 0) return false; if (!obj->isNative() || !CanAttachDenseElementHole(obj)) return false; // Guard on the shape, to prevent non-dense elements from appearing. writer.guardShape(objId, obj->as<NativeObject>().lastProperty()); if (obj->hasUncacheableProto()) { // If the shape does not imply the proto, emit an explicit proto guard. writer.guardProto(objId, obj->staticPrototype()); } JSObject* pobj = obj->staticPrototype(); while (pobj) { ObjOperandId protoId = writer.loadObject(pobj); // Non-singletons with uncacheable protos can change their proto // without a shape change, so also guard on the group (which determines // the proto) in this case. if (pobj->hasUncacheableProto() && !pobj->isSingleton()) writer.guardGroup(protoId, pobj->group()); // Make sure the shape matches, to avoid non-dense elements or anything // else that is being checked by CanAttachDenseElementHole. writer.guardShape(protoId, pobj->as<NativeObject>().lastProperty()); // Also make sure there are no dense elements. writer.guardNoDenseElements(protoId); pobj = pobj->staticPrototype(); } Int32OperandId int32IndexId = writer.guardIsInt32(indexId); writer.loadDenseElementHoleResult(objId, int32IndexId); writer.typeMonitorResult(); return true; }
PropertyCacheEntry * PropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, JSObject *pobj, const Shape *shape) { JS_ASSERT(this == &JS_PROPERTY_CACHE(cx)); JS_ASSERT(!cx->runtime->gcRunning); /* * Check for fill from js_SetPropertyHelper where the setter removed shape * from pobj (via unwatch or delete, e.g.). */ if (!pobj->nativeContains(cx, *shape)) { PCMETER(oddfills++); return JS_NO_PROP_CACHE_FILL; } /* * Check for overdeep scope and prototype chain. Because resolve, getter, * and setter hooks can change the prototype chain using JS_SetPrototype * after LookupPropertyWithFlags has returned, we calculate the protoIndex * here and not in LookupPropertyWithFlags. * * The scopeIndex can't be wrong. We require JS_SetParent calls to happen * before any running script might consult a parent-linked scope chain. If * this requirement is not satisfied, the fill in progress will never hit, * but scope shape tests ensure nothing malfunctions. */ JS_ASSERT_IF(obj == pobj, scopeIndex == 0); JSObject *tmp = obj; for (uintN i = 0; i < scopeIndex; i++) tmp = &tmp->asScope().enclosingScope(); uintN protoIndex = 0; while (tmp != pobj) { /* * Don't cache entries across prototype lookups which can mutate in * arbitrary ways without a shape change. */ if (tmp->hasUncacheableProto()) { PCMETER(noprotos++); return JS_NO_PROP_CACHE_FILL; } tmp = tmp->getProto(); /* * We cannot cache properties coming from native objects behind * non-native ones on the prototype chain. The non-natives can * mutate in arbitrary way without changing any shapes. */ if (!tmp || !tmp->isNative()) { PCMETER(noprotos++); return JS_NO_PROP_CACHE_FILL; } ++protoIndex; } typedef PropertyCacheEntry Entry; if (scopeIndex > Entry::MaxScopeIndex || protoIndex > Entry::MaxProtoIndex) { PCMETER(longchains++); return JS_NO_PROP_CACHE_FILL; } /* * Optimize the cached vword based on our parameters and the current pc's * opcode format flags. */ jsbytecode *pc; (void) cx->stack.currentScript(&pc); JSOp op = JSOp(*pc); const JSCodeSpec *cs = &js_CodeSpec[op]; if ((cs->format & JOF_SET) && obj->watched()) return JS_NO_PROP_CACHE_FILL; if (obj == pobj) { JS_ASSERT(scopeIndex == 0 && protoIndex == 0); } else { #ifdef DEBUG if (scopeIndex == 0) { JS_ASSERT(protoIndex != 0); JS_ASSERT((protoIndex == 1) == (obj->getProto() == pobj)); } #endif if (scopeIndex != 0 || protoIndex != 1) { /* * Make sure that a later shadowing assignment will enter * PurgeProtoChain and invalidate this entry, bug 479198. */ if (!obj->isDelegate()) return JS_NO_PROP_CACHE_FILL; } } PropertyCacheEntry *entry = &table[hash(pc, obj->lastProperty())]; PCMETER(entry->vword.isNull() || recycles++); entry->assign(pc, obj->lastProperty(), pobj->lastProperty(), shape, scopeIndex, protoIndex); empty = false; PCMETER(fills++); /* * The modfills counter is not exact. It increases if a getter or setter * recurse into the interpreter. */ PCMETER(entry == pctestentry || modfills++); PCMETER(pctestentry = NULL); return entry; }
PropertyCacheEntry * PropertyCache::fill(JSContext *cx, JSObject *obj, JSObject *pobj, Shape *shape) { JS_ASSERT(this == &JS_PROPERTY_CACHE(cx)); JS_ASSERT(!cx->runtime->isHeapBusy()); /* * Check for overdeep scope and prototype chain. Because resolve, getter, * and setter hooks can change the prototype chain using JS_SetPrototype * after LookupPropertyWithFlags has returned, we calculate the protoIndex * here and not in LookupPropertyWithFlags. */ JSObject *tmp = obj; unsigned protoIndex = 0; while (tmp != pobj) { /* * Don't cache entries across prototype lookups which can mutate in * arbitrary ways without a shape change. */ if (tmp->hasUncacheableProto()) { PCMETER(noprotos++); return JS_NO_PROP_CACHE_FILL; } tmp = tmp->getProto(); /* * We cannot cache properties coming from native objects behind * non-native ones on the prototype chain. The non-natives can * mutate in arbitrary way without changing any shapes. */ if (!tmp || !tmp->isNative()) { PCMETER(noprotos++); return JS_NO_PROP_CACHE_FILL; } ++protoIndex; } typedef PropertyCacheEntry Entry; if (protoIndex > Entry::MaxProtoIndex) { PCMETER(longchains++); return JS_NO_PROP_CACHE_FILL; } /* * Optimize the cached vword based on our parameters and the current pc's * opcode format flags. */ jsbytecode *pc; (void) cx->stack.currentScript(&pc); JSOp op = JSOp(*pc); const JSCodeSpec *cs = &js_CodeSpec[op]; if ((cs->format & JOF_SET) && obj->watched()) return JS_NO_PROP_CACHE_FILL; if (obj == pobj) { JS_ASSERT(protoIndex == 0); } else { JS_ASSERT(protoIndex != 0); JS_ASSERT((protoIndex == 1) == (obj->getProto() == pobj)); if (protoIndex != 1) { /* * Make sure that a later shadowing assignment will enter * PurgeProtoChain and invalidate this entry, bug 479198. */ if (!obj->isDelegate()) return JS_NO_PROP_CACHE_FILL; } } PropertyCacheEntry *entry = &table[hash(pc, obj->lastProperty())]; PCMETER(entry->vword.isNull() || recycles++); entry->assign(pc, obj->lastProperty(), pobj->lastProperty(), shape, protoIndex); empty = false; PCMETER(fills++); /* * The modfills counter is not exact. It increases if a getter or setter * recurse into the interpreter. */ PCMETER(entry == pctestentry || modfills++); PCMETER(pctestentry = NULL); return entry; }