#pragma once
#include "AllocatorForMode.h"
#include "AllocatorInlines.h"
#include "CompleteSubspaceInlines.h"
#include "CPU.h"
#include "CallFrameInlines.h"
#include "DeferGC.h"
#include "FreeListInlines.h"
#include "Handle.h"
#include "HeapInlines.h"
#include "IsoSubspaceInlines.h"
#include "JSBigInt.h"
#include "JSCast.h"
#include "JSDestructibleObject.h"
#include "JSObject.h"
#include "JSString.h"
#include "LocalAllocatorInlines.h"
#include "MarkedBlock.h"
#include "SlotVisitorInlines.h"
#include "Structure.h"
#include "Symbol.h"
#include <wtf/CompilationThread.h>
namespace JSC {
inline JSCell::JSCell(CreatingEarlyCellTag)
: m_cellState(CellState::DefinitelyWhite)
{
ASSERT(!isCompilationThread());
}
inline JSCell::JSCell(VM&, Structure* structure)
: m_structureID(structure->id())
, m_indexingTypeAndMisc(structure->indexingModeIncludingHistory())
, m_type(structure->typeInfo().type())
, m_flags(structure->typeInfo().inlineTypeFlags())
, m_cellState(CellState::DefinitelyWhite)
{
ASSERT(!isCompilationThread());
ensureStillAliveHere(structure);
}
inline void JSCell::finishCreation(VM& vm)
{
vm.heap.mutatorFence();
#if ENABLE(GC_VALIDATION)
ASSERT(vm.isInitializingObject());
vm.setInitializingObjectClass(0);
#else
UNUSED_PARAM(vm);
#endif
ASSERT(m_structureID);
}
inline void JSCell::finishCreation(VM& vm, Structure* structure, CreatingEarlyCellTag)
{
#if ENABLE(GC_VALIDATION)
ASSERT(vm.isInitializingObject());
vm.setInitializingObjectClass(0);
if (structure) {
#endif
m_structureID = structure->id();
m_indexingTypeAndMisc = structure->indexingModeIncludingHistory();
m_type = structure->typeInfo().type();
m_flags = structure->typeInfo().inlineTypeFlags();
#if ENABLE(GC_VALIDATION)
}
#else
UNUSED_PARAM(vm);
#endif
ASSERT(m_structureID || !vm.structureStructure);
}
inline JSType JSCell::type() const
{
return m_type;
}
inline IndexingType JSCell::indexingTypeAndMisc() const
{
return m_indexingTypeAndMisc;
}
inline IndexingType JSCell::indexingType() const
{
return indexingTypeAndMisc() & AllWritableArrayTypes;
}
inline IndexingType JSCell::indexingMode() const
{
return indexingTypeAndMisc() & AllArrayTypes;
}
ALWAYS_INLINE Structure* JSCell::structure() const
{
return structure(vm());
}
ALWAYS_INLINE Structure* JSCell::structure(VM& vm) const
{
return vm.getStructure(m_structureID);
}
inline void JSCell::visitChildren(JSCell* cell, SlotVisitor& visitor)
{
visitor.appendUnbarriered(cell->structure(visitor.vm()));
}
inline void JSCell::visitOutputConstraints(JSCell*, SlotVisitor&)
{
}
ALWAYS_INLINE VM& CallFrame::deprecatedVM() const
{
JSCell* callee = this->callee().asCell();
ASSERT(callee);
return callee->vm();
}
template<typename Type>
inline Allocator allocatorForNonVirtualConcurrently(VM& vm, size_t allocationSize, AllocatorForMode mode)
{
if (auto* subspace = subspaceForConcurrently<Type>(vm))
return subspace->allocatorForNonVirtual(allocationSize, mode);
return { };
}
template<typename T>
ALWAYS_INLINE void* tryAllocateCellHelper(Heap& heap, size_t size, GCDeferralContext* deferralContext, AllocationFailureMode failureMode)
{
VM& vm = heap.vm();
ASSERT(deferralContext || heap.isDeferred() || !DisallowGC::isInEffectOnCurrentThread());
ASSERT(size >= sizeof(T));
JSCell* result = static_cast<JSCell*>(subspaceFor<T>(vm)->allocateNonVirtual(vm, size, deferralContext, failureMode));
if (failureMode == AllocationFailureMode::ReturnNull && !result)
return nullptr;
#if ENABLE(GC_VALIDATION)
ASSERT(!vm.isInitializingObject());
vm.setInitializingObjectClass(T::info());
#endif
result->clearStructure();
return result;
}
template<typename T>
void* allocateCell(Heap& heap, size_t size)
{
return tryAllocateCellHelper<T>(heap, size, nullptr, AllocationFailureMode::Assert);
}
template<typename T>
void* tryAllocateCell(Heap& heap, size_t size)
{
return tryAllocateCellHelper<T>(heap, size, nullptr, AllocationFailureMode::ReturnNull);
}
template<typename T>
void* allocateCell(Heap& heap, GCDeferralContext* deferralContext, size_t size)
{
return tryAllocateCellHelper<T>(heap, size, deferralContext, AllocationFailureMode::Assert);
}
template<typename T>
void* tryAllocateCell(Heap& heap, GCDeferralContext* deferralContext, size_t size)
{
return tryAllocateCellHelper<T>(heap, size, deferralContext, AllocationFailureMode::ReturnNull);
}
inline bool JSCell::isObject() const
{
return TypeInfo::isObject(m_type);
}
inline bool JSCell::isString() const
{
return m_type == StringType;
}
inline bool JSCell::isHeapBigInt() const
{
return m_type == HeapBigIntType;
}
inline bool JSCell::isSymbol() const
{
return m_type == SymbolType;
}
inline bool JSCell::isGetterSetter() const
{
return m_type == GetterSetterType;
}
inline bool JSCell::isCustomGetterSetter() const
{
return m_type == CustomGetterSetterType;
}
inline bool JSCell::isProxy() const
{
return m_type == ImpureProxyType || m_type == PureForwardingProxyType || m_type == ProxyObjectType;
}
template<Concurrency concurrency>
ALWAYS_INLINE TriState JSCell::isCallableWithConcurrency(VM& vm)
{
if (!isObject())
return TriState::False;
if (type() == JSFunctionType)
return TriState::True;
if (inlineTypeFlags() & OverridesGetCallData) {
if constexpr (concurrency == Concurrency::MainThread)
return (methodTable(vm)->getCallData(this).type != CallData::Type::None) ? TriState::True : TriState::False;
if (type() == InternalFunctionType)
return (methodTable(vm)->getCallData(this).type != CallData::Type::None) ? TriState::True : TriState::False;
return TriState::Indeterminate;
}
return TriState::False;
}
template<Concurrency concurrency>
inline TriState JSCell::isConstructorWithConcurrency(VM& vm)
{
if (!isObject())
return TriState::False;
if constexpr (concurrency == Concurrency::MainThread)
return (methodTable(vm)->getConstructData(this).type != CallData::Type::None) ? TriState::True : TriState::False;
if (type() == JSFunctionType || type() == InternalFunctionType)
return (methodTable(vm)->getConstructData(this).type != CallData::Type::None) ? TriState::True : TriState::False;
return TriState::Indeterminate;
}
ALWAYS_INLINE bool JSCell::isCallable(VM& vm)
{
auto result = isCallableWithConcurrency<Concurrency::MainThread>(vm);
ASSERT(result != TriState::Indeterminate);
return result == TriState::True;
}
ALWAYS_INLINE bool JSCell::isConstructor(VM& vm)
{
auto result = isConstructorWithConcurrency<Concurrency::MainThread>(vm);
ASSERT(result != TriState::Indeterminate);
return result == TriState::True;
}
inline bool JSCell::isAPIValueWrapper() const
{
return m_type == APIValueWrapperType;
}
ALWAYS_INLINE void JSCell::setStructure(VM& vm, Structure* structure)
{
ASSERT(structure->classInfo() == this->structure(vm)->classInfo());
ASSERT(!this->structure(vm)
|| this->structure(vm)->transitionWatchpointSetHasBeenInvalidated()
|| Heap::heap(this)->structureIDTable().get(structure->id()) == structure);
m_structureID = structure->id();
m_flags = TypeInfo::mergeInlineTypeFlags(structure->typeInfo().inlineTypeFlags(), m_flags);
m_type = structure->typeInfo().type();
IndexingType newIndexingType = structure->indexingModeIncludingHistory();
if (m_indexingTypeAndMisc != newIndexingType) {
ASSERT(!(newIndexingType & ~AllArrayTypesAndHistory));
for (;;) {
IndexingType oldValue = m_indexingTypeAndMisc;
IndexingType newValue = (oldValue & ~AllArrayTypesAndHistory) | structure->indexingModeIncludingHistory();
if (WTF::atomicCompareExchangeWeakRelaxed(&m_indexingTypeAndMisc, oldValue, newValue))
break;
}
}
vm.heap.writeBarrier(this, structure);
}
inline const MethodTable* JSCell::methodTable(VM& vm) const
{
Structure* structure = this->structure(vm);
#if ASSERT_ENABLED
if (Structure* rootStructure = structure->structure(vm))
ASSERT(rootStructure == rootStructure->structure(vm));
#endif
return &structure->classInfo()->methodTable;
}
inline bool JSCell::inherits(VM& vm, const ClassInfo* info) const
{
return classInfo(vm)->isSubClassOf(info);
}
template<typename Target>
inline bool JSCell::inherits(VM& vm) const
{
return JSCastingHelpers::inherits<Target>(vm, this);
}
ALWAYS_INLINE JSValue JSCell::fastGetOwnProperty(VM& vm, Structure& structure, PropertyName name)
{
ASSERT(canUseFastGetOwnProperty(structure));
PropertyOffset offset = structure.get(vm, name);
if (offset != invalidOffset)
return asObject(this)->locationForOffset(offset)->get();
return JSValue();
}
inline bool JSCell::canUseFastGetOwnProperty(const Structure& structure)
{
return !structure.hasGetterSetterProperties()
&& !structure.hasCustomGetterSetterProperties()
&& !structure.typeInfo().overridesGetOwnPropertySlot();
}
ALWAYS_INLINE const ClassInfo* JSCell::classInfo(VM& vm) const
{
ASSERT(vm.heap.mutatorState() != MutatorState::Sweeping || !vm.currentThreadIsHoldingAPILock());
return structure(vm)->classInfo();
}
inline bool JSCell::toBoolean(JSGlobalObject* globalObject) const
{
if (isString())
return static_cast<const JSString*>(this)->toBoolean();
if (isHeapBigInt())
return static_cast<const JSBigInt*>(this)->toBoolean();
return !structure(getVM(globalObject))->masqueradesAsUndefined(globalObject);
}
inline TriState JSCell::pureToBoolean() const
{
if (isString())
return static_cast<const JSString*>(this)->toBoolean() ? TriState::True : TriState::False;
if (isHeapBigInt())
return static_cast<const JSBigInt*>(this)->toBoolean() ? TriState::True : TriState::False;
if (isSymbol())
return TriState::True;
return TriState::Indeterminate;
}
inline void JSCellLock::lock()
{
Atomic<IndexingType>* lock = bitwise_cast<Atomic<IndexingType>*>(&m_indexingTypeAndMisc);
if (UNLIKELY(!IndexingTypeLockAlgorithm::lockFast(*lock)))
lockSlow();
}
inline bool JSCellLock::tryLock()
{
Atomic<IndexingType>* lock = bitwise_cast<Atomic<IndexingType>*>(&m_indexingTypeAndMisc);
return IndexingTypeLockAlgorithm::tryLock(*lock);
}
inline void JSCellLock::unlock()
{
Atomic<IndexingType>* lock = bitwise_cast<Atomic<IndexingType>*>(&m_indexingTypeAndMisc);
if (UNLIKELY(!IndexingTypeLockAlgorithm::unlockFast(*lock)))
unlockSlow();
}
inline bool JSCellLock::isLocked() const
{
Atomic<IndexingType>* lock = bitwise_cast<Atomic<IndexingType>*>(&m_indexingTypeAndMisc);
return IndexingTypeLockAlgorithm::isLocked(*lock);
}
inline bool JSCell::perCellBit() const
{
return TypeInfo::perCellBit(inlineTypeFlags());
}
inline void JSCell::setPerCellBit(bool value)
{
if (value == perCellBit())
return;
if (value)
m_flags |= static_cast<TypeInfo::InlineTypeFlags>(TypeInfoPerCellBit);
else
m_flags &= ~static_cast<TypeInfo::InlineTypeFlags>(TypeInfoPerCellBit);
}
inline JSObject* JSCell::toObject(JSGlobalObject* globalObject) const
{
if (isObject())
return jsCast<JSObject*>(const_cast<JSCell*>(this));
return toObjectSlow(globalObject);
}
ALWAYS_INLINE bool JSCell::putInline(JSGlobalObject* globalObject, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
{
auto putMethod = methodTable(getVM(globalObject))->put;
if (LIKELY(putMethod == JSObject::put))
return JSObject::putInlineForJSObject(asObject(this), globalObject, propertyName, value, slot);
return putMethod(this, globalObject, propertyName, value, slot);
}
inline bool isWebAssemblyModule(const JSCell* cell)
{
return cell->type() == WebAssemblyModuleType;
}
}