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);
}
bool GetByIdVariant::attemptToMerge(const GetByIdVariant& other)
{
    if (m_offset != other.m_offset)
        return false;
    if (m_callLinkStatus || other.m_callLinkStatus)
        return false;

    if (!canMergeIntrinsicStructures(other))
        return false;

    if (m_conditionSet.isEmpty() != other.m_conditionSet.isEmpty())
        return false;
    
    ObjectPropertyConditionSet mergedConditionSet;
    if (!m_conditionSet.isEmpty()) {
        mergedConditionSet = m_conditionSet.mergedWith(other.m_conditionSet);
        if (!mergedConditionSet.isValid() || !mergedConditionSet.hasOneSlotBaseCondition())
            return false;
    }
    m_conditionSet = mergedConditionSet;
    
    m_structureSet.merge(other.m_structureSet);
    
    return true;
}
ObjectPropertyConditionSet ObjectPropertyConditionSet::mergedWith(
    const ObjectPropertyConditionSet& other) const
{
    if (!isValid() || !other.isValid())
        return invalid();

    Vector<ObjectPropertyCondition> result;
    
    if (!isEmpty())
        result.appendVector(m_data->vector);
    
    for (const ObjectPropertyCondition& newCondition : other) {
        bool foundMatch = false;
        for (const ObjectPropertyCondition& existingCondition : *this) {
            if (newCondition == existingCondition) {
                foundMatch = true;
                continue;
            }
            if (!newCondition.isCompatibleWith(existingCondition))
                return invalid();
        }
        if (!foundMatch)
            result.append(newCondition);
    }

    return create(result);
}
ObjectPropertyConditionSet generateConditionsForInstanceOf(
    VM& vm, JSCell* owner, ExecState* exec, Structure* headStructure, JSObject* prototype,
    bool shouldHit)
{
    bool didHit = false;
    if (ObjectPropertyConditionSetInternal::verbose)
        dataLog("Searching for prototype ", JSValue(prototype), " starting with structure ", RawPointer(headStructure), " with shouldHit = ", shouldHit, "\n");
    ObjectPropertyConditionSet result = generateConditions(
        vm, exec->lexicalGlobalObject(), headStructure, shouldHit ? prototype : nullptr,
        [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool {
            if (ObjectPropertyConditionSetInternal::verbose)
                dataLog("Encountered object: ", RawPointer(object), "\n");
            if (object == prototype) {
                RELEASE_ASSERT(shouldHit);
                didHit = true;
                return true;
            }

            Structure* structure = object->structure(vm);
            if (structure->hasPolyProto())
                return false;
            conditions.append(
                ObjectPropertyCondition::hasPrototype(
                    vm, owner, object, structure->storedPrototypeObject()));
            return true;
        });
    if (result.isValid()) {
        if (ObjectPropertyConditionSetInternal::verbose)
            dataLog("didHit = ", didHit, ", shouldHit = ", shouldHit, "\n");
        RELEASE_ASSERT(didHit == shouldHit);
    }
    return result;
}
PutByIdStatus PutByIdStatus::computeFromLLInt(CodeBlock* profiledBlock, unsigned bytecodeIndex, UniquedStringImpl* uid)
{
    UNUSED_PARAM(profiledBlock);
    UNUSED_PARAM(bytecodeIndex);
    UNUSED_PARAM(uid);

    VM& vm = *profiledBlock->vm();
    
    Instruction* instruction = profiledBlock->instructions().begin() + bytecodeIndex;

    StructureID structureID = instruction[4].u.structureID;
    if (!structureID)
        return PutByIdStatus(NoInformation);
    
    Structure* structure = vm.heap.structureIDTable().get(structureID);

    StructureID newStructureID = instruction[6].u.structureID;
    if (!newStructureID) {
        PropertyOffset offset = structure->getConcurrently(uid);
        if (!isValidOffset(offset))
            return PutByIdStatus(NoInformation);
        
        return PutByIdVariant::replace(structure, offset, structure->inferredTypeDescriptorFor(uid));
    }

    Structure* newStructure = vm.heap.structureIDTable().get(newStructureID);
    
    ASSERT(structure->transitionWatchpointSetHasBeenInvalidated());
    
    PropertyOffset offset = newStructure->getConcurrently(uid);
    if (!isValidOffset(offset))
        return PutByIdStatus(NoInformation);
    
    ObjectPropertyConditionSet conditionSet;
    if (!(instruction[8].u.putByIdFlags & PutByIdIsDirect)) {
        conditionSet =
            generateConditionsForPropertySetterMissConcurrently(
                *profiledBlock->vm(), profiledBlock->globalObject(), structure, uid);
        if (!conditionSet.isValid())
            return PutByIdStatus(NoInformation);
    }
    
    return PutByIdVariant::transition(
        structure, newStructure, conditionSet, offset, newStructure->inferredTypeDescriptorFor(uid));
}
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;
}