[JSC] Pass VM& parameter as much as possible
[WebKit.git] / Source / JavaScriptCore / bytecode / ObjectPropertyConditionSet.cpp
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 #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) != (numberOfConditionsWithKind(PropertyCondition::Equivalence) == 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             || condition.kind() == PropertyCondition::Equivalence) {
75             result = condition;
76             numFound++;
77         }
78     }
79     RELEASE_ASSERT(numFound == 1);
80     return result;
81 }
82
83 ObjectPropertyConditionSet ObjectPropertyConditionSet::mergedWith(
84     const ObjectPropertyConditionSet& other) const
85 {
86     if (!isValid() || !other.isValid())
87         return invalid();
88
89     Vector<ObjectPropertyCondition> result;
90     
91     if (!isEmpty())
92         result.appendVector(m_data->vector);
93     
94     for (const ObjectPropertyCondition& newCondition : other) {
95         bool foundMatch = false;
96         for (const ObjectPropertyCondition& existingCondition : *this) {
97             if (newCondition == existingCondition) {
98                 foundMatch = true;
99                 continue;
100             }
101             if (!newCondition.isCompatibleWith(existingCondition))
102                 return invalid();
103         }
104         if (!foundMatch)
105             result.append(newCondition);
106     }
107
108     return create(result);
109 }
110
111 bool ObjectPropertyConditionSet::structuresEnsureValidity() const
112 {
113     if (!isValid())
114         return false;
115     
116     for (const ObjectPropertyCondition& condition : *this) {
117         if (!condition.structureEnsuresValidity())
118             return false;
119     }
120     return true;
121 }
122
123 bool ObjectPropertyConditionSet::structuresEnsureValidityAssumingImpurePropertyWatchpoint() const
124 {
125     if (!isValid())
126         return false;
127     
128     for (const ObjectPropertyCondition& condition : *this) {
129         if (!condition.structureEnsuresValidityAssumingImpurePropertyWatchpoint())
130             return false;
131     }
132     return true;
133 }
134
135 bool ObjectPropertyConditionSet::needImpurePropertyWatchpoint() const
136 {
137     for (const ObjectPropertyCondition& condition : *this) {
138         if (condition.validityRequiresImpurePropertyWatchpoint())
139             return true;
140     }
141     return false;
142 }
143
144 bool ObjectPropertyConditionSet::areStillLive() const
145 {
146     for (const ObjectPropertyCondition& condition : *this) {
147         if (!condition.isStillLive())
148             return false;
149     }
150     return true;
151 }
152
153 void ObjectPropertyConditionSet::dumpInContext(PrintStream& out, DumpContext* context) const
154 {
155     if (!isValid()) {
156         out.print("<invalid>");
157         return;
158     }
159     
160     out.print("[");
161     if (m_data)
162         out.print(listDumpInContext(m_data->vector, context));
163     out.print("]");
164 }
165
166 void ObjectPropertyConditionSet::dump(PrintStream& out) const
167 {
168     dumpInContext(out, nullptr);
169 }
170
171 bool ObjectPropertyConditionSet::isValidAndWatchable() const
172 {
173     if (!isValid())
174         return false;
175
176     for (ObjectPropertyCondition condition : m_data->vector) {
177         if (!condition.isWatchable())
178             return false;
179     }
180     return true;
181 }
182
183 namespace {
184
185 namespace ObjectPropertyConditionSetInternal {
186 static const bool verbose = false;
187 }
188
189 ObjectPropertyCondition generateCondition(
190     VM& vm, JSCell* owner, JSObject* object, UniquedStringImpl* uid, PropertyCondition::Kind conditionKind)
191 {
192     Structure* structure = object->structure(vm);
193     if (ObjectPropertyConditionSetInternal::verbose)
194         dataLog("Creating condition ", conditionKind, " for ", pointerDump(structure), "\n");
195
196     ObjectPropertyCondition result;
197     switch (conditionKind) {
198     case PropertyCondition::Presence: {
199         unsigned attributes;
200         PropertyOffset offset = structure->getConcurrently(uid, attributes);
201         if (offset == invalidOffset)
202             return ObjectPropertyCondition();
203         result = ObjectPropertyCondition::presence(vm, owner, object, uid, offset, attributes);
204         break;
205     }
206     case PropertyCondition::Absence: {
207         if (structure->hasPolyProto())
208             return ObjectPropertyCondition();
209         result = ObjectPropertyCondition::absence(
210             vm, owner, object, uid, object->structure(vm)->storedPrototypeObject());
211         break;
212     }
213     case PropertyCondition::AbsenceOfSetEffect: {
214         if (structure->hasPolyProto())
215             return ObjectPropertyCondition();
216         result = ObjectPropertyCondition::absenceOfSetEffect(
217             vm, owner, object, uid, object->structure(vm)->storedPrototypeObject());
218         break;
219     }
220     case PropertyCondition::Equivalence: {
221         unsigned attributes;
222         PropertyOffset offset = structure->getConcurrently(uid, attributes);
223         if (offset == invalidOffset)
224             return ObjectPropertyCondition();
225         JSValue value = object->getDirect(offset);
226         result = ObjectPropertyCondition::equivalence(vm, owner, object, uid, value);
227         break;
228     }
229     default:
230         RELEASE_ASSERT_NOT_REACHED();
231         return ObjectPropertyCondition();
232     }
233
234     if (!result.isStillValidAssumingImpurePropertyWatchpoint()) {
235         if (ObjectPropertyConditionSetInternal::verbose)
236             dataLog("Failed to create condition: ", result, "\n");
237         return ObjectPropertyCondition();
238     }
239
240     if (ObjectPropertyConditionSetInternal::verbose)
241         dataLog("New condition: ", result, "\n");
242     return result;
243 }
244
245 enum Concurrency {
246     MainThread,
247     Concurrent
248 };
249 template<typename Functor>
250 ObjectPropertyConditionSet generateConditions(
251     VM& vm, JSGlobalObject* globalObject, Structure* structure, JSObject* prototype, const Functor& functor,
252     Concurrency concurrency = MainThread)
253 {
254     Vector<ObjectPropertyCondition> conditions;
255     
256     for (;;) {
257         if (ObjectPropertyConditionSetInternal::verbose)
258             dataLog("Considering structure: ", pointerDump(structure), "\n");
259         
260         if (structure->isProxy()) {
261             if (ObjectPropertyConditionSetInternal::verbose)
262                 dataLog("It's a proxy, so invalid.\n");
263             return ObjectPropertyConditionSet::invalid();
264         }
265
266         if (structure->hasPolyProto()) {
267             // FIXME: Integrate this with PolyProtoAccessChain:
268             // https://bugs.webkit.org/show_bug.cgi?id=177339
269             // Or at least allow OPC set generation when the
270             // base is not poly proto:
271             // https://bugs.webkit.org/show_bug.cgi?id=177721
272             return ObjectPropertyConditionSet::invalid();
273         }
274         
275         JSValue value = structure->prototypeForLookup(globalObject);
276         
277         if (value.isNull()) {
278             if (!prototype) {
279                 if (ObjectPropertyConditionSetInternal::verbose)
280                     dataLog("Reached end of prototype chain as expected, done.\n");
281                 break;
282             }
283             if (ObjectPropertyConditionSetInternal::verbose)
284                 dataLog("Unexpectedly reached end of prototype chain, so invalid.\n");
285             return ObjectPropertyConditionSet::invalid();
286         }
287         
288         JSObject* object = jsCast<JSObject*>(value);
289         structure = object->structure(vm);
290         
291         if (structure->isDictionary()) {
292             if (concurrency == MainThread) {
293                 if (structure->hasBeenFlattenedBefore()) {
294                     if (ObjectPropertyConditionSetInternal::verbose)
295                         dataLog("Dictionary has been flattened before, so invalid.\n");
296                     return ObjectPropertyConditionSet::invalid();
297                 }
298
299                 if (ObjectPropertyConditionSetInternal::verbose)
300                     dataLog("Flattening ", pointerDump(structure));
301                 structure->flattenDictionaryStructure(vm, object);
302             } else {
303                 if (ObjectPropertyConditionSetInternal::verbose)
304                     dataLog("Cannot flatten dictionary when not on main thread, so invalid.\n");
305                 return ObjectPropertyConditionSet::invalid();
306             }
307         }
308
309         if (!functor(conditions, object)) {
310             if (ObjectPropertyConditionSetInternal::verbose)
311                 dataLog("Functor failed, invalid.\n");
312             return ObjectPropertyConditionSet::invalid();
313         }
314         
315         if (object == prototype) {
316             if (ObjectPropertyConditionSetInternal::verbose)
317                 dataLog("Reached desired prototype, done.\n");
318             break;
319         }
320     }
321
322     if (ObjectPropertyConditionSetInternal::verbose)
323         dataLog("Returning conditions: ", listDump(conditions), "\n");
324     return ObjectPropertyConditionSet::create(conditions);
325 }
326
327 } // anonymous namespace
328
329 ObjectPropertyConditionSet generateConditionsForPropertyMiss(
330     VM& vm, JSCell* owner, ExecState* exec, Structure* headStructure, UniquedStringImpl* uid)
331 {
332     return generateConditions(
333         vm, exec->lexicalGlobalObject(), headStructure, nullptr,
334         [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool {
335             ObjectPropertyCondition result =
336                 generateCondition(vm, owner, object, uid, PropertyCondition::Absence);
337             if (!result)
338                 return false;
339             conditions.append(result);
340             return true;
341         });
342 }
343
344 ObjectPropertyConditionSet generateConditionsForPropertySetterMiss(
345     VM& vm, JSCell* owner, ExecState* exec, Structure* headStructure, UniquedStringImpl* uid)
346 {
347     return generateConditions(
348         vm, exec->lexicalGlobalObject(), headStructure, nullptr,
349         [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool {
350             ObjectPropertyCondition result =
351                 generateCondition(vm, owner, object, uid, PropertyCondition::AbsenceOfSetEffect);
352             if (!result)
353                 return false;
354             conditions.append(result);
355             return true;
356         });
357 }
358
359 ObjectPropertyConditionSet generateConditionsForPrototypePropertyHit(
360     VM& vm, JSCell* owner, ExecState* exec, Structure* headStructure, JSObject* prototype,
361     UniquedStringImpl* uid)
362 {
363     return generateConditions(
364         vm, exec->lexicalGlobalObject(), headStructure, prototype,
365         [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool {
366             PropertyCondition::Kind kind =
367                 object == prototype ? PropertyCondition::Presence : PropertyCondition::Absence;
368             ObjectPropertyCondition result =
369                 generateCondition(vm, owner, object, uid, kind);
370             if (!result)
371                 return false;
372             conditions.append(result);
373             return true;
374         });
375 }
376
377 ObjectPropertyConditionSet generateConditionsForPrototypePropertyHitCustom(
378     VM& vm, JSCell* owner, ExecState* exec, Structure* headStructure, JSObject* prototype,
379     UniquedStringImpl* uid)
380 {
381     return generateConditions(
382         vm, exec->lexicalGlobalObject(), headStructure, prototype,
383         [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool {
384             if (object == prototype)
385                 return true;
386             ObjectPropertyCondition result =
387                 generateCondition(vm, owner, object, uid, PropertyCondition::Absence);
388             if (!result)
389                 return false;
390             conditions.append(result);
391             return true;
392         });
393 }
394
395 ObjectPropertyConditionSet generateConditionsForInstanceOf(
396     VM& vm, JSCell* owner, ExecState* exec, Structure* headStructure, JSObject* prototype,
397     bool shouldHit)
398 {
399     bool didHit = false;
400     if (ObjectPropertyConditionSetInternal::verbose)
401         dataLog("Searching for prototype ", JSValue(prototype), " starting with structure ", RawPointer(headStructure), " with shouldHit = ", shouldHit, "\n");
402     ObjectPropertyConditionSet result = generateConditions(
403         vm, exec->lexicalGlobalObject(), headStructure, shouldHit ? prototype : nullptr,
404         [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool {
405             if (ObjectPropertyConditionSetInternal::verbose)
406                 dataLog("Encountered object: ", RawPointer(object), "\n");
407             if (object == prototype) {
408                 RELEASE_ASSERT(shouldHit);
409                 didHit = true;
410                 return true;
411             }
412             conditions.append(
413                 ObjectPropertyCondition::hasPrototype(
414                     vm, owner, object, object->structure(vm)->storedPrototypeObject()));
415             return true;
416         });
417     if (result.isValid()) {
418         if (ObjectPropertyConditionSetInternal::verbose)
419             dataLog("didHit = ", didHit, ", shouldHit = ", shouldHit, "\n");
420         RELEASE_ASSERT(didHit == shouldHit);
421     }
422     return result;
423 }
424
425 ObjectPropertyConditionSet generateConditionsForPrototypeEquivalenceConcurrently(
426     VM& vm, JSGlobalObject* globalObject, Structure* headStructure, JSObject* prototype, UniquedStringImpl* uid)
427 {
428     return generateConditions(vm, globalObject, headStructure, prototype,
429         [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool {
430             PropertyCondition::Kind kind =
431                 object == prototype ? PropertyCondition::Equivalence : PropertyCondition::Absence;
432             ObjectPropertyCondition result = generateCondition(vm, nullptr, object, uid, kind);
433             if (!result)
434                 return false;
435             conditions.append(result);
436             return true;
437         }, Concurrent);
438 }
439
440 ObjectPropertyConditionSet generateConditionsForPropertyMissConcurrently(
441     VM& vm, JSGlobalObject* globalObject, Structure* headStructure, UniquedStringImpl* uid)
442 {
443     return generateConditions(
444         vm, globalObject, headStructure, nullptr,
445         [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool {
446             ObjectPropertyCondition result = generateCondition(vm, nullptr, object, uid, PropertyCondition::Absence);
447             if (!result)
448                 return false;
449             conditions.append(result);
450             return true;
451         }, Concurrent);
452 }
453
454 ObjectPropertyConditionSet generateConditionsForPropertySetterMissConcurrently(
455     VM& vm, JSGlobalObject* globalObject, Structure* headStructure, UniquedStringImpl* uid)
456 {
457     return generateConditions(
458         vm, globalObject, headStructure, nullptr,
459         [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool {
460             ObjectPropertyCondition result =
461                 generateCondition(vm, nullptr, object, uid, PropertyCondition::AbsenceOfSetEffect);
462             if (!result)
463                 return false;
464             conditions.append(result);
465             return true;
466         }, Concurrent);
467 }
468
469 ObjectPropertyCondition generateConditionForSelfEquivalence(
470     VM& vm, JSCell* owner, JSObject* object, UniquedStringImpl* uid)
471 {
472     return generateCondition(vm, owner, object, uid, PropertyCondition::Equivalence);
473 }
474
475 } // namespace JSC
476