Remove excessive headers from JavaScriptCore
[WebKit-https.git] / Source / JavaScriptCore / runtime / StructureInlines.h
1 /*
2  * Copyright (C) 2013-2016 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 "JSArrayBufferView.h"
29 #include "JSCJSValueInlines.h"
30 #include "JSGlobalObject.h"
31 #include "PropertyMapHashTable.h"
32 #include "Structure.h"
33 #include "StructureChain.h"
34
35 namespace JSC {
36
37 inline Structure* Structure::create(VM& vm, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo, IndexingType indexingType, unsigned inlineCapacity)
38 {
39     ASSERT(vm.structureStructure);
40     ASSERT(classInfo);
41     Structure* structure = new (NotNull, allocateCell<Structure>(vm.heap)) Structure(vm, globalObject, prototype, typeInfo, classInfo, indexingType, inlineCapacity);
42     structure->finishCreation(vm);
43     return structure;
44 }
45
46 inline Structure* Structure::createStructure(VM& vm)
47 {
48     ASSERT(!vm.structureStructure);
49     Structure* structure = new (NotNull, allocateCell<Structure>(vm.heap)) Structure(vm);
50     structure->finishCreation(vm, CreatingEarlyCell);
51     return structure;
52 }
53
54 inline Structure* Structure::create(VM& vm, Structure* structure, DeferredStructureTransitionWatchpointFire* deferred)
55 {
56     ASSERT(vm.structureStructure);
57     Structure* newStructure = new (NotNull, allocateCell<Structure>(vm.heap)) Structure(vm, structure, deferred);
58     newStructure->finishCreation(vm);
59     return newStructure;
60 }
61
62 inline JSObject* Structure::storedPrototypeObject() const
63 {
64     JSValue value = m_prototype.get();
65     if (value.isNull())
66         return nullptr;
67     return asObject(value);
68 }
69
70 inline Structure* Structure::storedPrototypeStructure() const
71 {
72     JSObject* object = storedPrototypeObject();
73     if (!object)
74         return nullptr;
75     return object->structure();
76 }
77
78 ALWAYS_INLINE PropertyOffset Structure::get(VM& vm, PropertyName propertyName)
79 {
80     unsigned attributes;
81     bool hasInferredType;
82     return get(vm, propertyName, attributes, hasInferredType);
83 }
84     
85 ALWAYS_INLINE PropertyOffset Structure::get(VM& vm, PropertyName propertyName, unsigned& attributes)
86 {
87     bool hasInferredType;
88     return get(vm, propertyName, attributes, hasInferredType);
89 }
90
91 ALWAYS_INLINE PropertyOffset Structure::get(VM& vm, PropertyName propertyName, unsigned& attributes, bool& hasInferredType)
92 {
93     ASSERT(!isCompilationThread());
94     ASSERT(structure()->classInfo() == info());
95
96     PropertyTable* propertyTable = ensurePropertyTableIfNotEmpty(vm);
97     if (!propertyTable)
98         return invalidOffset;
99
100     PropertyMapEntry* entry = propertyTable->get(propertyName.uid());
101     if (!entry)
102         return invalidOffset;
103
104     attributes = entry->attributes;
105     hasInferredType = entry->hasInferredType;
106     return entry->offset;
107 }
108
109 template<typename Functor>
110 void Structure::forEachPropertyConcurrently(const Functor& functor)
111 {
112     Vector<Structure*, 8> structures;
113     Structure* structure;
114     PropertyTable* table;
115     
116     findStructuresAndMapForMaterialization(structures, structure, table);
117     
118     if (table) {
119         for (auto& entry : *table) {
120             if (!functor(entry)) {
121                 structure->m_lock.unlock();
122                 return;
123             }
124         }
125         structure->m_lock.unlock();
126     }
127     
128     for (unsigned i = structures.size(); i--;) {
129         structure = structures[i];
130         if (!structure->m_nameInPrevious)
131             continue;
132         
133         if (!functor(PropertyMapEntry(structure->m_nameInPrevious.get(), structure->m_offset, structure->attributesInPrevious())))
134             return;
135     }
136 }
137
138 inline PropertyOffset Structure::getConcurrently(UniquedStringImpl* uid)
139 {
140     unsigned attributesIgnored;
141     return getConcurrently(uid, attributesIgnored);
142 }
143
144 inline bool Structure::hasIndexingHeader(const JSCell* cell) const
145 {
146     if (hasIndexedProperties(indexingType()))
147         return true;
148     
149     if (!isTypedView(typedArrayTypeForType(m_blob.type())))
150         return false;
151     
152     return jsCast<const JSArrayBufferView*>(cell)->mode() == WastefulTypedArray;
153 }
154
155 inline bool Structure::masqueradesAsUndefined(JSGlobalObject* lexicalGlobalObject)
156 {
157     return typeInfo().masqueradesAsUndefined() && globalObject() == lexicalGlobalObject;
158 }
159
160 inline bool Structure::transitivelyTransitionedFrom(Structure* structureToFind)
161 {
162     for (Structure* current = this; current; current = current->previousID()) {
163         if (current == structureToFind)
164             return true;
165     }
166     return false;
167 }
168
169 inline JSValue Structure::prototypeForLookup(JSGlobalObject* globalObject) const
170 {
171     if (isObject())
172         return m_prototype.get();
173     if (typeInfo().type() == SymbolType)
174         return globalObject->symbolPrototype();
175
176     ASSERT(typeInfo().type() == StringType);
177     return globalObject->stringPrototype();
178 }
179
180 inline JSValue Structure::prototypeForLookup(ExecState* exec) const
181 {
182     return prototypeForLookup(exec->lexicalGlobalObject());
183 }
184
185 inline StructureChain* Structure::prototypeChain(VM& vm, JSGlobalObject* globalObject) const
186 {
187     // We cache our prototype chain so our clients can share it.
188     if (!isValid(globalObject, m_cachedPrototypeChain.get())) {
189         JSValue prototype = prototypeForLookup(globalObject);
190         m_cachedPrototypeChain.set(vm, this, StructureChain::create(vm, prototype.isNull() ? 0 : asObject(prototype)->structure()));
191     }
192     return m_cachedPrototypeChain.get();
193 }
194
195 inline StructureChain* Structure::prototypeChain(ExecState* exec) const
196 {
197     return prototypeChain(exec->vm(), exec->lexicalGlobalObject());
198 }
199
200 inline bool Structure::isValid(JSGlobalObject* globalObject, StructureChain* cachedPrototypeChain) const
201 {
202     if (!cachedPrototypeChain)
203         return false;
204
205     JSValue prototype = prototypeForLookup(globalObject);
206     WriteBarrier<Structure>* cachedStructure = cachedPrototypeChain->head();
207     while (*cachedStructure && !prototype.isNull()) {
208         if (asObject(prototype)->structure() != cachedStructure->get())
209             return false;
210         ++cachedStructure;
211         prototype = asObject(prototype)->getPrototypeDirect();
212     }
213     return prototype.isNull() && !*cachedStructure;
214 }
215
216 inline bool Structure::isValid(ExecState* exec, StructureChain* cachedPrototypeChain) const
217 {
218     return isValid(exec->lexicalGlobalObject(), cachedPrototypeChain);
219 }
220
221 inline void Structure::didReplaceProperty(PropertyOffset offset)
222 {
223     if (LIKELY(!hasRareData()))
224         return;
225     StructureRareData::PropertyWatchpointMap* map = rareData()->m_replacementWatchpointSets.get();
226     if (LIKELY(!map))
227         return;
228     WatchpointSet* set = map->get(offset);
229     if (LIKELY(!set))
230         return;
231     set->fireAll(*vm(), "Property did get replaced");
232 }
233
234 inline WatchpointSet* Structure::propertyReplacementWatchpointSet(PropertyOffset offset)
235 {
236     ConcurrentJSLocker locker(m_lock);
237     if (!hasRareData())
238         return nullptr;
239     WTF::loadLoadFence();
240     StructureRareData::PropertyWatchpointMap* map = rareData()->m_replacementWatchpointSets.get();
241     if (!map)
242         return nullptr;
243     return map->get(offset);
244 }
245
246 template<typename DetailsFunc>
247 ALWAYS_INLINE bool Structure::checkOffsetConsistency(PropertyTable* propertyTable, const DetailsFunc& detailsFunc) const
248 {
249     // We cannot reliably assert things about the property table in the concurrent
250     // compilation thread. It is possible for the table to be stolen and then have
251     // things added to it, which leads to the offsets being all messed up. We could
252     // get around this by grabbing a lock here, but I think that would be overkill.
253     if (isCompilationThread())
254         return true;
255     
256     unsigned totalSize = propertyTable->propertyStorageSize();
257     unsigned inlineOverflowAccordingToTotalSize = totalSize < m_inlineCapacity ? 0 : totalSize - m_inlineCapacity;
258
259     auto fail = [&] (const char* description) {
260         dataLog("Detected offset inconsistency: ", description, "!\n");
261         dataLog("this = ", RawPointer(this), "\n");
262         dataLog("m_offset = ", m_offset, "\n");
263         dataLog("m_inlineCapacity = ", m_inlineCapacity, "\n");
264         dataLog("propertyTable = ", RawPointer(propertyTable), "\n");
265         dataLog("numberOfSlotsForLastOffset = ", numberOfSlotsForLastOffset(m_offset, m_inlineCapacity), "\n");
266         dataLog("totalSize = ", totalSize, "\n");
267         dataLog("inlineOverflowAccordingToTotalSize = ", inlineOverflowAccordingToTotalSize, "\n");
268         dataLog("numberOfOutOfLineSlotsForLastOffset = ", numberOfOutOfLineSlotsForLastOffset(m_offset), "\n");
269         detailsFunc();
270         UNREACHABLE_FOR_PLATFORM();
271     };
272     
273     if (numberOfSlotsForLastOffset(m_offset, m_inlineCapacity) != totalSize)
274         fail("numberOfSlotsForLastOffset doesn't match totalSize");
275     if (inlineOverflowAccordingToTotalSize != numberOfOutOfLineSlotsForLastOffset(m_offset))
276         fail("inlineOverflowAccordingToTotalSize doesn't match numberOfOutOfLineSlotsForLastOffset");
277
278     return true;
279 }
280
281 ALWAYS_INLINE bool Structure::checkOffsetConsistency() const
282 {
283     PropertyTable* propertyTable = propertyTableOrNull();
284
285     if (!propertyTable) {
286         ASSERT(!isPinnedPropertyTable());
287         return true;
288     }
289
290     // We cannot reliably assert things about the property table in the concurrent
291     // compilation thread. It is possible for the table to be stolen and then have
292     // things added to it, which leads to the offsets being all messed up. We could
293     // get around this by grabbing a lock here, but I think that would be overkill.
294     if (isCompilationThread())
295         return true;
296
297     return checkOffsetConsistency(propertyTable, [] () { });
298 }
299
300 inline void Structure::checkConsistency()
301 {
302     checkOffsetConsistency();
303 }
304
305 inline size_t nextOutOfLineStorageCapacity(size_t currentCapacity)
306 {
307     if (!currentCapacity)
308         return initialOutOfLineCapacity;
309     return currentCapacity * outOfLineGrowthFactor;
310 }
311
312 inline void Structure::setObjectToStringValue(ExecState* exec, VM& vm, JSString* value, PropertySlot toStringTagSymbolSlot)
313 {
314     if (!hasRareData())
315         allocateRareData(vm);
316     rareData()->setObjectToStringValue(exec, vm, this, value, toStringTagSymbolSlot);
317 }
318
319 template<Structure::ShouldPin shouldPin, typename Func>
320 inline PropertyOffset Structure::add(VM& vm, PropertyName propertyName, unsigned attributes, const Func& func)
321 {
322     PropertyTable* table = ensurePropertyTable(vm);
323
324     GCSafeConcurrentJSLocker locker(m_lock, vm.heap);
325
326     switch (shouldPin) {
327     case ShouldPin::Yes:
328         pin(locker, vm, table);
329         break;
330     case ShouldPin::No:
331         setPropertyTable(vm, table);
332         break;
333     }
334     
335     ASSERT(!JSC::isValidOffset(get(vm, propertyName)));
336
337     checkConsistency();
338     if (attributes & DontEnum || propertyName.isSymbol())
339         setIsQuickPropertyAccessAllowedForEnumeration(false);
340
341     auto rep = propertyName.uid();
342
343     PropertyOffset newOffset = table->nextOffset(m_inlineCapacity);
344     
345     PropertyOffset newLastOffset = m_offset;
346     table->add(PropertyMapEntry(rep, newOffset, attributes), newLastOffset, PropertyTable::PropertyOffsetMayChange);
347     
348     func(locker, newOffset, newLastOffset);
349     
350     ASSERT(m_offset == newLastOffset);
351
352     checkConsistency();
353     return newOffset;
354 }
355
356 template<typename Func>
357 inline PropertyOffset Structure::remove(PropertyName propertyName, const Func& func)
358 {
359     ConcurrentJSLocker locker(m_lock);
360     
361     checkConsistency();
362
363     auto rep = propertyName.uid();
364     
365     // We ONLY remove from uncacheable dictionaries, which will have a pinned property table.
366     // The only way for them not to have a table is if they are empty.
367     PropertyTable* table = propertyTableOrNull();
368
369     if (!table)
370         return invalidOffset;
371
372     PropertyTable::find_iterator position = table->find(rep);
373     if (!position.first)
374         return invalidOffset;
375     
376     PropertyOffset offset = position.first->offset;
377
378     table->remove(position);
379     table->addDeletedOffset(offset);
380
381     checkConsistency();
382
383     func(locker, offset);
384     return offset;
385 }
386
387 template<typename Func>
388 inline PropertyOffset Structure::addPropertyWithoutTransition(VM& vm, PropertyName propertyName, unsigned attributes, const Func& func)
389 {
390     return add<ShouldPin::Yes>(vm, propertyName, attributes, func);
391 }
392
393 template<typename Func>
394 inline PropertyOffset Structure::removePropertyWithoutTransition(VM&, PropertyName propertyName, const Func& func)
395 {
396     ASSERT(isUncacheableDictionary());
397     ASSERT(isPinnedPropertyTable());
398     ASSERT(propertyTableOrNull());
399     
400     return remove(propertyName, func);
401 }
402
403 inline void Structure::setPropertyTable(VM& vm, PropertyTable* table)
404 {
405     m_propertyTableUnsafe.setMayBeNull(vm, this, table);
406 }
407     
408 } // namespace JSC