| /* | 
 |  * Copyright (C) 2008, 2014-2016 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 "StructureStubInfo.h" | 
 |  | 
 | #include "JSObject.h" | 
 | #include "JSCInlines.h" | 
 | #include "PolymorphicAccess.h" | 
 | #include "Repatch.h" | 
 |  | 
 | namespace JSC { | 
 |  | 
 | #if ENABLE(JIT) | 
 |  | 
 | namespace StructureStubInfoInternal { | 
 | static const bool verbose = false; | 
 | } | 
 |  | 
 | StructureStubInfo::StructureStubInfo(AccessType accessType) | 
 |     : callSiteIndex(UINT_MAX) | 
 |     , accessType(accessType) | 
 |     , cacheType(CacheType::Unset) | 
 |     , countdown(1) // For a totally clear stub, we'll patch it after the first execution. | 
 |     , repatchCount(0) | 
 |     , numberOfCoolDowns(0) | 
 |     , bufferingCountdown(Options::repatchBufferingCountdown()) | 
 |     , resetByGC(false) | 
 |     , tookSlowPath(false) | 
 |     , everConsidered(false) | 
 |     , prototypeIsKnownObject(false) | 
 | { | 
 | } | 
 |  | 
 | StructureStubInfo::~StructureStubInfo() | 
 | { | 
 | } | 
 |  | 
 | void StructureStubInfo::initGetByIdSelf(CodeBlock* codeBlock, Structure* baseObjectStructure, PropertyOffset offset) | 
 | { | 
 |     cacheType = CacheType::GetByIdSelf; | 
 |      | 
 |     u.byIdSelf.baseObjectStructure.set( | 
 |         *codeBlock->vm(), codeBlock, baseObjectStructure); | 
 |     u.byIdSelf.offset = offset; | 
 | } | 
 |  | 
 | void StructureStubInfo::initArrayLength() | 
 | { | 
 |     cacheType = CacheType::ArrayLength; | 
 | } | 
 |  | 
 | void StructureStubInfo::initPutByIdReplace(CodeBlock* codeBlock, Structure* baseObjectStructure, PropertyOffset offset) | 
 | { | 
 |     cacheType = CacheType::PutByIdReplace; | 
 |      | 
 |     u.byIdSelf.baseObjectStructure.set( | 
 |         *codeBlock->vm(), codeBlock, baseObjectStructure); | 
 |     u.byIdSelf.offset = offset; | 
 | } | 
 |  | 
 | void StructureStubInfo::deref() | 
 | { | 
 |     switch (cacheType) { | 
 |     case CacheType::Stub: | 
 |         delete u.stub; | 
 |         return; | 
 |     case CacheType::Unset: | 
 |     case CacheType::GetByIdSelf: | 
 |     case CacheType::PutByIdReplace: | 
 |     case CacheType::ArrayLength: | 
 |         return; | 
 |     } | 
 |  | 
 |     RELEASE_ASSERT_NOT_REACHED(); | 
 | } | 
 |  | 
 | void StructureStubInfo::aboutToDie() | 
 | { | 
 |     switch (cacheType) { | 
 |     case CacheType::Stub: | 
 |         u.stub->aboutToDie(); | 
 |         return; | 
 |     case CacheType::Unset: | 
 |     case CacheType::GetByIdSelf: | 
 |     case CacheType::PutByIdReplace: | 
 |     case CacheType::ArrayLength: | 
 |         return; | 
 |     } | 
 |  | 
 |     RELEASE_ASSERT_NOT_REACHED(); | 
 | } | 
 |  | 
 | AccessGenerationResult StructureStubInfo::addAccessCase( | 
 |     const GCSafeConcurrentJSLocker& locker, CodeBlock* codeBlock, const Identifier& ident, std::unique_ptr<AccessCase> accessCase) | 
 | { | 
 |     VM& vm = *codeBlock->vm(); | 
 |      | 
 |     if (StructureStubInfoInternal::verbose) | 
 |         dataLog("Adding access case: ", accessCase, "\n"); | 
 |      | 
 |     if (!accessCase) | 
 |         return AccessGenerationResult::GaveUp; | 
 |      | 
 |     AccessGenerationResult result; | 
 |      | 
 |     if (cacheType == CacheType::Stub) { | 
 |         result = u.stub->addCase(locker, vm, codeBlock, *this, ident, WTFMove(accessCase)); | 
 |          | 
 |         if (StructureStubInfoInternal::verbose) | 
 |             dataLog("Had stub, result: ", result, "\n"); | 
 |  | 
 |         if (result.shouldResetStubAndFireWatchpoints()) | 
 |             return result; | 
 |  | 
 |         if (!result.buffered()) { | 
 |             bufferedStructures.clear(); | 
 |             return result; | 
 |         } | 
 |     } else { | 
 |         std::unique_ptr<PolymorphicAccess> access = std::make_unique<PolymorphicAccess>(); | 
 |          | 
 |         Vector<std::unique_ptr<AccessCase>, 2> accessCases; | 
 |          | 
 |         std::unique_ptr<AccessCase> previousCase = | 
 |             AccessCase::fromStructureStubInfo(vm, codeBlock, *this); | 
 |         if (previousCase) | 
 |             accessCases.append(WTFMove(previousCase)); | 
 |          | 
 |         accessCases.append(WTFMove(accessCase)); | 
 |          | 
 |         result = access->addCases(locker, vm, codeBlock, *this, ident, WTFMove(accessCases)); | 
 |          | 
 |         if (StructureStubInfoInternal::verbose) | 
 |             dataLog("Created stub, result: ", result, "\n"); | 
 |  | 
 |         if (result.shouldResetStubAndFireWatchpoints()) | 
 |             return result; | 
 |  | 
 |         if (!result.buffered()) { | 
 |             bufferedStructures.clear(); | 
 |             return result; | 
 |         } | 
 |          | 
 |         cacheType = CacheType::Stub; | 
 |         u.stub = access.release(); | 
 |     } | 
 |      | 
 |     RELEASE_ASSERT(!result.generatedSomeCode()); | 
 |      | 
 |     // If we didn't buffer any cases then bail. If this made no changes then we'll just try again | 
 |     // subject to cool-down. | 
 |     if (!result.buffered()) { | 
 |         if (StructureStubInfoInternal::verbose) | 
 |             dataLog("Didn't buffer anything, bailing.\n"); | 
 |         bufferedStructures.clear(); | 
 |         return result; | 
 |     } | 
 |      | 
 |     // The buffering countdown tells us if we should be repatching now. | 
 |     if (bufferingCountdown) { | 
 |         if (StructureStubInfoInternal::verbose) | 
 |             dataLog("Countdown is too high: ", bufferingCountdown, ".\n"); | 
 |         return result; | 
 |     } | 
 |      | 
 |     // Forget the buffered structures so that all future attempts to cache get fully handled by the | 
 |     // PolymorphicAccess. | 
 |     bufferedStructures.clear(); | 
 |      | 
 |     result = u.stub->regenerate(locker, vm, codeBlock, *this, ident); | 
 |      | 
 |     if (StructureStubInfoInternal::verbose) | 
 |         dataLog("Regeneration result: ", result, "\n"); | 
 |      | 
 |     RELEASE_ASSERT(!result.buffered()); | 
 |      | 
 |     if (!result.generatedSomeCode()) | 
 |         return result; | 
 |      | 
 |     // If we generated some code then we don't want to attempt to repatch in the future until we | 
 |     // gather enough cases. | 
 |     bufferingCountdown = Options::repatchBufferingCountdown(); | 
 |     return result; | 
 | } | 
 |  | 
 | void StructureStubInfo::reset(CodeBlock* codeBlock) | 
 | { | 
 |     bufferedStructures.clear(); | 
 |  | 
 |     if (cacheType == CacheType::Unset) | 
 |         return; | 
 |  | 
 |     if (Options::verboseOSR()) { | 
 |         // This can be called from GC destructor calls, so we don't try to do a full dump | 
 |         // of the CodeBlock. | 
 |         dataLog("Clearing structure cache (kind ", static_cast<int>(accessType), ") in ", RawPointer(codeBlock), ".\n"); | 
 |     } | 
 |  | 
 |     switch (accessType) { | 
 |     case AccessType::TryGet: | 
 |         resetGetByID(codeBlock, *this, GetByIDKind::Try); | 
 |         break; | 
 |     case AccessType::Get: | 
 |         resetGetByID(codeBlock, *this, GetByIDKind::Normal); | 
 |         break; | 
 |     case AccessType::GetWithThis: | 
 |         resetGetByID(codeBlock, *this, GetByIDKind::WithThis); | 
 |         break; | 
 |     case AccessType::GetDirect: | 
 |         resetGetByID(codeBlock, *this, GetByIDKind::Direct); | 
 |         break; | 
 |     case AccessType::Put: | 
 |         resetPutByID(codeBlock, *this); | 
 |         break; | 
 |     case AccessType::In: | 
 |         resetIn(*this); | 
 |         break; | 
 |     case AccessType::InstanceOf: | 
 |         resetInstanceOf(*this); | 
 |         break; | 
 |     } | 
 |      | 
 |     deref(); | 
 |     cacheType = CacheType::Unset; | 
 | } | 
 |  | 
 | void StructureStubInfo::visitWeakReferences(CodeBlock* codeBlock) | 
 | { | 
 |     VM& vm = *codeBlock->vm(); | 
 |      | 
 |     bufferedStructures.genericFilter( | 
 |         [&] (Structure* structure) -> bool { | 
 |             return Heap::isMarked(structure); | 
 |         }); | 
 |  | 
 |     switch (cacheType) { | 
 |     case CacheType::GetByIdSelf: | 
 |     case CacheType::PutByIdReplace: | 
 |         if (Heap::isMarked(u.byIdSelf.baseObjectStructure.get())) | 
 |             return; | 
 |         break; | 
 |     case CacheType::Stub: | 
 |         if (u.stub->visitWeak(vm)) | 
 |             return; | 
 |         break; | 
 |     default: | 
 |         return; | 
 |     } | 
 |  | 
 |     reset(codeBlock); | 
 |     resetByGC = true; | 
 | } | 
 |  | 
 | bool StructureStubInfo::propagateTransitions(SlotVisitor& visitor) | 
 | { | 
 |     switch (cacheType) { | 
 |     case CacheType::Unset: | 
 |     case CacheType::ArrayLength: | 
 |         return true; | 
 |     case CacheType::GetByIdSelf: | 
 |     case CacheType::PutByIdReplace: | 
 |         return u.byIdSelf.baseObjectStructure->markIfCheap(visitor); | 
 |     case CacheType::Stub: | 
 |         return u.stub->propagateTransitions(visitor); | 
 |     } | 
 |      | 
 |     RELEASE_ASSERT_NOT_REACHED(); | 
 |     return true; | 
 | } | 
 |  | 
 | bool StructureStubInfo::containsPC(void* pc) const | 
 | { | 
 |     if (cacheType != CacheType::Stub) | 
 |         return false; | 
 |     return u.stub->containsPC(pc); | 
 | } | 
 |  | 
 | #endif // ENABLE(JIT) | 
 |  | 
 | } // namespace JSC |