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