1/*
2 * Copyright (C) 2015-2018 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#pragma once
27
28#include "JSObject.h"
29#include "PropertyCondition.h"
30#include <wtf/HashMap.h>
31
32namespace JSC {
33
34class TrackedReferences;
35
36class ObjectPropertyCondition {
37public:
38 ObjectPropertyCondition()
39 : m_object(nullptr)
40 {
41 }
42
43 ObjectPropertyCondition(WTF::HashTableDeletedValueType token)
44 : m_object(nullptr)
45 , m_condition(token)
46 {
47 }
48
49 ObjectPropertyCondition(JSObject* object, const PropertyCondition& condition)
50 : m_object(object)
51 , m_condition(condition)
52 {
53 }
54
55 static ObjectPropertyCondition presenceWithoutBarrier(
56 JSObject* object, UniquedStringImpl* uid, PropertyOffset offset, unsigned attributes)
57 {
58 ObjectPropertyCondition result;
59 result.m_object = object;
60 result.m_condition = PropertyCondition::presenceWithoutBarrier(uid, offset, attributes);
61 return result;
62 }
63
64 static ObjectPropertyCondition presence(
65 VM& vm, JSCell* owner, JSObject* object, UniquedStringImpl* uid, PropertyOffset offset,
66 unsigned attributes)
67 {
68 if (owner)
69 vm.heap.writeBarrier(owner);
70 return presenceWithoutBarrier(object, uid, offset, attributes);
71 }
72
73 // NOTE: The prototype is the storedPrototype, not the prototypeForLookup.
74 static ObjectPropertyCondition absenceWithoutBarrier(
75 JSObject* object, UniquedStringImpl* uid, JSObject* prototype)
76 {
77 ObjectPropertyCondition result;
78 result.m_object = object;
79 result.m_condition = PropertyCondition::absenceWithoutBarrier(uid, prototype);
80 return result;
81 }
82
83 static ObjectPropertyCondition absence(
84 VM& vm, JSCell* owner, JSObject* object, UniquedStringImpl* uid, JSObject* prototype)
85 {
86 if (owner)
87 vm.heap.writeBarrier(owner);
88 return absenceWithoutBarrier(object, uid, prototype);
89 }
90
91 static ObjectPropertyCondition absenceOfSetEffectWithoutBarrier(
92 JSObject* object, UniquedStringImpl* uid, JSObject* prototype)
93 {
94 ObjectPropertyCondition result;
95 result.m_object = object;
96 result.m_condition = PropertyCondition::absenceOfSetEffectWithoutBarrier(uid, prototype);
97 return result;
98 }
99
100 static ObjectPropertyCondition absenceOfSetEffect(
101 VM& vm, JSCell* owner, JSObject* object, UniquedStringImpl* uid, JSObject* prototype)
102 {
103 if (owner)
104 vm.heap.writeBarrier(owner);
105 return absenceOfSetEffectWithoutBarrier(object, uid, prototype);
106 }
107
108 static ObjectPropertyCondition equivalenceWithoutBarrier(
109 JSObject* object, UniquedStringImpl* uid, JSValue value)
110 {
111 ObjectPropertyCondition result;
112 result.m_object = object;
113 result.m_condition = PropertyCondition::equivalenceWithoutBarrier(uid, value);
114 return result;
115 }
116
117 static ObjectPropertyCondition equivalence(
118 VM& vm, JSCell* owner, JSObject* object, UniquedStringImpl* uid, JSValue value)
119 {
120 if (owner)
121 vm.heap.writeBarrier(owner);
122 return equivalenceWithoutBarrier(object, uid, value);
123 }
124
125 static ObjectPropertyCondition hasPrototypeWithoutBarrier(JSObject* object, JSObject* prototype)
126 {
127 ObjectPropertyCondition result;
128 result.m_object = object;
129 result.m_condition = PropertyCondition::hasPrototypeWithoutBarrier(prototype);
130 return result;
131 }
132
133 static ObjectPropertyCondition hasPrototype(
134 VM& vm, JSCell* owner, JSObject* object, JSObject* prototype)
135 {
136 if (owner)
137 vm.heap.writeBarrier(owner);
138 return hasPrototypeWithoutBarrier(object, prototype);
139 }
140
141 explicit operator bool() const { return !!m_condition; }
142
143 JSObject* object() const { return m_object; }
144 PropertyCondition condition() const { return m_condition; }
145
146 PropertyCondition::Kind kind() const { return condition().kind(); }
147 UniquedStringImpl* uid() const { return condition().uid(); }
148 bool hasOffset() const { return condition().hasOffset(); }
149 PropertyOffset offset() const { return condition().offset(); }
150 unsigned hasAttributes() const { return condition().hasAttributes(); }
151 unsigned attributes() const { return condition().attributes(); }
152 bool hasPrototype() const { return condition().hasPrototype(); }
153 JSObject* prototype() const { return condition().prototype(); }
154 bool hasRequiredValue() const { return condition().hasRequiredValue(); }
155 JSValue requiredValue() const { return condition().requiredValue(); }
156
157 void dumpInContext(PrintStream&, DumpContext*) const;
158 void dump(PrintStream&) const;
159
160 unsigned hash() const
161 {
162 return WTF::PtrHash<JSObject*>::hash(m_object) ^ m_condition.hash();
163 }
164
165 bool operator==(const ObjectPropertyCondition& other) const
166 {
167 return m_object == other.m_object
168 && m_condition == other.m_condition;
169 }
170
171 bool isHashTableDeletedValue() const
172 {
173 return !m_object && m_condition.isHashTableDeletedValue();
174 }
175
176 // Two conditions are compatible if they are identical or if they speak of different uids or
177 // different objects. If false is returned, you have to decide how to resolve the conflict -
178 // for example if there is a Presence and an Equivalence then in some cases you'll want the
179 // more general of the two while in other cases you'll want the more specific of the two. This
180 // will also return false for contradictions, like Presence and Absence on the same
181 // object/uid. By convention, invalid conditions aren't compatible with anything.
182 bool isCompatibleWith(const ObjectPropertyCondition& other) const
183 {
184 if (!*this || !other)
185 return false;
186 return *this == other || uid() != other.uid() || object() != other.object();
187 }
188
189 // These validity-checking methods can optionally take a Struture* instead of loading the
190 // Structure* from the object. If you're in the concurrent JIT, then you must use the forms
191 // that take an explicit Structure* because you want the compiler to optimize for the same
192 // structure that you validated (i.e. avoid a TOCTOU race).
193
194 // Checks if the object's structure claims that the property won't be intercepted. Validity
195 // does not require watchpoints on the object.
196 bool structureEnsuresValidityAssumingImpurePropertyWatchpoint(Structure*) const;
197 bool structureEnsuresValidityAssumingImpurePropertyWatchpoint() const;
198
199 // Returns true if we need an impure property watchpoint to ensure validity even if
200 // isStillValidAccordingToStructure() returned true.
201 bool validityRequiresImpurePropertyWatchpoint(Structure*) const;
202 bool validityRequiresImpurePropertyWatchpoint() const;
203
204 // Checks if the condition still holds setting aside the need for an impure property watchpoint.
205 // Validity might still require watchpoints on the object.
206 bool isStillValidAssumingImpurePropertyWatchpoint(Structure*) const;
207 bool isStillValidAssumingImpurePropertyWatchpoint() const;
208
209 // Checks if the condition still holds. May conservatively return false, if the object and
210 // structure alone don't guarantee the condition. Note that this may return true if the
211 // condition still requires some watchpoints on the object in addition to checking the
212 // structure. If you want to check if the condition holds by using the structure alone,
213 // use structureEnsuresValidity().
214 bool isStillValid(Structure*) const;
215 bool isStillValid() const;
216
217 // Shorthand for condition().isStillValid(structure).
218 bool structureEnsuresValidity(Structure*) const;
219 bool structureEnsuresValidity() const;
220
221 // This means that it's still valid and we could enforce validity by setting a transition
222 // watchpoint on the structure and possibly an impure property watchpoint.
223 bool isWatchableAssumingImpurePropertyWatchpoint(
224 Structure*,
225 PropertyCondition::WatchabilityEffort = PropertyCondition::MakeNoChanges) const;
226 bool isWatchableAssumingImpurePropertyWatchpoint(
227 PropertyCondition::WatchabilityEffort = PropertyCondition::MakeNoChanges) const;
228
229 // This means that it's still valid and we could enforce validity by setting a transition
230 // watchpoint on the structure.
231 bool isWatchable(
232 Structure*,
233 PropertyCondition::WatchabilityEffort = PropertyCondition::MakeNoChanges) const;
234 bool isWatchable(
235 PropertyCondition::WatchabilityEffort = PropertyCondition::MakeNoChanges) const;
236
237 bool watchingRequiresStructureTransitionWatchpoint() const
238 {
239 return condition().watchingRequiresStructureTransitionWatchpoint();
240 }
241 bool watchingRequiresReplacementWatchpoint() const
242 {
243 return condition().watchingRequiresReplacementWatchpoint();
244 }
245
246 // This means that the objects involved in this are still live.
247 bool isStillLive(VM&) const;
248
249 void validateReferences(const TrackedReferences&) const;
250
251 bool isValidValueForPresence(VM& vm, JSValue value) const
252 {
253 return condition().isValidValueForPresence(vm, value);
254 }
255
256 ObjectPropertyCondition attemptToMakeEquivalenceWithoutBarrier(VM&) const;
257
258private:
259 JSObject* m_object;
260 PropertyCondition m_condition;
261};
262
263struct ObjectPropertyConditionHash {
264 static unsigned hash(const ObjectPropertyCondition& key) { return key.hash(); }
265 static bool equal(
266 const ObjectPropertyCondition& a, const ObjectPropertyCondition& b)
267 {
268 return a == b;
269 }
270 static const bool safeToCompareToEmptyOrDeleted = true;
271};
272
273} // namespace JSC
274
275namespace WTF {
276
277template<typename T> struct DefaultHash;
278template<> struct DefaultHash<JSC::ObjectPropertyCondition> {
279 typedef JSC::ObjectPropertyConditionHash Hash;
280};
281
282template<typename T> struct HashTraits;
283template<> struct HashTraits<JSC::ObjectPropertyCondition> : SimpleClassHashTraits<JSC::ObjectPropertyCondition> { };
284
285} // namespace WTF
286