diff options
Diffstat (limited to 'Source/JavaScriptCore/bytecode/PropertyCondition.h')
-rw-r--r-- | Source/JavaScriptCore/bytecode/PropertyCondition.h | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/bytecode/PropertyCondition.h b/Source/JavaScriptCore/bytecode/PropertyCondition.h new file mode 100644 index 000000000..1d5568f8d --- /dev/null +++ b/Source/JavaScriptCore/bytecode/PropertyCondition.h @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2015-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. + */ + +#ifndef PropertyCondition_h +#define PropertyCondition_h + +#include "JSObject.h" +#include <wtf/HashMap.h> + +namespace JSC { + +class TrackedReferences; + +class PropertyCondition { +public: + enum Kind { + Presence, + Absence, + AbsenceOfSetter, + Equivalence // An adaptive watchpoint on this will be a pair of watchpoints, and when the structure transitions, we will set the replacement watchpoint on the new structure. + }; + + PropertyCondition() + : m_uid(nullptr) + , m_kind(Presence) + { + memset(&u, 0, sizeof(u)); + } + + PropertyCondition(WTF::HashTableDeletedValueType) + : m_uid(nullptr) + , m_kind(Absence) + { + memset(&u, 0, sizeof(u)); + } + + static PropertyCondition presenceWithoutBarrier(UniquedStringImpl* uid, PropertyOffset offset, unsigned attributes) + { + PropertyCondition result; + result.m_uid = uid; + result.m_kind = Presence; + result.u.presence.offset = offset; + result.u.presence.attributes = attributes; + return result; + } + + static PropertyCondition presence( + VM&, JSCell*, UniquedStringImpl* uid, PropertyOffset offset, unsigned attributes) + { + return presenceWithoutBarrier(uid, offset, attributes); + } + + // NOTE: The prototype is the storedPrototype not the prototypeForLookup. + static PropertyCondition absenceWithoutBarrier(UniquedStringImpl* uid, JSObject* prototype) + { + PropertyCondition result; + result.m_uid = uid; + result.m_kind = Absence; + result.u.absence.prototype = prototype; + return result; + } + + static PropertyCondition absence( + VM& vm, JSCell* owner, UniquedStringImpl* uid, JSObject* prototype) + { + if (owner) + vm.heap.writeBarrier(owner); + return absenceWithoutBarrier(uid, prototype); + } + + static PropertyCondition absenceOfSetterWithoutBarrier( + UniquedStringImpl* uid, JSObject* prototype) + { + PropertyCondition result; + result.m_uid = uid; + result.m_kind = AbsenceOfSetter; + result.u.absence.prototype = prototype; + return result; + } + + static PropertyCondition absenceOfSetter( + VM& vm, JSCell* owner, UniquedStringImpl* uid, JSObject* prototype) + { + if (owner) + vm.heap.writeBarrier(owner); + return absenceOfSetterWithoutBarrier(uid, prototype); + } + + static PropertyCondition equivalenceWithoutBarrier( + UniquedStringImpl* uid, JSValue value) + { + PropertyCondition result; + result.m_uid = uid; + result.m_kind = Equivalence; + result.u.equivalence.value = JSValue::encode(value); + return result; + } + + static PropertyCondition equivalence( + VM& vm, JSCell* owner, UniquedStringImpl* uid, JSValue value) + { + if (value.isCell() && owner) + vm.heap.writeBarrier(owner); + return equivalenceWithoutBarrier(uid, value); + } + + explicit operator bool() const { return m_uid || m_kind != Presence; } + + Kind kind() const { return m_kind; } + UniquedStringImpl* uid() const { return m_uid; } + + bool hasOffset() const { return !!*this && m_kind == Presence; }; + PropertyOffset offset() const + { + ASSERT(hasOffset()); + return u.presence.offset; + } + bool hasAttributes() const { return !!*this && m_kind == Presence; }; + unsigned attributes() const + { + ASSERT(hasAttributes()); + return u.presence.attributes; + } + + bool hasPrototype() const { return !!*this && (m_kind == Absence || m_kind == AbsenceOfSetter); } + JSObject* prototype() const + { + ASSERT(hasPrototype()); + return u.absence.prototype; + } + + bool hasRequiredValue() const { return !!*this && m_kind == Equivalence; } + JSValue requiredValue() const + { + ASSERT(hasRequiredValue()); + return JSValue::decode(u.equivalence.value); + } + + void dumpInContext(PrintStream&, DumpContext*) const; + void dump(PrintStream&) const; + + unsigned hash() const + { + unsigned result = WTF::PtrHash<UniquedStringImpl*>::hash(m_uid) + static_cast<unsigned>(m_kind); + switch (m_kind) { + case Presence: + result ^= u.presence.offset; + result ^= u.presence.attributes; + break; + case Absence: + case AbsenceOfSetter: + result ^= WTF::PtrHash<JSObject*>::hash(u.absence.prototype); + break; + case Equivalence: + result ^= EncodedJSValueHash::hash(u.equivalence.value); + break; + } + return result; + } + + bool operator==(const PropertyCondition& other) const + { + if (m_uid != other.m_uid) + return false; + if (m_kind != other.m_kind) + return false; + switch (m_kind) { + case Presence: + return u.presence.offset == other.u.presence.offset + && u.presence.attributes == other.u.presence.attributes; + case Absence: + case AbsenceOfSetter: + return u.absence.prototype == other.u.absence.prototype; + case Equivalence: + return u.equivalence.value == other.u.equivalence.value; + } + RELEASE_ASSERT_NOT_REACHED(); + return false; + } + + bool isHashTableDeletedValue() const + { + return !m_uid && m_kind == Absence; + } + + // Two conditions are compatible if they are identical or if they speak of different uids. If + // false is returned, you have to decide how to resolve the conflict - for example if there is + // a Presence and an Equivalence then in some cases you'll want the more general of the two + // while in other cases you'll want the more specific of the two. This will also return false + // for contradictions, like Presence and Absence on the same uid. By convention, invalid + // conditions aren't compatible with anything. + bool isCompatibleWith(const PropertyCondition& other) const + { + if (!*this || !other) + return false; + return *this == other || uid() != other.uid(); + } + + // Checks if the object's structure claims that the property won't be intercepted. + bool isStillValidAssumingImpurePropertyWatchpoint(Structure*, JSObject* base = nullptr) const; + + // Returns true if we need an impure property watchpoint to ensure validity even if + // isStillValidAccordingToStructure() returned true. + bool validityRequiresImpurePropertyWatchpoint(Structure*) const; + + // Checks if the condition is still valid right now for the given object and structure. + // May conservatively return false, if the object and structure alone don't guarantee the + // condition. This happens for an Absence condition on an object that may have impure + // properties. If the object is not supplied, then a "true" return indicates that checking if + // an object has the given structure guarantees the condition still holds. If an object is + // supplied, then you may need to use some other watchpoints on the object to guarantee the + // condition in addition to the structure check. + bool isStillValid(Structure*, JSObject* base = nullptr) const; + + // In some cases, the condition is not watchable, but could be made watchable by enabling the + // appropriate watchpoint. For example, replacement watchpoints are enabled only when some + // access is cached on the property in some structure. This is mainly to save space for + // dictionary properties or properties that never get very hot. But, it's always safe to + // enable watching, provided that this is called from the main thread. + enum WatchabilityEffort { + // This is the default. It means that we don't change the state of any Structure or + // object, and implies that if the property happens not to be watchable then we don't make + // it watchable. This is mandatory if calling from a JIT thread. This is also somewhat + // preferable when first deciding whether to watch a condition for the first time (i.e. + // not from a watchpoint fire that causes us to see if we should adapt), since a + // watchpoint not being initialized for watching implies that maybe we don't know enough + // yet to make it profitable to watch -- as in, the thing being watched may not have + // stabilized yet. We prefer to only assume that a condition will hold if it has been + // known to hold for a while already. + MakeNoChanges, + + // Do what it takes to ensure that the property can be watched, if doing so has no + // user-observable effect. For now this just means that we will ensure that a property + // replacement watchpoint is enabled if it hadn't been enabled already. Do not use this + // from JIT threads, since the act of enabling watchpoints is not thread-safe. + EnsureWatchability + }; + + // This means that it's still valid and we could enforce validity by setting a transition + // watchpoint on the structure and possibly an impure property watchpoint. + bool isWatchableAssumingImpurePropertyWatchpoint( + Structure*, JSObject* base = nullptr, WatchabilityEffort = MakeNoChanges) const; + + // This means that it's still valid and we could enforce validity by setting a transition + // watchpoint on the structure. + bool isWatchable( + Structure*, JSObject* base = nullptr, WatchabilityEffort = MakeNoChanges) const; + + bool watchingRequiresStructureTransitionWatchpoint() const + { + // Currently, this is required for all of our conditions. + return !!*this; + } + bool watchingRequiresReplacementWatchpoint() const + { + return !!*this && m_kind == Equivalence; + } + + // This means that the objects involved in this are still live. + bool isStillLive() const; + + void validateReferences(const TrackedReferences&) const; + + static bool isValidValueForAttributes(JSValue value, unsigned attributes); + + bool isValidValueForPresence(JSValue) const; + + PropertyCondition attemptToMakeEquivalenceWithoutBarrier(JSObject* base) const; + +private: + bool isWatchableWhenValid(Structure*, WatchabilityEffort) const; + + UniquedStringImpl* m_uid; + Kind m_kind; + union { + struct { + PropertyOffset offset; + unsigned attributes; + } presence; + struct { + JSObject* prototype; + } absence; + struct { + EncodedJSValue value; + } equivalence; + } u; +}; + +struct PropertyConditionHash { + static unsigned hash(const PropertyCondition& key) { return key.hash(); } + static bool equal( + const PropertyCondition& a, const PropertyCondition& b) + { + return a == b; + } + static const bool safeToCompareToEmptyOrDeleted = true; +}; + +} // namespace JSC + +namespace WTF { + +void printInternal(PrintStream&, JSC::PropertyCondition::Kind); + +template<typename T> struct DefaultHash; +template<> struct DefaultHash<JSC::PropertyCondition> { + typedef JSC::PropertyConditionHash Hash; +}; + +template<typename T> struct HashTraits; +template<> struct HashTraits<JSC::PropertyCondition> : SimpleClassHashTraits<JSC::PropertyCondition> { }; + +} // namespace WTF + +#endif // PropertyCondition_h + |