DFG should have adaptive structure watchpoints
[WebKit-https.git] / Source / JavaScriptCore / bytecode / ObjectPropertyConditionSet.cpp
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 #include "config.h"
27 #include "ObjectPropertyConditionSet.h"
28
29 #include "JSCInlines.h"
30 #include <wtf/ListDump.h>
31
32 namespace JSC {
33
34 ObjectPropertyCondition ObjectPropertyConditionSet::forObject(JSObject* object) const
35 {
36     for (const ObjectPropertyCondition& condition : *this) {
37         if (condition.object() == object)
38             return condition;
39     }
40     return ObjectPropertyCondition();
41 }
42
43 ObjectPropertyCondition ObjectPropertyConditionSet::forConditionKind(
44     PropertyCondition::Kind kind) const
45 {
46     for (const ObjectPropertyCondition& condition : *this) {
47         if (condition.kind() == kind)
48             return condition;
49     }
50     return ObjectPropertyCondition();
51 }
52
53 unsigned ObjectPropertyConditionSet::numberOfConditionsWithKind(PropertyCondition::Kind kind) const
54 {
55     unsigned result = 0;
56     for (const ObjectPropertyCondition& condition : *this) {
57         if (condition.kind() == kind)
58             result++;
59     }
60     return result;
61 }
62
63 bool ObjectPropertyConditionSet::hasOneSlotBaseCondition() const
64 {
65     return numberOfConditionsWithKind(PropertyCondition::Presence) == 1;
66 }
67
68 ObjectPropertyCondition ObjectPropertyConditionSet::slotBaseCondition() const
69 {
70     ObjectPropertyCondition result;
71     unsigned numFound = 0;
72     for (const ObjectPropertyCondition& condition : *this) {
73         if (condition.kind() == PropertyCondition::Presence) {
74             result = condition;
75             numFound++;
76         }
77     }
78     RELEASE_ASSERT(numFound == 1);
79     return result;
80 }
81
82 ObjectPropertyConditionSet ObjectPropertyConditionSet::mergedWith(
83     const ObjectPropertyConditionSet& other) const
84 {
85     if (!isValid() || !other.isValid())
86         return invalid();
87
88     Vector<ObjectPropertyCondition> result;
89     
90     if (!isEmpty())
91         result.appendVector(m_data->vector);
92     
93     for (const ObjectPropertyCondition& newCondition : other) {
94         for (const ObjectPropertyCondition& existingCondition : *this) {
95             if (newCondition == existingCondition)
96                 continue;
97             if (!newCondition.isCompatibleWith(existingCondition))
98                 return invalid();
99             result.append(newCondition);
100         }
101     }
102
103     return create(result);
104 }
105
106 bool ObjectPropertyConditionSet::structuresEnsureValidity() const
107 {
108     if (!isValid())
109         return false;
110     
111     for (const ObjectPropertyCondition& condition : *this) {
112         if (!condition.structureEnsuresValidity())
113             return false;
114     }
115     return true;
116 }
117
118 bool ObjectPropertyConditionSet::structuresEnsureValidityAssumingImpurePropertyWatchpoint() const
119 {
120     if (!isValid())
121         return false;
122     
123     for (const ObjectPropertyCondition& condition : *this) {
124         if (!condition.structureEnsuresValidityAssumingImpurePropertyWatchpoint())
125             return false;
126     }
127     return true;
128 }
129
130 bool ObjectPropertyConditionSet::needImpurePropertyWatchpoint() const
131 {
132     for (const ObjectPropertyCondition& condition : *this) {
133         if (condition.validityRequiresImpurePropertyWatchpoint())
134             return true;
135     }
136     return false;
137 }
138
139 bool ObjectPropertyConditionSet::areStillLive() const
140 {
141     for (const ObjectPropertyCondition& condition : *this) {
142         if (!condition.isStillLive())
143             return false;
144     }
145     return true;
146 }
147
148 void ObjectPropertyConditionSet::dumpInContext(PrintStream& out, DumpContext* context) const
149 {
150     if (!isValid()) {
151         out.print("<invalid>");
152         return;
153     }
154     
155     out.print("[");
156     if (m_data)
157         out.print(listDumpInContext(m_data->vector, context));
158     out.print("]");
159 }
160
161 void ObjectPropertyConditionSet::dump(PrintStream& out) const
162 {
163     dumpInContext(out, nullptr);
164 }
165
166 namespace {
167
168 bool verbose = false;
169
170 ObjectPropertyCondition generateCondition(
171     VM& vm, JSCell* owner, JSObject* object, UniquedStringImpl* uid, PropertyCondition::Kind conditionKind)
172 {
173     Structure* structure = object->structure();
174     if (verbose)
175         dataLog("Creating condition ", conditionKind, " for ", pointerDump(structure), "\n");
176
177     ObjectPropertyCondition result;
178     switch (conditionKind) {
179     case PropertyCondition::Presence: {
180         unsigned attributes;
181         PropertyOffset offset = structure->getConcurrently(uid, attributes);
182         if (offset == invalidOffset)
183             return ObjectPropertyCondition();
184         result = ObjectPropertyCondition::presence(vm, owner, object, uid, offset, attributes);
185         break;
186     }
187     case PropertyCondition::Absence: {
188         result = ObjectPropertyCondition::absence(
189             vm, owner, object, uid, object->structure()->storedPrototypeObject());
190         break;
191     }
192     case PropertyCondition::AbsenceOfSetter: {
193         result = ObjectPropertyCondition::absenceOfSetter(
194             vm, owner, object, uid, object->structure()->storedPrototypeObject());
195         break;
196     }
197     default:
198         RELEASE_ASSERT_NOT_REACHED();
199         return ObjectPropertyCondition();
200     }
201
202     if (!result.structureEnsuresValidityAssumingImpurePropertyWatchpoint()) {
203         if (verbose)
204             dataLog("Failed to create condition: ", result, "\n");
205         return ObjectPropertyCondition();
206     }
207
208     if (verbose)
209         dataLog("New condition: ", result, "\n");
210     return result;
211 }
212
213 enum Concurrency {
214     MainThread,
215     Concurrent
216 };
217 template<typename Functor>
218 ObjectPropertyConditionSet generateConditions(
219     VM& vm, JSGlobalObject* globalObject, Structure* structure, JSObject* prototype, const Functor& functor,
220     Concurrency concurrency = MainThread)
221 {
222     Vector<ObjectPropertyCondition> conditions;
223     
224     for (;;) {
225         if (verbose)
226             dataLog("Considering structure: ", pointerDump(structure), "\n");
227         
228         if (structure->isProxy()) {
229             if (verbose)
230                 dataLog("It's a proxy, so invalid.\n");
231             return ObjectPropertyConditionSet::invalid();
232         }
233         
234         JSValue value = structure->prototypeForLookup(globalObject);
235         
236         if (value.isNull()) {
237             if (!prototype) {
238                 if (verbose)
239                     dataLog("Reached end up prototype chain as expected, done.\n");
240                 break;
241             }
242             if (verbose)
243                 dataLog("Unexpectedly reached end of prototype chain, so invalid.\n");
244             return ObjectPropertyConditionSet::invalid();
245         }
246         
247         JSObject* object = jsCast<JSObject*>(value);
248         structure = object->structure(vm);
249         
250         // Since we're accessing a prototype repeatedly, it's a good bet that it should not be
251         // treated as a dictionary.
252         if (structure->isDictionary()) {
253             if (concurrency == MainThread)
254                 structure->flattenDictionaryStructure(vm, object);
255             else {
256                 if (verbose)
257                     dataLog("Cannot flatten dictionary when not on main thread, so invalid.\n");
258                 return ObjectPropertyConditionSet::invalid();
259             }
260         }
261
262         if (!functor(conditions, object)) {
263             if (verbose)
264                 dataLog("Functor failed, invalid.\n");
265             return ObjectPropertyConditionSet::invalid();
266         }
267         
268         if (object == prototype) {
269             if (verbose)
270                 dataLog("Reached desired prototype, done.\n");
271             break;
272         }
273     }
274
275     if (verbose)
276         dataLog("Returning conditions: ", listDump(conditions), "\n");
277     return ObjectPropertyConditionSet::create(conditions);
278 }
279
280 } // anonymous namespace
281
282 ObjectPropertyConditionSet generateConditionsForPropertyMiss(
283     VM& vm, JSCell* owner, ExecState* exec, Structure* headStructure, UniquedStringImpl* uid)
284 {
285     return generateConditions(
286         vm, exec->lexicalGlobalObject(), headStructure, nullptr,
287         [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool {
288             ObjectPropertyCondition result =
289                 generateCondition(vm, owner, object, uid, PropertyCondition::Absence);
290             if (!result)
291                 return false;
292             conditions.append(result);
293             return true;
294         });
295 }
296
297 ObjectPropertyConditionSet generateConditionsForPropertySetterMiss(
298     VM& vm, JSCell* owner, ExecState* exec, Structure* headStructure, UniquedStringImpl* uid)
299 {
300     return generateConditions(
301         vm, exec->lexicalGlobalObject(), headStructure, nullptr,
302         [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool {
303             ObjectPropertyCondition result =
304                 generateCondition(vm, owner, object, uid, PropertyCondition::AbsenceOfSetter);
305             if (!result)
306                 return false;
307             conditions.append(result);
308             return true;
309         });
310 }
311
312 ObjectPropertyConditionSet generateConditionsForPrototypePropertyHit(
313     VM& vm, JSCell* owner, ExecState* exec, Structure* headStructure, JSObject* prototype,
314     UniquedStringImpl* uid)
315 {
316     return generateConditions(
317         vm, exec->lexicalGlobalObject(), headStructure, prototype,
318         [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool {
319             PropertyCondition::Kind kind =
320                 object == prototype ? PropertyCondition::Presence : PropertyCondition::Absence;
321             ObjectPropertyCondition result =
322                 generateCondition(vm, owner, object, uid, kind);
323             if (!result)
324                 return false;
325             conditions.append(result);
326             return true;
327         });
328 }
329
330 ObjectPropertyConditionSet generateConditionsForPrototypePropertyHitCustom(
331     VM& vm, JSCell* owner, ExecState* exec, Structure* headStructure, JSObject* prototype,
332     UniquedStringImpl* uid)
333 {
334     return generateConditions(
335         vm, exec->lexicalGlobalObject(), headStructure, prototype,
336         [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool {
337             if (object == prototype)
338                 return true;
339             ObjectPropertyCondition result =
340                 generateCondition(vm, owner, object, uid, PropertyCondition::Absence);
341             if (!result)
342                 return false;
343             conditions.append(result);
344             return true;
345         });
346 }
347
348 ObjectPropertyConditionSet generateConditionsForPropertySetterMissConcurrently(
349     VM& vm, JSGlobalObject* globalObject, Structure* headStructure, UniquedStringImpl* uid)
350 {
351     return generateConditions(
352         vm, globalObject, headStructure, nullptr,
353         [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool {
354             ObjectPropertyCondition result =
355                 generateCondition(vm, nullptr, object, uid, PropertyCondition::AbsenceOfSetter);
356             if (!result)
357                 return false;
358             conditions.append(result);
359             return true;
360         }, Concurrent);
361 }
362
363 } // namespace JSC
364