| /* | 
 |  * Copyright (C) 2012-2021 Apple Inc. All rights reserved. | 
 |  * | 
 |  * Redistribution and use in source and binary forms, with or without | 
 |  * modification, are permitted provided that the following conditions | 
 |  * are met: | 
 |  * 1. Redistributions of source code must retain the above copyright | 
 |  *    notice, this list of conditions and the following disclaimer. | 
 |  * 2. Redistributions in binary form must reproduce the above copyright | 
 |  *    notice, this list of conditions and the following disclaimer in the | 
 |  *    documentation and/or other materials provided with the distribution. | 
 |  * | 
 |  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | 
 |  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
 |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 
 |  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR | 
 |  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | 
 |  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | 
 |  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | 
 |  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | 
 |  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
 |  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
 |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  | 
 |  */ | 
 |  | 
 | #include "config.h" | 
 | #include "PutByStatus.h" | 
 |  | 
 | #include "BytecodeStructs.h" | 
 | #include "CacheableIdentifierInlines.h" | 
 | #include "CodeBlock.h" | 
 | #include "ComplexGetStatus.h" | 
 | #include "GetterSetterAccessCase.h" | 
 | #include "ICStatusUtils.h" | 
 | #include "PolymorphicAccess.h" | 
 | #include "StructureInlines.h" | 
 | #include "StructureStubInfo.h" | 
 | #include <wtf/ListDump.h> | 
 |  | 
 | namespace JSC { | 
 |  | 
 | bool PutByStatus::appendVariant(const PutByVariant& variant) | 
 | { | 
 |     return appendICStatusVariant(m_variants, variant); | 
 | } | 
 |  | 
 | void PutByStatus::shrinkToFit() | 
 | { | 
 |     m_variants.shrinkToFit(); | 
 | } | 
 |  | 
 | PutByStatus PutByStatus::computeFromLLInt(CodeBlock* profiledBlock, BytecodeIndex bytecodeIndex) | 
 | { | 
 |     VM& vm = profiledBlock->vm(); | 
 |      | 
 |     auto instruction = profiledBlock->instructions().at(bytecodeIndex.offset()); | 
 |  | 
 |     switch (instruction->opcodeID()) { | 
 |     case op_put_by_id: | 
 |         break; | 
 |     case op_put_by_val: | 
 |     case op_put_by_val_direct: | 
 |         return PutByStatus(NoInformation); | 
 |     case op_put_private_name: | 
 |         // We do no have a code retrieving LLInt information for `op_put_private_name`. | 
 |         // We can add support for it if this is required in future changes, since we have | 
 |         // IC implemented for this operation on LLInt. | 
 |         return PutByStatus(NoInformation); | 
 |     default: { | 
 |         RELEASE_ASSERT_NOT_REACHED(); | 
 |         break; | 
 |     } | 
 |     } | 
 |  | 
 |     auto bytecode = instruction->as<OpPutById>(); | 
 |     auto& metadata = bytecode.metadata(profiledBlock); | 
 |     const Identifier* identifier = &(profiledBlock->identifier(bytecode.m_property)); | 
 |     UniquedStringImpl* uid = identifier->impl(); | 
 |  | 
 |     StructureID structureID = metadata.m_oldStructureID; | 
 |     if (!structureID) | 
 |         return PutByStatus(NoInformation); | 
 |      | 
 |     Structure* structure = structureID.decode(); | 
 |  | 
 |     StructureID newStructureID = metadata.m_newStructureID; | 
 |     if (!newStructureID) { | 
 |         PropertyOffset offset = structure->getConcurrently(uid); | 
 |         if (!isValidOffset(offset)) | 
 |             return PutByStatus(NoInformation); | 
 |          | 
 |         return PutByVariant::replace(nullptr, structure, offset); | 
 |     } | 
 |  | 
 |     Structure* newStructure = newStructureID.decode(); | 
 |      | 
 |     ASSERT(structure->transitionWatchpointSetHasBeenInvalidated()); | 
 |      | 
 |     PropertyOffset offset = newStructure->getConcurrently(uid); | 
 |     if (!isValidOffset(offset)) | 
 |         return PutByStatus(NoInformation); | 
 |      | 
 |     ObjectPropertyConditionSet conditionSet; | 
 |     if (!(bytecode.m_flags.isDirect())) { | 
 |         conditionSet = | 
 |             generateConditionsForPropertySetterMissConcurrently( | 
 |                 vm, profiledBlock->globalObject(), structure, uid); | 
 |         if (!conditionSet.isValid()) | 
 |             return PutByStatus(NoInformation); | 
 |     } | 
 |      | 
 |     return PutByVariant::transition(nullptr, structure, newStructure, conditionSet, offset); | 
 | } | 
 |  | 
 | #if ENABLE(JIT) | 
 | PutByStatus::PutByStatus(StubInfoSummary summary, StructureStubInfo& stubInfo) | 
 | { | 
 |     switch (summary) { | 
 |     case StubInfoSummary::NoInformation: | 
 |         m_state = NoInformation; | 
 |         return; | 
 |     case StubInfoSummary::Simple: | 
 |     case StubInfoSummary::MakesCalls: | 
 |         RELEASE_ASSERT_NOT_REACHED(); | 
 |         return; | 
 |     case StubInfoSummary::TakesSlowPath: | 
 |         m_state = stubInfo.tookSlowPath ? ObservedTakesSlowPath : LikelyTakesSlowPath; | 
 |         return; | 
 |     case StubInfoSummary::TakesSlowPathAndMakesCalls: | 
 |         m_state = stubInfo.tookSlowPath ? ObservedSlowPathAndMakesCalls : MakesCalls; | 
 |         return; | 
 |     } | 
 |     RELEASE_ASSERT_NOT_REACHED(); | 
 | } | 
 |  | 
 | PutByStatus PutByStatus::computeFor(CodeBlock* profiledBlock, ICStatusMap& map, BytecodeIndex bytecodeIndex, ExitFlag didExit, CallLinkStatus::ExitSiteData callExitSiteData) | 
 | { | 
 |     ConcurrentJSLocker locker(profiledBlock->m_lock); | 
 |      | 
 |     UNUSED_PARAM(profiledBlock); | 
 |     UNUSED_PARAM(bytecodeIndex); | 
 | #if ENABLE(DFG_JIT) | 
 |     if (didExit) | 
 |         return PutByStatus(LikelyTakesSlowPath); | 
 |      | 
 |     StructureStubInfo* stubInfo = map.get(CodeOrigin(bytecodeIndex)).stubInfo; | 
 |     PutByStatus result = computeForStubInfo( | 
 |         locker, profiledBlock, stubInfo, callExitSiteData); | 
 |     if (!result) | 
 |         return computeFromLLInt(profiledBlock, bytecodeIndex); | 
 |      | 
 |     return result; | 
 | #else // ENABLE(JIT) | 
 |     UNUSED_PARAM(map); | 
 |     UNUSED_PARAM(didExit); | 
 |     UNUSED_PARAM(callExitSiteData); | 
 |     return PutByStatus(NoInformation); | 
 | #endif // ENABLE(JIT) | 
 | } | 
 |  | 
 | PutByStatus PutByStatus::computeForStubInfo(const ConcurrentJSLocker& locker, CodeBlock* baselineBlock, StructureStubInfo* stubInfo, CodeOrigin codeOrigin) | 
 | { | 
 |     return computeForStubInfo( | 
 |         locker, baselineBlock, stubInfo, | 
 |         CallLinkStatus::computeExitSiteData(baselineBlock, codeOrigin.bytecodeIndex())); | 
 | } | 
 |  | 
 | PutByStatus PutByStatus::computeForStubInfo(const ConcurrentJSLocker& locker, CodeBlock* profiledBlock, StructureStubInfo* stubInfo, CallLinkStatus::ExitSiteData callExitSiteData) | 
 | { | 
 |     StubInfoSummary summary = StructureStubInfo::summary(profiledBlock->vm(), stubInfo); | 
 |     if (!isInlineable(summary)) | 
 |         return PutByStatus(summary, *stubInfo); | 
 |      | 
 |     switch (stubInfo->cacheType()) { | 
 |     case CacheType::Unset: | 
 |         // This means that we attempted to cache but failed for some reason. | 
 |         return PutByStatus(JSC::slowVersion(summary), *stubInfo); | 
 |          | 
 |     case CacheType::PutByIdReplace: { | 
 |         CacheableIdentifier identifier = stubInfo->identifier(); | 
 |         UniquedStringImpl* uid = identifier.uid(); | 
 |         RELEASE_ASSERT(uid); | 
 |         Structure* structure = stubInfo->inlineAccessBaseStructure(profiledBlock->vm()); | 
 |         PropertyOffset offset = structure->getConcurrently(uid); | 
 |         if (isValidOffset(offset)) | 
 |             return PutByVariant::replace(WTFMove(identifier), structure, offset); | 
 |         return PutByStatus(JSC::slowVersion(summary), *stubInfo); | 
 |     } | 
 |          | 
 |     case CacheType::Stub: { | 
 |         PolymorphicAccess* list = stubInfo->m_stub.get(); | 
 |  | 
 |         PutByStatus result; | 
 |         result.m_state = Simple; | 
 |          | 
 |         for (unsigned i = 0; i < list->size(); ++i) { | 
 |             const AccessCase& access = list->at(i); | 
 |             if (access.viaProxy()) | 
 |                 return PutByStatus(JSC::slowVersion(summary), *stubInfo); | 
 |             if (access.usesPolyProto()) | 
 |                 return PutByStatus(JSC::slowVersion(summary), *stubInfo); | 
 |              | 
 |             switch (access.type()) { | 
 |             case AccessCase::Replace: { | 
 |                 Structure* structure = access.structure(); | 
 |                 PropertyOffset offset = structure->getConcurrently(access.uid()); | 
 |                 if (!isValidOffset(offset)) | 
 |                     return PutByStatus(JSC::slowVersion(summary), *stubInfo); | 
 |                 auto variant = PutByVariant::replace(access.identifier(), structure, offset); | 
 |                 if (!result.appendVariant(variant)) | 
 |                     return PutByStatus(JSC::slowVersion(summary), *stubInfo); | 
 |                 break; | 
 |             } | 
 |                  | 
 |             case AccessCase::Transition: { | 
 |                 PropertyOffset offset = access.newStructure()->getConcurrently(access.uid()); | 
 |                 if (!isValidOffset(offset)) | 
 |                     return PutByStatus(JSC::slowVersion(summary), *stubInfo); | 
 |                 ObjectPropertyConditionSet conditionSet = access.conditionSet(); | 
 |                 if (!conditionSet.structuresEnsureValidity()) | 
 |                     return PutByStatus(JSC::slowVersion(summary), *stubInfo); | 
 |                 auto variant = PutByVariant::transition(access.identifier(), access.structure(), access.newStructure(), conditionSet, offset); | 
 |                 if (!result.appendVariant(variant)) | 
 |                     return PutByStatus(JSC::slowVersion(summary), *stubInfo); | 
 |                 break; | 
 |             } | 
 |                  | 
 |             case AccessCase::Setter: { | 
 |                 Structure* structure = access.structure(); | 
 |                  | 
 |                 ComplexGetStatus complexGetStatus = ComplexGetStatus::computeFor(structure, access.conditionSet(), access.uid()); | 
 |                  | 
 |                 switch (complexGetStatus.kind()) { | 
 |                 case ComplexGetStatus::ShouldSkip: | 
 |                     continue; | 
 |                      | 
 |                 case ComplexGetStatus::TakesSlowPath: | 
 |                     return PutByStatus(JSC::slowVersion(summary), *stubInfo); | 
 |                      | 
 |                 case ComplexGetStatus::Inlineable: { | 
 |                     std::unique_ptr<CallLinkStatus> callLinkStatus = | 
 |                         makeUnique<CallLinkStatus>(); | 
 |                     if (CallLinkInfo* callLinkInfo = access.as<GetterSetterAccessCase>().callLinkInfo()) { | 
 |                         *callLinkStatus = CallLinkStatus::computeFor( | 
 |                             locker, profiledBlock, *callLinkInfo, callExitSiteData); | 
 |                     } | 
 |                      | 
 |                     auto variant = PutByVariant::setter(access.identifier(), structure, complexGetStatus.offset(), complexGetStatus.conditionSet(), WTFMove(callLinkStatus)); | 
 |                     if (!result.appendVariant(variant)) | 
 |                         return PutByStatus(JSC::slowVersion(summary), *stubInfo); | 
 |                 } | 
 |                 } | 
 |                 break; | 
 |             } | 
 |                  | 
 |             case AccessCase::CustomValueSetter: | 
 |             case AccessCase::CustomAccessorSetter: | 
 |                 return PutByStatus(MakesCalls); | 
 |  | 
 |             default: | 
 |                 return PutByStatus(JSC::slowVersion(summary), *stubInfo); | 
 |             } | 
 |         } | 
 |          | 
 |         result.shrinkToFit(); | 
 |         return result; | 
 |     } | 
 |          | 
 |     default: | 
 |         return PutByStatus(JSC::slowVersion(summary), *stubInfo); | 
 |     } | 
 | } | 
 |  | 
 | PutByStatus PutByStatus::computeFor(CodeBlock* baselineBlock, ICStatusMap& baselineMap, ICStatusContextStack& contextStack, CodeOrigin codeOrigin) | 
 | { | 
 |     BytecodeIndex bytecodeIndex = codeOrigin.bytecodeIndex(); | 
 |     CallLinkStatus::ExitSiteData callExitSiteData = CallLinkStatus::computeExitSiteData(baselineBlock, bytecodeIndex); | 
 |     ExitFlag didExit = hasBadCacheExitSite(baselineBlock, bytecodeIndex); | 
 |  | 
 |     for (ICStatusContext* context : contextStack) { | 
 |         ICStatus status = context->get(codeOrigin); | 
 |          | 
 |         auto bless = [&] (const PutByStatus& result) -> PutByStatus { | 
 |             if (!context->isInlined(codeOrigin)) { | 
 |                 PutByStatus baselineResult = computeFor( | 
 |                     baselineBlock, baselineMap, bytecodeIndex, didExit, | 
 |                     callExitSiteData); | 
 |                 baselineResult.merge(result); | 
 |                 return baselineResult; | 
 |             } | 
 |             if (didExit.isSet(ExitFromInlined)) | 
 |                 return result.slowVersion(); | 
 |             return result; | 
 |         }; | 
 |          | 
 |         if (status.stubInfo) { | 
 |             PutByStatus result; | 
 |             { | 
 |                 ConcurrentJSLocker locker(context->optimizedCodeBlock->m_lock); | 
 |                 result = computeForStubInfo( | 
 |                     locker, context->optimizedCodeBlock, status.stubInfo, callExitSiteData); | 
 |             } | 
 |             if (result.isSet()) | 
 |                 return bless(result); | 
 |         } | 
 |          | 
 |         if (status.putStatus) | 
 |             return bless(*status.putStatus); | 
 |     } | 
 |      | 
 |     return computeFor(baselineBlock, baselineMap, bytecodeIndex, didExit, callExitSiteData); | 
 | } | 
 |  | 
 | PutByStatus PutByStatus::computeFor(JSGlobalObject* globalObject, const StructureSet& set, CacheableIdentifier identifier, bool isDirect, PrivateFieldPutKind privateFieldPutKind) | 
 | { | 
 |     UniquedStringImpl* uid = identifier.uid(); | 
 |     if (parseIndex(*uid)) | 
 |         return PutByStatus(LikelyTakesSlowPath); | 
 |  | 
 |     if (set.isEmpty()) | 
 |         return PutByStatus(); | 
 |      | 
 |     VM& vm = globalObject->vm(); | 
 |     PutByStatus 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 PutByStatus(LikelyTakesSlowPath); | 
 |  | 
 |         if (!structure->propertyAccessesAreCacheable()) | 
 |             return PutByStatus(LikelyTakesSlowPath); | 
 |      | 
 |         unsigned attributes; | 
 |         PropertyOffset offset = structure->getConcurrently(uid, attributes); | 
 |         if (isValidOffset(offset)) { | 
 |             // We can't have a valid offset for structures on `PutPrivateNameById` define mode | 
 |             // since it means we are redefining a private field. In such case, we need to take  | 
 |             // slow path to throw exception. | 
 |             if (privateFieldPutKind.isDefine()) | 
 |                 return PutByStatus(LikelyTakesSlowPath); | 
 |  | 
 |             if (attributes & PropertyAttribute::CustomAccessorOrValue) | 
 |                 return PutByStatus(MakesCalls); | 
 |  | 
 |             if (attributes & (PropertyAttribute::Accessor | PropertyAttribute::ReadOnly)) | 
 |                 return PutByStatus(LikelyTakesSlowPath); | 
 |              | 
 |             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 PutByStatus(LikelyTakesSlowPath); | 
 |             } | 
 |  | 
 |             PutByVariant variant = PutByVariant::replace(identifier, structure, offset); | 
 |             if (!result.appendVariant(variant)) | 
 |                 return PutByStatus(LikelyTakesSlowPath); | 
 |             continue; | 
 |         } | 
 |  | 
 |         // We can have a case with PutPrivateNameById in set mode and it | 
 |         // should never cause a structure transition because it means we are | 
 |         // trying to store in a not installed private field. We need to take | 
 |         // slow path to throw excpetion if it ever gets executed. | 
 |         if (privateFieldPutKind.isSet()) | 
 |             return PutByStatus(LikelyTakesSlowPath); | 
 |  | 
 |         // 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 PutByStatus(LikelyTakesSlowPath); | 
 |  | 
 |         // 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 PutByStatus(LikelyTakesSlowPath); | 
 |      | 
 |         ObjectPropertyConditionSet conditionSet; | 
 |         if (!isDirect) { | 
 |             ASSERT(privateFieldPutKind.isNone()); | 
 |             conditionSet = generateConditionsForPropertySetterMissConcurrently( | 
 |                 vm, globalObject, structure, uid); | 
 |             if (!conditionSet.isValid()) | 
 |                 return PutByStatus(LikelyTakesSlowPath); | 
 |         } | 
 |      | 
 |         // 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 PutByStatus(LikelyTakesSlowPath); | 
 |         ASSERT(isValidOffset(offset)); | 
 |      | 
 |         bool didAppend = result.appendVariant(PutByVariant::transition(identifier, structure, transition, conditionSet, offset)); | 
 |         if (!didAppend) | 
 |             return PutByStatus(LikelyTakesSlowPath); | 
 |     } | 
 |      | 
 |     result.shrinkToFit(); | 
 |     return result; | 
 | } | 
 | #endif | 
 |  | 
 | bool PutByStatus::makesCalls() const | 
 | { | 
 |     switch (m_state) { | 
 |     case NoInformation: | 
 |     case LikelyTakesSlowPath: | 
 |     case ObservedTakesSlowPath: | 
 |         return false; | 
 |     case MakesCalls: | 
 |     case ObservedSlowPathAndMakesCalls: | 
 |         return true; | 
 |     case Simple: { | 
 |         for (unsigned i = m_variants.size(); i--;) { | 
 |             if (m_variants[i].makesCalls()) | 
 |                 return true; | 
 |         } | 
 |         return false; | 
 |     } | 
 |     default: | 
 |         RELEASE_ASSERT_NOT_REACHED(); | 
 |     } | 
 | } | 
 |  | 
 | PutByStatus PutByStatus::slowVersion() const | 
 | { | 
 |     if (observedStructureStubInfoSlowPath()) | 
 |         return PutByStatus(makesCalls() ? ObservedSlowPathAndMakesCalls : ObservedTakesSlowPath); | 
 |     return PutByStatus(makesCalls() ? MakesCalls : LikelyTakesSlowPath); | 
 | } | 
 |  | 
 | CacheableIdentifier PutByStatus::singleIdentifier() const | 
 | { | 
 |     return singleIdentifierForICStatus(m_variants); | 
 | } | 
 |  | 
 | template<typename Visitor> | 
 | void PutByStatus::visitAggregateImpl(Visitor& visitor) | 
 | { | 
 |     for (PutByVariant& variant : m_variants) | 
 |         variant.visitAggregate(visitor); | 
 | } | 
 |  | 
 | DEFINE_VISIT_AGGREGATE(PutByStatus); | 
 |  | 
 | template<typename Visitor> | 
 | void PutByStatus::markIfCheap(Visitor& visitor) | 
 | { | 
 |     for (PutByVariant& variant : m_variants) | 
 |         variant.markIfCheap(visitor); | 
 | } | 
 |  | 
 | template void PutByStatus::markIfCheap(AbstractSlotVisitor&); | 
 | template void PutByStatus::markIfCheap(SlotVisitor&); | 
 |  | 
 | bool PutByStatus::finalize(VM& vm) | 
 | { | 
 |     for (PutByVariant& variant : m_variants) { | 
 |         if (!variant.finalize(vm)) | 
 |             return false; | 
 |     } | 
 |     return true; | 
 | } | 
 |  | 
 | void PutByStatus::merge(const PutByStatus& other) | 
 | { | 
 |     if (other.m_state == NoInformation) | 
 |         return; | 
 |  | 
 |     auto mergeSlow = [&] () { | 
 |         if (observedStructureStubInfoSlowPath() || other.observedStructureStubInfoSlowPath()) | 
 |             *this = PutByStatus((makesCalls() || other.makesCalls()) ? ObservedSlowPathAndMakesCalls : ObservedTakesSlowPath); | 
 |         else | 
 |             *this = PutByStatus((makesCalls() || other.makesCalls()) ? MakesCalls : LikelyTakesSlowPath); | 
 |     }; | 
 |  | 
 |     switch (m_state) { | 
 |     case NoInformation: | 
 |         *this = other; | 
 |         return; | 
 |          | 
 |     case Simple: | 
 |         if (other.m_state != Simple) | 
 |             return mergeSlow(); | 
 |          | 
 |         for (const PutByVariant& other : other.m_variants) { | 
 |             if (!appendVariant(other)) | 
 |                 return mergeSlow(); | 
 |         } | 
 |         shrinkToFit(); | 
 |         return; | 
 |          | 
 |     case LikelyTakesSlowPath: | 
 |     case ObservedTakesSlowPath: | 
 |     case MakesCalls: | 
 |     case ObservedSlowPathAndMakesCalls: | 
 |         return mergeSlow(); | 
 |     } | 
 |      | 
 |     RELEASE_ASSERT_NOT_REACHED(); | 
 | } | 
 |  | 
 | void PutByStatus::filter(const StructureSet& set) | 
 | { | 
 |     if (m_state != Simple) | 
 |         return; | 
 |     filterICStatusVariants(m_variants, set); | 
 |     for (PutByVariant& variant : m_variants) | 
 |         variant.fixTransitionToReplaceIfNecessary(); | 
 |     if (m_variants.isEmpty()) | 
 |         m_state = NoInformation; | 
 | } | 
 |  | 
 | void PutByStatus::dump(PrintStream& out) const | 
 | { | 
 |     switch (m_state) { | 
 |     case NoInformation: | 
 |         out.print("(NoInformation)"); | 
 |         return; | 
 |     case Simple: | 
 |         out.print("(", listDump(m_variants), ")"); | 
 |         return; | 
 |     case LikelyTakesSlowPath: | 
 |         out.print("LikelyTakesSlowPath"); | 
 |         return; | 
 |     case ObservedTakesSlowPath: | 
 |         out.print("ObservedTakesSlowPath"); | 
 |         return; | 
 |     case MakesCalls: | 
 |         out.print("MakesCalls"); | 
 |         return; | 
 |     case ObservedSlowPathAndMakesCalls: | 
 |         out.print("ObservedSlowPathAndMakesCalls"); | 
 |         return; | 
 |     } | 
 |      | 
 |     RELEASE_ASSERT_NOT_REACHED(); | 
 | } | 
 |  | 
 | } // namespace JSC | 
 |  |