/* * Copyright (C) 2014, 2015 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. */ #ifndef PolymorphicAccess_h #define PolymorphicAccess_h #if ENABLE(JIT) #include "CodeOrigin.h" #include "JSFunctionInlines.h" #include "MacroAssembler.h" #include "ObjectPropertyConditionSet.h" #include "Opcode.h" #include "ScratchRegisterAllocator.h" #include "Structure.h" #include namespace JSC { class CodeBlock; class PolymorphicAccess; class StructureStubInfo; class WatchpointsOnStructureStubInfo; class ScratchRegisterAllocator; struct AccessGenerationState; class AccessCase { WTF_MAKE_NONCOPYABLE(AccessCase); WTF_MAKE_FAST_ALLOCATED; public: enum AccessType { Load, Transition, Replace, Miss, Getter, Setter, CustomValueGetter, CustomAccessorGetter, CustomValueSetter, CustomAccessorSetter, IntrinsicGetter, InHit, InMiss, ArrayLength, StringLength }; static bool isGet(AccessType type) { switch (type) { case Transition: case Replace: case Setter: case CustomValueSetter: case CustomAccessorSetter: case InHit: case InMiss: return false; case Load: case Miss: case Getter: case CustomValueGetter: case CustomAccessorGetter: case IntrinsicGetter: case ArrayLength: case StringLength: return true; } } static bool isPut(AccessType type) { switch (type) { case Load: case Miss: case Getter: case CustomValueGetter: case CustomAccessorGetter: case IntrinsicGetter: case InHit: case InMiss: case ArrayLength: case StringLength: return false; case Transition: case Replace: case Setter: case CustomValueSetter: case CustomAccessorSetter: return true; } } static bool isIn(AccessType type) { switch (type) { case Load: case Miss: case Getter: case CustomValueGetter: case CustomAccessorGetter: case IntrinsicGetter: case Transition: case Replace: case Setter: case CustomValueSetter: case CustomAccessorSetter: case ArrayLength: case StringLength: return false; case InHit: case InMiss: return true; } } static std::unique_ptr get( VM&, JSCell* owner, AccessType, PropertyOffset, Structure*, const ObjectPropertyConditionSet& = ObjectPropertyConditionSet(), bool viaProxy = false, WatchpointSet* additionalSet = nullptr, PropertySlot::GetValueFunc = nullptr, JSObject* customSlotBase = nullptr); static std::unique_ptr replace(VM&, JSCell* owner, Structure*, PropertyOffset); static std::unique_ptr transition( VM&, JSCell* owner, Structure* oldStructure, Structure* newStructure, PropertyOffset, const ObjectPropertyConditionSet& = ObjectPropertyConditionSet()); static std::unique_ptr setter( VM&, JSCell* owner, AccessType, Structure*, PropertyOffset, const ObjectPropertyConditionSet&, PutPropertySlot::PutValueFunc = nullptr, JSObject* customSlotBase = nullptr); static std::unique_ptr in( VM&, JSCell* owner, AccessType, Structure*, const ObjectPropertyConditionSet& = ObjectPropertyConditionSet()); static std::unique_ptr getLength(VM&, JSCell* owner, AccessType); static std::unique_ptr getIntrinsic(VM&, JSCell* owner, JSFunction* intrinsic, PropertyOffset, Structure*, const ObjectPropertyConditionSet&); static std::unique_ptr fromStructureStubInfo(VM&, JSCell* owner, StructureStubInfo&); ~AccessCase(); std::unique_ptr clone() const; AccessType type() const { return m_type; } PropertyOffset offset() const { return m_offset; } bool viaProxy() const { return m_rareData ? m_rareData->viaProxy : false; } Structure* structure() const { if (m_type == Transition) return m_structure->previousID(); return m_structure.get(); } bool guardedByStructureCheck() const; Structure* newStructure() const { ASSERT(m_type == Transition); return m_structure.get(); } ObjectPropertyConditionSet conditionSet() const { return m_conditionSet; } JSFunction* intrinsicFunction() const { ASSERT(type() == IntrinsicGetter && m_rareData); return m_rareData->intrinsicFunction.get(); } Intrinsic intrinsic() const { return intrinsicFunction()->intrinsic(); } WatchpointSet* additionalSet() const { return m_rareData ? m_rareData->additionalSet.get() : nullptr; } JSObject* customSlotBase() const { return m_rareData ? m_rareData->customSlotBase.get() : nullptr; } JSObject* alternateBase() const; bool doesCalls() const { switch (type()) { case Getter: case Setter: case CustomValueGetter: case CustomAccessorGetter: case CustomValueSetter: case CustomAccessorSetter: return true; default: return false; } } bool isGetter() const { switch (type()) { case Getter: case CustomValueGetter: case CustomAccessorGetter: return true; default: return false; } } CallLinkInfo* callLinkInfo() const { if (!m_rareData) return nullptr; return m_rareData->callLinkInfo.get(); } // Is it still possible for this case to ever be taken? bool couldStillSucceed() const; static bool canEmitIntrinsicGetter(JSFunction*, Structure*); // If this method returns true, then it's a good idea to remove 'other' from the access once 'this' // is added. This method assumes that in case of contradictions, 'this' represents a newer, and so // more useful, truth. This method can be conservative; it will return false when it doubt. bool canReplace(const AccessCase& other); void dump(PrintStream& out) const; private: friend class CodeBlock; friend class PolymorphicAccess; AccessCase(); bool visitWeak(VM&) const; // Fall through on success. Two kinds of failures are supported: fall-through, which means that we // should try a different case; and failure, which means that this was the right case but it needs // help from the slow path. void generateWithGuard(AccessGenerationState&, MacroAssembler::JumpList& fallThrough); // Fall through on success, add a jump to the failure list on failure. void generate(AccessGenerationState&); void emitIntrinsicGetter(AccessGenerationState&); AccessType m_type { Load }; PropertyOffset m_offset { invalidOffset }; // Usually this is the structure that we expect the base object to have. But, this is the *new* // structure for a transition and we rely on the fact that it has a strong reference to the old // structure. For proxies, this is the structure of the object behind the proxy. WriteBarrier m_structure; ObjectPropertyConditionSet m_conditionSet; class RareData { WTF_MAKE_FAST_ALLOCATED; public: RareData() : viaProxy(false) { customAccessor.opaque = nullptr; } bool viaProxy; RefPtr additionalSet; std::unique_ptr callLinkInfo; union { PropertySlot::GetValueFunc getter; PutPropertySlot::PutValueFunc setter; void* opaque; } customAccessor; WriteBarrier customSlotBase; WriteBarrier intrinsicFunction; }; std::unique_ptr m_rareData; }; class PolymorphicAccess { WTF_MAKE_NONCOPYABLE(PolymorphicAccess); WTF_MAKE_FAST_ALLOCATED; public: PolymorphicAccess(); ~PolymorphicAccess(); // This may return null, in which case the old stub routine is left intact. You are required to // pass a vector of non-null access cases. This will prune the access cases by rejecting any case // in the list that is subsumed by a later case in the list. MacroAssemblerCodePtr regenerateWithCases( VM&, CodeBlock*, StructureStubInfo&, const Identifier&, Vector>); MacroAssemblerCodePtr regenerateWithCase( VM&, CodeBlock*, StructureStubInfo&, const Identifier&, std::unique_ptr); bool isEmpty() const { return m_list.isEmpty(); } unsigned size() const { return m_list.size(); } const AccessCase& at(unsigned i) const { return *m_list[i]; } const AccessCase& operator[](unsigned i) const { return *m_list[i]; } // If this returns false then we are requesting a reset of the owning StructureStubInfo. bool visitWeak(VM&) const; void aboutToDie(); void dump(PrintStream& out) const; bool containsPC(void* pc) const { if (!m_stubRoutine) return false; uintptr_t pcAsInt = bitwise_cast(pc); return m_stubRoutine->startAddress() <= pcAsInt && pcAsInt <= m_stubRoutine->endAddress(); } private: friend class AccessCase; friend class CodeBlock; friend struct AccessGenerationState; typedef Vector, 2> ListType; MacroAssemblerCodePtr regenerate( VM&, CodeBlock*, StructureStubInfo&, const Identifier&, ListType& cases); ListType m_list; RefPtr m_stubRoutine; std::unique_ptr m_watchpoints; std::unique_ptr>> m_weakReferences; }; struct AccessGenerationState { AccessGenerationState() : m_calculatedRegistersForCallAndExceptionHandling(false) , m_needsToRestoreRegistersIfException(false) , m_calculatedCallSiteIndex(false) { } CCallHelpers* jit { nullptr }; ScratchRegisterAllocator* allocator; ScratchRegisterAllocator::PreservedState preservedReusedRegisterState; PolymorphicAccess* access { nullptr }; StructureStubInfo* stubInfo { nullptr }; MacroAssembler::JumpList success; MacroAssembler::JumpList failAndRepatch; MacroAssembler::JumpList failAndIgnore; GPRReg baseGPR { InvalidGPRReg }; JSValueRegs valueRegs; GPRReg scratchGPR { InvalidGPRReg }; Vector> callbacks; const Identifier* ident; std::unique_ptr watchpoints; Vector> weakReferences; Watchpoint* addWatchpoint(const ObjectPropertyCondition& = ObjectPropertyCondition()); void restoreScratch(); void succeed(); void calculateLiveRegistersForCallAndExceptionHandling(); void preserveLiveRegistersToStackForCall(); void restoreLiveRegistersFromStackForCall(bool isGetter); void restoreLiveRegistersFromStackForCallWithThrownException(); void restoreLiveRegistersFromStackForCall(const RegisterSet& dontRestore); const RegisterSet& liveRegistersForCall() { RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling); return m_liveRegistersForCall; } CallSiteIndex callSiteIndexForExceptionHandlingOrOriginal(); CallSiteIndex callSiteIndexForExceptionHandling() { RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling); RELEASE_ASSERT(m_needsToRestoreRegistersIfException); RELEASE_ASSERT(m_calculatedCallSiteIndex); return m_callSiteIndex; } const HandlerInfo& originalExceptionHandler() const; unsigned numberOfStackBytesUsedForRegisterPreservation() const { RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling); return m_numberOfStackBytesUsedForRegisterPreservation; } bool needsToRestoreRegistersIfException() const { return m_needsToRestoreRegistersIfException; } CallSiteIndex originalCallSiteIndex() const; private: const RegisterSet& liveRegistersToPreserveAtExceptionHandlingCallSite() { RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling); return m_liveRegistersToPreserveAtExceptionHandlingCallSite; } RegisterSet m_liveRegistersToPreserveAtExceptionHandlingCallSite; RegisterSet m_liveRegistersForCall; CallSiteIndex m_callSiteIndex { CallSiteIndex(std::numeric_limits::max()) }; unsigned m_numberOfStackBytesUsedForRegisterPreservation { std::numeric_limits::max() }; bool m_calculatedRegistersForCallAndExceptionHandling : 1; bool m_needsToRestoreRegistersIfException : 1; bool m_calculatedCallSiteIndex : 1; }; } // namespace JSC namespace WTF { void printInternal(PrintStream&, JSC::AccessCase::AccessType); } // namespace WTF #endif // ENABLE(JIT) #endif // PolymorphicAccess_h