void StructureRareData::setObjectToStringValue(ExecState* exec, VM& vm, Structure* ownStructure, JSString* value, PropertySlot toStringTagSymbolSlot) { if (m_giveUpOnObjectToStringValueCache) return; ObjectPropertyConditionSet conditionSet; if (toStringTagSymbolSlot.isValue()) { // We don't handle the own property case of Symbol.toStringTag because we would never know if a new // object transitioning to the same structure had the same value stored in Symbol.toStringTag. // Additionally, this is a super unlikely case anyway. if (!toStringTagSymbolSlot.isCacheable() || toStringTagSymbolSlot.slotBase()->structure(vm) == ownStructure) return; // This will not create a condition for the current structure but that is good because we know the Symbol.toStringTag // is not on the ownStructure so we will transisition if one is added and this cache will no longer be used. conditionSet = generateConditionsForPrototypePropertyHit(vm, this, exec, ownStructure, toStringTagSymbolSlot.slotBase(), vm.propertyNames->toStringTagSymbol.impl()); ASSERT(!conditionSet.isValid() || conditionSet.hasOneSlotBaseCondition()); } else if (toStringTagSymbolSlot.isUnset()) conditionSet = generateConditionsForPropertyMiss(vm, this, exec, ownStructure, vm.propertyNames->toStringTagSymbol.impl()); else return; if (!conditionSet.isValid()) { m_giveUpOnObjectToStringValueCache = true; return; } ObjectPropertyCondition equivCondition; for (const ObjectPropertyCondition& condition : conditionSet) { if (condition.condition().kind() == PropertyCondition::Presence) { ASSERT(isValidOffset(condition.offset())); condition.object()->structure(vm)->startWatchingPropertyForReplacements(vm, condition.offset()); equivCondition = condition.attemptToMakeEquivalenceWithoutBarrier(); // The equivalence condition won't be watchable if we have already seen a replacement. if (!equivCondition.isWatchable()) { m_giveUpOnObjectToStringValueCache = true; return; } } else if (!condition.isWatchable()) { m_giveUpOnObjectToStringValueCache = true; return; } } ASSERT(conditionSet.structuresEnsureValidity()); for (ObjectPropertyCondition condition : conditionSet) { if (condition.condition().kind() == PropertyCondition::Presence) { m_objectToStringAdaptiveInferredValueWatchpoint = std::make_unique<ObjectToStringAdaptiveInferredPropertyValueWatchpoint>(equivCondition, this); m_objectToStringAdaptiveInferredValueWatchpoint->install(); } else m_objectToStringAdaptiveWatchpointSet.add(condition, this)->install(); } m_objectToStringValue.set(vm, this, value); }
PutByIdStatus PutByIdStatus::computeForStubInfo( const ConcurrentJITLocker& locker, CodeBlock* profiledBlock, StructureStubInfo* stubInfo, UniquedStringImpl* uid, CallLinkStatus::ExitSiteData callExitSiteData) { if (!stubInfo || !stubInfo->everConsidered) return PutByIdStatus(); if (stubInfo->tookSlowPath) return PutByIdStatus(TakesSlowPath); switch (stubInfo->cacheType) { case CacheType::Unset: // This means that we attempted to cache but failed for some reason. return PutByIdStatus(TakesSlowPath); case CacheType::PutByIdReplace: { PropertyOffset offset = stubInfo->u.byIdSelf.baseObjectStructure->getConcurrently(uid); if (isValidOffset(offset)) { return PutByIdVariant::replace( stubInfo->u.byIdSelf.baseObjectStructure.get(), offset, InferredType::Top); } return PutByIdStatus(TakesSlowPath); } case CacheType::Stub: { PolymorphicAccess* list = stubInfo->u.stub; PutByIdStatus result; result.m_state = Simple; State slowPathState = TakesSlowPath; for (unsigned i = 0; i < list->size(); ++i) { const AccessCase& access = list->at(i); if (access.doesCalls()) slowPathState = MakesCalls; } for (unsigned i = 0; i < list->size(); ++i) { const AccessCase& access = list->at(i); if (access.viaProxy()) return PutByIdStatus(slowPathState); PutByIdVariant variant; switch (access.type()) { case AccessCase::Replace: { Structure* structure = access.structure(); PropertyOffset offset = structure->getConcurrently(uid); if (!isValidOffset(offset)) return PutByIdStatus(slowPathState); variant = PutByIdVariant::replace( structure, offset, structure->inferredTypeDescriptorFor(uid)); break; } case AccessCase::Transition: { PropertyOffset offset = access.newStructure()->getConcurrently(uid); if (!isValidOffset(offset)) return PutByIdStatus(slowPathState); ObjectPropertyConditionSet conditionSet = access.conditionSet(); if (!conditionSet.structuresEnsureValidity()) return PutByIdStatus(slowPathState); variant = PutByIdVariant::transition( access.structure(), access.newStructure(), conditionSet, offset, access.newStructure()->inferredTypeDescriptorFor(uid)); break; } case AccessCase::Setter: { Structure* structure = access.structure(); ComplexGetStatus complexGetStatus = ComplexGetStatus::computeFor( structure, access.conditionSet(), uid); switch (complexGetStatus.kind()) { case ComplexGetStatus::ShouldSkip: continue; case ComplexGetStatus::TakesSlowPath: return PutByIdStatus(slowPathState); case ComplexGetStatus::Inlineable: { CallLinkInfo* callLinkInfo = access.callLinkInfo(); ASSERT(callLinkInfo); std::unique_ptr<CallLinkStatus> callLinkStatus = std::make_unique<CallLinkStatus>( CallLinkStatus::computeFor( locker, profiledBlock, *callLinkInfo, callExitSiteData)); variant = PutByIdVariant::setter( structure, complexGetStatus.offset(), complexGetStatus.conditionSet(), WTFMove(callLinkStatus)); } } break; } case AccessCase::CustomSetter: return PutByIdStatus(MakesCalls); default: return PutByIdStatus(slowPathState); } if (!result.appendVariant(variant)) return PutByIdStatus(slowPathState); } return result; } default: return PutByIdStatus(TakesSlowPath); } }