1/*
2 * Copyright (C) 2015-2019 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 customFunctionEquivalence(
126 VM& vm, JSCell* owner, JSObject* object, UniquedStringImpl* uid)
127 {
128 ObjectPropertyCondition result;
129 result.m_object = object;
130 result.m_condition = PropertyCondition::customFunctionEquivalence(uid);
131 if (owner)
132 vm.heap.writeBarrier(owner);
133 return result;
134 }
135
136 static ObjectPropertyCondition hasPrototypeWithoutBarrier(JSObject* object, JSObject* prototype)
137 {
138 ObjectPropertyCondition result;
139 result.m_object = object;
140 result.m_condition = PropertyCondition::hasPrototypeWithoutBarrier(prototype);
141 return result;
142 }
143
144 static ObjectPropertyCondition hasPrototype(
145 VM& vm, JSCell* owner, JSObject* object, JSObject* prototype)
146 {
147 if (owner)
148 vm.heap.writeBarrier(owner);
149 return hasPrototypeWithoutBarrier(object, prototype);
150 }
151
152 explicit operator bool() const { return !!m_condition; }
153
154 JSObject* object() const { return m_object; }
155 PropertyCondition condition() const { return m_condition; }
156
157 PropertyCondition::Kind kind() const { return condition().kind(); }
158 UniquedStringImpl* uid() const { return condition().uid(); }
159 bool hasOffset() const { return condition().hasOffset(); }
160 PropertyOffset offset() const { return condition().offset(); }
161 unsigned hasAttributes() const { return condition().hasAttributes(); }
162 unsigned attributes() const { return condition().attributes(); }
163 bool hasPrototype() const { return condition().hasPrototype(); }
164 JSObject* prototype() const { return condition().prototype(); }
165 bool hasRequiredValue() const { return condition().hasRequiredValue(); }
166 JSValue requiredValue() const { return condition().requiredValue(); }
167
168 void dumpInContext(PrintStream&, DumpContext*) const;
169 void dump(PrintStream&) const;
170
171 unsigned hash() const
172 {
173 return WTF::PtrHash<JSObject*>::hash(m_object) ^ m_condition.hash();
174 }
175
176 bool operator==(const ObjectPropertyCondition& other) const
177 {
178 return m_object == other.m_object
179 && m_condition == other.m_condition;
180 }
181
182 bool isHashTableDeletedValue() const
183 {
184 return !m_object && m_condition.isHashTableDeletedValue();
185 }
186
187 // Two conditions are compatible if they are identical or if they speak of different uids or
188 // different objects. If false is returned, you have to decide how to resolve the conflict -
189 // for example if there is a Presence and an Equivalence then in some cases you'll want the
190 // more general of the two while in other cases you'll want the more specific of the two. This
191 // will also return false for contradictions, like Presence and Absence on the same
192 // object/uid. By convention, invalid conditions aren't compatible with anything.
193 bool isCompatibleWith(const ObjectPropertyCondition& other) const
194 {
195 if (!*this || !other)
196 return false;
197 return *this == other || uid() != other.uid() || object() != other.object();
198 }
199
200 // These validity-checking methods can optionally take a Struture* instead of loading the
201 // Structure* from the object. If you're in the concurrent JIT, then you must use the forms
202 // that take an explicit Structure* because you want the compiler to optimize for the same
203 // structure that you validated (i.e. avoid a TOCTOU race).
204
205 // Checks if the object's structure claims that the property won't be intercepted. Validity
206 // does not require watchpoints on the object.
207 bool structureEnsuresValidityAssumingImpurePropertyWatchpoint() const;
208
209 // Returns true if we need an impure property watchpoint to ensure validity even if
210 // isStillValidAccordingToStructure() returned true.
211 bool validityRequiresImpurePropertyWatchpoint(Structure*) const;
212 bool validityRequiresImpurePropertyWatchpoint() const;
213
214 // Checks if the condition still holds setting aside the need for an impure property watchpoint.
215 // Validity might still require watchpoints on the object.
216 bool isStillValidAssumingImpurePropertyWatchpoint(Structure*) const;
217 bool isStillValidAssumingImpurePropertyWatchpoint() const;
218
219 // Checks if the condition still holds. May conservatively return false, if the object and
220 // structure alone don't guarantee the condition. Note that this may return true if the
221 // condition still requires some watchpoints on the object in addition to checking the
222 // structure. If you want to check if the condition holds by using the structure alone,
223 // use structureEnsuresValidity().
224 bool isStillValid(Structure*) const;
225 bool isStillValid() const;
226
227 // Shorthand for condition().isStillValid(structure).
228 bool structureEnsuresValidity(Structure*) const;
229 bool structureEnsuresValidity() const;
230
231 // This means that it's still valid and we could enforce validity by setting a transition
232 // watchpoint on the structure and possibly an impure property watchpoint.
233 bool isWatchableAssumingImpurePropertyWatchpoint(
234 Structure*,
235 PropertyCondition::WatchabilityEffort = PropertyCondition::MakeNoChanges) const;
236 bool isWatchableAssumingImpurePropertyWatchpoint(
237 PropertyCondition::WatchabilityEffort = PropertyCondition::MakeNoChanges) const;
238
239 // This means that it's still valid and we could enforce validity by setting a transition
240 // watchpoint on the structure, and a value change watchpoint if we're Equivalence.
241 bool isWatchable(
242 Structure*,
243 PropertyCondition::WatchabilityEffort = PropertyCondition::MakeNoChanges) const;
244 bool isWatchable(
245 PropertyCondition::WatchabilityEffort = PropertyCondition::MakeNoChanges) const;
246
247 bool watchingRequiresStructureTransitionWatchpoint() const
248 {
249 return condition().watchingRequiresStructureTransitionWatchpoint();
250 }
251 bool watchingRequiresReplacementWatchpoint() const
252 {
253 return condition().watchingRequiresReplacementWatchpoint();
254 }
255
256 template<typename Functor>
257 void forEachDependentCell(const Functor& functor) const
258 {
259 functor(m_object);
260 m_condition.forEachDependentCell(functor);
261 }
262
263 // This means that the objects involved in this are still live.
264 bool isStillLive(VM&) const;
265
266 void validateReferences(const TrackedReferences&) const;
267
268 bool isValidValueForPresence(VM& vm, JSValue value) const
269 {
270 return condition().isValidValueForPresence(vm, value);
271 }
272
273 ObjectPropertyCondition attemptToMakeEquivalenceWithoutBarrier(VM&) const;
274
275private:
276 JSObject* m_object;
277 PropertyCondition m_condition;
278};
279
280struct ObjectPropertyConditionHash {
281 static unsigned hash(const ObjectPropertyCondition& key) { return key.hash(); }
282 static bool equal(
283 const ObjectPropertyCondition& a, const ObjectPropertyCondition& b)
284 {
285 return a == b;
286 }
287 static constexpr bool safeToCompareToEmptyOrDeleted = true;
288};
289
290} // namespace JSC
291
292namespace WTF {
293
294template<typename T> struct DefaultHash;
295template<> struct DefaultHash<JSC::ObjectPropertyCondition> {
296 typedef JSC::ObjectPropertyConditionHash Hash;
297};
298
299template<typename T> struct HashTraits;
300template<> struct HashTraits<JSC::ObjectPropertyCondition> : SimpleClassHashTraits<JSC::ObjectPropertyCondition> { };
301
302} // namespace WTF
303