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 | |
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& 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 | |
151 | template<typename CellType, SubspaceAccess> |
152 | CompleteSubspace* JSCell::subspaceFor(VM& vm) |
153 | { |
154 | if (CellType::needsDestruction) |
155 | return &vm.destructibleCellSpace; |
156 | return &vm.cellSpace; |
157 | } |
158 | |
159 | template<typename Type> |
160 | inline 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 | |
167 | template<typename T> |
168 | ALWAYS_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 | |
184 | template<typename T> |
185 | void* allocateCell(Heap& heap, size_t size) |
186 | { |
187 | return tryAllocateCellHelper<T>(heap, size, nullptr, AllocationFailureMode::Assert); |
188 | } |
189 | |
190 | template<typename T> |
191 | void* tryAllocateCell(Heap& heap, size_t size) |
192 | { |
193 | return tryAllocateCellHelper<T>(heap, size, nullptr, AllocationFailureMode::ReturnNull); |
194 | } |
195 | |
196 | template<typename T> |
197 | void* allocateCell(Heap& heap, GCDeferralContext* deferralContext, size_t size) |
198 | { |
199 | return tryAllocateCellHelper<T>(heap, size, deferralContext, AllocationFailureMode::Assert); |
200 | } |
201 | |
202 | template<typename T> |
203 | void* tryAllocateCell(Heap& heap, GCDeferralContext* deferralContext, size_t size) |
204 | { |
205 | return tryAllocateCellHelper<T>(heap, size, deferralContext, AllocationFailureMode::ReturnNull); |
206 | } |
207 | |
208 | inline bool JSCell::isObject() const |
209 | { |
210 | return TypeInfo::isObject(m_type); |
211 | } |
212 | |
213 | inline bool JSCell::isString() const |
214 | { |
215 | return m_type == StringType; |
216 | } |
217 | |
218 | inline bool JSCell::isBigInt() const |
219 | { |
220 | return m_type == BigIntType; |
221 | } |
222 | |
223 | inline bool JSCell::isSymbol() const |
224 | { |
225 | return m_type == SymbolType; |
226 | } |
227 | |
228 | inline bool JSCell::isGetterSetter() const |
229 | { |
230 | return m_type == GetterSetterType; |
231 | } |
232 | |
233 | inline bool JSCell::isCustomGetterSetter() const |
234 | { |
235 | return m_type == CustomGetterSetterType; |
236 | } |
237 | |
238 | inline bool JSCell::isProxy() const |
239 | { |
240 | return m_type == ImpureProxyType || m_type == PureForwardingProxyType || m_type == ProxyObjectType; |
241 | } |
242 | |
243 | ALWAYS_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 | |
254 | inline 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 | |
262 | inline bool JSCell::isConstructor(VM& vm) |
263 | { |
264 | ConstructType constructType; |
265 | ConstructData constructData; |
266 | return isConstructor(vm, constructType, constructData); |
267 | } |
268 | |
269 | inline bool JSCell::isConstructor(VM& vm, ConstructType& constructType, ConstructData& constructData) |
270 | { |
271 | constructType = methodTable(vm)->getConstructData(this, constructData); |
272 | return constructType != ConstructType::None; |
273 | } |
274 | |
275 | inline bool JSCell::isAPIValueWrapper() const |
276 | { |
277 | return m_type == APIValueWrapperType; |
278 | } |
279 | |
280 | ALWAYS_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 | |
302 | inline 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 | |
312 | inline bool JSCell::inherits(VM& vm, const ClassInfo* info) const |
313 | { |
314 | return classInfo(vm)->isSubClassOf(info); |
315 | } |
316 | |
317 | template<typename Target> |
318 | inline bool JSCell::inherits(VM& vm) const |
319 | { |
320 | return JSCastingHelpers::inherits<Target>(vm, this); |
321 | } |
322 | |
323 | ALWAYS_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 | |
332 | inline bool JSCell::canUseFastGetOwnProperty(const Structure& structure) |
333 | { |
334 | return !structure.hasGetterSetterProperties() |
335 | && !structure.hasCustomGetterSetterProperties() |
336 | && !structure.typeInfo().overridesGetOwnPropertySlot(); |
337 | } |
338 | |
339 | ALWAYS_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 | |
350 | inline 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 | |
359 | inline 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 | |
370 | inline void JSCellLock::lock() |
371 | { |
372 | Atomic<IndexingType>* lock = bitwise_cast<Atomic<IndexingType>*>(&m_indexingTypeAndMisc); |
373 | if (UNLIKELY(!IndexingTypeLockAlgorithm::lockFast(*lock))) |
374 | lockSlow(); |
375 | } |
376 | |
377 | inline bool JSCellLock::tryLock() |
378 | { |
379 | Atomic<IndexingType>* lock = bitwise_cast<Atomic<IndexingType>*>(&m_indexingTypeAndMisc); |
380 | return IndexingTypeLockAlgorithm::tryLock(*lock); |
381 | } |
382 | |
383 | inline void JSCellLock::unlock() |
384 | { |
385 | Atomic<IndexingType>* lock = bitwise_cast<Atomic<IndexingType>*>(&m_indexingTypeAndMisc); |
386 | if (UNLIKELY(!IndexingTypeLockAlgorithm::unlockFast(*lock))) |
387 | unlockSlow(); |
388 | } |
389 | |
390 | inline bool JSCellLock::isLocked() const |
391 | { |
392 | Atomic<IndexingType>* lock = bitwise_cast<Atomic<IndexingType>*>(&m_indexingTypeAndMisc); |
393 | return IndexingTypeLockAlgorithm::isLocked(*lock); |
394 | } |
395 | |
396 | inline bool JSCell::perCellBit() const |
397 | { |
398 | return TypeInfo::perCellBit(inlineTypeFlags()); |
399 | } |
400 | |
401 | inline 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 | |
412 | inline 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 | |
419 | ALWAYS_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 | |
427 | inline bool isWebAssemblyToJSCallee(const JSCell* cell) |
428 | { |
429 | return cell->type() == WebAssemblyToJSCalleeType; |
430 | } |
431 | |
432 | } // namespace JSC |
433 | |