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 "CallFrameInlines.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 | |
50 | namespace JSC { |
51 | |
52 | inline JSCell::JSCell(CreatingEarlyCellTag) |
53 | : m_cellState(CellState::DefinitelyWhite) |
54 | { |
55 | ASSERT(!isCompilationThread()); |
56 | } |
57 | |
58 | inline 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 | |
68 | inline 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 | |
82 | inline 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 | |
102 | inline JSType JSCell::type() const |
103 | { |
104 | return m_type; |
105 | } |
106 | |
107 | inline IndexingType JSCell::indexingTypeAndMisc() const |
108 | { |
109 | return m_indexingTypeAndMisc; |
110 | } |
111 | |
112 | inline IndexingType JSCell::indexingType() const |
113 | { |
114 | return indexingTypeAndMisc() & AllWritableArrayTypes; |
115 | } |
116 | |
117 | inline IndexingType JSCell::indexingMode() const |
118 | { |
119 | return indexingTypeAndMisc() & AllArrayTypes; |
120 | } |
121 | |
122 | ALWAYS_INLINE Structure* JSCell::structure() const |
123 | { |
124 | return structure(vm()); |
125 | } |
126 | |
127 | ALWAYS_INLINE Structure* JSCell::structure(VM& vm) const |
128 | { |
129 | return vm.getStructure(m_structureID); |
130 | } |
131 | |
132 | inline void JSCell::visitChildren(JSCell* cell, SlotVisitor& visitor) |
133 | { |
134 | visitor.appendUnbarriered(cell->structure(visitor.vm())); |
135 | } |
136 | |
137 | inline void JSCell::visitOutputConstraints(JSCell*, SlotVisitor&) |
138 | { |
139 | } |
140 | |
141 | ALWAYS_INLINE VM& CallFrame::deprecatedVM() const |
142 | { |
143 | JSCell* callee = this->callee().asCell(); |
144 | ASSERT(callee); |
145 | ASSERT(&callee->vm()); |
146 | return callee->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(JSGlobalObject* globalObject) 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(getVM(globalObject))->masqueradesAsUndefined(globalObject); |
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(JSGlobalObject* globalObject) const |
411 | { |
412 | if (isObject()) |
413 | return jsCast<JSObject*>(const_cast<JSCell*>(this)); |
414 | return toObjectSlow(globalObject); |
415 | } |
416 | |
417 | ALWAYS_INLINE bool JSCell::putInline(JSGlobalObject* globalObject, PropertyName propertyName, JSValue value, PutPropertySlot& slot) |
418 | { |
419 | auto putMethod = methodTable(getVM(globalObject))->put; |
420 | if (LIKELY(putMethod == JSObject::put)) |
421 | return JSObject::putInlineForJSObject(asObject(this), globalObject, propertyName, value, slot); |
422 | return putMethod(this, globalObject, propertyName, value, slot); |
423 | } |
424 | |
425 | inline bool isWebAssemblyToJSCallee(const JSCell* cell) |
426 | { |
427 | return cell->type() == WebAssemblyToJSCalleeType; |
428 | } |
429 | |
430 | } // namespace JSC |
431 | |