summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/runtime/JSCellInlines.h
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/runtime/JSCellInlines.h')
-rw-r--r--Source/JavaScriptCore/runtime/JSCellInlines.h265
1 files changed, 206 insertions, 59 deletions
diff --git a/Source/JavaScriptCore/runtime/JSCellInlines.h b/Source/JavaScriptCore/runtime/JSCellInlines.h
index f7c844645..38a2ecfb0 100644
--- a/Source/JavaScriptCore/runtime/JSCellInlines.h
+++ b/Source/JavaScriptCore/runtime/JSCellInlines.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -23,40 +23,51 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef JSCellInlines_h
-#define JSCellInlines_h
+#pragma once
+#include "CPU.h"
#include "CallFrame.h"
#include "DeferGC.h"
#include "Handle.h"
#include "JSCell.h"
+#include "JSDestructibleObject.h"
#include "JSObject.h"
#include "JSString.h"
+#include "MarkedBlock.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& vm, Structure* structure)
- : m_structure(vm, this, structure)
+inline JSCell::JSCell(VM&, Structure* structure)
+ : m_structureID(structure->id())
+ , m_indexingTypeAndMisc(structure->indexingTypeIncludingHistory())
+ , m_type(structure->typeInfo().type())
+ , m_flags(structure->typeInfo().inlineTypeFlags())
+ , m_cellState(CellState::DefinitelyWhite)
{
ASSERT(!isCompilationThread());
}
inline void JSCell::finishCreation(VM& vm)
{
+ // This object is ready to be escaped so the concurrent GC may see it at any time. We have
+ // to make sure that none of our stores sink below here.
+ vm.heap.mutatorFence();
#if ENABLE(GC_VALIDATION)
ASSERT(vm.isInitializingObject());
vm.setInitializingObjectClass(0);
#else
UNUSED_PARAM(vm);
#endif
- ASSERT(m_structure);
+ ASSERT(m_structureID);
}
inline void JSCell::finishCreation(VM& vm, Structure* structure, CreatingEarlyCellTag)
@@ -64,23 +75,70 @@ inline void JSCell::finishCreation(VM& vm, Structure* structure, CreatingEarlyCe
#if ENABLE(GC_VALIDATION)
ASSERT(vm.isInitializingObject());
vm.setInitializingObjectClass(0);
- if (structure)
+ if (structure) {
+#endif
+ m_structureID = structure->id();
+ m_indexingTypeAndMisc = structure->indexingTypeIncludingHistory();
+ m_type = structure->typeInfo().type();
+ m_flags = structure->typeInfo().inlineTypeFlags();
+#if ENABLE(GC_VALIDATION)
+ }
+#else
+ UNUSED_PARAM(vm);
#endif
- m_structure.setEarlyValue(vm, this, structure);
// Very first set of allocations won't have a real structure.
- ASSERT(m_structure || !vm.structureStructure);
+ ASSERT(m_structureID || !vm.structureStructure);
}
-inline Structure* JSCell::structure() const
+inline JSType JSCell::type() const
{
- return m_structure.get();
+ return m_type;
+}
+
+inline IndexingType JSCell::indexingTypeAndMisc() const
+{
+ return m_indexingTypeAndMisc;
+}
+
+inline IndexingType JSCell::indexingType() 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)
{
- MARK_LOG_PARENT(visitor, cell);
+ visitor.appendUnbarriered(cell->structure(visitor.vm()));
+}
- visitor.append(&cell->m_structure);
+inline void JSCell::visitOutputConstraints(JSCell*, SlotVisitor&)
+{
+}
+
+ALWAYS_INLINE VM& ExecState::vm() const
+{
+ ASSERT(callee());
+ ASSERT(callee()->vm());
+ ASSERT(!callee()->isLargeAllocation());
+ // This is an important optimization since we access this so often.
+ return *callee()->markedBlock().vm();
+}
+
+template<typename CellType>
+Subspace* JSCell::subspaceFor(VM& vm)
+{
+ if (CellType::needsDestruction)
+ return &vm.destructibleCellSpace;
+ return &vm.cellSpace;
}
template<typename T>
@@ -88,13 +146,7 @@ void* allocateCell(Heap& heap, size_t size)
{
ASSERT(!DisallowGC::isGCDisallowedOnCurrentThread());
ASSERT(size >= sizeof(T));
- JSCell* result = 0;
- if (T::needsDestruction && T::hasImmortalStructure)
- result = static_cast<JSCell*>(heap.allocateWithImmortalStructureDestructor(size));
- else if (T::needsDestruction)
- result = static_cast<JSCell*>(heap.allocateWithNormalDestructor(size));
- else
- result = static_cast<JSCell*>(heap.allocateWithoutDestructor(size));
+ JSCell* result = static_cast<JSCell*>(subspaceFor<T>(*heap.vm())->allocate(size));
#if ENABLE(GC_VALIDATION)
ASSERT(!heap.vm()->isInitializingObject());
heap.vm()->setInitializingObjectClass(T::info());
@@ -109,94 +161,189 @@ void* allocateCell(Heap& heap)
return allocateCell<T>(heap, sizeof(T));
}
-inline bool isZapped(const JSCell* cell)
+template<typename T>
+void* allocateCell(Heap& heap, GCDeferralContext* deferralContext, size_t size)
{
- return cell->isZapped();
+ ASSERT(size >= sizeof(T));
+ JSCell* result = static_cast<JSCell*>(subspaceFor<T>(*heap.vm())->allocate(deferralContext, size));
+#if ENABLE(GC_VALIDATION)
+ ASSERT(!heap.vm()->isInitializingObject());
+ heap.vm()->setInitializingObjectClass(T::info());
+#endif
+ result->clearStructure();
+ return result;
}
-
+
+template<typename T>
+void* allocateCell(Heap& heap, GCDeferralContext* deferralContext)
+{
+ return allocateCell<T>(heap, deferralContext, sizeof(T));
+}
+
inline bool JSCell::isObject() const
{
- return m_structure->isObject();
+ return TypeInfo::isObject(m_type);
}
inline bool JSCell::isString() const
{
- return m_structure->typeInfo().type() == StringType;
+ return m_type == StringType;
+}
+
+inline bool JSCell::isSymbol() const
+{
+ return m_type == SymbolType;
}
inline bool JSCell::isGetterSetter() const
{
- return m_structure->typeInfo().type() == GetterSetterType;
+ return m_type == GetterSetterType;
+}
+
+inline bool JSCell::isCustomGetterSetter() const
+{
+ return m_type == CustomGetterSetterType;
}
inline bool JSCell::isProxy() const
{
- return structure()->typeInfo().type() == ProxyType;
+ return m_type == ImpureProxyType || m_type == PureForwardingProxyType;
}
inline bool JSCell::isAPIValueWrapper() const
{
- return m_structure->typeInfo().type() == APIValueWrapperType;
+ return m_type == APIValueWrapperType;
}
-inline void JSCell::setStructure(VM& vm, Structure* structure)
+ALWAYS_INLINE void JSCell::setStructure(VM& vm, Structure* structure)
{
- ASSERT(structure->typeInfo().overridesVisitChildren() == this->structure()->typeInfo().overridesVisitChildren());
- ASSERT(structure->classInfo() == m_structure->classInfo());
- ASSERT(!m_structure
- || m_structure->transitionWatchpointSetHasBeenInvalidated()
- || m_structure.get() == structure);
- m_structure.set(vm, this, structure);
+ ASSERT(structure->classInfo() == this->structure()->classInfo());
+ ASSERT(!this->structure()
+ || this->structure()->transitionWatchpointSetHasBeenInvalidated()
+ || Heap::heap(this)->structureIDTable().get(structure->id()) == structure);
+ m_structureID = structure->id();
+ m_flags = structure->typeInfo().inlineTypeFlags();
+ m_type = structure->typeInfo().type();
+ IndexingType newIndexingType = structure->indexingTypeIncludingHistory();
+ if (m_indexingTypeAndMisc != newIndexingType) {
+ ASSERT(!(newIndexingType & ~AllArrayTypesAndHistory));
+ for (;;) {
+ IndexingType oldValue = m_indexingTypeAndMisc;
+ IndexingType newValue = (oldValue & ~AllArrayTypesAndHistory) | structure->indexingTypeIncludingHistory();
+ if (WTF::atomicCompareExchangeWeakRelaxed(&m_indexingTypeAndMisc, oldValue, newValue))
+ break;
+ }
+ }
+ vm.heap.writeBarrier(this, structure);
}
-inline const MethodTable* JSCell::methodTableForDestruction() const
+inline const MethodTable* JSCell::methodTable() const
{
- return &classInfo()->methodTable;
+ VM& vm = *Heap::heap(this)->vm();
+ return methodTable(vm);
}
-inline const MethodTable* JSCell::methodTable() const
+inline const MethodTable* JSCell::methodTable(VM& vm) const
{
- if (Structure* rootStructure = m_structure->structure())
- RELEASE_ASSERT(rootStructure == rootStructure->structure());
+ Structure* structure = this->structure(vm);
+ if (Structure* rootStructure = structure->structure(vm))
+ ASSERT_UNUSED(rootStructure, rootStructure == rootStructure->structure(vm));
- return &classInfo()->methodTable;
+ return &structure->classInfo()->methodTable;
}
-inline bool JSCell::inherits(const ClassInfo* info) const
+inline bool JSCell::inherits(VM& vm, const ClassInfo* info) const
{
- return classInfo()->isSubClassOf(info);
+ return classInfo(vm)->isSubClassOf(info);
}
-// Fast call to get a property where we may not yet have converted the string to an
-// identifier. The first time we perform a property access with a given string, try
-// performing the property map lookup without forming an identifier. We detect this
-// case by checking whether the hash has yet been set for this string.
-ALWAYS_INLINE JSValue JSCell::fastGetOwnProperty(ExecState* exec, const String& name)
+ALWAYS_INLINE JSValue JSCell::fastGetOwnProperty(VM& vm, Structure& structure, PropertyName name)
{
- if (!structure()->typeInfo().overridesGetOwnPropertySlot() && !structure()->hasGetterSetterProperties()) {
- PropertyOffset offset = name.impl()->hasHash()
- ? structure()->get(exec->vm(), Identifier(exec, name))
- : structure()->get(exec->vm(), name);
- if (offset != invalidOffset)
- return asObject(this)->locationForOffset(offset)->get();
- }
+ 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
+{
+ // What we really want to assert here is that we're not currently destructing this object (which makes its classInfo
+ // invalid). If mutatorState() == MutatorState::Running, then we're not currently sweeping, and therefore cannot be
+ // destructing the object. The GC thread or JIT threads, unlike the mutator thread, are able to access classInfo
+ // independent of whether the mutator thread is sweeping or not. Hence, we also check for ownerThread() !=
+ // std::this_thread::get_id() to allow the GC thread or JIT threads to pass this assertion.
+ ASSERT(vm.heap.mutatorState() != MutatorState::Sweeping || vm.apiLock().ownerThread() != std::this_thread::get_id());
+ return structure(vm)->classInfo();
+}
+
inline bool JSCell::toBoolean(ExecState* exec) const
{
- if (isString())
+ if (isString())
return static_cast<const JSString*>(this)->toBoolean();
return !structure()->masqueradesAsUndefined(exec->lexicalGlobalObject());
}
inline TriState JSCell::pureToBoolean() const
{
- if (isString())
+ if (isString())
return static_cast<const JSString*>(this)->toBoolean() ? TrueTriState : FalseTriState;
+ if (isSymbol())
+ return TrueTriState;
return MixedTriState;
}
-} // namespace JSC
+inline void JSCell::callDestructor(VM& vm)
+{
+ if (isZapped())
+ return;
+ ASSERT(structureID());
+ if (inlineTypeFlags() & StructureIsImmortal) {
+ Structure* structure = this->structure(vm);
+ const ClassInfo* classInfo = structure->classInfo();
+ MethodTable::DestroyFunctionPtr destroy = classInfo->methodTable.destroy;
+ destroy(this);
+ } else
+ static_cast<JSDestructibleObject*>(this)->classInfo()->methodTable.destroy(this);
+ zap();
+}
+
+inline void JSCell::lock()
+{
+ Atomic<IndexingType>* lock = bitwise_cast<Atomic<IndexingType>*>(&m_indexingTypeAndMisc);
+ IndexingTypeLockAlgorithm::lock(*lock);
+}
+
+inline bool JSCell::tryLock()
+{
+ Atomic<IndexingType>* lock = bitwise_cast<Atomic<IndexingType>*>(&m_indexingTypeAndMisc);
+ return IndexingTypeLockAlgorithm::tryLock(*lock);
+}
+
+inline void JSCell::unlock()
+{
+ Atomic<IndexingType>* lock = bitwise_cast<Atomic<IndexingType>*>(&m_indexingTypeAndMisc);
+ IndexingTypeLockAlgorithm::unlock(*lock);
+}
+
+inline bool JSCell::isLocked() const
+{
+ Atomic<IndexingType>* lock = bitwise_cast<Atomic<IndexingType>*>(&m_indexingTypeAndMisc);
+ return IndexingTypeLockAlgorithm::isLocked(*lock);
+}
-#endif // JSCellInlines_h
+inline JSObject* JSCell::toObject(ExecState* exec, JSGlobalObject* globalObject) const
+{
+ if (isObject())
+ return jsCast<JSObject*>(const_cast<JSCell*>(this));
+ return toObjectSlow(exec, globalObject);
+}
+
+} // namespace JSC