|  | /* | 
|  | * 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 "GetByStatus.h" | 
|  |  | 
|  | #include "BytecodeStructs.h" | 
|  | #include "CacheableIdentifierInlines.h" | 
|  | #include "CodeBlock.h" | 
|  | #include "ComplexGetStatus.h" | 
|  | #include "GetterSetterAccessCase.h" | 
|  | #include "ICStatusUtils.h" | 
|  | #include "IntrinsicGetterAccessCase.h" | 
|  | #include "ModuleNamespaceAccessCase.h" | 
|  | #include "PolymorphicAccess.h" | 
|  | #include "StructureStubInfo.h" | 
|  | #include <wtf/ListDump.h> | 
|  |  | 
|  | namespace JSC { | 
|  |  | 
|  | bool GetByStatus::appendVariant(const GetByVariant& variant) | 
|  | { | 
|  | return appendICStatusVariant(m_variants, variant); | 
|  | } | 
|  |  | 
|  | void GetByStatus::shrinkToFit() | 
|  | { | 
|  | m_variants.shrinkToFit(); | 
|  | } | 
|  |  | 
|  | GetByStatus GetByStatus::computeFromLLInt(CodeBlock* profiledBlock, BytecodeIndex bytecodeIndex) | 
|  | { | 
|  | VM& vm = profiledBlock->vm(); | 
|  |  | 
|  | auto instruction = profiledBlock->instructions().at(bytecodeIndex.offset()); | 
|  |  | 
|  | StructureID structureID; | 
|  | const Identifier* identifier = nullptr; | 
|  | switch (instruction->opcodeID()) { | 
|  | case op_get_by_id: { | 
|  | auto& metadata = instruction->as<OpGetById>().metadata(profiledBlock); | 
|  | // FIXME: We should not just bail if we see a get_by_id_proto_load. | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=158039 | 
|  | if (metadata.m_modeMetadata.mode != GetByIdMode::Default) | 
|  | return GetByStatus(NoInformation, false); | 
|  | structureID = metadata.m_modeMetadata.defaultMode.structureID; | 
|  |  | 
|  | identifier = &(profiledBlock->identifier(instruction->as<OpGetById>().m_property)); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case op_try_get_by_id: | 
|  | structureID = instruction->as<OpTryGetById>().metadata(profiledBlock).m_structureID; | 
|  | identifier = &(profiledBlock->identifier(instruction->as<OpTryGetById>().m_property)); | 
|  | break; | 
|  | case op_get_by_id_direct: | 
|  | structureID = instruction->as<OpGetByIdDirect>().metadata(profiledBlock).m_structureID; | 
|  | identifier = &(profiledBlock->identifier(instruction->as<OpGetByIdDirect>().m_property)); | 
|  | break; | 
|  |  | 
|  | case op_get_by_val: | 
|  | return GetByStatus(NoInformation, false); | 
|  |  | 
|  | case op_iterator_open: { | 
|  | ASSERT(bytecodeIndex.checkpoint() == OpIteratorOpen::getNext); | 
|  | auto& metadata = instruction->as<OpIteratorOpen>().metadata(profiledBlock); | 
|  |  | 
|  | // FIXME: We should not just bail if we see a get_by_id_proto_load. | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=158039 | 
|  | if (metadata.m_modeMetadata.mode != GetByIdMode::Default) | 
|  | return GetByStatus(NoInformation, false); | 
|  | structureID = metadata.m_modeMetadata.defaultMode.structureID; | 
|  | identifier = &vm.propertyNames->next; | 
|  | break; | 
|  | } | 
|  |  | 
|  | case op_iterator_next: { | 
|  | auto& metadata = instruction->as<OpIteratorNext>().metadata(profiledBlock); | 
|  | if (bytecodeIndex.checkpoint() == OpIteratorNext::getDone) { | 
|  | if (metadata.m_doneModeMetadata.mode != GetByIdMode::Default) | 
|  | return GetByStatus(NoInformation, false); | 
|  | structureID = metadata.m_doneModeMetadata.defaultMode.structureID; | 
|  | identifier = &vm.propertyNames->done; | 
|  | } else { | 
|  | ASSERT(bytecodeIndex.checkpoint() == OpIteratorNext::getValue); | 
|  | if (metadata.m_valueModeMetadata.mode != GetByIdMode::Default) | 
|  | return GetByStatus(NoInformation, false); | 
|  | structureID = metadata.m_valueModeMetadata.defaultMode.structureID; | 
|  | identifier = &vm.propertyNames->value; | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | case op_get_private_name: | 
|  | // FIXME: Consider using LLInt caches or IC information to populate GetByStatus | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=217245 | 
|  | return GetByStatus(NoInformation, false); | 
|  |  | 
|  | default: { | 
|  | ASSERT_NOT_REACHED(); | 
|  | return GetByStatus(NoInformation, false); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!structureID) | 
|  | return GetByStatus(NoInformation, false); | 
|  |  | 
|  | Structure* structure = vm.heap.structureIDTable().get(structureID); | 
|  |  | 
|  | if (structure->takesSlowPathInDFGForImpureProperty()) | 
|  | return GetByStatus(NoInformation, false); | 
|  |  | 
|  | unsigned attributes; | 
|  | PropertyOffset offset = structure->getConcurrently(identifier->impl(), attributes); | 
|  | if (!isValidOffset(offset)) | 
|  | return GetByStatus(NoInformation, false); | 
|  | if (attributes & PropertyAttribute::CustomAccessorOrValue) | 
|  | return GetByStatus(NoInformation, false); | 
|  |  | 
|  | GetByStatus result(Simple, false); | 
|  | result.appendVariant(GetByVariant(nullptr, StructureSet(structure), offset)); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | GetByStatus GetByStatus::computeFor(CodeBlock* profiledBlock, ICStatusMap& map, BytecodeIndex bytecodeIndex, ExitFlag didExit, CallLinkStatus::ExitSiteData callExitSiteData) | 
|  | { | 
|  | ConcurrentJSLocker locker(profiledBlock->m_lock); | 
|  |  | 
|  | GetByStatus result; | 
|  |  | 
|  | #if ENABLE(DFG_JIT) | 
|  | result = computeForStubInfoWithoutExitSiteFeedback( | 
|  | locker, profiledBlock, map.get(CodeOrigin(bytecodeIndex)).stubInfo, callExitSiteData); | 
|  |  | 
|  | if (didExit) | 
|  | return result.slowVersion(); | 
|  | #else | 
|  | UNUSED_PARAM(map); | 
|  | UNUSED_PARAM(didExit); | 
|  | UNUSED_PARAM(callExitSiteData); | 
|  | #endif | 
|  |  | 
|  | if (!result) | 
|  | return computeFromLLInt(profiledBlock, bytecodeIndex); | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | #if ENABLE(JIT) | 
|  | GetByStatus::GetByStatus(StubInfoSummary summary, StructureStubInfo* stubInfo) | 
|  | : m_wasSeenInJIT(true) | 
|  | { | 
|  | switch (summary) { | 
|  | case StubInfoSummary::NoInformation: | 
|  | m_state = NoInformation; | 
|  | return; | 
|  | case StubInfoSummary::Simple: | 
|  | case StubInfoSummary::MakesCalls: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | return; | 
|  | case StubInfoSummary::TakesSlowPath: | 
|  | ASSERT(stubInfo); | 
|  | m_state = stubInfo->tookSlowPath ? ObservedTakesSlowPath : LikelyTakesSlowPath; | 
|  | return; | 
|  | case StubInfoSummary::TakesSlowPathAndMakesCalls: | 
|  | ASSERT(stubInfo); | 
|  | m_state = stubInfo->tookSlowPath ? ObservedSlowPathAndMakesCalls : MakesCalls; | 
|  | return; | 
|  | } | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | } | 
|  |  | 
|  | GetByStatus::GetByStatus(const ModuleNamespaceAccessCase& accessCase) | 
|  | : m_moduleNamespaceData(Box<ModuleNamespaceData>::create(ModuleNamespaceData { accessCase.moduleNamespaceObject(), accessCase.moduleEnvironment(), accessCase.scopeOffset(), accessCase.identifier() })) | 
|  | , m_state(ModuleNamespace) | 
|  | , m_wasSeenInJIT(true) | 
|  | { | 
|  | } | 
|  |  | 
|  | GetByStatus GetByStatus::computeForStubInfoWithoutExitSiteFeedback( | 
|  | const ConcurrentJSLocker& locker, CodeBlock* profiledBlock, StructureStubInfo* stubInfo, CallLinkStatus::ExitSiteData callExitSiteData) | 
|  | { | 
|  | StubInfoSummary summary = StructureStubInfo::summary(profiledBlock->vm(), stubInfo); | 
|  | if (!isInlineable(summary)) | 
|  | return GetByStatus(summary, stubInfo); | 
|  |  | 
|  | // Finally figure out if we can derive an access strategy. | 
|  | GetByStatus result; | 
|  | result.m_state = Simple; | 
|  | result.m_wasSeenInJIT = true; // This is interesting for bytecode dumping only. | 
|  | switch (stubInfo->cacheType()) { | 
|  | case CacheType::Unset: | 
|  | return GetByStatus(NoInformation); | 
|  |  | 
|  | case CacheType::GetByIdSelf: { | 
|  | Structure* structure = stubInfo->inlineAccessBaseStructure(profiledBlock->vm()); | 
|  | if (structure->takesSlowPathInDFGForImpureProperty()) | 
|  | return GetByStatus(JSC::slowVersion(summary), stubInfo); | 
|  | CacheableIdentifier identifier = stubInfo->identifier(); | 
|  | UniquedStringImpl* uid = identifier.uid(); | 
|  | RELEASE_ASSERT(uid); | 
|  | GetByVariant variant(WTFMove(identifier)); | 
|  | unsigned attributes; | 
|  | variant.m_offset = structure->getConcurrently(uid, attributes); | 
|  | if (!isValidOffset(variant.m_offset)) | 
|  | return GetByStatus(JSC::slowVersion(summary), stubInfo); | 
|  | if (attributes & PropertyAttribute::CustomAccessorOrValue) | 
|  | return GetByStatus(JSC::slowVersion(summary), stubInfo); | 
|  |  | 
|  | variant.m_structureSet.add(structure); | 
|  | bool didAppend = result.appendVariant(variant); | 
|  | ASSERT_UNUSED(didAppend, didAppend); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | case CacheType::Stub: { | 
|  | PolymorphicAccess* list = stubInfo->m_stub.get(); | 
|  | if (list->size() == 1) { | 
|  | const AccessCase& access = list->at(0); | 
|  | switch (access.type()) { | 
|  | case AccessCase::ModuleNamespaceLoad: | 
|  | return GetByStatus(access.as<ModuleNamespaceAccessCase>()); | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (unsigned listIndex = 0; listIndex < list->size(); ++listIndex) { | 
|  | const AccessCase& access = list->at(listIndex); | 
|  | if (access.viaProxy()) | 
|  | return GetByStatus(JSC::slowVersion(summary), stubInfo); | 
|  |  | 
|  | if (access.usesPolyProto()) | 
|  | return GetByStatus(JSC::slowVersion(summary), stubInfo); | 
|  |  | 
|  | if (!access.requiresIdentifierNameMatch()) { | 
|  | // FIXME: We could use this for indexed loads in the future. This is pretty solid profiling | 
|  | // information, and probably better than ArrayProfile when it's available. | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=204215 | 
|  | return GetByStatus(JSC::slowVersion(summary), stubInfo); | 
|  | } | 
|  |  | 
|  | Structure* structure = access.structure(); | 
|  | if (!structure) { | 
|  | // The null structure cases arise due to array.length and string.length. We have no way | 
|  | // of creating a GetByVariant for those, and we don't really have to since the DFG | 
|  | // handles those cases in FixupPhase using value profiling. That's a bit awkward - we | 
|  | // shouldn't have to use value profiling to discover something that the AccessCase | 
|  | // could have told us. But, it works well enough. So, our only concern here is to not | 
|  | // crash on null structure. | 
|  | return GetByStatus(JSC::slowVersion(summary), stubInfo); | 
|  | } | 
|  |  | 
|  | ComplexGetStatus complexGetStatus = ComplexGetStatus::computeFor( | 
|  | structure, access.conditionSet(), access.uid()); | 
|  |  | 
|  | switch (complexGetStatus.kind()) { | 
|  | case ComplexGetStatus::ShouldSkip: | 
|  | continue; | 
|  |  | 
|  | case ComplexGetStatus::TakesSlowPath: | 
|  | return GetByStatus(JSC::slowVersion(summary), stubInfo); | 
|  |  | 
|  | case ComplexGetStatus::Inlineable: { | 
|  | std::unique_ptr<CallLinkStatus> callLinkStatus; | 
|  | JSFunction* intrinsicFunction = nullptr; | 
|  | FunctionPtr<CustomAccessorPtrTag> customAccessorGetter; | 
|  | std::unique_ptr<DOMAttributeAnnotation> domAttribute; | 
|  | bool haveDOMAttribute = false; | 
|  |  | 
|  | switch (access.type()) { | 
|  | case AccessCase::Load: | 
|  | case AccessCase::GetGetter: | 
|  | case AccessCase::Miss: { | 
|  | break; | 
|  | } | 
|  | case AccessCase::IntrinsicGetter: { | 
|  | intrinsicFunction = access.as<IntrinsicGetterAccessCase>().intrinsicFunction(); | 
|  | break; | 
|  | } | 
|  | case AccessCase::Getter: { | 
|  | callLinkStatus = makeUnique<CallLinkStatus>(); | 
|  | if (CallLinkInfo* callLinkInfo = access.as<GetterSetterAccessCase>().callLinkInfo()) { | 
|  | *callLinkStatus = CallLinkStatus::computeFor( | 
|  | locker, profiledBlock, *callLinkInfo, callExitSiteData); | 
|  | } | 
|  | break; | 
|  | } | 
|  | case AccessCase::CustomAccessorGetter: { | 
|  | customAccessorGetter = access.as<GetterSetterAccessCase>().customAccessor(); | 
|  | if (!access.as<GetterSetterAccessCase>().domAttribute()) | 
|  | return GetByStatus(JSC::slowVersion(summary), stubInfo); | 
|  | domAttribute = WTF::makeUnique<DOMAttributeAnnotation>(*access.as<GetterSetterAccessCase>().domAttribute()); | 
|  | haveDOMAttribute = true; | 
|  | result.m_state = Custom; | 
|  | break; | 
|  | } | 
|  | default: { | 
|  | // FIXME: It would be totally sweet to support more of these at some point in the | 
|  | // future. https://bugs.webkit.org/show_bug.cgi?id=133052 | 
|  | return GetByStatus(JSC::slowVersion(summary), stubInfo); | 
|  | } } | 
|  |  | 
|  | ASSERT((AccessCase::Miss == access.type() || access.isCustom()) == (access.offset() == invalidOffset)); | 
|  | GetByVariant variant(access.identifier(), StructureSet(structure), complexGetStatus.offset(), | 
|  | complexGetStatus.conditionSet(), WTFMove(callLinkStatus), | 
|  | intrinsicFunction, | 
|  | customAccessorGetter, | 
|  | WTFMove(domAttribute)); | 
|  |  | 
|  | if (!result.appendVariant(variant)) | 
|  | return GetByStatus(JSC::slowVersion(summary), stubInfo); | 
|  |  | 
|  | if (haveDOMAttribute) { | 
|  | // Give up when custom accesses are not merged into one. | 
|  | if (result.numVariants() != 1) | 
|  | return GetByStatus(JSC::slowVersion(summary), stubInfo); | 
|  | } else { | 
|  | // Give up when custom access and simple access are mixed. | 
|  | if (result.m_state == Custom) | 
|  | return GetByStatus(JSC::slowVersion(summary), stubInfo); | 
|  | } | 
|  | break; | 
|  | } } | 
|  | } | 
|  |  | 
|  | result.shrinkToFit(); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | default: | 
|  | return GetByStatus(JSC::slowVersion(summary), stubInfo); | 
|  | } | 
|  |  | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | return GetByStatus(); | 
|  | } | 
|  |  | 
|  | GetByStatus GetByStatus::computeFor( | 
|  | CodeBlock* profiledBlock, ICStatusMap& baselineMap, | 
|  | ICStatusContextStack& icContextStack, CodeOrigin codeOrigin) | 
|  | { | 
|  | BytecodeIndex bytecodeIndex = codeOrigin.bytecodeIndex(); | 
|  | CallLinkStatus::ExitSiteData callExitSiteData = CallLinkStatus::computeExitSiteData(profiledBlock, bytecodeIndex); | 
|  | ExitFlag didExit = hasBadCacheExitSite(profiledBlock, bytecodeIndex); | 
|  |  | 
|  | for (ICStatusContext* context : icContextStack) { | 
|  | ICStatus status = context->get(codeOrigin); | 
|  |  | 
|  | auto bless = [&] (const GetByStatus& result) -> GetByStatus { | 
|  | if (!context->isInlined(codeOrigin)) { | 
|  | // Merge with baseline result, which also happens to contain exit data for both | 
|  | // inlined and not-inlined. | 
|  | GetByStatus baselineResult = computeFor( | 
|  | profiledBlock, baselineMap, bytecodeIndex, didExit, | 
|  | callExitSiteData); | 
|  | baselineResult.merge(result); | 
|  | return baselineResult; | 
|  | } | 
|  | if (didExit.isSet(ExitFromInlined)) | 
|  | return result.slowVersion(); | 
|  | return result; | 
|  | }; | 
|  |  | 
|  | if (status.stubInfo) { | 
|  | GetByStatus result; | 
|  | { | 
|  | ConcurrentJSLocker locker(context->optimizedCodeBlock->m_lock); | 
|  | result = computeForStubInfoWithoutExitSiteFeedback( | 
|  | locker, context->optimizedCodeBlock, status.stubInfo, callExitSiteData); | 
|  | } | 
|  | if (result.isSet()) | 
|  | return bless(result); | 
|  | } | 
|  |  | 
|  | if (status.getStatus) | 
|  | return bless(*status.getStatus); | 
|  | } | 
|  |  | 
|  | return computeFor(profiledBlock, baselineMap, bytecodeIndex, didExit, callExitSiteData); | 
|  | } | 
|  |  | 
|  | GetByStatus GetByStatus::computeFor(const StructureSet& set, UniquedStringImpl* uid) | 
|  | { | 
|  | // For now we only handle the super simple self access case. We could handle the | 
|  | // prototype case in the future. | 
|  | // | 
|  | // Note that this code is also used for GetByIdDirect since this function only looks | 
|  | // into direct properties. When supporting prototype chains, we should split this for | 
|  | // GetById and GetByIdDirect. | 
|  |  | 
|  | if (set.isEmpty()) | 
|  | return GetByStatus(); | 
|  |  | 
|  | if (parseIndex(*uid)) | 
|  | return GetByStatus(LikelyTakesSlowPath); | 
|  |  | 
|  | GetByStatus 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 GetByStatus(LikelyTakesSlowPath); | 
|  |  | 
|  | if (!structure->propertyAccessesAreCacheable()) | 
|  | return GetByStatus(LikelyTakesSlowPath); | 
|  |  | 
|  | unsigned attributes; | 
|  | PropertyOffset offset = structure->getConcurrently(uid, attributes); | 
|  | if (!isValidOffset(offset)) | 
|  | return GetByStatus(LikelyTakesSlowPath); // It's probably a prototype lookup. Give up on life for now, even though we could totally be way smarter about it. | 
|  | if (attributes & PropertyAttribute::Accessor) | 
|  | return GetByStatus(MakesCalls); // We could be smarter here, like strength-reducing this to a Call. | 
|  | if (attributes & PropertyAttribute::CustomAccessorOrValue) | 
|  | return GetByStatus(LikelyTakesSlowPath); | 
|  |  | 
|  | if (!result.appendVariant(GetByVariant(nullptr, structure, offset))) | 
|  | return GetByStatus(LikelyTakesSlowPath); | 
|  | } | 
|  |  | 
|  | result.shrinkToFit(); | 
|  | return result; | 
|  | } | 
|  | #endif // ENABLE(JIT) | 
|  |  | 
|  | bool GetByStatus::makesCalls() const | 
|  | { | 
|  | switch (m_state) { | 
|  | case NoInformation: | 
|  | case LikelyTakesSlowPath: | 
|  | case ObservedTakesSlowPath: | 
|  | case Custom: | 
|  | case ModuleNamespace: | 
|  | return false; | 
|  | case Simple: | 
|  | for (unsigned i = m_variants.size(); i--;) { | 
|  | if (m_variants[i].callLinkStatus()) | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | case MakesCalls: | 
|  | case ObservedSlowPathAndMakesCalls: | 
|  | return true; | 
|  | } | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | GetByStatus GetByStatus::slowVersion() const | 
|  | { | 
|  | if (observedStructureStubInfoSlowPath()) | 
|  | return GetByStatus(makesCalls() ? ObservedSlowPathAndMakesCalls : ObservedTakesSlowPath, wasSeenInJIT()); | 
|  | return GetByStatus(makesCalls() ? MakesCalls : LikelyTakesSlowPath, wasSeenInJIT()); | 
|  | } | 
|  |  | 
|  | void GetByStatus::merge(const GetByStatus& other) | 
|  | { | 
|  | if (other.m_state == NoInformation) | 
|  | return; | 
|  |  | 
|  | auto mergeSlow = [&] () { | 
|  | if (observedStructureStubInfoSlowPath() || other.observedStructureStubInfoSlowPath()) | 
|  | *this = GetByStatus((makesCalls() || other.makesCalls()) ? ObservedSlowPathAndMakesCalls : ObservedTakesSlowPath); | 
|  | else | 
|  | *this = GetByStatus((makesCalls() || other.makesCalls()) ? MakesCalls : LikelyTakesSlowPath); | 
|  | }; | 
|  |  | 
|  | switch (m_state) { | 
|  | case NoInformation: | 
|  | *this = other; | 
|  | return; | 
|  |  | 
|  | case Simple: | 
|  | case Custom: | 
|  | if (m_state != other.m_state) | 
|  | return mergeSlow(); | 
|  |  | 
|  | for (const GetByVariant& otherVariant : other.m_variants) { | 
|  | if (!appendVariant(otherVariant)) | 
|  | return mergeSlow(); | 
|  | } | 
|  | shrinkToFit(); | 
|  | return; | 
|  |  | 
|  | case ModuleNamespace: | 
|  | if (other.m_state != ModuleNamespace) | 
|  | return mergeSlow(); | 
|  |  | 
|  | if (m_moduleNamespaceData->m_moduleNamespaceObject != other.m_moduleNamespaceData->m_moduleNamespaceObject) | 
|  | return mergeSlow(); | 
|  |  | 
|  | if (m_moduleNamespaceData->m_moduleEnvironment != other.m_moduleNamespaceData->m_moduleEnvironment) | 
|  | return mergeSlow(); | 
|  |  | 
|  | if (m_moduleNamespaceData->m_scopeOffset != other.m_moduleNamespaceData->m_scopeOffset) | 
|  | return mergeSlow(); | 
|  |  | 
|  | return; | 
|  |  | 
|  | case LikelyTakesSlowPath: | 
|  | case ObservedTakesSlowPath: | 
|  | case MakesCalls: | 
|  | case ObservedSlowPathAndMakesCalls: | 
|  | return mergeSlow(); | 
|  | } | 
|  |  | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | } | 
|  |  | 
|  | void GetByStatus::filter(const StructureSet& set) | 
|  | { | 
|  | if (m_state != Simple) | 
|  | return; | 
|  | filterICStatusVariants(m_variants, set); | 
|  | if (m_variants.isEmpty()) | 
|  | m_state = NoInformation; | 
|  | } | 
|  |  | 
|  | template<typename Visitor> | 
|  | void GetByStatus::visitAggregateImpl(Visitor& visitor) | 
|  | { | 
|  | if (isModuleNamespace()) | 
|  | m_moduleNamespaceData->m_identifier.visitAggregate(visitor); | 
|  | for (GetByVariant& variant : m_variants) | 
|  | variant.visitAggregate(visitor); | 
|  | } | 
|  |  | 
|  | DEFINE_VISIT_AGGREGATE(GetByStatus); | 
|  |  | 
|  | template<typename Visitor> | 
|  | void GetByStatus::markIfCheap(Visitor& visitor) | 
|  | { | 
|  | for (GetByVariant& variant : m_variants) | 
|  | variant.markIfCheap(visitor); | 
|  | } | 
|  |  | 
|  | template void GetByStatus::markIfCheap(AbstractSlotVisitor&); | 
|  | template void GetByStatus::markIfCheap(SlotVisitor&); | 
|  |  | 
|  | bool GetByStatus::finalize(VM& vm) | 
|  | { | 
|  | for (GetByVariant& variant : m_variants) { | 
|  | if (!variant.finalize(vm)) | 
|  | return false; | 
|  | } | 
|  | if (isModuleNamespace()) { | 
|  | if (m_moduleNamespaceData->m_moduleNamespaceObject && !vm.heap.isMarked(m_moduleNamespaceData->m_moduleNamespaceObject)) | 
|  | return false; | 
|  | if (m_moduleNamespaceData->m_moduleEnvironment && !vm.heap.isMarked(m_moduleNamespaceData->m_moduleEnvironment)) | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | CacheableIdentifier GetByStatus::singleIdentifier() const | 
|  | { | 
|  | if (isModuleNamespace()) | 
|  | return m_moduleNamespaceData->m_identifier; | 
|  |  | 
|  | return singleIdentifierForICStatus(m_variants); | 
|  | } | 
|  |  | 
|  | void GetByStatus::dump(PrintStream& out) const | 
|  | { | 
|  | out.print("("); | 
|  | switch (m_state) { | 
|  | case NoInformation: | 
|  | out.print("NoInformation"); | 
|  | break; | 
|  | case Simple: | 
|  | out.print("Simple"); | 
|  | break; | 
|  | case Custom: | 
|  | out.print("Custom"); | 
|  | break; | 
|  | case ModuleNamespace: | 
|  | out.print("ModuleNamespace"); | 
|  | break; | 
|  | case LikelyTakesSlowPath: | 
|  | out.print("LikelyTakesSlowPath"); | 
|  | break; | 
|  | case ObservedTakesSlowPath: | 
|  | out.print("ObservedTakesSlowPath"); | 
|  | break; | 
|  | case MakesCalls: | 
|  | out.print("MakesCalls"); | 
|  | break; | 
|  | case ObservedSlowPathAndMakesCalls: | 
|  | out.print("ObservedSlowPathAndMakesCalls"); | 
|  | break; | 
|  | } | 
|  | out.print(", ", listDump(m_variants), ", seenInJIT = ", m_wasSeenInJIT, ")"); | 
|  | } | 
|  |  | 
|  | } // namespace JSC | 
|  |  |