1/*
2 * Copyright (C) 2013-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 StructureInlines_h
27#define StructureInlines_h
28
29#include "JSArrayBufferView.h"
30#include "JSCJSValueInlines.h"
31#include "JSGlobalObject.h"
32#include "PropertyMapHashTable.h"
33#include "Structure.h"
34#include "StructureChain.h"
35
36namespace JSC {
37
38inline Structure* Structure::create(VM& vm, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo, IndexingType indexingType, unsigned inlineCapacity)
39{
40 ASSERT(vm.structureStructure);
41 ASSERT(classInfo);
42 Structure* structure = new (NotNull, allocateCell<Structure>(vm.heap)) Structure(vm, globalObject, prototype, typeInfo, classInfo, indexingType, inlineCapacity);
43 structure->finishCreation(vm);
44 return structure;
45}
46
47inline Structure* Structure::createStructure(VM& vm)
48{
49 ASSERT(!vm.structureStructure);
50 Structure* structure = new (NotNull, allocateCell<Structure>(vm.heap)) Structure(vm);
51 structure->finishCreation(vm, CreatingEarlyCell);
52 return structure;
53}
54
55inline Structure* Structure::create(VM& vm, Structure* structure, DeferredStructureTransitionWatchpointFire* deferred)
56{
57 ASSERT(vm.structureStructure);
58 Structure* newStructure = new (NotNull, allocateCell<Structure>(vm.heap)) Structure(vm, structure, deferred);
59 newStructure->finishCreation(vm);
60 return newStructure;
61}
62
63inline JSObject* Structure::storedPrototypeObject() const
64{
65 JSValue value = m_prototype.get();
66 if (value.isNull())
67 return nullptr;
68 return asObject(value);
69}
70
71inline Structure* Structure::storedPrototypeStructure() const
72{
73 JSObject* object = storedPrototypeObject();
74 if (!object)
75 return nullptr;
76 return object->structure();
77}
78
79ALWAYS_INLINE PropertyOffset Structure::get(VM& vm, PropertyName propertyName)
80{
81 unsigned attributes;
82 bool hasInferredType;
83 return get(vm, propertyName, attributes, hasInferredType);
84}
85
86ALWAYS_INLINE PropertyOffset Structure::get(VM& vm, PropertyName propertyName, unsigned& attributes)
87{
88 bool hasInferredType;
89 return get(vm, propertyName, attributes, hasInferredType);
90}
91
92ALWAYS_INLINE PropertyOffset Structure::get(VM& vm, PropertyName propertyName, unsigned& attributes, bool& hasInferredType)
93{
94 ASSERT(!isCompilationThread());
95 ASSERT(structure()->classInfo() == info());
96
97 PropertyTable* propertyTable;
98 materializePropertyMapIfNecessary(vm, propertyTable);
99 if (!propertyTable)
100 return invalidOffset;
101
102 PropertyMapEntry* entry = propertyTable->get(propertyName.uid());
103 if (!entry)
104 return invalidOffset;
105
106 attributes = entry->attributes;
107 hasInferredType = entry->hasInferredType;
108 return entry->offset;
109}
110
111template<typename Functor>
112void Structure::forEachPropertyConcurrently(const Functor& functor)
113{
114 Vector<Structure*, 8> structures;
115 Structure* structure;
116 PropertyTable* table;
117
118 findStructuresAndMapForMaterialization(structures, structure, table);
119
120 if (table) {
121 for (auto& entry : *table) {
122 if (!functor(entry)) {
123 structure->m_lock.unlock();
124 return;
125 }
126 }
127 structure->m_lock.unlock();
128 }
129
130 for (unsigned i = structures.size(); i--;) {
131 structure = structures[i];
132 if (!structure->m_nameInPrevious)
133 continue;
134
135 if (!functor(PropertyMapEntry(structure->m_nameInPrevious.get(), structure->m_offset, structure->attributesInPrevious())))
136 return;
137 }
138}
139
140inline PropertyOffset Structure::getConcurrently(UniquedStringImpl* uid)
141{
142 unsigned attributesIgnored;
143 return getConcurrently(uid, attributesIgnored);
144}
145
146inline bool Structure::hasIndexingHeader(const JSCell* cell) const
147{
148 if (hasIndexedProperties(indexingType()))
149 return true;
150
151 if (!isTypedView(m_classInfo->typedArrayStorageType))
152 return false;
153
154 return jsCast<const JSArrayBufferView*>(cell)->mode() == WastefulTypedArray;
155}
156
157inline bool Structure::masqueradesAsUndefined(JSGlobalObject* lexicalGlobalObject)
158{
159 return typeInfo().masqueradesAsUndefined() && globalObject() == lexicalGlobalObject;
160}
161
162inline bool Structure::transitivelyTransitionedFrom(Structure* structureToFind)
163{
164 for (Structure* current = this; current; current = current->previousID()) {
165 if (current == structureToFind)
166 return true;
167 }
168 return false;
169}
170
171inline JSValue Structure::prototypeForLookup(JSGlobalObject* globalObject) const
172{
173 if (isObject())
174 return m_prototype.get();
175 if (typeInfo().type() == SymbolType)
176 return globalObject->symbolPrototype();
177
178 ASSERT(typeInfo().type() == StringType);
179 return globalObject->stringPrototype();
180}
181
182inline JSValue Structure::prototypeForLookup(ExecState* exec) const
183{
184 return prototypeForLookup(exec->lexicalGlobalObject());
185}
186
187inline StructureChain* Structure::prototypeChain(VM& vm, JSGlobalObject* globalObject) const
188{
189 // We cache our prototype chain so our clients can share it.
190 if (!isValid(globalObject, m_cachedPrototypeChain.get())) {
191 JSValue prototype = prototypeForLookup(globalObject);
192 m_cachedPrototypeChain.set(vm, this, StructureChain::create(vm, prototype.isNull() ? 0 : asObject(prototype)->structure()));
193 }
194 return m_cachedPrototypeChain.get();
195}
196
197inline StructureChain* Structure::prototypeChain(ExecState* exec) const
198{
199 return prototypeChain(exec->vm(), exec->lexicalGlobalObject());
200}
201
202inline bool Structure::isValid(JSGlobalObject* globalObject, StructureChain* cachedPrototypeChain) const
203{
204 if (!cachedPrototypeChain)
205 return false;
206
207 JSValue prototype = prototypeForLookup(globalObject);
208 WriteBarrier<Structure>* cachedStructure = cachedPrototypeChain->head();
209 while (*cachedStructure && !prototype.isNull()) {
210 if (asObject(prototype)->structure() != cachedStructure->get())
211 return false;
212 ++cachedStructure;
213 prototype = asObject(prototype)->prototype();
214 }
215 return prototype.isNull() && !*cachedStructure;
216}
217
218inline bool Structure::isValid(ExecState* exec, StructureChain* cachedPrototypeChain) const
219{
220 return isValid(exec->lexicalGlobalObject(), cachedPrototypeChain);
221}
222
223inline bool Structure::putWillGrowOutOfLineStorage()
224{
225 checkOffsetConsistency();
226
227 ASSERT(outOfLineCapacity() >= outOfLineSize());
228
229 if (!propertyTable()) {
230 unsigned currentSize = numberOfOutOfLineSlotsForLastOffset(m_offset);
231 ASSERT(outOfLineCapacity() >= currentSize);
232 return currentSize == outOfLineCapacity();
233 }
234
235 ASSERT(totalStorageCapacity() >= propertyTable()->propertyStorageSize());
236 if (propertyTable()->hasDeletedOffset())
237 return false;
238
239 ASSERT(totalStorageCapacity() >= propertyTable()->size());
240 return propertyTable()->size() == totalStorageCapacity();
241}
242
243ALWAYS_INLINE WriteBarrier<PropertyTable>& Structure::propertyTable()
244{
245 ASSERT(!globalObject() || !globalObject()->vm().heap.isCollecting());
246 return m_propertyTableUnsafe;
247}
248
249inline void Structure::didReplaceProperty(PropertyOffset offset)
250{
251 if (LIKELY(!hasRareData()))
252 return;
253 StructureRareData::PropertyWatchpointMap* map = rareData()->m_replacementWatchpointSets.get();
254 if (LIKELY(!map))
255 return;
256 WatchpointSet* set = map->get(offset);
257 if (LIKELY(!set))
258 return;
259 set->fireAll("Property did get replaced");
260}
261
262inline WatchpointSet* Structure::propertyReplacementWatchpointSet(PropertyOffset offset)
263{
264 ConcurrentJITLocker locker(m_lock);
265 if (!hasRareData())
266 return nullptr;
267 WTF::loadLoadFence();
268 StructureRareData::PropertyWatchpointMap* map = rareData()->m_replacementWatchpointSets.get();
269 if (!map)
270 return nullptr;
271 return map->get(offset);
272}
273
274ALWAYS_INLINE bool Structure::checkOffsetConsistency() const
275{
276 PropertyTable* propertyTable = m_propertyTableUnsafe.get();
277
278 if (!propertyTable) {
279 ASSERT(!isPinnedPropertyTable());
280 return true;
281 }
282
283 // We cannot reliably assert things about the property table in the concurrent
284 // compilation thread. It is possible for the table to be stolen and then have
285 // things added to it, which leads to the offsets being all messed up. We could
286 // get around this by grabbing a lock here, but I think that would be overkill.
287 if (isCompilationThread())
288 return true;
289
290 RELEASE_ASSERT(numberOfSlotsForLastOffset(m_offset, m_inlineCapacity) == propertyTable->propertyStorageSize());
291 unsigned totalSize = propertyTable->propertyStorageSize();
292 RELEASE_ASSERT((totalSize < inlineCapacity() ? 0 : totalSize - inlineCapacity()) == numberOfOutOfLineSlotsForLastOffset(m_offset));
293
294 return true;
295}
296
297inline size_t nextOutOfLineStorageCapacity(size_t currentCapacity)
298{
299 if (!currentCapacity)
300 return initialOutOfLineCapacity;
301 return currentCapacity * outOfLineGrowthFactor;
302}
303
304inline size_t Structure::suggestedNewOutOfLineStorageCapacity()
305{
306 return nextOutOfLineStorageCapacity(outOfLineCapacity());
307}
308
309inline void Structure::setObjectToStringValue(ExecState* exec, VM& vm, JSString* value, PropertySlot toStringTagSymbolSlot)
310{
311 if (!hasRareData())
312 allocateRareData(vm);
313 rareData()->setObjectToStringValue(exec, vm, this, value, toStringTagSymbolSlot);
314}
315
316} // namespace JSC
317
318#endif // StructureInlines_h
319
320