FiltrationResult AbstractValue::filter(Graph& graph, const StructureSet& other) { if (isClear()) return FiltrationOK; // FIXME: This could be optimized for the common case of m_type not // having structures, array modes, or a specific value. // https://bugs.webkit.org/show_bug.cgi?id=109663 m_type &= other.speculationFromStructures(); m_arrayModes &= other.arrayModesFromStructures(); m_currentKnownStructure.filter(other); // It's possible that prior to the above two statements we had (Foo, TOP), where // Foo is a SpeculatedType that is disjoint with the passed StructureSet. In that // case, we will now have (None, [someStructure]). In general, we need to make // sure that new information gleaned from the SpeculatedType needs to be fed back // into the information gleaned from the StructureSet. m_currentKnownStructure.filter(m_type); if (m_currentKnownStructure.hasSingleton()) setFuturePossibleStructure(graph, m_currentKnownStructure.singleton()); filterArrayModesByType(); filterValueByType(); return normalizeClarity(); }
bool StructureSet::merge(const StructureSet& other) { if (other.isThin()) { if (other.singleStructure()) return add(other.singleStructure()); return false; } OutOfLineList* list = other.structureList(); if (list->m_length >= 2) { if (isThin()) { OutOfLineList* myNewList = OutOfLineList::create( list->m_length + !!singleStructure()); if (singleStructure()) { myNewList->m_length = 1; myNewList->list()[0] = singleStructure(); } set(myNewList); } bool changed = false; for (unsigned i = 0; i < list->m_length; ++i) changed |= addOutOfLine(list->list()[i]); return changed; } ASSERT(list->m_length); return add(list->list()[0]); }
GetByIdStatus GetByIdStatus::computeFor(const StructureSet& set, StringImpl* uid) { // For now we only handle the super simple self access case. We could handle the // prototype case in the future. if (set.isEmpty()) return GetByIdStatus(); if (toUInt32FromStringImpl(uid) != PropertyName::NotAnIndex) return GetByIdStatus(TakesSlowPath); GetByIdStatus result; result.m_state = Simple; result.m_wasSeenInJIT = false; for (unsigned i = 0; i < set.size(); ++i) { Structure* structure = set[i]; if (structure->typeInfo().overridesGetOwnPropertySlot() && structure->typeInfo().type() != GlobalObjectType) return GetByIdStatus(TakesSlowPath); if (!structure->propertyAccessesAreCacheable()) return GetByIdStatus(TakesSlowPath); unsigned attributes; PropertyOffset offset = structure->getConcurrently(uid, attributes); if (!isValidOffset(offset)) return GetByIdStatus(TakesSlowPath); // It's probably a prototype lookup. Give up on life for now, even though we could totally be way smarter about it. if (attributes & Accessor) return GetByIdStatus(MakesCalls); // We could be smarter here, like strenght-reducing this to a Call. if (!result.appendVariant(GetByIdVariant(structure, offset))) return GetByIdStatus(TakesSlowPath); } return result; }
bool StructureSet::isSubsetOf(const StructureSet& other) const { if (isThin()) { if (!singleStructure()) return true; return other.contains(singleStructure()); } if (other.isThin()) { if (!other.singleStructure()) return false; OutOfLineList* list = structureList(); if (list->m_length >= 2) return false; if (list->list()[0] == other.singleStructure()) return true; return false; } OutOfLineList* list = structureList(); for (unsigned i = 0; i < list->m_length; ++i) { if (!other.containsOutOfLine(list->list()[i])) return false; } return true; }
void StructureSet::exclude(const StructureSet& other) { if (other.isThin()) { if (other.singleStructure()) remove(other.singleStructure()); return; } if (isThin()) { if (!singleStructure()) return; if (other.contains(singleStructure())) clear(); return; } OutOfLineList* list = structureList(); for (unsigned i = 0; i < list->m_length; ++i) { if (!other.containsOutOfLine(list->list()[i])) continue; list->list()[i--] = list->list()[--list->m_length]; } if (!list->m_length) clear(); }
void noticeStructureCheck(VariableAccessData* variable, const StructureSet& set) { if (set.size() != 1) { noticeStructureCheck(variable, 0); return; } noticeStructureCheck(variable, set.singletonStructure()); }
void AbstractValue::set(Graph& graph, const StructureSet& set) { m_structure = set; m_arrayModes = set.arrayModesFromStructures(); m_type = set.speculationFromStructures(); m_value = JSValue(); checkConsistency(); assertIsRegistered(graph); }
void StructureSet::copyFromOutOfLine(const StructureSet& other) { ASSERT(!other.isThin() && other.m_pointer != reservedValue); OutOfLineList* otherList = other.structureList(); OutOfLineList* myList = OutOfLineList::create(otherList->m_length); myList->m_length = otherList->m_length; for (unsigned i = otherList->m_length; i--;) myList->list()[i] = otherList->list()[i]; set(myList); }
FiltrationResult AbstractValue::changeStructure(Graph& graph, const StructureSet& other) { m_type &= other.speculationFromStructures(); m_arrayModes = other.arrayModesFromStructures(); m_structure = other; filterValueByType(); return normalizeClarity(graph); }
void StructureSet::filter(const StructureSet& other) { if (other.isThin()) { if (!other.singleStructure() || !contains(other.singleStructure())) clear(); else { clear(); set(other.singleStructure()); } return; } ContainsOutOfLine containsOutOfLine(other); genericFilter(containsOutOfLine); }
void StructureAbstractValue::filter(const StructureSet& other) { SAMPLE("StructureAbstractValue filter set"); if (isTop()) { m_set = other; return; } if (isClobbered()) { // We have two choices here: // // Do nothing: It's legal to keep our set intact, which would essentially mean that for // now, our set would behave like TOP but after the next invalidation point it wold be // a finite set again. This may be a good choice if 'other' is much bigger than our // m_set. // // Replace m_set with other and clear the clobber bit: This is also legal, and means that // we're no longer clobbered. This is usually better because it immediately gives us a // smaller set. // // This scenario should come up rarely. We usually don't do anything to an abstract value // after it is clobbered. But we apply some heuristics. if (other.size() > m_set.size() + clobberedSupremacyThreshold) return; // Keep the clobbered set. m_set = other; setClobbered(false); return; } m_set.filter(other); }
void StructureAbstractValue::observeTransitions(const TransitionVector& vector) { SAMPLE("StructureAbstractValue observeTransitions"); if (isTop()) return; StructureSet newStructures; for (unsigned i = vector.size(); i--;) { ASSERT(!vector[i].previous->dfgShouldWatch()); if (!m_set.contains(vector[i].previous)) continue; newStructures.add(vector[i].next); } if (!m_set.merge(newStructures)) return; if (m_set.size() > polymorphismLimit) makeTop(); }
GetByIdVariant::GetByIdVariant( const StructureSet& structureSet, PropertyOffset offset, const ObjectPropertyConditionSet& conditionSet, std::unique_ptr<CallLinkStatus> callLinkStatus, JSFunction* intrinsicFunction) : m_structureSet(structureSet) , m_conditionSet(conditionSet) , m_offset(offset) , m_callLinkStatus(WTFMove(callLinkStatus)) , m_intrinsicFunction(intrinsicFunction) { if (!structureSet.size()) { ASSERT(offset == invalidOffset); ASSERT(conditionSet.isEmpty()); } if (intrinsicFunction) ASSERT(intrinsic() != NoIntrinsic); }
GetByIdVariant::GetByIdVariant( const StructureSet& structureSet, PropertyOffset offset, const IntendedStructureChain* chain, std::unique_ptr<CallLinkStatus> callLinkStatus) : m_structureSet(structureSet) , m_alternateBase(nullptr) , m_offset(offset) , m_callLinkStatus(WTF::move(callLinkStatus)) { if (!structureSet.size()) { ASSERT(offset == invalidOffset); ASSERT(!chain); } if (chain && chain->size()) { m_alternateBase = chain->terminalPrototype(); chain->gatherChecks(m_constantChecks); } }
bool StructureSet::operator==(const StructureSet& other) const { if (size() != other.size()) return false; return isSubsetOf(other); }
void registerStructures(const StructureSet& set) { for (unsigned i = set.size(); i--;) registerStructure(set[i]); }
PutByIdStatus PutByIdStatus::computeFor(JSGlobalObject* globalObject, const StructureSet& set, UniquedStringImpl* uid, bool isDirect) { if (parseIndex(*uid)) return PutByIdStatus(TakesSlowPath); if (set.isEmpty()) return PutByIdStatus(); PutByIdStatus result; result.m_state = Simple; for (unsigned i = 0; i < set.size(); ++i) { Structure* structure = set[i]; if (structure->typeInfo().overridesGetOwnPropertySlot() && structure->typeInfo().type() != GlobalObjectType) return PutByIdStatus(TakesSlowPath); if (!structure->propertyAccessesAreCacheable()) return PutByIdStatus(TakesSlowPath); unsigned attributes; PropertyOffset offset = structure->getConcurrently(uid, attributes); if (isValidOffset(offset)) { if (attributes & CustomAccessor) return PutByIdStatus(MakesCalls); if (attributes & (Accessor | ReadOnly)) return PutByIdStatus(TakesSlowPath); WatchpointSet* replaceSet = structure->propertyReplacementWatchpointSet(offset); if (!replaceSet || replaceSet->isStillValid()) { // When this executes, it'll create, and fire, this replacement watchpoint set. // That means that this has probably never executed or that something fishy is // going on. Also, we cannot create or fire the watchpoint set from the concurrent // JIT thread, so even if we wanted to do this, we'd need to have a lazy thingy. // So, better leave this alone and take slow path. return PutByIdStatus(TakesSlowPath); } PutByIdVariant variant = PutByIdVariant::replace(structure, offset, structure->inferredTypeDescriptorFor(uid)); if (!result.appendVariant(variant)) return PutByIdStatus(TakesSlowPath); continue; } // Our hypothesis is that we're doing a transition. Before we prove that this is really // true, we want to do some sanity checks. // Don't cache put transitions on dictionaries. if (structure->isDictionary()) return PutByIdStatus(TakesSlowPath); // If the structure corresponds to something that isn't an object, then give up, since // we don't want to be adding properties to strings. if (!structure->typeInfo().isObject()) return PutByIdStatus(TakesSlowPath); ObjectPropertyConditionSet conditionSet; if (!isDirect) { conditionSet = generateConditionsForPropertySetterMissConcurrently( globalObject->vm(), globalObject, structure, uid); if (!conditionSet.isValid()) return PutByIdStatus(TakesSlowPath); } // We only optimize if there is already a structure that the transition is cached to. Structure* transition = Structure::addPropertyTransitionToExistingStructureConcurrently(structure, uid, 0, offset); if (!transition) return PutByIdStatus(TakesSlowPath); ASSERT(isValidOffset(offset)); bool didAppend = result.appendVariant( PutByIdVariant::transition( structure, transition, conditionSet, offset, transition->inferredTypeDescriptorFor(uid))); if (!didAppend) return PutByIdStatus(TakesSlowPath); } return result; }
PutByIdStatus PutByIdStatus::computeFor(JSGlobalObject* globalObject, const StructureSet& set, StringImpl* uid, bool isDirect) { if (toUInt32FromStringImpl(uid) != PropertyName::NotAnIndex) return PutByIdStatus(TakesSlowPath); if (set.isEmpty()) return PutByIdStatus(); PutByIdStatus result; result.m_state = Simple; for (unsigned i = 0; i < set.size(); ++i) { Structure* structure = set[i]; if (structure->typeInfo().overridesGetOwnPropertySlot() && structure->typeInfo().type() != GlobalObjectType) return PutByIdStatus(TakesSlowPath); if (!structure->propertyAccessesAreCacheable()) return PutByIdStatus(TakesSlowPath); unsigned attributes; PropertyOffset offset = structure->getConcurrently(uid, attributes); if (isValidOffset(offset)) { if (attributes & CustomAccessor) return PutByIdStatus(MakesCalls); if (attributes & (Accessor | ReadOnly)) return PutByIdStatus(TakesSlowPath); WatchpointSet* replaceSet = structure->propertyReplacementWatchpointSet(offset); if (!replaceSet || replaceSet->isStillValid()) { // When this executes, it'll create, and fire, this replacement watchpoint set. // That means that this has probably never executed or that something fishy is // going on. Also, we cannot create or fire the watchpoint set from the concurrent // JIT thread, so even if we wanted to do this, we'd need to have a lazy thingy. // So, better leave this alone and take slow path. return PutByIdStatus(TakesSlowPath); } if (!result.appendVariant(PutByIdVariant::replace(structure, offset))) return PutByIdStatus(TakesSlowPath); continue; } // Our hypothesis is that we're doing a transition. Before we prove that this is really // true, we want to do some sanity checks. // Don't cache put transitions on dictionaries. if (structure->isDictionary()) return PutByIdStatus(TakesSlowPath); // If the structure corresponds to something that isn't an object, then give up, since // we don't want to be adding properties to strings. if (structure->typeInfo().type() == StringType) return PutByIdStatus(TakesSlowPath); RefPtr<IntendedStructureChain> chain; if (!isDirect) { chain = adoptRef(new IntendedStructureChain(globalObject, structure)); // If the prototype chain has setters or read-only properties, then give up. if (chain->mayInterceptStoreTo(uid)) return PutByIdStatus(TakesSlowPath); // If the prototype chain hasn't been normalized (i.e. there are proxies or dictionaries) // then give up. The dictionary case would only happen if this structure has not been // used in an optimized put_by_id transition. And really the only reason why we would // bail here is that I don't really feel like having the optimizing JIT go and flatten // dictionaries if we have evidence to suggest that those objects were never used as // prototypes in a cacheable prototype access - i.e. there's a good chance that some of // the other checks below will fail. if (structure->isProxy() || !chain->isNormalized()) return PutByIdStatus(TakesSlowPath); } // We only optimize if there is already a structure that the transition is cached to. Structure* transition = Structure::addPropertyTransitionToExistingStructureConcurrently(structure, uid, 0, offset); if (!transition) return PutByIdStatus(TakesSlowPath); ASSERT(isValidOffset(offset)); bool didAppend = result.appendVariant( PutByIdVariant::transition(structure, transition, chain.get(), offset)); if (!didAppend) return PutByIdStatus(TakesSlowPath); } return result; }