3571a0863c2c245597a0e176f13ab2660c4844be
[WebKit-https.git] / Source / JavaScriptCore / runtime / JSCellInlines.h
1 /*
2  * Copyright (C) 2012-2017 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 "CPU.h"
29 #include "CallFrame.h"
30 #include "DeferGC.h"
31 #include "Handle.h"
32 #include "JSCell.h"
33 #include "JSDestructibleObject.h"
34 #include "JSObject.h"
35 #include "JSString.h"
36 #include "MarkedBlock.h"
37 #include "Structure.h"
38 #include "Symbol.h"
39 #include <wtf/CompilationThread.h>
40
41 namespace JSC {
42
43 inline JSCell::JSCell(CreatingEarlyCellTag)
44     : m_cellState(CellState::DefinitelyWhite)
45 {
46     ASSERT(!isCompilationThread());
47 }
48
49 inline JSCell::JSCell(VM&, Structure* structure)
50     : m_structureID(structure->id())
51     , m_indexingTypeAndMisc(structure->indexingTypeIncludingHistory())
52     , m_type(structure->typeInfo().type())
53     , m_flags(structure->typeInfo().inlineTypeFlags())
54     , m_cellState(CellState::DefinitelyWhite)
55 {
56     ASSERT(!isCompilationThread());
57 }
58
59 inline void JSCell::finishCreation(VM& vm)
60 {
61     // This object is ready to be escaped so the concurrent GC may see it at any time. We have
62     // to make sure that none of our stores sink below here.
63     vm.heap.mutatorFence();
64 #if ENABLE(GC_VALIDATION)
65     ASSERT(vm.isInitializingObject());
66     vm.setInitializingObjectClass(0);
67 #else
68     UNUSED_PARAM(vm);
69 #endif
70     ASSERT(m_structureID);
71 }
72
73 inline void JSCell::finishCreation(VM& vm, Structure* structure, CreatingEarlyCellTag)
74 {
75 #if ENABLE(GC_VALIDATION)
76     ASSERT(vm.isInitializingObject());
77     vm.setInitializingObjectClass(0);
78     if (structure) {
79 #endif
80         m_structureID = structure->id();
81         m_indexingTypeAndMisc = structure->indexingTypeIncludingHistory();
82         m_type = structure->typeInfo().type();
83         m_flags = structure->typeInfo().inlineTypeFlags();
84 #if ENABLE(GC_VALIDATION)
85     }
86 #else
87     UNUSED_PARAM(vm);
88 #endif
89     // Very first set of allocations won't have a real structure.
90     ASSERT(m_structureID || !vm.structureStructure);
91 }
92
93 inline JSType JSCell::type() const
94 {
95     return m_type;
96 }
97
98 inline IndexingType JSCell::indexingTypeAndMisc() const
99 {
100     return m_indexingTypeAndMisc;
101 }
102
103 inline IndexingType JSCell::indexingType() const
104 {
105     return indexingTypeAndMisc() & AllArrayTypes;
106 }
107
108 ALWAYS_INLINE Structure* JSCell::structure() const
109 {
110     return structure(*vm());
111 }
112
113 ALWAYS_INLINE Structure* JSCell::structure(VM& vm) const
114 {
115     return vm.getStructure(m_structureID);
116 }
117
118 inline void JSCell::visitChildren(JSCell* cell, SlotVisitor& visitor)
119 {
120     visitor.appendUnbarriered(cell->structure(visitor.vm()));
121 }
122
123 inline void JSCell::visitOutputConstraints(JSCell*, SlotVisitor&)
124 {
125 }
126
127 ALWAYS_INLINE VM& ExecState::vm() const
128 {
129     JSCell* callee = this->callee().asCell();
130     ASSERT(callee);
131     ASSERT(callee->vm());
132     ASSERT(!callee->isLargeAllocation());
133     // This is an important optimization since we access this so often.
134     return *callee->markedBlock().vm();
135 }
136
137 template<typename CellType>
138 Subspace* JSCell::subspaceFor(VM& vm)
139 {
140     if (CellType::needsDestruction)
141         return &vm.destructibleCellSpace;
142     return &vm.cellSpace;
143 }
144
145 template<typename T, AllocationFailureMode mode, GCDeferralContextArgPresense deferralContextArgPresence>
146 ALWAYS_INLINE void* tryAllocateCellHelper(Heap& heap, GCDeferralContext* deferralContext, size_t size)
147 {
148     ASSERT(deferralContext || !DisallowGC::isInEffectOnCurrentThread());
149     ASSERT(size >= sizeof(T));
150     JSCell* result;
151     if (mode == AllocationFailureMode::ShouldAssertOnFailure) {
152         result = (deferralContextArgPresence == GCDeferralContextArgPresense::HasArg)
153             ? static_cast<JSCell*>(subspaceFor<T>(*heap.vm())->allocate(deferralContext, size))
154             : static_cast<JSCell*>(subspaceFor<T>(*heap.vm())->allocate(size));
155     } else {
156         result = (deferralContextArgPresence == GCDeferralContextArgPresense::HasArg)
157             ? static_cast<JSCell*>(subspaceFor<T>(*heap.vm())->tryAllocate(deferralContext, size))
158             : static_cast<JSCell*>(subspaceFor<T>(*heap.vm())->tryAllocate(size));
159         if (UNLIKELY(!result))
160             return nullptr;
161     }
162 #if ENABLE(GC_VALIDATION)
163     ASSERT(!heap.vm()->isInitializingObject());
164     heap.vm()->setInitializingObjectClass(T::info());
165 #endif
166     result->clearStructure();
167     return result;
168 }
169
170 template<typename T>
171 void* allocateCell(Heap& heap, size_t size)
172 {
173     return tryAllocateCellHelper<T, AllocationFailureMode::ShouldAssertOnFailure, GCDeferralContextArgPresense::DoesNotHaveArg>(heap, nullptr, size);
174 }
175
176 template<typename T>
177 void* tryAllocateCell(Heap& heap, size_t size)
178 {
179     return tryAllocateCellHelper<T, AllocationFailureMode::ShouldNotAssertOnFailure, GCDeferralContextArgPresense::DoesNotHaveArg>(heap, nullptr, size);
180 }
181
182 template<typename T>
183 void* allocateCell(Heap& heap, GCDeferralContext* deferralContext, size_t size)
184 {
185     return tryAllocateCellHelper<T, AllocationFailureMode::ShouldAssertOnFailure, GCDeferralContextArgPresense::HasArg>(heap, deferralContext, size);
186 }
187
188 template<typename T>
189 void* tryAllocateCell(Heap& heap, GCDeferralContext* deferralContext, size_t size)
190 {
191     return tryAllocateCellHelper<T, AllocationFailureMode::ShouldNotAssertOnFailure, GCDeferralContextArgPresense::HasArg>(heap, deferralContext, size);
192 }
193
194 inline bool JSCell::isObject() const
195 {
196     return TypeInfo::isObject(m_type);
197 }
198
199 inline bool JSCell::isString() const
200 {
201     return m_type == StringType;
202 }
203
204 inline bool JSCell::isSymbol() const
205 {
206     return m_type == SymbolType;
207 }
208
209 inline bool JSCell::isGetterSetter() const
210 {
211     return m_type == GetterSetterType;
212 }
213
214 inline bool JSCell::isCustomGetterSetter() const
215 {
216     return m_type == CustomGetterSetterType;
217 }
218
219 inline bool JSCell::isProxy() const
220 {
221     return m_type == ImpureProxyType || m_type == PureForwardingProxyType;
222 }
223
224 inline bool JSCell::isAPIValueWrapper() const
225 {
226     return m_type == APIValueWrapperType;
227 }
228
229 ALWAYS_INLINE void JSCell::setStructure(VM& vm, Structure* structure)
230 {
231     ASSERT(structure->classInfo() == this->structure()->classInfo());
232     ASSERT(!this->structure()
233         || this->structure()->transitionWatchpointSetHasBeenInvalidated()
234         || Heap::heap(this)->structureIDTable().get(structure->id()) == structure);
235     m_structureID = structure->id();
236     m_flags = structure->typeInfo().inlineTypeFlags();
237     m_type = structure->typeInfo().type();
238     IndexingType newIndexingType = structure->indexingTypeIncludingHistory();
239     if (m_indexingTypeAndMisc != newIndexingType) {
240         ASSERT(!(newIndexingType & ~AllArrayTypesAndHistory));
241         for (;;) {
242             IndexingType oldValue = m_indexingTypeAndMisc;
243             IndexingType newValue = (oldValue & ~AllArrayTypesAndHistory) | structure->indexingTypeIncludingHistory();
244             if (WTF::atomicCompareExchangeWeakRelaxed(&m_indexingTypeAndMisc, oldValue, newValue))
245                 break;
246         }
247     }
248     vm.heap.writeBarrier(this, structure);
249 }
250
251 inline const MethodTable* JSCell::methodTable() const
252 {
253     VM& vm = *Heap::heap(this)->vm();
254     return methodTable(vm);
255 }
256
257 inline const MethodTable* JSCell::methodTable(VM& vm) const
258 {
259     Structure* structure = this->structure(vm);
260     if (Structure* rootStructure = structure->structure(vm))
261         ASSERT_UNUSED(rootStructure, rootStructure == rootStructure->structure(vm));
262
263     return &structure->classInfo()->methodTable;
264 }
265
266 inline bool JSCell::inherits(VM& vm, const ClassInfo* info) const
267 {
268     return classInfo(vm)->isSubClassOf(info);
269 }
270
271 ALWAYS_INLINE JSValue JSCell::fastGetOwnProperty(VM& vm, Structure& structure, PropertyName name)
272 {
273     ASSERT(canUseFastGetOwnProperty(structure));
274     PropertyOffset offset = structure.get(vm, name);
275     if (offset != invalidOffset)
276         return asObject(this)->locationForOffset(offset)->get();
277     return JSValue();
278 }
279
280 inline bool JSCell::canUseFastGetOwnProperty(const Structure& structure)
281 {
282     return !structure.hasGetterSetterProperties() 
283         && !structure.hasCustomGetterSetterProperties()
284         && !structure.typeInfo().overridesGetOwnPropertySlot();
285 }
286
287 ALWAYS_INLINE const ClassInfo* JSCell::classInfo(VM& vm) const
288 {
289     // What we really want to assert here is that we're not currently destructing this object (which makes its classInfo
290     // invalid). If mutatorState() == MutatorState::Running, then we're not currently sweeping, and therefore cannot be
291     // destructing the object. The GC thread or JIT threads, unlike the mutator thread, are able to access classInfo
292     // independent of whether the mutator thread is sweeping or not. Hence, we also check for !currentThreadIsHoldingAPILock()
293     // to allow the GC thread or JIT threads to pass this assertion.
294     ASSERT(vm.heap.mutatorState() != MutatorState::Sweeping || !vm.currentThreadIsHoldingAPILock());
295     return structure(vm)->classInfo();
296 }
297
298 inline bool JSCell::toBoolean(ExecState* exec) const
299 {
300     if (isString())
301         return static_cast<const JSString*>(this)->toBoolean();
302     return !structure()->masqueradesAsUndefined(exec->lexicalGlobalObject());
303 }
304
305 inline TriState JSCell::pureToBoolean() const
306 {
307     if (isString())
308         return static_cast<const JSString*>(this)->toBoolean() ? TrueTriState : FalseTriState;
309     if (isSymbol())
310         return TrueTriState;
311     return MixedTriState;
312 }
313
314 inline void JSCell::callDestructor(VM& vm)
315 {
316     if (isZapped())
317         return;
318     ASSERT(structureID());
319     if (inlineTypeFlags() & StructureIsImmortal) {
320         Structure* structure = this->structure(vm);
321         const ClassInfo* classInfo = structure->classInfo();
322         MethodTable::DestroyFunctionPtr destroy = classInfo->methodTable.destroy;
323         destroy(this);
324     } else
325         static_cast<JSDestructibleObject*>(this)->classInfo()->methodTable.destroy(this);
326     zap();
327 }
328
329 inline void JSCell::lock()
330 {
331     Atomic<IndexingType>* lock = bitwise_cast<Atomic<IndexingType>*>(&m_indexingTypeAndMisc);
332     IndexingTypeLockAlgorithm::lock(*lock);
333 }
334
335 inline bool JSCell::tryLock()
336 {
337     Atomic<IndexingType>* lock = bitwise_cast<Atomic<IndexingType>*>(&m_indexingTypeAndMisc);
338     return IndexingTypeLockAlgorithm::tryLock(*lock);
339 }
340
341 inline void JSCell::unlock()
342 {
343     Atomic<IndexingType>* lock = bitwise_cast<Atomic<IndexingType>*>(&m_indexingTypeAndMisc);
344     IndexingTypeLockAlgorithm::unlock(*lock);
345 }
346
347 inline bool JSCell::isLocked() const
348 {
349     Atomic<IndexingType>* lock = bitwise_cast<Atomic<IndexingType>*>(&m_indexingTypeAndMisc);
350     return IndexingTypeLockAlgorithm::isLocked(*lock);
351 }
352
353 inline JSObject* JSCell::toObject(ExecState* exec, JSGlobalObject* globalObject) const
354 {
355     if (isObject())
356         return jsCast<JSObject*>(const_cast<JSCell*>(this));
357     return toObjectSlow(exec, globalObject);
358 }
359
360 ALWAYS_INLINE bool JSCell::putInline(ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
361 {
362     auto putMethod = methodTable(exec->vm())->put;
363     if (LIKELY(putMethod == JSObject::put))
364         return JSObject::putInlineForJSObject(asObject(this), exec, propertyName, value, slot);
365     return putMethod(this, exec, propertyName, value, slot);
366 }
367
368 inline bool isWebAssemblyToJSCallee(const JSCell* cell)
369 {
370     return cell->type() == WebAssemblyToJSCalleeType;
371 }
372
373 } // namespace JSC