summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/bytecode/PropertyCondition.h
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/bytecode/PropertyCondition.h')
-rw-r--r--Source/JavaScriptCore/bytecode/PropertyCondition.h338
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
+