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