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 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;
}
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);
}
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);
}
示例#5
0
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));
}
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);
}
示例#7
0
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;
}
示例#8
0
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);
    }
}