1/*
2 * Copyright (C) 2015 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#ifndef InferredType_h
27#define InferredType_h
28
29#include "ConcurrentJITLock.h"
30#include "JSCell.h"
31#include "PropertyName.h"
32#include "PutByIdFlags.h"
33#include "Watchpoint.h"
34
35namespace JSC {
36
37// This is an object used for the inference of the types of object properties.
38
39class InferredType final : public JSCell {
40public:
41 typedef JSCell Base;
42
43 static InferredType* create(VM&);
44
45 static const bool needsDestruction = true;
46 static void destroy(JSCell*);
47
48 static const unsigned StructureFlags = StructureIsImmortal | Base::StructureFlags;
49
50 static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype);
51
52 static void visitChildren(JSCell*, SlotVisitor&);
53
54 DECLARE_INFO;
55
56 enum Kind : uint8_t {
57 Bottom,
58 Boolean,
59 Other,
60 Int32,
61 Number,
62 String,
63 Symbol,
64 ObjectWithStructure,
65 ObjectWithStructureOrOther,
66 Object,
67 ObjectOrOther,
68 Top
69 };
70
71 static Kind kindForFlags(PutByIdFlags);
72
73 static bool hasStructure(Kind kind)
74 {
75 switch (kind) {
76 case ObjectWithStructure:
77 case ObjectWithStructureOrOther:
78 return true;
79 default:
80 return false;
81 }
82 }
83
84 class Descriptor {
85 public:
86 Descriptor()
87 : m_kind(Bottom)
88 , m_structure(nullptr)
89 {
90 }
91
92 Descriptor(Kind kind, Structure* structure = nullptr)
93 : m_kind(kind)
94 , m_structure(structure)
95 {
96 if (hasStructure(kind))
97 ASSERT(structure);
98 }
99
100 static Descriptor forValue(JSValue);
101
102 static Descriptor forFlags(VM&, PutByIdFlags);
103
104 explicit operator bool() const
105 {
106 return m_kind != Bottom || m_structure;
107 }
108
109 Kind kind() const { return m_kind; }
110 Structure* structure() const { return m_structure; }
111
112 PutByIdFlags putByIdFlags() const;
113
114 ALWAYS_INLINE bool includesValue(JSValue value)
115 {
116 switch (m_kind) {
117 case Bottom:
118 return false;
119 case Boolean:
120 return value.isBoolean();
121 case Other:
122 return value.isUndefinedOrNull();
123 case Int32:
124 return value.isInt32();
125 case Number:
126 return value.isNumber();
127 case String:
128 return value.isString();
129 case Symbol:
130 return value.isSymbol();
131 case ObjectWithStructure:
132 return value.isCell() && value.asCell()->structure() == m_structure;
133 case ObjectWithStructureOrOther:
134 return value.isUndefinedOrNull()
135 || (value.isCell() && value.asCell()->structure() == m_structure);
136 case Object:
137 return value.isObject();
138 case ObjectOrOther:
139 return value.isUndefinedOrNull() || value.isObject();
140 case Top:
141 return true;
142 }
143
144 RELEASE_ASSERT_NOT_REACHED();
145 }
146
147 bool operator==(const Descriptor& other) const
148 {
149 return m_kind == other.m_kind
150 && m_structure == other.m_structure;
151 }
152
153 bool operator!=(const Descriptor& other) const
154 {
155 return !(*this == other);
156 }
157
158 unsigned hash() const
159 {
160 return WTF::PtrHash<Structure*>::hash(m_structure) ^ static_cast<unsigned>(m_kind);
161 }
162
163 void merge(const Descriptor&);
164 void removeStructure();
165
166 // Returns true if this descriptor is more general than the other one.
167 bool subsumes(const Descriptor&) const;
168
169 void dumpInContext(PrintStream&, DumpContext*) const;
170 void dump(PrintStream&) const;
171
172 private:
173 Kind m_kind;
174 Structure* m_structure;
175 };
176
177 ConcurrentJITLock& lock() const { return m_lock; }
178
179 Descriptor descriptorMainThread() const
180 {
181 return Descriptor(m_kind, m_structure ? m_structure->structure() : nullptr);
182 }
183
184 Descriptor descriptor(const ConcurrentJITLocker&) const
185 {
186 return descriptorMainThread();
187 }
188 Descriptor descriptor() const
189 {
190 ConcurrentJITLocker locker(m_lock);
191 return descriptor(locker);
192 }
193
194 Kind kind(const ConcurrentJITLocker& locker) const { return descriptor(locker).kind(); }
195
196 bool isTop() const { return m_kind == Top; }
197 bool isRelevant() const { return m_kind != Top; }
198
199 // Returns true if the InferredType is still relevant after the store. It's not relevant if it's Top.
200 ALWAYS_INLINE bool willStoreValue(VM& vm, PropertyName propertyName, JSValue value)
201 {
202 Descriptor currentDescriptor = descriptorMainThread();
203 if (currentDescriptor.includesValue(value))
204 return currentDescriptor.kind() != Top;
205 return willStoreValueSlow(vm, propertyName, value);
206 }
207
208 // Immediately makes this type irrelevant.
209 void makeTop(VM& vm, PropertyName propertyName)
210 {
211 if (isTop())
212 return;
213 makeTopSlow(vm, propertyName);
214 }
215
216 // Returns true if it currently makes sense to watch this InferredType for this descriptor. Note that
217 // this will always return false for Top.
218 bool canWatch(const ConcurrentJITLocker&, const Descriptor&);
219 bool canWatch(const Descriptor&);
220
221 void addWatchpoint(const ConcurrentJITLocker&, Watchpoint*);
222 void addWatchpoint(Watchpoint*);
223
224 void dump(PrintStream&) const;
225
226private:
227 InferredType(VM&);
228 ~InferredType();
229
230 bool willStoreValueSlow(VM&, PropertyName, JSValue);
231 void makeTopSlow(VM&, PropertyName);
232
233 // Helper for willStoreValueSlow() and makeTopSlow(). This returns true if we should fire the
234 // watchpoint set.
235 bool set(const ConcurrentJITLocker&, VM&, Descriptor);
236
237 void removeStructure();
238
239 mutable ConcurrentJITLock m_lock;
240
241 Kind m_kind { Bottom };
242
243 class InferredStructureWatchpoint : public Watchpoint {
244 public:
245 InferredStructureWatchpoint() { }
246 protected:
247 void fireInternal(const FireDetail&) override;
248 };
249
250 class InferredStructureFinalizer : public UnconditionalFinalizer {
251 public:
252 InferredStructureFinalizer() { }
253 protected:
254 void finalizeUnconditionally() override;
255 };
256
257 class InferredStructure {
258 WTF_MAKE_FAST_ALLOCATED;
259 public:
260 InferredStructure(VM&, InferredType* parent, Structure*);
261
262 Structure* structure() const { return m_structure.get(); };
263
264 private:
265 friend class InferredType;
266 friend class InferredStructureWatchpoint;
267 friend class InferredStructureFinalizer;
268
269 InferredType* m_parent;
270 WriteBarrier<Structure> m_structure;
271
272 InferredStructureWatchpoint m_watchpoint;
273 InferredStructureFinalizer m_finalizer;
274 };
275
276 std::unique_ptr<InferredStructure> m_structure;
277
278 // NOTE: If this is being watched, we transform to Top because that implies that it wouldn't be
279 // profitable to watch it again. Also, this set is initialized clear, and is never exposed to the DFG
280 // thread. The DFG will use the InferredType as the thing that it watches.
281 InlineWatchpointSet m_watchpointSet;
282};
283
284} // namespace JSC
285
286namespace WTF {
287
288void printInternal(PrintStream&, JSC::InferredType::Kind);
289
290} // namespace WTF
291
292#endif // InferredType_h
293
294