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