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