summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/runtime
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/runtime')
-rw-r--r--Source/JavaScriptCore/runtime/Completion.cpp4
-rw-r--r--Source/JavaScriptCore/runtime/GCActivityCallback.h6
-rw-r--r--Source/JavaScriptCore/runtime/InitializeThreading.cpp2
-rw-r--r--Source/JavaScriptCore/runtime/JSArray.cpp5
-rw-r--r--Source/JavaScriptCore/runtime/JSFunction.cpp9
-rw-r--r--Source/JavaScriptCore/runtime/JSGlobalData.cpp15
-rw-r--r--Source/JavaScriptCore/runtime/JSGlobalData.h18
-rw-r--r--Source/JavaScriptCore/runtime/JSGlobalObject.cpp6
-rw-r--r--Source/JavaScriptCore/runtime/JSGlobalObject.h4
-rw-r--r--Source/JavaScriptCore/runtime/JSLock.cpp221
-rw-r--r--Source/JavaScriptCore/runtime/JSLock.h83
-rw-r--r--Source/JavaScriptCore/runtime/JSObject.cpp73
-rw-r--r--Source/JavaScriptCore/runtime/JSObject.h290
-rw-r--r--Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp4
-rw-r--r--Source/JavaScriptCore/runtime/JSPropertyNameIterator.h14
-rw-r--r--Source/JavaScriptCore/runtime/JSString.h42
-rw-r--r--Source/JavaScriptCore/runtime/JSValue.cpp4
-rw-r--r--Source/JavaScriptCore/runtime/Operations.h2
-rw-r--r--Source/JavaScriptCore/runtime/Options.cpp249
-rw-r--r--Source/JavaScriptCore/runtime/Options.h225
-rw-r--r--Source/JavaScriptCore/runtime/PropertyMapHashTable.h42
-rw-r--r--Source/JavaScriptCore/runtime/PropertyOffset.h163
-rw-r--r--Source/JavaScriptCore/runtime/PropertySlot.h11
-rw-r--r--Source/JavaScriptCore/runtime/PutPropertySlot.h9
-rw-r--r--Source/JavaScriptCore/runtime/Structure.cpp99
-rw-r--r--Source/JavaScriptCore/runtime/Structure.h176
-rw-r--r--Source/JavaScriptCore/runtime/WeakGCMap.h3
27 files changed, 1170 insertions, 609 deletions
diff --git a/Source/JavaScriptCore/runtime/Completion.cpp b/Source/JavaScriptCore/runtime/Completion.cpp
index 311d660a0..1c35b9626 100644
--- a/Source/JavaScriptCore/runtime/Completion.cpp
+++ b/Source/JavaScriptCore/runtime/Completion.cpp
@@ -37,7 +37,7 @@ namespace JSC {
bool checkSyntax(ExecState* exec, const SourceCode& source, JSValue* returnedException)
{
- JSLock lock(exec);
+ JSLockHolder lock(exec);
ASSERT(exec->globalData().identifierTable == wtfThreadData().currentIdentifierTable());
ProgramExecutable* program = ProgramExecutable::create(exec, source);
@@ -53,7 +53,7 @@ bool checkSyntax(ExecState* exec, const SourceCode& source, JSValue* returnedExc
JSValue evaluate(ExecState* exec, ScopeChainNode* scopeChain, const SourceCode& source, JSValue thisValue, JSValue* returnedException)
{
- JSLock lock(exec);
+ JSLockHolder lock(exec);
ASSERT(exec->globalData().identifierTable == wtfThreadData().currentIdentifierTable());
if (exec->globalData().isCollectorBusy())
CRASH();
diff --git a/Source/JavaScriptCore/runtime/GCActivityCallback.h b/Source/JavaScriptCore/runtime/GCActivityCallback.h
index 18bbd31e0..67ee17420 100644
--- a/Source/JavaScriptCore/runtime/GCActivityCallback.h
+++ b/Source/JavaScriptCore/runtime/GCActivityCallback.h
@@ -69,7 +69,7 @@ protected:
class DefaultGCActivityCallback : public GCActivityCallback {
public:
- static PassOwnPtr<DefaultGCActivityCallback> create(Heap*);
+ static DefaultGCActivityCallback* create(Heap*);
DefaultGCActivityCallback(Heap*);
@@ -91,9 +91,9 @@ private:
#endif
};
-inline PassOwnPtr<DefaultGCActivityCallback> DefaultGCActivityCallback::create(Heap* heap)
+inline DefaultGCActivityCallback* DefaultGCActivityCallback::create(Heap* heap)
{
- return adoptPtr(new DefaultGCActivityCallback(heap));
+ return new DefaultGCActivityCallback(heap);
}
}
diff --git a/Source/JavaScriptCore/runtime/InitializeThreading.cpp b/Source/JavaScriptCore/runtime/InitializeThreading.cpp
index 4c0e123a4..c7fbd332e 100644
--- a/Source/JavaScriptCore/runtime/InitializeThreading.cpp
+++ b/Source/JavaScriptCore/runtime/InitializeThreading.cpp
@@ -53,7 +53,7 @@ static void initializeThreadingOnce()
{
WTF::double_conversion::initialize();
WTF::initializeThreading();
- Options::initializeOptions();
+ Options::initialize();
#if ENABLE(WRITE_BARRIER_PROFILING)
WriteBarrierCounters::initialize();
#endif
diff --git a/Source/JavaScriptCore/runtime/JSArray.cpp b/Source/JavaScriptCore/runtime/JSArray.cpp
index 96cc44780..7218604d1 100644
--- a/Source/JavaScriptCore/runtime/JSArray.cpp
+++ b/Source/JavaScriptCore/runtime/JSArray.cpp
@@ -1258,7 +1258,10 @@ JSValue JSArray::pop(ExecState* exec)
if (exec->hadException())
return jsUndefined();
// Call the [[Delete]] internal method of O with arguments indx and true.
- deletePropertyByIndex(this, exec, index);
+ if (!deletePropertyByIndex(this, exec, index)) {
+ throwTypeError(exec, "Unable to delete property.");
+ return jsUndefined();
+ }
// Call the [[Put]] internal method of O with arguments "length", indx, and true.
setLength(exec, index, true);
// Return element.
diff --git a/Source/JavaScriptCore/runtime/JSFunction.cpp b/Source/JavaScriptCore/runtime/JSFunction.cpp
index f2d9c81e2..0a65deee2 100644
--- a/Source/JavaScriptCore/runtime/JSFunction.cpp
+++ b/Source/JavaScriptCore/runtime/JSFunction.cpp
@@ -103,13 +103,16 @@ void JSFunction::finishCreation(ExecState* exec, NativeExecutable* executable, i
void JSFunction::finishCreation(ExecState* exec, FunctionExecutable* executable, ScopeChainNode* scopeChainNode)
{
- Base::finishCreation(exec->globalData());
+ JSGlobalData& globalData = exec->globalData();
+ Base::finishCreation(globalData);
ASSERT(inherits(&s_info));
// Switching the structure here is only safe if we currently have the function structure!
ASSERT(structure() == scopeChainNode->globalObject->functionStructure());
- setStructure(exec->globalData(), scopeChainNode->globalObject->namedFunctionStructure());
- putDirectOffset(exec->globalData(), scopeChainNode->globalObject->functionNameOffset(), executable->nameValue());
+ setStructureAndReallocateStorageIfNecessary(
+ globalData,
+ scopeChainNode->globalObject->namedFunctionStructure());
+ putDirectOffset(globalData, scopeChainNode->globalObject->functionNameOffset(), executable->nameValue());
}
Structure* JSFunction::cacheInheritorID(ExecState* exec)
diff --git a/Source/JavaScriptCore/runtime/JSGlobalData.cpp b/Source/JavaScriptCore/runtime/JSGlobalData.cpp
index 1fb90df40..dd05005c7 100644
--- a/Source/JavaScriptCore/runtime/JSGlobalData.cpp
+++ b/Source/JavaScriptCore/runtime/JSGlobalData.cpp
@@ -34,8 +34,10 @@
#include "CommonIdentifiers.h"
#include "DebuggerActivation.h"
#include "FunctionConstructor.h"
+#include "GCActivityCallback.h"
#include "GetterSetter.h"
#include "HostCallReturnValue.h"
+#include "IncrementalSweeper.h"
#include "Interpreter.h"
#include "JSActivation.h"
#include "JSAPIValueWrapper.h"
@@ -97,7 +99,7 @@ extern const HashTable stringConstructorTable;
#if ENABLE(ASSEMBLER) && (ENABLE(CLASSIC_INTERPRETER) || ENABLE(LLINT))
static bool enableAssembler(ExecutableAllocator& executableAllocator)
{
- if (!executableAllocator.isValid() || !Options::useJIT)
+ if (!executableAllocator.isValid() || !Options::useJIT())
return false;
#if USE(CF)
@@ -168,6 +170,7 @@ JSGlobalData::JSGlobalData(GlobalDataType globalDataType, ThreadStackType thread
#if CPU(X86) && ENABLE(JIT)
, m_timeoutCount(512)
#endif
+ , m_newStringsSinceLastHashConst(0)
#if ENABLE(ASSEMBLER) && (ENABLE(CLASSIC_INTERPRETER) || ENABLE(LLINT))
, m_canUseAssembler(enableAssembler(executableAllocator))
#endif
@@ -178,12 +181,9 @@ JSGlobalData::JSGlobalData(GlobalDataType globalDataType, ThreadStackType thread
{
interpreter = new Interpreter;
- if (isSharedInstance())
- turnOffVerifier();
-
// Need to be careful to keep everything consistent here
+ JSLockHolder lock(this);
IdentifierTable* existingEntryIdentifierTable = wtfThreadData().setCurrentIdentifierTable(identifierTable);
- JSLock lock(SilenceAssertionsOnly);
structureStructure.set(*this, Structure::createStructure(*this));
debuggerActivationStructure.set(*this, DebuggerActivation::createStructure(*this, 0, jsNull()));
activationStructure.set(*this, JSActivation::createStructure(*this, 0, jsNull()));
@@ -222,6 +222,9 @@ JSGlobalData::JSGlobalData(GlobalDataType globalDataType, ThreadStackType thread
JSGlobalData::~JSGlobalData()
{
+ ASSERT(!m_apiLock.currentThreadIsHoldingLock());
+ heap.activityCallback()->didStartVMShutdown();
+ heap.sweeper()->didStartVMShutdown();
heap.lastChanceToFinalize();
delete interpreter;
@@ -311,6 +314,7 @@ bool JSGlobalData::sharedInstanceExists()
JSGlobalData& JSGlobalData::sharedInstance()
{
+ GlobalJSLock globalLock;
JSGlobalData*& instance = sharedInstanceInternal();
if (!instance) {
instance = adoptRef(new JSGlobalData(APIShared, ThreadStackTypeSmall, SmallHeap)).leakRef();
@@ -321,7 +325,6 @@ JSGlobalData& JSGlobalData::sharedInstance()
JSGlobalData*& JSGlobalData::sharedInstanceInternal()
{
- ASSERT(JSLock::currentThreadIsHoldingLock());
static JSGlobalData* sharedInstance;
return sharedInstance;
}
diff --git a/Source/JavaScriptCore/runtime/JSGlobalData.h b/Source/JavaScriptCore/runtime/JSGlobalData.h
index f8833104a..90925778b 100644
--- a/Source/JavaScriptCore/runtime/JSGlobalData.h
+++ b/Source/JavaScriptCore/runtime/JSGlobalData.h
@@ -35,6 +35,7 @@
#include "Heap.h"
#include "Intrinsic.h"
#include "JITStubs.h"
+#include "JSLock.h"
#include "JSValue.h"
#include "LLIntData.h"
#include "NumericStrings.h"
@@ -46,8 +47,8 @@
#include <wtf/BumpPointerAllocator.h>
#include <wtf/Forward.h>
#include <wtf/HashMap.h>
-#include <wtf/RefCounted.h>
#include <wtf/SimpleStats.h>
+#include <wtf/ThreadSafeRefCounted.h>
#include <wtf/ThreadSpecific.h>
#include <wtf/WTFThreadData.h>
#if ENABLE(REGEXP_TRACING)
@@ -152,7 +153,7 @@ namespace JSC {
};
#endif
- class JSGlobalData : public RefCounted<JSGlobalData> {
+ class JSGlobalData : public ThreadSafeRefCounted<JSGlobalData> {
public:
// WebCore has a one-to-one mapping of threads to JSGlobalDatas;
// either create() or createLeaked() should only be called once
@@ -180,6 +181,10 @@ namespace JSC {
void makeUsableFromMultipleThreads() { heap.machineThreads().makeUsableFromMultipleThreads(); }
+ private:
+ JSLock m_apiLock;
+
+ public:
Heap heap; // The heap is our first data member to ensure that it's destructed after all the objects that reference it.
GlobalDataType globalDataType;
@@ -390,6 +395,13 @@ namespace JSC {
unsigned m_timeoutCount;
#endif
+ unsigned m_newStringsSinceLastHashConst;
+
+ static const unsigned s_minNumberOfNewStringsToHashConst = 100;
+
+ bool haveEnoughNewStringsToHashConst() { return m_newStringsSinceLastHashConst > s_minNumberOfNewStringsToHashConst; }
+ void resetNewStringsSinceLastHashConst() { m_newStringsSinceLastHashConst = 0; }
+
#define registerTypedArrayFunction(type, capitalizedType) \
void registerTypedArrayDescriptor(const capitalizedType##Array*, const TypedArrayDescriptor& descriptor) \
{ \
@@ -409,6 +421,8 @@ namespace JSC {
registerTypedArrayFunction(float64, Float64);
#undef registerTypedArrayFunction
+ JSLock& apiLock() { return m_apiLock; }
+
private:
friend class LLIntOffsetsExtractor;
diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp
index d19db4fd8..0edc0a8a9 100644
--- a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp
+++ b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp
@@ -115,7 +115,7 @@ template <typename T> static inline void visitIfNeeded(SlotVisitor& visitor, Wri
JSGlobalObject::JSGlobalObject(JSGlobalData& globalData, Structure* structure, const GlobalObjectMethodTable* globalObjectMethodTable)
: JSSegmentedVariableObject(globalData, structure, &m_symbolTable)
, m_globalScopeChain()
- , m_weakRandom(Options::forceWeakRandomSeed ? Options::forcedWeakRandomSeed : static_cast<unsigned>(randomNumber() * (std::numeric_limits<unsigned>::max() + 1.0)))
+ , m_weakRandom(Options::forceWeakRandomSeed() ? Options::forcedWeakRandomSeed() : static_cast<unsigned>(randomNumber() * (std::numeric_limits<unsigned>::max() + 1.0)))
, m_evalEnabled(true)
, m_globalObjectMethodTable(globalObjectMethodTable ? globalObjectMethodTable : &s_globalObjectMethodTable)
{
@@ -123,8 +123,6 @@ JSGlobalObject::JSGlobalObject(JSGlobalData& globalData, Structure* structure, c
JSGlobalObject::~JSGlobalObject()
{
- ASSERT(JSLock::currentThreadIsHoldingLock());
-
if (m_debugger)
m_debugger->detach(this);
@@ -139,7 +137,7 @@ void JSGlobalObject::destroy(JSCell* cell)
void JSGlobalObject::init(JSObject* thisValue)
{
- ASSERT(JSLock::currentThreadIsHoldingLock());
+ ASSERT(globalData().apiLock().currentThreadIsHoldingLock());
m_globalScopeChain.set(globalData(), this, ScopeChainNode::create(0, this, &globalData(), this, thisValue));
diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.h b/Source/JavaScriptCore/runtime/JSGlobalObject.h
index 1dcfc63cc..af03f32e6 100644
--- a/Source/JavaScriptCore/runtime/JSGlobalObject.h
+++ b/Source/JavaScriptCore/runtime/JSGlobalObject.h
@@ -132,7 +132,7 @@ namespace JSC {
WriteBarrier<Structure> m_functionStructure;
WriteBarrier<Structure> m_boundFunctionStructure;
WriteBarrier<Structure> m_namedFunctionStructure;
- size_t m_functionNameOffset;
+ PropertyOffset m_functionNameOffset;
WriteBarrier<Structure> m_numberObjectStructure;
WriteBarrier<Structure> m_privateNameStructure;
WriteBarrier<Structure> m_regExpMatchesArrayStructure;
@@ -262,7 +262,7 @@ namespace JSC {
Structure* functionStructure() const { return m_functionStructure.get(); }
Structure* boundFunctionStructure() const { return m_boundFunctionStructure.get(); }
Structure* namedFunctionStructure() const { return m_namedFunctionStructure.get(); }
- size_t functionNameOffset() const { return m_functionNameOffset; }
+ PropertyOffset functionNameOffset() const { return m_functionNameOffset; }
Structure* numberObjectStructure() const { return m_numberObjectStructure.get(); }
Structure* privateNameStructure() const { return m_privateNameStructure.get(); }
Structure* internalFunctionStructure() const { return m_internalFunctionStructure.get(); }
diff --git a/Source/JavaScriptCore/runtime/JSLock.cpp b/Source/JavaScriptCore/runtime/JSLock.cpp
index 90e2f5d2a..be30c0c9c 100644
--- a/Source/JavaScriptCore/runtime/JSLock.cpp
+++ b/Source/JavaScriptCore/runtime/JSLock.cpp
@@ -23,6 +23,7 @@
#include "Heap.h"
#include "CallFrame.h"
+#include "JSGlobalObject.h"
#include "JSObject.h"
#include "ScopeChain.h"
@@ -37,95 +38,96 @@ namespace JSC {
// So it's safe to disable it on non-mac platforms where we don't have native pthreads.
#if (OS(DARWIN) || USE(PTHREADS))
-// Acquire this mutex before accessing lock-related data.
-static pthread_mutex_t JSMutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t sharedInstanceLock = PTHREAD_MUTEX_INITIALIZER;
-// Thread-specific key that tells whether a thread holds the JSMutex, and how many times it was taken recursively.
-pthread_key_t JSLockCount;
+GlobalJSLock::GlobalJSLock()
+{
+ pthread_mutex_lock(&sharedInstanceLock);
+}
-static void createJSLockCount()
+GlobalJSLock::~GlobalJSLock()
{
- pthread_key_create(&JSLockCount, 0);
+ pthread_mutex_unlock(&sharedInstanceLock);
}
-pthread_once_t createJSLockCountOnce = PTHREAD_ONCE_INIT;
+JSLockHolder::JSLockHolder(ExecState* exec)
+ : m_globalData(&exec->globalData())
+{
+ m_globalData->apiLock().lock();
+}
-// Lock nesting count.
-intptr_t JSLock::lockCount()
+JSLockHolder::JSLockHolder(JSGlobalData* globalData)
+ : m_globalData(globalData)
{
- pthread_once(&createJSLockCountOnce, createJSLockCount);
+ m_globalData->apiLock().lock();
+}
- return reinterpret_cast<intptr_t>(pthread_getspecific(JSLockCount));
+JSLockHolder::JSLockHolder(JSGlobalData& globalData)
+ : m_globalData(&globalData)
+{
+ m_globalData->apiLock().lock();
}
-static void setLockCount(intptr_t count)
+JSLockHolder::~JSLockHolder()
{
- ASSERT(count >= 0);
- pthread_setspecific(JSLockCount, reinterpret_cast<void*>(count));
+ m_globalData->apiLock().unlock();
}
-JSLock::JSLock(ExecState* exec)
- : m_lockBehavior(exec->globalData().isSharedInstance() ? LockForReal : SilenceAssertionsOnly)
+JSLock::JSLock()
+ : m_lockCount(0)
{
- lock(m_lockBehavior);
+ m_spinLock.Init();
}
-JSLock::JSLock(JSGlobalData* globalData)
- : m_lockBehavior(globalData->isSharedInstance() ? LockForReal : SilenceAssertionsOnly)
+JSLock::~JSLock()
{
- lock(m_lockBehavior);
}
-void JSLock::lock(JSLockBehavior lockBehavior)
+void JSLock::lock()
{
-#ifdef NDEBUG
- // Locking "not for real" is a debug-only feature.
- if (lockBehavior == SilenceAssertionsOnly)
- return;
-#endif
+ ThreadIdentifier currentThread = WTF::currentThread();
+ {
+ SpinLockHolder holder(&m_spinLock);
+ if (m_ownerThread == currentThread && m_lockCount) {
+ m_lockCount++;
+ return;
+ }
+ }
- pthread_once(&createJSLockCountOnce, createJSLockCount);
+ m_lock.lock();
- intptr_t currentLockCount = lockCount();
- if (!currentLockCount && lockBehavior == LockForReal) {
- int result = pthread_mutex_lock(&JSMutex);
- ASSERT_UNUSED(result, !result);
+ {
+ SpinLockHolder holder(&m_spinLock);
+ m_ownerThread = currentThread;
+ ASSERT(!m_lockCount);
+ m_lockCount = 1;
}
- setLockCount(currentLockCount + 1);
}
-void JSLock::unlock(JSLockBehavior lockBehavior)
+void JSLock::unlock()
{
- ASSERT(lockCount());
+ ASSERT(currentThreadIsHoldingLock());
-#ifdef NDEBUG
- // Locking "not for real" is a debug-only feature.
- if (lockBehavior == SilenceAssertionsOnly)
- return;
-#endif
+ SpinLockHolder holder(&m_spinLock);
+ m_lockCount--;
- intptr_t newLockCount = lockCount() - 1;
- setLockCount(newLockCount);
- if (!newLockCount && lockBehavior == LockForReal) {
- int result = pthread_mutex_unlock(&JSMutex);
- ASSERT_UNUSED(result, !result);
- }
+ if (!m_lockCount)
+ m_lock.unlock();
}
void JSLock::lock(ExecState* exec)
{
- lock(exec->globalData().isSharedInstance() ? LockForReal : SilenceAssertionsOnly);
+ exec->globalData().apiLock().lock();
}
void JSLock::unlock(ExecState* exec)
{
- unlock(exec->globalData().isSharedInstance() ? LockForReal : SilenceAssertionsOnly);
+ exec->globalData().apiLock().unlock();
}
bool JSLock::currentThreadIsHoldingLock()
{
- pthread_once(&createJSLockCountOnce, createJSLockCount);
- return !!pthread_getspecific(JSLockCount);
+ return m_lockCount && m_ownerThread == WTF::currentThread();
}
// This is fairly nasty. We allow multiple threads to run on the same
@@ -149,7 +151,7 @@ bool JSLock::currentThreadIsHoldingLock()
// this to happen, and were its stack to grow further, then it may potentially
// write over the second thread's call frames.
//
-// In avoid JS stack corruption we enforce a policy of only ever allowing two
+// To avoid JS stack corruption we enforce a policy of only ever allowing two
// threads to use a JS context concurrently, and only allowing the second of
// these threads to execute until it has completed and fully returned from its
// outermost call into JSC. We enforce this policy using 'lockDropDepth'. The
@@ -158,7 +160,7 @@ bool JSLock::currentThreadIsHoldingLock()
// same thread again, enter JSC (through evaluate script or call function), and exit
// again through a callback, then the locks will not be dropped when DropAllLocks
// is called (since lockDropDepth is non-zero). Since this thread is still holding
-// the locks, only it will re able to re-enter JSC (either be returning from the
+// the locks, only it will be able to re-enter JSC (either be returning from the
// callback, or by re-entering through another call to evaulate script or call
// function).
//
@@ -168,61 +170,84 @@ bool JSLock::currentThreadIsHoldingLock()
// order in which they were made - though implementing the less restrictive policy
// would likely increase complexity and overhead.
//
-static unsigned lockDropDepth = 0;
-JSLock::DropAllLocks::DropAllLocks(ExecState* exec)
- : m_lockBehavior(exec->globalData().isSharedInstance() ? LockForReal : SilenceAssertionsOnly)
+// This function returns the number of locks that were dropped.
+unsigned JSLock::dropAllLocks()
{
- pthread_once(&createJSLockCountOnce, createJSLockCount);
+ if (m_lockDropDepth++)
+ return 0;
- if (lockDropDepth++) {
- m_lockCount = 0;
- return;
- }
+ return dropAllLocksUnconditionally();
+}
+
+unsigned JSLock::dropAllLocksUnconditionally()
+{
+ unsigned lockCount = m_lockCount;
+ for (unsigned i = 0; i < lockCount; i++)
+ unlock();
- m_lockCount = JSLock::lockCount();
- for (intptr_t i = 0; i < m_lockCount; i++)
- JSLock::unlock(m_lockBehavior);
+ return lockCount;
}
-JSLock::DropAllLocks::DropAllLocks(JSLockBehavior JSLockBehavior)
- : m_lockBehavior(JSLockBehavior)
+void JSLock::grabAllLocks(unsigned lockCount)
{
- pthread_once(&createJSLockCountOnce, createJSLockCount);
+ for (unsigned i = 0; i < lockCount; i++)
+ lock();
- if (lockDropDepth++) {
- m_lockCount = 0;
- return;
- }
+ m_lockDropDepth--;
+}
- // It is necessary to drop even "unreal" locks, because having a non-zero lock count
- // will prevent a real lock from being taken.
+JSLock::DropAllLocks::DropAllLocks(ExecState* exec)
+ : m_lockCount(0)
+ , m_globalData(&exec->globalData())
+{
+ m_lockCount = m_globalData->apiLock().dropAllLocks();
+}
- m_lockCount = JSLock::lockCount();
- for (intptr_t i = 0; i < m_lockCount; i++)
- JSLock::unlock(m_lockBehavior);
+JSLock::DropAllLocks::DropAllLocks(JSGlobalData* globalData)
+ : m_lockCount(0)
+ , m_globalData(globalData)
+{
+ m_lockCount = m_globalData->apiLock().dropAllLocks();
}
JSLock::DropAllLocks::~DropAllLocks()
{
- for (intptr_t i = 0; i < m_lockCount; i++)
- JSLock::lock(m_lockBehavior);
-
- --lockDropDepth;
+ m_globalData->apiLock().grabAllLocks(m_lockCount);
}
#else // (OS(DARWIN) || USE(PTHREADS))
-JSLock::JSLock(ExecState*)
- : m_lockBehavior(SilenceAssertionsOnly)
+GlobalJSLock::GlobalJSLock()
+{
+}
+
+GlobalJSLock::~GlobalJSLock()
+{
+}
+
+JSLockHolder::JSLockHolder(JSGlobalData*)
{
}
-// If threading support is off, set the lock count to a constant value of 1 so ssertions
-// that the lock is held don't fail
-intptr_t JSLock::lockCount()
+JSLockHolder::JSLockHolder(JSGlobalData&)
+{
+}
+
+JSLockHolder::JSLockHolder(ExecState*)
+{
+}
+
+JSLockHolder::~JSLockHolder()
+{
+}
+
+JSLock::JSLock()
+{
+}
+
+JSLock::~JSLock()
{
- return 1;
}
bool JSLock::currentThreadIsHoldingLock()
@@ -230,11 +255,11 @@ bool JSLock::currentThreadIsHoldingLock()
return true;
}
-void JSLock::lock(JSLockBehavior)
+void JSLock::lock()
{
}
-void JSLock::unlock(JSLockBehavior)
+void JSLock::unlock()
{
}
@@ -246,11 +271,33 @@ void JSLock::unlock(ExecState*)
{
}
+void JSLock::lock(JSGlobalData&)
+{
+}
+
+void JSLock::unlock(JSGlobalData&)
+{
+}
+
+unsigned JSLock::dropAllLocks()
+{
+ return 0;
+}
+
+unsigned JSLock::dropAllLocksUnconditionally()
+{
+ return 0;
+}
+
+void JSLock::grabAllLocks(unsigned)
+{
+}
+
JSLock::DropAllLocks::DropAllLocks(ExecState*)
{
}
-JSLock::DropAllLocks::DropAllLocks(JSLockBehavior)
+JSLock::DropAllLocks::DropAllLocks(JSGlobalData*)
{
}
diff --git a/Source/JavaScriptCore/runtime/JSLock.h b/Source/JavaScriptCore/runtime/JSLock.h
index a0eb96975..94108d013 100644
--- a/Source/JavaScriptCore/runtime/JSLock.h
+++ b/Source/JavaScriptCore/runtime/JSLock.h
@@ -23,6 +23,9 @@
#include <wtf/Assertions.h>
#include <wtf/Noncopyable.h>
+#include <wtf/RefPtr.h>
+#include <wtf/TCSpinLock.h>
+#include <wtf/Threading.h>
namespace JSC {
@@ -30,8 +33,9 @@ namespace JSC {
// important to lock before doing anything that allocates a
// JavaScript data structure or that interacts with shared state
// such as the protect count hash table. The simplest way to lock
- // is to create a local JSLock object in the scope where the lock
- // must be held. The lock is recursive so nesting is ok. The JSLock
+ // is to create a local JSLockHolder object in the scope where the lock
+ // must be held and pass it the context that requires protection.
+ // The lock is recursive so nesting is ok. The JSLock
// object also acts as a convenience short-hand for running important
// initialization routines.
@@ -44,62 +48,65 @@ namespace JSC {
// DropAllLocks object takes care to release the JSLock only if your
// thread acquired it to begin with.
- // For contexts other than the single shared one, implicit locking is not done,
- // but we still need to perform all the counting in order to keep debug
- // assertions working, so that clients that use the shared context don't break.
-
class ExecState;
class JSGlobalData;
- enum JSLockBehavior { SilenceAssertionsOnly, LockForReal };
+ // This class is used to protect the initialization of the legacy single
+ // shared JSGlobalData.
+ class GlobalJSLock {
+ WTF_MAKE_NONCOPYABLE(GlobalJSLock);
+ public:
+ JS_EXPORT_PRIVATE GlobalJSLock();
+ JS_EXPORT_PRIVATE ~GlobalJSLock();
+ };
+
+ class JSLockHolder {
+ public:
+ JS_EXPORT_PRIVATE JSLockHolder(JSGlobalData*);
+ JS_EXPORT_PRIVATE JSLockHolder(JSGlobalData&);
+ JS_EXPORT_PRIVATE JSLockHolder(ExecState*);
+
+ JS_EXPORT_PRIVATE ~JSLockHolder();
+ private:
+ RefPtr<JSGlobalData> m_globalData;
+ };
class JSLock {
WTF_MAKE_NONCOPYABLE(JSLock);
public:
- JS_EXPORT_PRIVATE JSLock(ExecState*);
- JSLock(JSGlobalData*);
-
- JSLock(JSLockBehavior lockBehavior)
- : m_lockBehavior(lockBehavior)
- {
-#ifdef NDEBUG
- // Locking "not for real" is a debug-only feature.
- if (lockBehavior == SilenceAssertionsOnly)
- return;
-#endif
- lock(lockBehavior);
- }
-
- ~JSLock()
- {
-#ifdef NDEBUG
- // Locking "not for real" is a debug-only feature.
- if (m_lockBehavior == SilenceAssertionsOnly)
- return;
-#endif
- unlock(m_lockBehavior);
- }
-
- JS_EXPORT_PRIVATE static void lock(JSLockBehavior);
- JS_EXPORT_PRIVATE static void unlock(JSLockBehavior);
+ JSLock();
+ JS_EXPORT_PRIVATE ~JSLock();
+
+ JS_EXPORT_PRIVATE void lock();
+ JS_EXPORT_PRIVATE void unlock();
+
static void lock(ExecState*);
static void unlock(ExecState*);
+ static void lock(JSGlobalData&);
+ static void unlock(JSGlobalData&);
+
+ JS_EXPORT_PRIVATE bool currentThreadIsHoldingLock();
- JS_EXPORT_PRIVATE static intptr_t lockCount();
- JS_EXPORT_PRIVATE static bool currentThreadIsHoldingLock();
+ unsigned dropAllLocks();
+ unsigned dropAllLocksUnconditionally();
+ void grabAllLocks(unsigned lockCount);
- JSLockBehavior m_lockBehavior;
+ SpinLock m_spinLock;
+ Mutex m_lock;
+ ThreadIdentifier m_ownerThread;
+ intptr_t m_lockCount;
+ unsigned m_lockDropDepth;
class DropAllLocks {
WTF_MAKE_NONCOPYABLE(DropAllLocks);
public:
JS_EXPORT_PRIVATE DropAllLocks(ExecState* exec);
- JS_EXPORT_PRIVATE DropAllLocks(JSLockBehavior);
+ JS_EXPORT_PRIVATE DropAllLocks(JSGlobalData*);
JS_EXPORT_PRIVATE ~DropAllLocks();
private:
intptr_t m_lockCount;
- JSLockBehavior m_lockBehavior;
+ RefPtr<JSGlobalData> m_globalData;
};
};
diff --git a/Source/JavaScriptCore/runtime/JSObject.cpp b/Source/JavaScriptCore/runtime/JSObject.cpp
index 66cc89809..ccc49fd5c 100644
--- a/Source/JavaScriptCore/runtime/JSObject.cpp
+++ b/Source/JavaScriptCore/runtime/JSObject.cpp
@@ -93,24 +93,54 @@ void JSObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
bool wasCheckingForDefaultMarkViolation = visitor.m_isCheckingForDefaultMarkViolation;
visitor.m_isCheckingForDefaultMarkViolation = false;
#endif
+
+ JSCell::visitChildren(thisObject, visitor);
+
+ PropertyStorage storage = thisObject->outOfLineStorage();
+ if (storage) {
+ size_t storageSize = thisObject->structure()->outOfLineSizeForKnownNonFinalObject();
+ // We have this extra temp here to slake GCC's thirst for the blood of those who dereference type-punned pointers.
+ void* temp = storage;
+ visitor.copyAndAppend(&temp, thisObject->structure()->outOfLineCapacity() * sizeof(WriteBarrierBase<Unknown>), storage->slot(), storageSize);
+ storage = static_cast<PropertyStorage>(temp);
+ thisObject->m_outOfLineStorage.set(storage, StorageBarrier::Unchecked);
+ }
+
+ if (thisObject->m_inheritorID)
+ visitor.append(&thisObject->m_inheritorID);
+
+#if !ASSERT_DISABLED
+ visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
+#endif
+}
+void JSFinalObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
+{
+ JSFinalObject* thisObject = jsCast<JSFinalObject*>(cell);
+ ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info);
+#if !ASSERT_DISABLED
+ bool wasCheckingForDefaultMarkViolation = visitor.m_isCheckingForDefaultMarkViolation;
+ visitor.m_isCheckingForDefaultMarkViolation = false;
+#endif
+
JSCell::visitChildren(thisObject, visitor);
- PropertyStorage storage = thisObject->propertyStorage();
- size_t storageSize = thisObject->structure()->propertyStorageSize();
- if (thisObject->isUsingInlineStorage())
- visitor.appendValues(storage, storageSize);
- else {
+ PropertyStorage storage = thisObject->outOfLineStorage();
+ if (storage) {
+ size_t storageSize = thisObject->structure()->outOfLineSizeForKnownFinalObject();
// We have this extra temp here to slake GCC's thirst for the blood of those who dereference type-punned pointers.
void* temp = storage;
- visitor.copyAndAppend(&temp, thisObject->structure()->propertyStorageCapacity() * sizeof(WriteBarrierBase<Unknown>), storage->slot(), storageSize);
+ visitor.copyAndAppend(&temp, thisObject->structure()->outOfLineCapacity() * sizeof(WriteBarrierBase<Unknown>), storage->slot(), storageSize);
storage = static_cast<PropertyStorage>(temp);
- thisObject->m_propertyStorage.set(storage, StorageBarrier::Unchecked);
+ thisObject->m_outOfLineStorage.set(storage, StorageBarrier::Unchecked);
}
if (thisObject->m_inheritorID)
visitor.append(&thisObject->m_inheritorID);
+ size_t storageSize = thisObject->structure()->inlineSizeForKnownFinalObject();
+ visitor.appendValues(thisObject->inlineStorage(), storageSize);
+
#if !ASSERT_DISABLED
visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
#endif
@@ -153,8 +183,8 @@ void JSObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSV
for (JSObject* obj = thisObject; ; obj = asObject(prototype)) {
unsigned attributes;
JSCell* specificValue;
- size_t offset = obj->structure()->get(globalData, propertyName, attributes, specificValue);
- if (offset != WTF::notFound) {
+ PropertyOffset offset = obj->structure()->get(globalData, propertyName, attributes, specificValue);
+ if (offset != invalidOffset) {
if (attributes & ReadOnly) {
if (slot.isStrictMode())
throwError(exec, createTypeError(exec, StrictModeReadonlyPropertyWriteError));
@@ -272,7 +302,7 @@ bool JSObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName proper
unsigned attributes;
JSCell* specificValue;
- if (thisObject->structure()->get(exec->globalData(), propertyName, attributes, specificValue) != WTF::notFound) {
+ if (isValidOffset(thisObject->structure()->get(exec->globalData(), propertyName, attributes, specificValue))) {
if (attributes & DontDelete && !exec->globalData().isInDefineOwnProperty())
return false;
thisObject->removeDirect(exec->globalData(), propertyName);
@@ -394,7 +424,7 @@ bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyN
bool JSObject::getPropertySpecificValue(ExecState* exec, PropertyName propertyName, JSCell*& specificValue) const
{
unsigned attributes;
- if (structure()->get(exec->globalData(), propertyName, attributes, specificValue) != WTF::notFound)
+ if (isValidOffset(structure()->get(exec->globalData(), propertyName, attributes, specificValue)))
return true;
// This could be a function within the static table? - should probably
@@ -516,20 +546,20 @@ void JSObject::reifyStaticFunctionsForDelete(ExecState* exec)
bool JSObject::removeDirect(JSGlobalData& globalData, PropertyName propertyName)
{
- if (structure()->get(globalData, propertyName) == WTF::notFound)
+ if (!isValidOffset(structure()->get(globalData, propertyName)))
return false;
- size_t offset;
+ PropertyOffset offset;
if (structure()->isUncacheableDictionary()) {
offset = structure()->removePropertyWithoutTransition(globalData, propertyName);
- if (offset == WTF::notFound)
+ if (offset == invalidOffset)
return false;
putUndefinedAtDirectOffset(offset);
return true;
}
setStructure(globalData, Structure::removePropertyTransition(globalData, structure(), propertyName, offset));
- if (offset == WTF::notFound)
+ if (offset == invalidOffset)
return false;
putUndefinedAtDirectOffset(offset);
return true;
@@ -559,25 +589,22 @@ Structure* JSObject::createInheritorID(JSGlobalData& globalData)
return m_inheritorID.get();
}
-PropertyStorage JSObject::growPropertyStorage(JSGlobalData& globalData, size_t oldSize, size_t newSize)
+PropertyStorage JSObject::growOutOfLineStorage(JSGlobalData& globalData, size_t oldSize, size_t newSize)
{
ASSERT(newSize > oldSize);
// It's important that this function not rely on structure(), since
// we might be in the middle of a transition.
- PropertyStorage oldPropertyStorage = m_propertyStorage.get();
+ PropertyStorage oldPropertyStorage = m_outOfLineStorage.get();
PropertyStorage newPropertyStorage = 0;
- if (isUsingInlineStorage()) {
+ if (!oldPropertyStorage) {
// We have this extra temp here to slake GCC's thirst for the blood of those who dereference type-punned pointers.
void* temp = newPropertyStorage;
if (!globalData.heap.tryAllocateStorage(sizeof(WriteBarrierBase<Unknown>) * newSize, &temp))
CRASH();
newPropertyStorage = static_cast<PropertyStorage>(temp);
-
- for (unsigned i = 0; i < oldSize; ++i)
- newPropertyStorage[i] = oldPropertyStorage[i];
} else {
// We have this extra temp here to slake GCC's thirst for the blood of those who dereference type-punned pointers.
void* temp = oldPropertyStorage;
@@ -594,8 +621,8 @@ bool JSObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, Prope
{
unsigned attributes = 0;
JSCell* cell = 0;
- size_t offset = object->structure()->get(exec->globalData(), propertyName, attributes, cell);
- if (offset == WTF::notFound)
+ PropertyOffset offset = object->structure()->get(exec->globalData(), propertyName, attributes, cell);
+ if (offset == invalidOffset)
return false;
descriptor.setDescriptor(object->getDirectOffset(offset), attributes);
return true;
diff --git a/Source/JavaScriptCore/runtime/JSObject.h b/Source/JavaScriptCore/runtime/JSObject.h
index fdb708dd9..9972d6077 100644
--- a/Source/JavaScriptCore/runtime/JSObject.h
+++ b/Source/JavaScriptCore/runtime/JSObject.h
@@ -79,10 +79,13 @@ namespace JSC {
Accessor = 1 << 5, // property is a getter/setter
};
+ class JSFinalObject;
+
class JSObject : public JSCell {
friend class BatchedTransitionOptimizer;
friend class JIT;
friend class JSCell;
+ friend class JSFinalObject;
friend class MarkedBlock;
JS_EXPORT_PRIVATE friend bool setUpStaticFunctionSlot(ExecState*, const HashEntry*, JSObject*, PropertyName, PropertySlot&);
@@ -161,26 +164,72 @@ namespace JSC {
// This get function only looks at the property map.
JSValue getDirect(JSGlobalData& globalData, PropertyName propertyName) const
{
- size_t offset = structure()->get(globalData, propertyName);
- return offset != WTF::notFound ? getDirectOffset(offset) : JSValue();
+ PropertyOffset offset = structure()->get(globalData, propertyName);
+ checkOffset(offset, structure()->typeInfo().type());
+ return offset != invalidOffset ? getDirectOffset(offset) : JSValue();
}
WriteBarrierBase<Unknown>* getDirectLocation(JSGlobalData& globalData, PropertyName propertyName)
{
- size_t offset = structure()->get(globalData, propertyName);
- return offset != WTF::notFound ? locationForOffset(offset) : 0;
+ PropertyOffset offset = structure()->get(globalData, propertyName);
+ checkOffset(offset, structure()->typeInfo().type());
+ return offset != invalidOffset ? locationForOffset(offset) : 0;
}
WriteBarrierBase<Unknown>* getDirectLocation(JSGlobalData& globalData, PropertyName propertyName, unsigned& attributes)
{
JSCell* specificFunction;
- size_t offset = structure()->get(globalData, propertyName, attributes, specificFunction);
- return offset != WTF::notFound ? locationForOffset(offset) : 0;
+ PropertyOffset offset = structure()->get(globalData, propertyName, attributes, specificFunction);
+ return offset != invalidOffset ? locationForOffset(offset) : 0;
}
- size_t offsetForLocation(WriteBarrierBase<Unknown>* location) const
+ bool hasInlineStorage() const { return structure()->hasInlineStorage(); }
+ ConstPropertyStorage inlineStorageUnsafe() const
+ {
+ return bitwise_cast<ConstPropertyStorage>(this + 1);
+ }
+ PropertyStorage inlineStorageUnsafe()
+ {
+ return bitwise_cast<PropertyStorage>(this + 1);
+ }
+ ConstPropertyStorage inlineStorage() const
+ {
+ ASSERT(hasInlineStorage());
+ return inlineStorageUnsafe();
+ }
+ PropertyStorage inlineStorage()
{
- return location - propertyStorage();
+ ASSERT(hasInlineStorage());
+ return inlineStorageUnsafe();
+ }
+
+ ConstPropertyStorage outOfLineStorage() const { return m_outOfLineStorage.get(); }
+ PropertyStorage outOfLineStorage() { return m_outOfLineStorage.get(); }
+
+ const WriteBarrierBase<Unknown>* locationForOffset(PropertyOffset offset) const
+ {
+ if (isInlineOffset(offset))
+ return &inlineStorage()[offsetInInlineStorage(offset)];
+ return &outOfLineStorage()[offsetInOutOfLineStorage(offset)];
+ }
+
+ WriteBarrierBase<Unknown>* locationForOffset(PropertyOffset offset)
+ {
+ if (isInlineOffset(offset))
+ return &inlineStorage()[offsetInInlineStorage(offset)];
+ return &outOfLineStorage()[offsetInOutOfLineStorage(offset)];
+ }
+
+ PropertyOffset offsetForLocation(WriteBarrierBase<Unknown>* location) const
+ {
+ PropertyOffset result;
+ size_t offsetInInlineStorage = location - inlineStorageUnsafe();
+ if (offsetInInlineStorage < static_cast<size_t>(inlineStorageCapacity))
+ result = offsetInInlineStorage;
+ else
+ result = location - outOfLineStorage() + firstOutOfLineOffset;
+ validateOffset(result, structure()->typeInfo().type());
+ return result;
}
void transitionTo(JSGlobalData&, Structure*);
@@ -197,9 +246,9 @@ namespace JSC {
bool putOwnDataProperty(JSGlobalData&, PropertyName, JSValue, PutPropertySlot&);
// Fast access to known property offsets.
- JSValue getDirectOffset(size_t offset) const { return propertyStorage()[offset].get(); }
- void putDirectOffset(JSGlobalData& globalData, size_t offset, JSValue value) { propertyStorage()[offset].set(globalData, this, value); }
- void putUndefinedAtDirectOffset(size_t offset) { propertyStorage()[offset].setUndefined(); }
+ JSValue getDirectOffset(PropertyOffset offset) const { return locationForOffset(offset)->get(); }
+ void putDirectOffset(JSGlobalData& globalData, PropertyOffset offset, JSValue value) { locationForOffset(offset)->set(globalData, this, value); }
+ void putUndefinedAtDirectOffset(PropertyOffset offset) { locationForOffset(offset)->setUndefined(); }
JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool shouldThrow);
@@ -220,17 +269,18 @@ namespace JSC {
bool staticFunctionsReified() { return structure()->staticFunctionsReified(); }
void reifyStaticFunctionsForDelete(ExecState* exec);
- JS_EXPORT_PRIVATE PropertyStorage growPropertyStorage(JSGlobalData&, size_t oldSize, size_t newSize);
- bool isUsingInlineStorage() const { return static_cast<const void*>(m_propertyStorage.get()) == static_cast<const void*>(this + 1); }
- void setPropertyStorage(JSGlobalData&, PropertyStorage, Structure*);
+ JS_EXPORT_PRIVATE PropertyStorage growOutOfLineStorage(JSGlobalData&, size_t oldSize, size_t newSize);
+ void setOutOfLineStorage(JSGlobalData&, PropertyStorage, Structure*);
+
+ bool reallocateStorageIfNecessary(JSGlobalData&, unsigned oldCapacity, Structure*);
+ void setStructureAndReallocateStorageIfNecessary(JSGlobalData&, unsigned oldCapacity, Structure*);
+ void setStructureAndReallocateStorageIfNecessary(JSGlobalData&, Structure*);
- void* addressOfPropertyStorage()
+ void* addressOfOutOfLineStorage()
{
- return &m_propertyStorage;
+ return &m_outOfLineStorage;
}
- static const unsigned baseExternalStorageCapacity = 16;
-
void flattenDictionaryObject(JSGlobalData& globalData)
{
structure()->flattenDictionaryStructure(globalData, this);
@@ -244,20 +294,19 @@ namespace JSC {
}
static size_t offsetOfInlineStorage();
- static size_t offsetOfPropertyStorage();
+ static size_t offsetOfOutOfLineStorage();
static size_t offsetOfInheritorID();
static JS_EXPORTDATA const ClassInfo s_info;
protected:
- void finishCreation(JSGlobalData& globalData, PropertyStorage inlineStorage)
+ void finishCreation(JSGlobalData& globalData)
{
Base::finishCreation(globalData);
ASSERT(inherits(&s_info));
- ASSERT(structure()->propertyStorageCapacity() < baseExternalStorageCapacity);
+ ASSERT(!structure()->outOfLineCapacity());
ASSERT(structure()->isEmpty());
ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype()));
- ASSERT_UNUSED(inlineStorage, static_cast<void*>(inlineStorage) == static_cast<void*>(this + 1));
ASSERT(structure()->isObject());
ASSERT(classInfo());
}
@@ -271,7 +320,7 @@ namespace JSC {
// To instantiate objects you likely want JSFinalObject, below.
// To create derived types you likely want JSNonFinalObject, below.
- JSObject(JSGlobalData&, Structure*, PropertyStorage inlineStorage);
+ JSObject(JSGlobalData&, Structure*);
void resetInheritorID()
{
@@ -289,19 +338,6 @@ namespace JSC {
void isObject();
void isString();
- ConstPropertyStorage propertyStorage() const { return m_propertyStorage.get(); }
- PropertyStorage propertyStorage() { return m_propertyStorage.get(); }
-
- const WriteBarrierBase<Unknown>* locationForOffset(size_t offset) const
- {
- return &propertyStorage()[offset];
- }
-
- WriteBarrierBase<Unknown>* locationForOffset(size_t offset)
- {
- return &propertyStorage()[offset];
- }
-
template<PutMode>
bool putDirectInternal(JSGlobalData&, PropertyName, JSValue, unsigned attr, PutPropertySlot&, JSCell*);
@@ -311,21 +347,11 @@ namespace JSC {
const HashEntry* findPropertyHashEntry(ExecState*, PropertyName) const;
Structure* createInheritorID(JSGlobalData&);
- StorageBarrier m_propertyStorage;
+ StorageBarrier m_outOfLineStorage;
WriteBarrier<Structure> m_inheritorID;
};
-#if USE(JSVALUE32_64)
-#define JSNonFinalObject_inlineStorageCapacity 4
-#define JSFinalObject_inlineStorageCapacity 6
-#else
-#define JSNonFinalObject_inlineStorageCapacity 2
-#define JSFinalObject_inlineStorageCapacity 4
-#endif
-
-COMPILE_ASSERT((JSFinalObject_inlineStorageCapacity >= JSNonFinalObject_inlineStorageCapacity), final_storage_is_at_least_as_large_as_non_final);
-
// JSNonFinalObject is a type of JSObject that has some internal storage,
// but also preserves some space in the collector cell for additional
// data members in derived types.
@@ -340,22 +366,23 @@ COMPILE_ASSERT((JSFinalObject_inlineStorageCapacity >= JSNonFinalObject_inlineSt
return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info);
}
+ static bool hasInlineStorage()
+ {
+ return false;
+ }
+
protected:
explicit JSNonFinalObject(JSGlobalData& globalData, Structure* structure)
- : JSObject(globalData, structure, m_inlineStorage)
+ : JSObject(globalData, structure)
{
}
void finishCreation(JSGlobalData& globalData)
{
- Base::finishCreation(globalData, m_inlineStorage);
- ASSERT(!(OBJECT_OFFSETOF(JSNonFinalObject, m_inlineStorage) % sizeof(double)));
- ASSERT(this->structure()->propertyStorageCapacity() == JSNonFinalObject_inlineStorageCapacity);
+ Base::finishCreation(globalData);
+ ASSERT(!this->structure()->totalStorageCapacity());
ASSERT(classInfo());
}
-
- private:
- WriteBarrier<Unknown> m_inlineStorage[JSNonFinalObject_inlineStorageCapacity];
};
class JSFinalObject;
@@ -374,14 +401,23 @@ COMPILE_ASSERT((JSFinalObject_inlineStorageCapacity >= JSNonFinalObject_inlineSt
return Structure::create(globalData, globalObject, prototype, TypeInfo(FinalObjectType, StructureFlags), &s_info);
}
+ JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&);
+
static JS_EXPORTDATA const ClassInfo s_info;
+ static bool hasInlineStorage()
+ {
+ return true;
+ }
protected:
+ void visitChildrenCommon(SlotVisitor&);
+
void finishCreation(JSGlobalData& globalData)
{
- Base::finishCreation(globalData, m_inlineStorage);
+ Base::finishCreation(globalData);
ASSERT(!(OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage) % sizeof(double)));
- ASSERT(this->structure()->propertyStorageCapacity() == JSFinalObject_inlineStorageCapacity);
+ ASSERT(this->structure()->inlineCapacity() == static_cast<unsigned>(inlineStorageCapacity));
+ ASSERT(this->structure()->totalStorageCapacity() == static_cast<unsigned>(inlineStorageCapacity));
ASSERT(classInfo());
}
@@ -389,13 +425,13 @@ COMPILE_ASSERT((JSFinalObject_inlineStorageCapacity >= JSNonFinalObject_inlineSt
friend class LLIntOffsetsExtractor;
explicit JSFinalObject(JSGlobalData& globalData, Structure* structure)
- : JSObject(globalData, structure, m_inlineStorage)
+ : JSObject(globalData, structure)
{
}
static const unsigned StructureFlags = JSObject::StructureFlags;
- WriteBarrierBase<Unknown> m_inlineStorage[JSFinalObject_inlineStorageCapacity];
+ WriteBarrierBase<Unknown> m_inlineStorage[INLINE_STORAGE_CAPACITY];
};
inline JSFinalObject* JSFinalObject::create(ExecState* exec, Structure* structure)
@@ -417,13 +453,12 @@ inline bool isJSFinalObject(JSValue value)
inline size_t JSObject::offsetOfInlineStorage()
{
- ASSERT(OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage) == OBJECT_OFFSETOF(JSNonFinalObject, m_inlineStorage));
return OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage);
}
-inline size_t JSObject::offsetOfPropertyStorage()
+inline size_t JSObject::offsetOfOutOfLineStorage()
{
- return OBJECT_OFFSETOF(JSObject, m_propertyStorage);
+ return OBJECT_OFFSETOF(JSObject, m_outOfLineStorage);
}
inline size_t JSObject::offsetOfInheritorID()
@@ -461,12 +496,18 @@ inline bool JSObject::isGlobalThis() const
return structure()->typeInfo().type() == GlobalThisType;
}
-inline void JSObject::setPropertyStorage(JSGlobalData& globalData, PropertyStorage storage, Structure* structure)
+inline void JSObject::setOutOfLineStorage(JSGlobalData& globalData, PropertyStorage storage, Structure* structure)
{
- ASSERT(storage);
ASSERT(structure);
+ if (!storage) {
+ ASSERT(!structure->outOfLineCapacity());
+ ASSERT(!structure->outOfLineSize());
+ } else {
+ ASSERT(structure->outOfLineCapacity());
+ ASSERT(structure->outOfLineSize());
+ }
setStructure(globalData, structure);
- m_propertyStorage.set(globalData, this, storage);
+ m_outOfLineStorage.set(globalData, this, storage);
}
inline JSObject* constructEmptyObject(ExecState* exec, Structure* structure)
@@ -504,9 +545,9 @@ inline JSObject* asObject(JSValue value)
return asObject(value.asCell());
}
-inline JSObject::JSObject(JSGlobalData& globalData, Structure* structure, PropertyStorage inlineStorage)
+inline JSObject::JSObject(JSGlobalData& globalData, Structure* structure)
: JSCell(globalData, structure)
- , m_propertyStorage(globalData, this, inlineStorage)
+ , m_outOfLineStorage(globalData, this, 0)
{
}
@@ -530,11 +571,6 @@ inline Structure* JSObject::inheritorID(JSGlobalData& globalData)
return createInheritorID(globalData);
}
-inline bool Structure::isUsingInlineStorage() const
-{
- return propertyStorageCapacity() < JSObject::baseExternalStorageCapacity;
-}
-
inline bool JSCell::inherits(const ClassInfo* info) const
{
return classInfo()->isSubClassOf(info);
@@ -591,10 +627,10 @@ ALWAYS_INLINE bool JSCell::fastGetOwnPropertySlot(ExecState* exec, PropertyName
ALWAYS_INLINE JSValue JSCell::fastGetOwnProperty(ExecState* exec, const UString& name)
{
if (!structure()->typeInfo().overridesGetOwnPropertySlot() && !structure()->hasGetterSetterProperties()) {
- size_t offset = name.impl()->hasHash()
+ PropertyOffset offset = name.impl()->hasHash()
? structure()->get(exec->globalData(), Identifier(exec, name))
: structure()->get(exec->globalData(), name);
- if (offset != WTF::notFound)
+ if (offset != invalidOffset)
return asObject(this)->locationForOffset(offset)->get();
}
return JSValue();
@@ -656,8 +692,8 @@ inline bool JSObject::putDirectInternal(JSGlobalData& globalData, PropertyName p
if (structure()->isDictionary()) {
unsigned currentAttributes;
JSCell* currentSpecificFunction;
- size_t offset = structure()->get(globalData, propertyName, currentAttributes, currentSpecificFunction);
- if (offset != WTF::notFound) {
+ PropertyOffset offset = structure()->get(globalData, propertyName, currentAttributes, currentSpecificFunction);
+ if (offset != invalidOffset) {
// If there is currently a specific function, and there now either isn't,
// or the new value is different, then despecify.
if (currentSpecificFunction && (specificFunction != currentSpecificFunction))
@@ -680,13 +716,14 @@ inline bool JSObject::putDirectInternal(JSGlobalData& globalData, PropertyName p
if ((mode == PutModePut) && !isExtensible())
return false;
- PropertyStorage newStorage = propertyStorage();
- if (structure()->shouldGrowPropertyStorage())
- newStorage = growPropertyStorage(globalData, structure()->propertyStorageCapacity(), structure()->suggestedNewPropertyStorageSize());
+ PropertyStorage newStorage = outOfLineStorage();
+ if (structure()->putWillGrowOutOfLineStorage())
+ newStorage = growOutOfLineStorage(globalData, structure()->outOfLineCapacity(), structure()->suggestedNewOutOfLineStorageCapacity());
offset = structure()->addPropertyWithoutTransition(globalData, propertyName, attributes, specificFunction);
- setPropertyStorage(globalData, newStorage, structure());
+ setOutOfLineStorage(globalData, newStorage, structure());
- ASSERT(offset < structure()->propertyStorageCapacity());
+ validateOffset(offset);
+ ASSERT(structure()->isValidOffset(offset));
putDirectOffset(globalData, offset, value);
// See comment on setNewProperty call below.
if (!specificFunction)
@@ -694,15 +731,16 @@ inline bool JSObject::putDirectInternal(JSGlobalData& globalData, PropertyName p
return true;
}
- size_t offset;
- size_t currentCapacity = structure()->propertyStorageCapacity();
+ PropertyOffset offset;
+ size_t currentCapacity = structure()->outOfLineCapacity();
if (Structure* structure = Structure::addPropertyTransitionToExistingStructure(this->structure(), propertyName, attributes, specificFunction, offset)) {
- PropertyStorage newStorage = propertyStorage();
- if (currentCapacity != structure->propertyStorageCapacity())
- newStorage = growPropertyStorage(globalData, currentCapacity, structure->propertyStorageCapacity());
+ PropertyStorage newStorage = outOfLineStorage();
+ if (currentCapacity != structure->outOfLineCapacity())
+ newStorage = growOutOfLineStorage(globalData, currentCapacity, structure->outOfLineCapacity());
- ASSERT(offset < structure->propertyStorageCapacity());
- setPropertyStorage(globalData, newStorage, structure);
+ validateOffset(offset);
+ ASSERT(structure->isValidOffset(offset));
+ setOutOfLineStorage(globalData, newStorage, structure);
putDirectOffset(globalData, offset, value);
// This is a new property; transitions with specific values are not currently cachable,
// so leave the slot in an uncachable state.
@@ -714,7 +752,7 @@ inline bool JSObject::putDirectInternal(JSGlobalData& globalData, PropertyName p
unsigned currentAttributes;
JSCell* currentSpecificFunction;
offset = structure()->get(globalData, propertyName, currentAttributes, currentSpecificFunction);
- if (offset != WTF::notFound) {
+ if (offset != invalidOffset) {
if ((mode == PutModePut) && currentAttributes & ReadOnly)
return false;
@@ -746,14 +784,12 @@ inline bool JSObject::putDirectInternal(JSGlobalData& globalData, PropertyName p
if ((mode == PutModePut) && !isExtensible())
return false;
- PropertyStorage newStorage = propertyStorage();
- if (structure()->shouldGrowPropertyStorage())
- newStorage = growPropertyStorage(globalData, structure()->propertyStorageCapacity(), structure()->suggestedNewPropertyStorageSize());
-
Structure* structure = Structure::addPropertyTransition(globalData, this->structure(), propertyName, attributes, specificFunction, offset);
+
+ validateOffset(offset);
+ ASSERT(structure->isValidOffset(offset));
+ setStructureAndReallocateStorageIfNecessary(globalData, structure);
- ASSERT(offset < structure->propertyStorageCapacity());
- setPropertyStorage(globalData, newStorage, structure);
putDirectOffset(globalData, offset, value);
// This is a new property; transitions with specific values are not currently cachable,
// so leave the slot in an uncachable state.
@@ -762,6 +798,26 @@ inline bool JSObject::putDirectInternal(JSGlobalData& globalData, PropertyName p
return true;
}
+inline void JSObject::setStructureAndReallocateStorageIfNecessary(JSGlobalData& globalData, unsigned oldCapacity, Structure* newStructure)
+{
+ ASSERT(oldCapacity <= newStructure->outOfLineCapacity());
+
+ if (oldCapacity == newStructure->outOfLineCapacity()) {
+ setStructure(globalData, newStructure);
+ return;
+ }
+
+ PropertyStorage newStorage = growOutOfLineStorage(
+ globalData, oldCapacity, newStructure->outOfLineCapacity());
+ setOutOfLineStorage(globalData, newStorage, newStructure);
+}
+
+inline void JSObject::setStructureAndReallocateStorageIfNecessary(JSGlobalData& globalData, Structure* newStructure)
+{
+ setStructureAndReallocateStorageIfNecessary(
+ globalData, structure()->outOfLineCapacity(), newStructure);
+}
+
inline bool JSObject::putOwnDataProperty(JSGlobalData& globalData, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
{
ASSERT(value);
@@ -787,22 +843,14 @@ inline void JSObject::putDirect(JSGlobalData& globalData, PropertyName propertyN
inline void JSObject::putDirectWithoutTransition(JSGlobalData& globalData, PropertyName propertyName, JSValue value, unsigned attributes)
{
ASSERT(!value.isGetterSetter() && !(attributes & Accessor));
- PropertyStorage newStorage = propertyStorage();
- if (structure()->shouldGrowPropertyStorage())
- newStorage = growPropertyStorage(globalData, structure()->propertyStorageCapacity(), structure()->suggestedNewPropertyStorageSize());
- size_t offset = structure()->addPropertyWithoutTransition(globalData, propertyName, attributes, getCallableObject(value));
- setPropertyStorage(globalData, newStorage, structure());
+ PropertyStorage newStorage = outOfLineStorage();
+ if (structure()->putWillGrowOutOfLineStorage())
+ newStorage = growOutOfLineStorage(globalData, structure()->outOfLineCapacity(), structure()->suggestedNewOutOfLineStorageCapacity());
+ PropertyOffset offset = structure()->addPropertyWithoutTransition(globalData, propertyName, attributes, getCallableObject(value));
+ setOutOfLineStorage(globalData, newStorage, structure());
putDirectOffset(globalData, offset, value);
}
-inline void JSObject::transitionTo(JSGlobalData& globalData, Structure* newStructure)
-{
- PropertyStorage newStorage = propertyStorage();
- if (structure()->propertyStorageCapacity() != newStructure->propertyStorageCapacity())
- newStorage = growPropertyStorage(globalData, structure()->propertyStorageCapacity(), newStructure->propertyStorageCapacity());
- setPropertyStorage(globalData, newStorage, newStructure);
-}
-
inline JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const
{
return methodTable()->defaultValue(this, exec, preferredType);
@@ -877,8 +925,6 @@ inline void JSValue::putByIndex(ExecState* exec, unsigned propertyName, JSValue
asCell()->methodTable()->putByIndex(asCell(), exec, propertyName, value, shouldThrow);
}
-// --- JSValue inlines ----------------------------
-
ALWAYS_INLINE JSObject* Register::function() const
{
if (!jsValue())
@@ -893,6 +939,32 @@ ALWAYS_INLINE Register Register::withCallee(JSObject* callee)
return r;
}
+// This is a helper for patching code where you want to emit a load or store and
+// the base is:
+// For inline offsets: a pointer to the out-of-line storage pointer.
+// For out-of-line offsets: the base of the out-of-line storage.
+inline size_t offsetRelativeToPatchedStorage(PropertyOffset offset)
+{
+ if (isOutOfLineOffset(offset))
+ return sizeof(EncodedJSValue) * offsetInOutOfLineStorage(offset);
+ return JSObject::offsetOfInlineStorage() - JSObject::offsetOfOutOfLineStorage() + sizeof(EncodedJSValue) * offsetInInlineStorage(offset);
+}
+
+inline int indexRelativeToBase(PropertyOffset offset)
+{
+ if (isOutOfLineOffset(offset))
+ return offsetInOutOfLineStorage(offset);
+ ASSERT(!(JSObject::offsetOfInlineStorage() % sizeof(EncodedJSValue)));
+ return JSObject::offsetOfInlineStorage() / sizeof(EncodedJSValue) + offsetInInlineStorage(offset);
+}
+
+inline int offsetRelativeToBase(PropertyOffset offset)
+{
+ if (isOutOfLineOffset(offset))
+ return offsetInOutOfLineStorage(offset) * sizeof(EncodedJSValue);
+ return JSObject::offsetOfInlineStorage() + offsetInInlineStorage(offset) * sizeof(EncodedJSValue);
+}
+
} // namespace JSC
#endif // JSObject_h
diff --git a/Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp b/Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp
index 6ceb3c411..aaf946d3d 100644
--- a/Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp
+++ b/Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp
@@ -56,10 +56,10 @@ JSPropertyNameIterator* JSPropertyNameIterator::create(ExecState* exec, JSObject
size_t numCacheableSlots = 0;
if (!o->structure()->hasNonEnumerableProperties() && !o->structure()->hasGetterSetterProperties()
&& !o->structure()->isUncacheableDictionary() && !o->structure()->typeInfo().overridesGetPropertyNames())
- numCacheableSlots = o->structure()->propertyStorageSize();
+ numCacheableSlots = o->structure()->totalStorageSize();
JSPropertyNameIterator* jsPropertyNameIterator = new (NotNull, allocateCell<JSPropertyNameIterator>(*exec->heap())) JSPropertyNameIterator(exec, propertyNames.data(), numCacheableSlots);
- jsPropertyNameIterator->finishCreation(exec, propertyNames.data());
+ jsPropertyNameIterator->finishCreation(exec, propertyNames.data(), o);
if (o->structure()->isDictionary())
return jsPropertyNameIterator;
diff --git a/Source/JavaScriptCore/runtime/JSPropertyNameIterator.h b/Source/JavaScriptCore/runtime/JSPropertyNameIterator.h
index 5b65e59f2..653ee0463 100644
--- a/Source/JavaScriptCore/runtime/JSPropertyNameIterator.h
+++ b/Source/JavaScriptCore/runtime/JSPropertyNameIterator.h
@@ -47,12 +47,6 @@ namespace JSC {
typedef JSCell Base;
static JSPropertyNameIterator* create(ExecState*, JSObject*);
- static JSPropertyNameIterator* create(ExecState* exec, PropertyNameArrayData* propertyNameArrayData, size_t numCacheableSlot)
- {
- JSPropertyNameIterator* iterator = new (NotNull, allocateCell<JSPropertyNameIterator>(*exec->heap())) JSPropertyNameIterator(exec, propertyNameArrayData, numCacheableSlot);
- iterator->finishCreation(exec, propertyNameArrayData);
- return iterator;
- }
static void destroy(JSCell*);
@@ -63,11 +57,11 @@ namespace JSC {
static void visitChildren(JSCell*, SlotVisitor&);
- bool getOffset(size_t i, int& offset)
+ bool getOffset(size_t i, PropertyOffset& offset)
{
if (i >= m_numCacheableSlots)
return false;
- offset = i;
+ offset = i + m_offsetBase;
return true;
}
@@ -88,12 +82,13 @@ namespace JSC {
static const ClassInfo s_info;
protected:
- void finishCreation(ExecState* exec, PropertyNameArrayData* propertyNameArrayData)
+ void finishCreation(ExecState* exec, PropertyNameArrayData* propertyNameArrayData, JSObject* object)
{
Base::finishCreation(exec->globalData());
PropertyNameArrayData::PropertyNameVector& propertyNameVector = propertyNameArrayData->propertyNameVector();
for (size_t i = 0; i < m_jsStringsSize; ++i)
m_jsStrings[i].set(exec->globalData(), this, jsOwnedString(exec, propertyNameVector[i].ustring()));
+ m_offsetBase = object->structure()->firstValidOffset();
}
private:
@@ -105,6 +100,7 @@ namespace JSC {
WriteBarrier<StructureChain> m_cachedPrototypeChain;
uint32_t m_numCacheableSlots;
uint32_t m_jsStringsSize;
+ PropertyOffset m_offsetBase;
OwnArrayPtr<WriteBarrier<Unknown> > m_jsStrings;
};
diff --git a/Source/JavaScriptCore/runtime/JSString.h b/Source/JavaScriptCore/runtime/JSString.h
index 4fb157c8b..d6fc4c2a1 100644
--- a/Source/JavaScriptCore/runtime/JSString.h
+++ b/Source/JavaScriptCore/runtime/JSString.h
@@ -67,6 +67,7 @@ namespace JSC {
friend class JSGlobalData;
friend class SpecializedThunkJIT;
friend class JSRopeString;
+ friend class MarkStack;
friend class SlotVisitor;
friend struct ThunkHelpers;
@@ -77,12 +78,14 @@ namespace JSC {
private:
JSString(JSGlobalData& globalData, PassRefPtr<StringImpl> value)
: JSCell(globalData, globalData.stringStructure.get())
+ , m_flags(0)
, m_value(value)
{
}
JSString(JSGlobalData& globalData)
: JSCell(globalData, globalData.stringStructure.get())
+ , m_flags(0)
{
}
@@ -91,7 +94,8 @@ namespace JSC {
ASSERT(!m_value.isNull());
Base::finishCreation(globalData);
m_length = length;
- m_is8Bit = m_value.impl()->is8Bit();
+ setIs8Bit(m_value.impl()->is8Bit());
+ globalData.m_newStringsSinceLastHashConst++;
}
void finishCreation(JSGlobalData& globalData, size_t length, size_t cost)
@@ -99,8 +103,9 @@ namespace JSC {
ASSERT(!m_value.isNull());
Base::finishCreation(globalData);
m_length = length;
- m_is8Bit = m_value.impl()->is8Bit();
+ setIs8Bit(m_value.impl()->is8Bit());
Heap::heap(this)->reportExtraMemoryCost(cost);
+ globalData.m_newStringsSinceLastHashConst++;
}
protected:
@@ -108,7 +113,8 @@ namespace JSC {
{
Base::finishCreation(globalData);
m_length = 0;
- m_is8Bit = true;
+ setIs8Bit(true);
+ globalData.m_newStringsSinceLastHashConst++;
}
public:
@@ -161,10 +167,30 @@ namespace JSC {
protected:
bool isRope() const { return m_value.isNull(); }
- bool is8Bit() const { return m_is8Bit; }
+ bool is8Bit() const { return m_flags & Is8Bit; }
+ void setIs8Bit(bool flag)
+ {
+ if (flag)
+ m_flags |= Is8Bit;
+ else
+ m_flags &= ~Is8Bit;
+ }
+ bool shouldTryHashConst();
+ bool isHashConstSingleton() const { return m_flags & IsHashConstSingleton; }
+ void clearHashConstSingleton() { m_flags &= ~IsHashConstSingleton; }
+ void setHashConstSingleton() { m_flags |= IsHashConstSingleton; }
+ bool tryHashConstLock();
+ void releaseHashConstLock();
+
+ unsigned m_flags;
+
+ enum {
+ HashConstLock = 1u << 2,
+ IsHashConstSingleton = 1u << 1,
+ Is8Bit = 1u
+ };
// A string is represented either by a UString or a rope of fibers.
- bool m_is8Bit : 1;
unsigned m_length;
mutable UString m_value;
@@ -231,7 +257,7 @@ namespace JSC {
{
Base::finishCreation(globalData);
m_length = s1->length() + s2->length();
- m_is8Bit = (s1->is8Bit() && s2->is8Bit());
+ setIs8Bit(s1->is8Bit() && s2->is8Bit());
m_fibers[0].set(globalData, this, s1);
m_fibers[1].set(globalData, this, s2);
}
@@ -240,7 +266,7 @@ namespace JSC {
{
Base::finishCreation(globalData);
m_length = s1->length() + s2->length() + s3->length();
- m_is8Bit = (s1->is8Bit() && s2->is8Bit() && s3->is8Bit());
+ setIs8Bit(s1->is8Bit() && s2->is8Bit() && s3->is8Bit());
m_fibers[0].set(globalData, this, s1);
m_fibers[1].set(globalData, this, s2);
m_fibers[2].set(globalData, this, s3);
@@ -255,7 +281,7 @@ namespace JSC {
{
m_fibers[index].set(globalData, this, jsString);
m_length += jsString->m_length;
- m_is8Bit = m_is8Bit && jsString->m_is8Bit;
+ setIs8Bit(is8Bit() && jsString->is8Bit());
}
static JSRopeString* createNull(JSGlobalData& globalData)
diff --git a/Source/JavaScriptCore/runtime/JSValue.cpp b/Source/JavaScriptCore/runtime/JSValue.cpp
index e10867176..c34431178 100644
--- a/Source/JavaScriptCore/runtime/JSValue.cpp
+++ b/Source/JavaScriptCore/runtime/JSValue.cpp
@@ -130,8 +130,8 @@ void JSValue::putToPrimitive(ExecState* exec, PropertyName propertyName, JSValue
for (; ; obj = asObject(prototype)) {
unsigned attributes;
JSCell* specificValue;
- size_t offset = obj->structure()->get(globalData, propertyName, attributes, specificValue);
- if (offset != WTF::notFound) {
+ PropertyOffset offset = obj->structure()->get(globalData, propertyName, attributes, specificValue);
+ if (offset != invalidOffset) {
if (attributes & ReadOnly) {
if (slot.isStrictMode())
throwError(exec, createTypeError(exec, StrictModeReadonlyPropertyWriteError));
diff --git a/Source/JavaScriptCore/runtime/Operations.h b/Source/JavaScriptCore/runtime/Operations.h
index b2081f3dd..497b19d82 100644
--- a/Source/JavaScriptCore/runtime/Operations.h
+++ b/Source/JavaScriptCore/runtime/Operations.h
@@ -297,7 +297,7 @@ namespace JSC {
return jsAddSlowCase(callFrame, v1, v2);
}
- inline size_t normalizePrototypeChain(CallFrame* callFrame, JSValue base, JSValue slotBase, const Identifier& propertyName, size_t& slotOffset)
+ inline size_t normalizePrototypeChain(CallFrame* callFrame, JSValue base, JSValue slotBase, const Identifier& propertyName, PropertyOffset& slotOffset)
{
JSCell* cell = base.asCell();
size_t count = 0;
diff --git a/Source/JavaScriptCore/runtime/Options.cpp b/Source/JavaScriptCore/runtime/Options.cpp
index 894ca8cc0..b5ce39c0d 100644
--- a/Source/JavaScriptCore/runtime/Options.cpp
+++ b/Source/JavaScriptCore/runtime/Options.cpp
@@ -26,9 +26,15 @@
#include "config.h"
#include "Options.h"
+#include <algorithm>
#include <limits>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <wtf/NumberOfCores.h>
#include <wtf/PageBlock.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/StringExtras.h>
#if OS(DARWIN) && ENABLE(PARALLEL_GC)
#include <sys/sysctl.h>
@@ -37,71 +43,9 @@
// Set to 1 to control the heuristics using environment variables.
#define ENABLE_RUN_TIME_HEURISTICS 0
-#if ENABLE(RUN_TIME_HEURISTICS)
-#include <stdio.h>
-#include <stdlib.h>
-#include <wtf/StdLibExtras.h>
-#endif
-
-namespace JSC { namespace Options {
-
-bool useJIT;
-
-bool showDisassembly;
-bool showDFGDisassembly;
-
-unsigned maximumOptimizationCandidateInstructionCount;
-
-unsigned maximumFunctionForCallInlineCandidateInstructionCount;
-unsigned maximumFunctionForConstructInlineCandidateInstructionCount;
-
-unsigned maximumInliningDepth;
-
-int32_t thresholdForJITAfterWarmUp;
-int32_t thresholdForJITSoon;
-
-int32_t thresholdForOptimizeAfterWarmUp;
-int32_t thresholdForOptimizeAfterLongWarmUp;
-int32_t thresholdForOptimizeSoon;
-int32_t executionCounterIncrementForLoop;
-int32_t executionCounterIncrementForReturn;
+namespace JSC {
-bool randomizeExecutionCountsBetweenCheckpoints;
-int32_t maximumExecutionCountsBetweenCheckpoints;
-
-unsigned desiredSpeculativeSuccessFailRatio;
-
-double likelyToTakeSlowCaseThreshold;
-double couldTakeSlowCaseThreshold;
-unsigned likelyToTakeSlowCaseMinimumCount;
-unsigned couldTakeSlowCaseMinimumCount;
-
-double osrExitProminenceForFrequentExitSite;
-
-unsigned largeFailCountThresholdBase;
-unsigned largeFailCountThresholdBaseForLoop;
-unsigned forcedOSRExitCountForReoptimization;
-
-unsigned reoptimizationRetryCounterMax;
-unsigned reoptimizationRetryCounterStep;
-
-unsigned minimumOptimizationDelay;
-unsigned maximumOptimizationDelay;
-double desiredProfileLivenessRate;
-double desiredProfileFullnessRate;
-
-double doubleVoteRatioForDoubleFormat;
-
-unsigned minimumNumberOfScansBetweenRebalance;
-unsigned gcMarkStackSegmentSize;
-unsigned numberOfGCMarkers;
-unsigned opaqueRootMergeThreshold;
-
-bool forceWeakRandomSeed;
-unsigned forcedWeakRandomSeed;
-
-#if ENABLE(RUN_TIME_HEURISTICS)
static bool parse(const char* string, bool& value)
{
if (!strcasecmp(string, "true") || !strcasecmp(string, "yes") || !strcmp(string, "1")) {
@@ -130,27 +74,22 @@ static bool parse(const char* string, double& value)
return sscanf(string, "%lf", &value) == 1;
}
-template<typename T, typename U>
-void setHeuristic(T& variable, const char* name, U value)
+#if ENABLE(RUN_TIME_HEURISTICS)
+template<typename T>
+void overrideOptionWithHeuristic(T& variable, const char* name)
{
const char* stringValue = getenv(name);
- if (!stringValue) {
- variable = safeCast<T>(value);
+ if (!stringValue)
return;
- }
if (parse(stringValue, variable))
return;
fprintf(stderr, "WARNING: failed to parse %s=%s\n", name, stringValue);
- variable = safeCast<T>(value);
}
-
-#define SET(variable, value) setHeuristic(variable, "JSC_" #variable, value)
-#else
-#define SET(variable, value) variable = value
#endif
+
static unsigned computeNumberOfGCMarkers(int maxNumberOfGCMarkers)
{
int cpusToUse = 1;
@@ -167,80 +106,116 @@ static unsigned computeNumberOfGCMarkers(int maxNumberOfGCMarkers)
return cpusToUse;
}
-void initializeOptions()
-{
- SET(useJIT, true);
-
- SET(showDisassembly, false);
- SET(showDFGDisassembly, false);
-
- SET(maximumOptimizationCandidateInstructionCount, 10000);
-
- SET(maximumFunctionForCallInlineCandidateInstructionCount, 180);
- SET(maximumFunctionForConstructInlineCandidateInstructionCount, 100);
-
- SET(maximumInliningDepth, 5);
+Options::Entry Options::s_options[Options::numberOfOptions];
- SET(thresholdForJITAfterWarmUp, 100);
- SET(thresholdForJITSoon, 100);
+// Realize the names for each of the options:
+const Options::EntryInfo Options::s_optionsInfo[Options::numberOfOptions] = {
+#define FOR_EACH_OPTION(type_, name_, defaultValue_) \
+ { #name_, Options::type_##Type },
+ JSC_OPTIONS(FOR_EACH_OPTION)
+#undef FOR_EACH_OPTION
+};
- SET(thresholdForOptimizeAfterWarmUp, 1000);
- SET(thresholdForOptimizeAfterLongWarmUp, 5000);
- SET(thresholdForOptimizeSoon, 1000);
-
- SET(executionCounterIncrementForLoop, 1);
- SET(executionCounterIncrementForReturn, 15);
-
- SET(randomizeExecutionCountsBetweenCheckpoints, false);
- SET(maximumExecutionCountsBetweenCheckpoints, 1000);
-
- SET(desiredSpeculativeSuccessFailRatio, 6);
-
- SET(likelyToTakeSlowCaseThreshold, 0.15);
- SET(couldTakeSlowCaseThreshold, 0.05); // Shouldn't be zero because some ops will spuriously take slow case, for example for linking or caching.
- SET(likelyToTakeSlowCaseMinimumCount, 100);
- SET(couldTakeSlowCaseMinimumCount, 10);
+void Options::initialize()
+{
+ // Initialize each of the options with their default values:
+#define FOR_EACH_OPTION(type_, name_, defaultValue_) \
+ name_() = defaultValue_;
+ JSC_OPTIONS(FOR_EACH_OPTION)
+#undef FOR_EACH_OPTION
+
+ // Allow environment vars to override options if applicable.
+ // The evn var should be the name of the option prefixed with
+ // "JSC_".
+#if ENABLE(RUN_TIME_HEURISTICS)
+#define FOR_EACH_OPTION(type_, name_, defaultValue_) \
+ overrideOptionWithHeuristic(name_(), "JSC_" #name_);
+ JSC_OPTIONS(FOR_EACH_OPTION)
+#undef FOR_EACH_OPTION
+#endif // RUN_TIME_HEURISTICS
+
+#if 0
+ ; // Deconfuse editors that do auto indentation
+#endif
- SET(osrExitProminenceForFrequentExitSite, 0.3);
-
- SET(largeFailCountThresholdBase, 20);
- SET(largeFailCountThresholdBaseForLoop, 1);
- SET(forcedOSRExitCountForReoptimization, 250);
+ // Do range checks where needed and make corrections to the options:
+ ASSERT(thresholdForOptimizeAfterLongWarmUp() >= thresholdForOptimizeAfterWarmUp());
+ ASSERT(thresholdForOptimizeAfterWarmUp() >= thresholdForOptimizeSoon());
+ ASSERT(thresholdForOptimizeAfterWarmUp() >= 0);
- SET(reoptimizationRetryCounterStep, 1);
-
- SET(minimumOptimizationDelay, 1);
- SET(maximumOptimizationDelay, 5);
- SET(desiredProfileLivenessRate, 0.75);
- SET(desiredProfileFullnessRate, 0.35);
-
- SET(doubleVoteRatioForDoubleFormat, 2);
-
- SET(minimumNumberOfScansBetweenRebalance, 100);
- SET(gcMarkStackSegmentSize, pageSize());
- SET(opaqueRootMergeThreshold, 1000);
- SET(numberOfGCMarkers, computeNumberOfGCMarkers(7)); // We don't scale so well beyond 7.
-
- ASSERT(thresholdForOptimizeAfterLongWarmUp >= thresholdForOptimizeAfterWarmUp);
- ASSERT(thresholdForOptimizeAfterWarmUp >= thresholdForOptimizeSoon);
- ASSERT(thresholdForOptimizeAfterWarmUp >= 0);
-
// Compute the maximum value of the reoptimization retry counter. This is simply
// the largest value at which we don't overflow the execute counter, when using it
// to left-shift the execution counter by this amount. Currently the value ends
// up being 18, so this loop is not so terrible; it probably takes up ~100 cycles
// total on a 32-bit processor.
- reoptimizationRetryCounterMax = 0;
- while ((static_cast<int64_t>(thresholdForOptimizeAfterLongWarmUp) << (reoptimizationRetryCounterMax + 1)) <= static_cast<int64_t>(std::numeric_limits<int32_t>::max()))
- reoptimizationRetryCounterMax++;
-
- ASSERT((static_cast<int64_t>(thresholdForOptimizeAfterLongWarmUp) << reoptimizationRetryCounterMax) > 0);
- ASSERT((static_cast<int64_t>(thresholdForOptimizeAfterLongWarmUp) << reoptimizationRetryCounterMax) <= static_cast<int64_t>(std::numeric_limits<int32_t>::max()));
-
- SET(forceWeakRandomSeed, false);
- SET(forcedWeakRandomSeed, 0);
+ reoptimizationRetryCounterMax() = 0;
+ while ((static_cast<int64_t>(thresholdForOptimizeAfterLongWarmUp()) << (reoptimizationRetryCounterMax() + 1)) <= static_cast<int64_t>(std::numeric_limits<int32>::max()))
+ reoptimizationRetryCounterMax()++;
+
+ ASSERT((static_cast<int64_t>(thresholdForOptimizeAfterLongWarmUp()) << reoptimizationRetryCounterMax()) > 0);
+ ASSERT((static_cast<int64_t>(thresholdForOptimizeAfterLongWarmUp()) << reoptimizationRetryCounterMax()) <= static_cast<int64_t>(std::numeric_limits<int32>::max()));
}
-} } // namespace JSC::Options
+// Parses a single command line option in the format "<optionName>=<value>"
+// (no spaces allowed) and set the specified option if appropriate.
+bool Options::setOption(const char* arg)
+{
+ // arg should look like this:
+ // <jscOptionName>=<appropriate value>
+ const char* equalStr = strchr(arg, '=');
+ if (!equalStr)
+ return false;
+
+ const char* valueStr = equalStr + 1;
+
+ // For each option, check if the specify arg is a match. If so, set the arg
+ // if the value makes sense. Otherwise, move on to checking the next option.
+#define FOR_EACH_OPTION(type_, name_, defaultValue_) \
+ if (!strncmp(arg, #name_, equalStr - arg)) { \
+ type_ value; \
+ bool success = parse(valueStr, value); \
+ if (success) { \
+ name_() = value; \
+ return true; \
+ } \
+ return false; \
+ }
+
+ JSC_OPTIONS(FOR_EACH_OPTION)
+#undef FOR_EACH_OPTION
+
+ return false; // No option matched.
+}
+
+void Options::dumpAllOptions(FILE* stream)
+{
+ fprintf(stream, "JSC runtime options:\n");
+ for (int id = 0; id < numberOfOptions; id++)
+ dumpOption(static_cast<OptionID>(id), stream, " ", "\n");
+}
+
+void Options::dumpOption(OptionID id, FILE* stream, const char* header, const char* footer)
+{
+ if (id >= numberOfOptions)
+ return; // Illegal option.
+
+ fprintf(stream, "%s%s: ", header, s_optionsInfo[id].name);
+ switch (s_optionsInfo[id].type) {
+ case boolType:
+ fprintf(stream, "%s", s_options[id].u.boolVal?"true":"false");
+ break;
+ case unsignedType:
+ fprintf(stream, "%u", s_options[id].u.unsignedVal);
+ break;
+ case doubleType:
+ fprintf(stream, "%lf", s_options[id].u.doubleVal);
+ break;
+ case int32Type:
+ fprintf(stream, "%d", s_options[id].u.int32Val);
+ break;
+ }
+ fprintf(stream, "%s", footer);
+}
+} // namespace JSC
diff --git a/Source/JavaScriptCore/runtime/Options.h b/Source/JavaScriptCore/runtime/Options.h
index 1bce5b944..0a55bda6b 100644
--- a/Source/JavaScriptCore/runtime/Options.h
+++ b/Source/JavaScriptCore/runtime/Options.h
@@ -26,70 +26,167 @@
#ifndef Options_h
#define Options_h
+#include "JSExportMacros.h"
#include <stdint.h>
-
-namespace JSC { namespace Options {
-
-extern bool useJIT;
-
-extern bool showDisassembly;
-extern bool showDFGDisassembly; // showDisassembly implies showDFGDisassembly.
-
-extern unsigned maximumOptimizationCandidateInstructionCount;
-
-extern unsigned maximumFunctionForCallInlineCandidateInstructionCount;
-extern unsigned maximumFunctionForConstructInlineCandidateInstructionCount;
-
-extern unsigned maximumInliningDepth; // Depth of inline stack, so 1 = no inlining, 2 = one level, etc.
-
-extern int32_t thresholdForJITAfterWarmUp;
-extern int32_t thresholdForJITSoon;
-
-extern int32_t thresholdForOptimizeAfterWarmUp;
-extern int32_t thresholdForOptimizeAfterLongWarmUp;
-extern int32_t thresholdForOptimizeSoon;
-extern int32_t thresholdForOptimizeNextInvocation;
-
-extern int32_t executionCounterIncrementForLoop;
-extern int32_t executionCounterIncrementForReturn;
-
-extern bool randomizeExecutionCountsBetweenCheckpoints;
-extern int32_t maximumExecutionCountsBetweenCheckpoints;
-
-extern unsigned desiredSpeculativeSuccessFailRatio;
-
-extern double likelyToTakeSlowCaseThreshold;
-extern double couldTakeSlowCaseThreshold;
-extern unsigned likelyToTakeSlowCaseMinimumCount;
-extern unsigned couldTakeSlowCaseMinimumCount;
-
-extern double osrExitProminenceForFrequentExitSite;
-
-extern unsigned largeFailCountThresholdBase;
-extern unsigned largeFailCountThresholdBaseForLoop;
-extern unsigned forcedOSRExitCountForReoptimization;
-
-extern unsigned reoptimizationRetryCounterMax;
-extern unsigned reoptimizationRetryCounterStep;
-
-extern unsigned minimumOptimizationDelay;
-extern unsigned maximumOptimizationDelay;
-extern double desiredProfileLivenessRate;
-extern double desiredProfileFullnessRate;
-
-extern double doubleVoteRatioForDoubleFormat;
-
-extern unsigned minimumNumberOfScansBetweenRebalance;
-extern unsigned gcMarkStackSegmentSize;
-JS_EXPORTDATA extern unsigned numberOfGCMarkers;
-JS_EXPORTDATA extern unsigned opaqueRootMergeThreshold;
-
-extern bool forceWeakRandomSeed;
-extern unsigned forcedWeakRandomSeed;
-
-void initializeOptions();
-
-} } // namespace JSC::Options
+#include <stdio.h>
+
+namespace JSC {
+
+// How do JSC VM options work?
+// ===========================
+// The JSC_OPTIONS() macro below defines a list of all JSC options in use,
+// along with their types and default values. The options values are actually
+// realized as an array of Options::Entry elements.
+//
+// Options::initialize() will initialize the array of options values with
+// the defaults specified in JSC_OPTIONS() below. After that, the values can
+// be programmatically read and written to using an accessor method with the
+// same name as the option. For example, the option "useJIT" can be read and
+// set like so:
+//
+// bool jitIsOn = Options::useJIT(); // Get the option value.
+// Options::useJIT() = false; // Sets the option value.
+//
+// If you want to tweak any of these values programmatically for testing
+// purposes, you can do so in Options::initialize() after the default values
+// are set.
+//
+// Alternatively, you can enable RUN_TIME_HEURISTICS which will allow you
+// to override the default values by specifying environment variables of the
+// form: JSC_<name of JSC option>.
+//
+// Note: Options::initialize() tries to ensure some sanity on the option values
+// which are set by doing some range checks, and value corrections. These
+// checks are done after the option values are set. If you alter the option
+// values after the sanity checks (for your own testing), then you're liable to
+// ensure that the new values set are sane and reasonable for your own run.
+
+
+#define JSC_OPTIONS(v) \
+ v(bool, useJIT, true) \
+ v(bool, useDFGJIT, true) \
+ \
+ /* showDisassembly implies showDFGDisassembly. */ \
+ v(bool, showDisassembly, false) \
+ v(bool, showDFGDisassembly, false) \
+ \
+ v(unsigned, maximumOptimizationCandidateInstructionCount, 10000) \
+ \
+ v(unsigned, maximumFunctionForCallInlineCandidateInstructionCount, 180) \
+ v(unsigned, maximumFunctionForConstructInlineCandidateInstructionCount, 100) \
+ \
+ /* Depth of inline stack, so 1 = no inlining, 2 = one level, etc. */ \
+ v(unsigned, maximumInliningDepth, 5) \
+ \
+ v(int32, thresholdForJITAfterWarmUp, 100) \
+ v(int32, thresholdForJITSoon, 100) \
+ \
+ v(int32, thresholdForOptimizeAfterWarmUp, 1000) \
+ v(int32, thresholdForOptimizeAfterLongWarmUp, 5000) \
+ v(int32, thresholdForOptimizeSoon, 1000) \
+ \
+ v(int32, executionCounterIncrementForLoop, 1) \
+ v(int32, executionCounterIncrementForReturn, 15) \
+ \
+ v(bool, randomizeExecutionCountsBetweenCheckpoints, false) \
+ v(int32, maximumExecutionCountsBetweenCheckpoints, 1000) \
+ \
+ v(double, likelyToTakeSlowCaseThreshold, 0.15) \
+ v(double, couldTakeSlowCaseThreshold, 0.05) \
+ v(unsigned, likelyToTakeSlowCaseMinimumCount, 100) \
+ v(unsigned, couldTakeSlowCaseMinimumCount, 10) \
+ \
+ v(double, osrExitProminenceForFrequentExitSite, 0.3) \
+ v(unsigned, osrExitCountForReoptimization, 100) \
+ v(unsigned, osrExitCountForReoptimizationFromLoop, 5) \
+ \
+ v(unsigned, reoptimizationRetryCounterMax, 0) \
+ v(unsigned, reoptimizationRetryCounterStep, 1) \
+ \
+ v(unsigned, minimumOptimizationDelay, 1) \
+ v(unsigned, maximumOptimizationDelay, 5) \
+ v(double, desiredProfileLivenessRate, 0.75) \
+ v(double, desiredProfileFullnessRate, 0.35) \
+ \
+ v(double, doubleVoteRatioForDoubleFormat, 2) \
+ \
+ v(unsigned, minimumNumberOfScansBetweenRebalance, 100) \
+ v(unsigned, gcMarkStackSegmentSize, pageSize()) \
+ v(unsigned, numberOfGCMarkers, computeNumberOfGCMarkers(7)) \
+ v(unsigned, opaqueRootMergeThreshold, 1000) \
+ \
+ v(bool, forceWeakRandomSeed, false) \
+ v(unsigned, forcedWeakRandomSeed, 0)
+
+
+class Options {
+public:
+ // This typedef is to allow us to eliminate the '_' in the field name in
+ // union inside Entry. This is needed to keep the style checker happy.
+ typedef int32_t int32;
+
+ // Declare the option IDs:
+ enum OptionID {
+#define FOR_EACH_OPTION(type_, name_, defaultValue_) \
+ OPT_##name_,
+ JSC_OPTIONS(FOR_EACH_OPTION)
+#undef FOR_EACH_OPTION
+ numberOfOptions
+ };
+
+
+ static void initialize();
+
+ // Parses a single command line option in the format "<optionName>=<value>"
+ // (no spaces allowed) and set the specified option if appropriate.
+ JS_EXPORT_PRIVATE static bool setOption(const char* arg);
+ JS_EXPORT_PRIVATE static void dumpAllOptions(FILE* stream = stdout);
+ static void dumpOption(OptionID id, FILE* stream = stdout, const char* header = "", const char* footer = "");
+
+ // Declare accessors for each option:
+#define FOR_EACH_OPTION(type_, name_, defaultValue_) \
+ ALWAYS_INLINE static type_& name_() { return s_options[OPT_##name_].u.type_##Val; }
+
+ JSC_OPTIONS(FOR_EACH_OPTION)
+#undef FOR_EACH_OPTION
+
+private:
+ enum EntryType {
+ boolType,
+ unsignedType,
+ doubleType,
+ int32Type
+ };
+
+ // For storing for an option value:
+ struct Entry {
+ union {
+ bool boolVal;
+ unsigned unsignedVal;
+ double doubleVal;
+ int32 int32Val;
+ } u;
+ };
+
+ // For storing constant meta data about each option:
+ struct EntryInfo {
+ const char* name;
+ EntryType type;
+ };
+
+ Options();
+
+ // Declare the options:
+#define FOR_EACH_OPTION(type_, name_, defaultValue_) \
+ type_ m_##name_;
+ JSC_OPTIONS(FOR_EACH_OPTION)
+#undef FOR_EACH_OPTION
+
+ // Declare the singleton instance of the options store:
+ JS_EXPORTDATA static Entry s_options[numberOfOptions];
+ static const EntryInfo s_optionsInfo[numberOfOptions];
+};
+
+} // namespace JSC
#endif // Options_h
-
diff --git a/Source/JavaScriptCore/runtime/PropertyMapHashTable.h b/Source/JavaScriptCore/runtime/PropertyMapHashTable.h
index c47f3476a..5953f5e87 100644
--- a/Source/JavaScriptCore/runtime/PropertyMapHashTable.h
+++ b/Source/JavaScriptCore/runtime/PropertyMapHashTable.h
@@ -21,6 +21,7 @@
#ifndef PropertyMapHashTable_h
#define PropertyMapHashTable_h
+#include "PropertyOffset.h"
#include "UString.h"
#include "WriteBarrier.h"
#include <wtf/HashTable.h>
@@ -72,11 +73,11 @@ inline unsigned nextPowerOf2(unsigned v)
struct PropertyMapEntry {
StringImpl* key;
- unsigned offset;
+ PropertyOffset offset;
unsigned attributes;
WriteBarrier<JSCell> specificValue;
- PropertyMapEntry(JSGlobalData& globalData, JSCell* owner, StringImpl* key, unsigned offset, unsigned attributes, JSCell* specificValue)
+ PropertyMapEntry(JSGlobalData& globalData, JSCell* owner, StringImpl* key, PropertyOffset offset, unsigned attributes, JSCell* specificValue)
: key(key)
, offset(offset)
, attributes(attributes)
@@ -174,8 +175,10 @@ public:
// Used to maintain a list of unused entries in the property storage.
void clearDeletedOffsets();
bool hasDeletedOffset();
- unsigned getDeletedOffset();
- void addDeletedOffset(unsigned offset);
+ PropertyOffset getDeletedOffset();
+ void addDeletedOffset(PropertyOffset);
+
+ PropertyOffset nextOffset(JSType);
// Copy this PropertyTable, ensuring the copy has at least the capacity provided.
PassOwnPtr<PropertyTable> copy(JSGlobalData&, JSCell* owner, unsigned newCapacity);
@@ -230,7 +233,7 @@ private:
unsigned* m_index;
unsigned m_keyCount;
unsigned m_deletedCount;
- OwnPtr< Vector<unsigned> > m_deletedOffsets;
+ OwnPtr< Vector<PropertyOffset> > m_deletedOffsets;
static const unsigned MinimumTableSize = 16;
static const unsigned EmptyEntryIndex = 0;
@@ -264,9 +267,9 @@ inline PropertyTable::PropertyTable(JSGlobalData&, JSCell* owner, const Property
}
// Copy the m_deletedOffsets vector.
- Vector<unsigned>* otherDeletedOffsets = other.m_deletedOffsets.get();
+ Vector<PropertyOffset>* otherDeletedOffsets = other.m_deletedOffsets.get();
if (otherDeletedOffsets)
- m_deletedOffsets = adoptPtr(new Vector<unsigned>(*otherDeletedOffsets));
+ m_deletedOffsets = adoptPtr(new Vector<PropertyOffset>(*otherDeletedOffsets));
}
inline PropertyTable::PropertyTable(JSGlobalData&, JSCell* owner, unsigned initialCapacity, const PropertyTable& other)
@@ -288,9 +291,9 @@ inline PropertyTable::PropertyTable(JSGlobalData&, JSCell* owner, unsigned initi
}
// Copy the m_deletedOffsets vector.
- Vector<unsigned>* otherDeletedOffsets = other.m_deletedOffsets.get();
+ Vector<PropertyOffset>* otherDeletedOffsets = other.m_deletedOffsets.get();
if (otherDeletedOffsets)
- m_deletedOffsets = adoptPtr(new Vector<unsigned>(*otherDeletedOffsets));
+ m_deletedOffsets = adoptPtr(new Vector<PropertyOffset>(*otherDeletedOffsets));
}
inline PropertyTable::~PropertyTable()
@@ -469,20 +472,31 @@ inline bool PropertyTable::hasDeletedOffset()
return m_deletedOffsets && !m_deletedOffsets->isEmpty();
}
-inline unsigned PropertyTable::getDeletedOffset()
+inline PropertyOffset PropertyTable::getDeletedOffset()
{
- unsigned offset = m_deletedOffsets->last();
+ PropertyOffset offset = m_deletedOffsets->last();
m_deletedOffsets->removeLast();
return offset;
}
-inline void PropertyTable::addDeletedOffset(unsigned offset)
+inline void PropertyTable::addDeletedOffset(PropertyOffset offset)
{
if (!m_deletedOffsets)
- m_deletedOffsets = adoptPtr(new Vector<unsigned>);
+ m_deletedOffsets = adoptPtr(new Vector<PropertyOffset>);
m_deletedOffsets->append(offset);
}
+inline PropertyOffset PropertyTable::nextOffset(JSType type)
+{
+ if (hasDeletedOffset())
+ return getDeletedOffset();
+
+ if (type == FinalObjectType)
+ return size();
+
+ return size() + firstOutOfLineOffset;
+}
+
inline PassOwnPtr<PropertyTable> PropertyTable::copy(JSGlobalData& globalData, JSCell* owner, unsigned newCapacity)
{
ASSERT(newCapacity >= m_keyCount);
@@ -499,7 +513,7 @@ inline size_t PropertyTable::sizeInMemory()
{
size_t result = sizeof(PropertyTable) + dataSize();
if (m_deletedOffsets)
- result += (m_deletedOffsets->capacity() * sizeof(unsigned));
+ result += (m_deletedOffsets->capacity() * sizeof(PropertyOffset));
return result;
}
#endif
diff --git a/Source/JavaScriptCore/runtime/PropertyOffset.h b/Source/JavaScriptCore/runtime/PropertyOffset.h
new file mode 100644
index 000000000..c0d1316c4
--- /dev/null
+++ b/Source/JavaScriptCore/runtime/PropertyOffset.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PropertyOffset_h
+#define PropertyOffset_h
+
+#include "JSType.h"
+#include <wtf/Platform.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/UnusedParam.h>
+
+namespace JSC {
+
+#if USE(JSVALUE32_64)
+#define INLINE_STORAGE_CAPACITY 6
+#else
+#define INLINE_STORAGE_CAPACITY 4
+#endif
+
+typedef int PropertyOffset;
+
+static const PropertyOffset invalidOffset = -1;
+static const PropertyOffset inlineStorageCapacity = INLINE_STORAGE_CAPACITY;
+static const PropertyOffset firstOutOfLineOffset = inlineStorageCapacity;
+
+// Declare all of the functions because they tend to do forward calls.
+inline void checkOffset(PropertyOffset);
+inline void checkOffset(PropertyOffset, JSType);
+inline void validateOffset(PropertyOffset);
+inline void validateOffset(PropertyOffset, JSType);
+inline bool isValidOffset(PropertyOffset);
+inline bool isInlineOffset(PropertyOffset);
+inline bool isOutOfLineOffset(PropertyOffset);
+inline size_t offsetInInlineStorage(PropertyOffset);
+inline size_t offsetInOutOfLineStorage(PropertyOffset);
+inline size_t offsetInRespectiveStorage(PropertyOffset);
+inline size_t numberOfOutOfLineSlotsForLastOffset(PropertyOffset);
+inline size_t numberOfSlotsForLastOffset(PropertyOffset, JSType);
+inline PropertyOffset nextPropertyOffsetFor(PropertyOffset, JSType);
+inline PropertyOffset firstPropertyOffsetFor(JSType);
+
+inline void checkOffset(PropertyOffset offset)
+{
+ UNUSED_PARAM(offset);
+ ASSERT(offset >= invalidOffset);
+}
+
+inline void checkOffset(PropertyOffset offset, JSType type)
+{
+ UNUSED_PARAM(offset);
+ UNUSED_PARAM(type);
+ ASSERT(offset >= invalidOffset);
+ ASSERT(offset == invalidOffset
+ || type == FinalObjectType
+ || isOutOfLineOffset(offset));
+}
+
+inline void validateOffset(PropertyOffset offset)
+{
+ checkOffset(offset);
+ ASSERT(isValidOffset(offset));
+}
+
+inline void validateOffset(PropertyOffset offset, JSType type)
+{
+ checkOffset(offset, type);
+ ASSERT(isValidOffset(offset));
+}
+
+inline bool isValidOffset(PropertyOffset offset)
+{
+ checkOffset(offset);
+ return offset != invalidOffset;
+}
+
+inline bool isInlineOffset(PropertyOffset offset)
+{
+ checkOffset(offset);
+ return offset < inlineStorageCapacity;
+}
+
+inline bool isOutOfLineOffset(PropertyOffset offset)
+{
+ checkOffset(offset);
+ return !isInlineOffset(offset);
+}
+
+inline size_t offsetInInlineStorage(PropertyOffset offset)
+{
+ validateOffset(offset);
+ ASSERT(isInlineOffset(offset));
+ return offset;
+}
+
+inline size_t offsetInOutOfLineStorage(PropertyOffset offset)
+{
+ validateOffset(offset);
+ ASSERT(isOutOfLineOffset(offset));
+ return offset - firstOutOfLineOffset;
+}
+
+inline size_t offsetInRespectiveStorage(PropertyOffset offset)
+{
+ if (isInlineOffset(offset))
+ return offsetInInlineStorage(offset);
+ return offsetInOutOfLineStorage(offset);
+}
+
+inline size_t numberOfOutOfLineSlotsForLastOffset(PropertyOffset offset)
+{
+ checkOffset(offset);
+ if (offset < firstOutOfLineOffset)
+ return 0;
+ return offset - firstOutOfLineOffset + 1;
+}
+
+inline size_t numberOfSlotsForLastOffset(PropertyOffset offset, JSType type)
+{
+ checkOffset(offset, type);
+ if (type == FinalObjectType)
+ return offset + 1;
+ return numberOfOutOfLineSlotsForLastOffset(offset);
+}
+
+inline PropertyOffset nextPropertyOffsetFor(PropertyOffset offset, JSType type)
+{
+ checkOffset(offset, type);
+ if (type != FinalObjectType && offset == invalidOffset)
+ return firstOutOfLineOffset;
+ return offset + 1;
+}
+
+inline PropertyOffset firstPropertyOffsetFor(JSType type)
+{
+ return nextPropertyOffsetFor(invalidOffset, type);
+}
+
+} // namespace JSC
+
+#endif // PropertyOffset_h
+
diff --git a/Source/JavaScriptCore/runtime/PropertySlot.h b/Source/JavaScriptCore/runtime/PropertySlot.h
index 131cf7a92..c673eaa50 100644
--- a/Source/JavaScriptCore/runtime/PropertySlot.h
+++ b/Source/JavaScriptCore/runtime/PropertySlot.h
@@ -23,6 +23,7 @@
#include "JSValue.h"
#include "PropertyName.h"
+#include "PropertyOffset.h"
#include "Register.h"
#include <wtf/Assertions.h>
#include <wtf/NotFound.h>
@@ -89,7 +90,7 @@ namespace JSC {
CachedPropertyType cachedPropertyType() const { return m_cachedPropertyType; }
bool isCacheable() const { return m_cachedPropertyType != Uncacheable; }
bool isCacheableValue() const { return m_cachedPropertyType == Value; }
- size_t cachedOffset() const
+ PropertyOffset cachedOffset() const
{
ASSERT(isCacheable());
return m_offset;
@@ -104,7 +105,7 @@ namespace JSC {
m_value = value;
}
- void setValue(JSValue slotBase, JSValue value, size_t offset)
+ void setValue(JSValue slotBase, JSValue value, PropertyOffset offset)
{
ASSERT(value);
m_getValue = JSC_VALUE_MARKER;
@@ -160,7 +161,7 @@ namespace JSC {
m_data.getterFunc = getterFunc;
}
- void setCacheableGetterSlot(JSValue slotBase, JSObject* getterFunc, unsigned offset)
+ void setCacheableGetterSlot(JSValue slotBase, JSObject* getterFunc, PropertyOffset offset)
{
ASSERT(getterFunc);
m_getValue = GETTER_FUNCTION_MARKER;
@@ -206,7 +207,7 @@ namespace JSC {
{
// Clear offset even in release builds, in case this PropertySlot has been used before.
// (For other data members, we don't need to clear anything because reuse would meaningfully overwrite them.)
- m_offset = 0;
+ m_offset = invalidOffset;
m_cachedPropertyType = Uncacheable;
}
@@ -232,7 +233,7 @@ namespace JSC {
JSValue m_value;
JSValue m_thisValue;
- size_t m_offset;
+ PropertyOffset m_offset;
CachedPropertyType m_cachedPropertyType;
};
diff --git a/Source/JavaScriptCore/runtime/PutPropertySlot.h b/Source/JavaScriptCore/runtime/PutPropertySlot.h
index 69d1f8bd2..0f694e33b 100644
--- a/Source/JavaScriptCore/runtime/PutPropertySlot.h
+++ b/Source/JavaScriptCore/runtime/PutPropertySlot.h
@@ -45,14 +45,14 @@ namespace JSC {
{
}
- void setExistingProperty(JSObject* base, size_t offset)
+ void setExistingProperty(JSObject* base, PropertyOffset offset)
{
m_type = ExistingProperty;
m_base = base;
m_offset = offset;
}
- void setNewProperty(JSObject* base, size_t offset)
+ void setNewProperty(JSObject* base, PropertyOffset offset)
{
m_type = NewProperty;
m_base = base;
@@ -64,7 +64,8 @@ namespace JSC {
bool isStrictMode() const { return m_isStrictMode; }
bool isCacheable() const { return m_type != Uncachable; }
- size_t cachedOffset() const {
+ PropertyOffset cachedOffset() const
+ {
ASSERT(isCacheable());
return m_offset;
}
@@ -72,7 +73,7 @@ namespace JSC {
private:
Type m_type;
JSObject* m_base;
- size_t m_offset;
+ PropertyOffset m_offset;
bool m_isStrictMode;
};
diff --git a/Source/JavaScriptCore/runtime/Structure.cpp b/Source/JavaScriptCore/runtime/Structure.cpp
index 569126147..509ff3d45 100644
--- a/Source/JavaScriptCore/runtime/Structure.cpp
+++ b/Source/JavaScriptCore/runtime/Structure.cpp
@@ -156,8 +156,8 @@ Structure::Structure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSV
, m_prototype(globalData, this, prototype)
, m_classInfo(classInfo)
, m_transitionWatchpointSet(InitializedWatching)
- , m_propertyStorageCapacity(typeInfo.isFinalObject() ? JSFinalObject_inlineStorageCapacity : JSNonFinalObject_inlineStorageCapacity)
- , m_offset(noOffset)
+ , m_outOfLineCapacity(0)
+ , m_offset(invalidOffset)
, m_dictionaryKind(NoneDictionaryKind)
, m_isPinnedPropertyTable(false)
, m_hasGetterSetterProperties(false)
@@ -179,8 +179,8 @@ Structure::Structure(JSGlobalData& globalData)
, m_prototype(globalData, this, jsNull())
, m_classInfo(&s_info)
, m_transitionWatchpointSet(InitializedWatching)
- , m_propertyStorageCapacity(0)
- , m_offset(noOffset)
+ , m_outOfLineCapacity(0)
+ , m_offset(invalidOffset)
, m_dictionaryKind(NoneDictionaryKind)
, m_isPinnedPropertyTable(false)
, m_hasGetterSetterProperties(false)
@@ -200,8 +200,8 @@ Structure::Structure(JSGlobalData& globalData, const Structure* previous)
, m_prototype(globalData, this, previous->storedPrototype())
, m_classInfo(previous->m_classInfo)
, m_transitionWatchpointSet(InitializedWatching)
- , m_propertyStorageCapacity(previous->m_propertyStorageCapacity)
- , m_offset(noOffset)
+ , m_outOfLineCapacity(previous->m_outOfLineCapacity)
+ , m_offset(invalidOffset)
, m_dictionaryKind(previous->m_dictionaryKind)
, m_isPinnedPropertyTable(false)
, m_hasGetterSetterProperties(previous->m_hasGetterSetterProperties)
@@ -239,7 +239,7 @@ void Structure::materializePropertyMap(JSGlobalData& globalData)
ASSERT(structure->m_propertyTable);
ASSERT(!structure->m_previous);
- m_propertyTable = structure->m_propertyTable->copy(globalData, 0, m_offset + 1);
+ m_propertyTable = structure->m_propertyTable->copy(globalData, 0, numberOfSlotsForLastOffset(m_offset, m_typeInfo.type()));
break;
}
@@ -247,7 +247,7 @@ void Structure::materializePropertyMap(JSGlobalData& globalData)
}
if (!m_propertyTable)
- createPropertyMap(m_offset + 1);
+ createPropertyMap(numberOfSlotsForLastOffset(m_offset, m_typeInfo.type()));
for (ptrdiff_t i = structures.size() - 2; i >= 0; --i) {
structure = structures[i];
@@ -256,19 +256,21 @@ void Structure::materializePropertyMap(JSGlobalData& globalData)
}
}
-void Structure::growPropertyStorageCapacity()
+inline size_t nextOutOfLineStorageCapacity(size_t currentCapacity)
{
- if (isUsingInlineStorage())
- m_propertyStorageCapacity = JSObject::baseExternalStorageCapacity;
- else
- m_propertyStorageCapacity *= 2;
+ if (!currentCapacity)
+ return 4;
+ return currentCapacity * 2;
}
-size_t Structure::suggestedNewPropertyStorageSize()
+void Structure::growOutOfLineCapacity()
{
- if (isUsingInlineStorage())
- return JSObject::baseExternalStorageCapacity;
- return m_propertyStorageCapacity * 2;
+ m_outOfLineCapacity = nextOutOfLineStorageCapacity(m_outOfLineCapacity);
+}
+
+size_t Structure::suggestedNewOutOfLineStorageCapacity()
+{
+ return nextOutOfLineStorageCapacity(m_outOfLineCapacity);
}
void Structure::despecifyDictionaryFunction(JSGlobalData& globalData, PropertyName propertyName)
@@ -285,7 +287,7 @@ void Structure::despecifyDictionaryFunction(JSGlobalData& globalData, PropertyNa
entry->specificValue.clear();
}
-Structure* Structure::addPropertyTransitionToExistingStructure(Structure* structure, PropertyName propertyName, unsigned attributes, JSCell* specificValue, size_t& offset)
+Structure* Structure::addPropertyTransitionToExistingStructure(Structure* structure, PropertyName propertyName, unsigned attributes, JSCell* specificValue, PropertyOffset& offset)
{
ASSERT(!structure->isDictionary());
ASSERT(structure->isObject());
@@ -294,7 +296,7 @@ Structure* Structure::addPropertyTransitionToExistingStructure(Structure* struct
JSCell* specificValueInPrevious = existingTransition->m_specificValueInPrevious.get();
if (specificValueInPrevious && specificValueInPrevious != specificValue)
return 0;
- ASSERT(existingTransition->m_offset != noOffset);
+ validateOffset(existingTransition->m_offset, structure->m_typeInfo.type());
offset = existingTransition->m_offset;
return existingTransition;
}
@@ -302,7 +304,7 @@ Structure* Structure::addPropertyTransitionToExistingStructure(Structure* struct
return 0;
}
-Structure* Structure::addPropertyTransition(JSGlobalData& globalData, Structure* structure, PropertyName propertyName, unsigned attributes, JSCell* specificValue, size_t& offset)
+Structure* Structure::addPropertyTransition(JSGlobalData& globalData, Structure* structure, PropertyName propertyName, unsigned attributes, JSCell* specificValue, PropertyOffset& offset)
{
// If we have a specific function, we may have got to this point if there is
// already a transition with the correct property name and attributes, but
@@ -325,8 +327,8 @@ Structure* Structure::addPropertyTransition(JSGlobalData& globalData, Structure*
Structure* transition = toCacheableDictionaryTransition(globalData, structure);
ASSERT(structure != transition);
offset = transition->putSpecificValue(globalData, propertyName, attributes, specificValue);
- if (transition->propertyStorageSize() > transition->propertyStorageCapacity())
- transition->growPropertyStorageCapacity();
+ if (transition->outOfLineSize() > transition->outOfLineCapacity())
+ transition->growOutOfLineCapacity();
return transition;
}
@@ -351,15 +353,15 @@ Structure* Structure::addPropertyTransition(JSGlobalData& globalData, Structure*
}
offset = transition->putSpecificValue(globalData, propertyName, attributes, specificValue);
- if (transition->propertyStorageSize() > transition->propertyStorageCapacity())
- transition->growPropertyStorageCapacity();
+ if (transition->outOfLineSize() > transition->outOfLineCapacity())
+ transition->growOutOfLineCapacity();
transition->m_offset = offset;
structure->m_transitionTable.add(globalData, transition);
return transition;
}
-Structure* Structure::removePropertyTransition(JSGlobalData& globalData, Structure* structure, PropertyName propertyName, size_t& offset)
+Structure* Structure::removePropertyTransition(JSGlobalData& globalData, Structure* structure, PropertyName propertyName, PropertyOffset& offset)
{
ASSERT(!structure->isUncacheableDictionary());
@@ -546,18 +548,19 @@ Structure* Structure::flattenDictionaryStructure(JSGlobalData& globalData, JSObj
size_t propertyCount = m_propertyTable->size();
Vector<JSValue> values(propertyCount);
-
+
unsigned i = 0;
+ PropertyOffset firstOffset = firstPropertyOffsetFor(m_typeInfo.type());
PropertyTable::iterator end = m_propertyTable->end();
for (PropertyTable::iterator iter = m_propertyTable->begin(); iter != end; ++iter, ++i) {
values[i] = object->getDirectOffset(iter->offset);
// Update property table to have the new property offsets
- iter->offset = i;
+ iter->offset = i + firstOffset;
}
// Copy the original property values into their final locations
for (unsigned i = 0; i < propertyCount; i++)
- object->putDirectOffset(globalData, i, values[i]);
+ object->putDirectOffset(globalData, firstOffset + i, values[i]);
m_propertyTable->clearDeletedOffsets();
}
@@ -566,7 +569,7 @@ Structure* Structure::flattenDictionaryStructure(JSGlobalData& globalData, JSObj
return this;
}
-size_t Structure::addPropertyWithoutTransition(JSGlobalData& globalData, PropertyName propertyName, unsigned attributes, JSCell* specificValue)
+PropertyOffset Structure::addPropertyWithoutTransition(JSGlobalData& globalData, PropertyName propertyName, unsigned attributes, JSCell* specificValue)
{
ASSERT(!m_enumerationCache);
@@ -577,13 +580,13 @@ size_t Structure::addPropertyWithoutTransition(JSGlobalData& globalData, Propert
pin();
- size_t offset = putSpecificValue(globalData, propertyName, attributes, specificValue);
- if (propertyStorageSize() > propertyStorageCapacity())
- growPropertyStorageCapacity();
+ PropertyOffset offset = putSpecificValue(globalData, propertyName, attributes, specificValue);
+ if (outOfLineSize() > outOfLineCapacity())
+ growOutOfLineCapacity();
return offset;
}
-size_t Structure::removePropertyWithoutTransition(JSGlobalData& globalData, PropertyName propertyName)
+PropertyOffset Structure::removePropertyWithoutTransition(JSGlobalData& globalData, PropertyName propertyName)
{
ASSERT(isUncacheableDictionary());
ASSERT(!m_enumerationCache);
@@ -591,8 +594,7 @@ size_t Structure::removePropertyWithoutTransition(JSGlobalData& globalData, Prop
materializePropertyMapIfNecessaryForPinning(globalData);
pin();
- size_t offset = remove(propertyName);
- return offset;
+ return remove(propertyName);
}
void Structure::pin()
@@ -637,20 +639,20 @@ PassOwnPtr<PropertyTable> Structure::copyPropertyTable(JSGlobalData& globalData,
PassOwnPtr<PropertyTable> Structure::copyPropertyTableForPinning(JSGlobalData& globalData, Structure* owner)
{
- return adoptPtr(m_propertyTable ? new PropertyTable(globalData, owner, *m_propertyTable) : new PropertyTable(m_offset == noOffset ? 0 : m_offset));
+ return adoptPtr(m_propertyTable ? new PropertyTable(globalData, owner, *m_propertyTable) : new PropertyTable(numberOfSlotsForLastOffset(m_offset, m_typeInfo.type())));
}
-size_t Structure::get(JSGlobalData& globalData, PropertyName propertyName, unsigned& attributes, JSCell*& specificValue)
+PropertyOffset Structure::get(JSGlobalData& globalData, PropertyName propertyName, unsigned& attributes, JSCell*& specificValue)
{
ASSERT(structure()->classInfo() == &s_info);
materializePropertyMapIfNecessary(globalData);
if (!m_propertyTable)
- return WTF::notFound;
+ return invalidOffset;
PropertyMapEntry* entry = m_propertyTable->find(propertyName.uid()).first;
if (!entry)
- return WTF::notFound;
+ return invalidOffset;
attributes = entry->attributes;
specificValue = entry->specificValue.get();
@@ -683,9 +685,9 @@ void Structure::despecifyAllFunctions(JSGlobalData& globalData)
iter->specificValue.clear();
}
-size_t Structure::putSpecificValue(JSGlobalData& globalData, PropertyName propertyName, unsigned attributes, JSCell* specificValue)
+PropertyOffset Structure::putSpecificValue(JSGlobalData& globalData, PropertyName propertyName, unsigned attributes, JSCell* specificValue)
{
- ASSERT(get(globalData, propertyName) == notFound);
+ ASSERT(!JSC::isValidOffset(get(globalData, propertyName)));
checkConsistency();
if (attributes & DontEnum)
@@ -696,12 +698,7 @@ size_t Structure::putSpecificValue(JSGlobalData& globalData, PropertyName proper
if (!m_propertyTable)
createPropertyMap();
- unsigned newOffset;
-
- if (m_propertyTable->hasDeletedOffset())
- newOffset = m_propertyTable->getDeletedOffset();
- else
- newOffset = m_propertyTable->size();
+ PropertyOffset newOffset = m_propertyTable->nextOffset(m_typeInfo.type());
m_propertyTable->add(PropertyMapEntry(globalData, this, rep, newOffset, attributes, specificValue));
@@ -709,20 +706,20 @@ size_t Structure::putSpecificValue(JSGlobalData& globalData, PropertyName proper
return newOffset;
}
-size_t Structure::remove(PropertyName propertyName)
+PropertyOffset Structure::remove(PropertyName propertyName)
{
checkConsistency();
StringImpl* rep = propertyName.uid();
if (!m_propertyTable)
- return notFound;
+ return invalidOffset;
PropertyTable::find_iterator position = m_propertyTable->find(rep);
if (!position.first)
- return notFound;
+ return invalidOffset;
- size_t offset = position.first->offset;
+ PropertyOffset offset = position.first->offset;
m_propertyTable->remove(position);
m_propertyTable->addDeletedOffset(offset);
diff --git a/Source/JavaScriptCore/runtime/Structure.h b/Source/JavaScriptCore/runtime/Structure.h
index 448a81c27..d2d025b98 100644
--- a/Source/JavaScriptCore/runtime/Structure.h
+++ b/Source/JavaScriptCore/runtime/Structure.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2009, 2012 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -87,9 +87,9 @@ namespace JSC {
public:
static void dumpStatistics();
- JS_EXPORT_PRIVATE static Structure* addPropertyTransition(JSGlobalData&, Structure*, PropertyName, unsigned attributes, JSCell* specificValue, size_t& offset);
- JS_EXPORT_PRIVATE static Structure* addPropertyTransitionToExistingStructure(Structure*, PropertyName, unsigned attributes, JSCell* specificValue, size_t& offset);
- static Structure* removePropertyTransition(JSGlobalData&, Structure*, PropertyName, size_t& offset);
+ JS_EXPORT_PRIVATE static Structure* addPropertyTransition(JSGlobalData&, Structure*, PropertyName, unsigned attributes, JSCell* specificValue, PropertyOffset&);
+ JS_EXPORT_PRIVATE static Structure* addPropertyTransitionToExistingStructure(Structure*, PropertyName, unsigned attributes, JSCell* specificValue, PropertyOffset&);
+ static Structure* removePropertyTransition(JSGlobalData&, Structure*, PropertyName, PropertyOffset&);
JS_EXPORT_PRIVATE static Structure* changePrototypeTransition(JSGlobalData&, Structure*, JSValue prototype);
JS_EXPORT_PRIVATE static Structure* despecifyFunctionTransition(JSGlobalData&, Structure*, PropertyName);
static Structure* attributeChangeTransition(JSGlobalData&, Structure*, PropertyName, unsigned attributes);
@@ -103,16 +103,32 @@ namespace JSC {
bool isFrozen(JSGlobalData&);
bool isExtensible() const { return !m_preventExtensions; }
bool didTransition() const { return m_didTransition; }
- bool shouldGrowPropertyStorage() { return propertyStorageCapacity() == propertyStorageSize(); }
- JS_EXPORT_PRIVATE size_t suggestedNewPropertyStorageSize();
+ bool putWillGrowOutOfLineStorage()
+ {
+ ASSERT(outOfLineCapacity() >= outOfLineSize());
+
+ if (!m_propertyTable) {
+ unsigned currentSize = numberOfOutOfLineSlotsForLastOffset(m_offset);
+ ASSERT(outOfLineCapacity() >= currentSize);
+ return currentSize == outOfLineCapacity();
+ }
+
+ ASSERT(totalStorageCapacity() >= m_propertyTable->propertyStorageSize());
+ if (m_propertyTable->hasDeletedOffset())
+ return false;
+
+ ASSERT(totalStorageCapacity() >= m_propertyTable->size());
+ return m_propertyTable->size() == totalStorageCapacity();
+ }
+ JS_EXPORT_PRIVATE size_t suggestedNewOutOfLineStorageCapacity();
Structure* flattenDictionaryStructure(JSGlobalData&, JSObject*);
static void destroy(JSCell*);
// These should be used with caution.
- JS_EXPORT_PRIVATE size_t addPropertyWithoutTransition(JSGlobalData&, PropertyName, unsigned attributes, JSCell* specificValue);
- size_t removePropertyWithoutTransition(JSGlobalData&, PropertyName);
+ JS_EXPORT_PRIVATE PropertyOffset addPropertyWithoutTransition(JSGlobalData&, PropertyName, unsigned attributes, JSCell* specificValue);
+ PropertyOffset removePropertyWithoutTransition(JSGlobalData&, PropertyName);
void setPrototypeWithoutTransition(JSGlobalData& globalData, JSValue prototype) { m_prototype.set(globalData, this, prototype); }
bool isDictionary() const { return m_dictionaryKind != NoneDictionaryKind; }
@@ -133,17 +149,114 @@ namespace JSC {
StructureChain* prototypeChain(ExecState*) const;
static void visitChildren(JSCell*, SlotVisitor&);
- Structure* previousID() const { ASSERT(structure()->classInfo() == &s_info); return m_previous.get(); }
+ Structure* previousID() const
+ {
+ ASSERT(structure()->classInfo() == &s_info);
+ return m_previous.get();
+ }
bool transitivelyTransitionedFrom(Structure* structureToFind);
- void growPropertyStorageCapacity();
- unsigned propertyStorageCapacity() const { ASSERT(structure()->classInfo() == &s_info); return m_propertyStorageCapacity; }
- unsigned propertyStorageSize() const { ASSERT(structure()->classInfo() == &s_info); return (m_propertyTable ? m_propertyTable->propertyStorageSize() : static_cast<unsigned>(m_offset + 1)); }
- bool isUsingInlineStorage() const;
+ void growOutOfLineCapacity();
+ unsigned outOfLineCapacity() const
+ {
+ ASSERT(structure()->classInfo() == &s_info);
+ return m_outOfLineCapacity;
+ }
+ unsigned outOfLineSizeForKnownFinalObject() const
+ {
+ ASSERT(m_typeInfo.type() == FinalObjectType);
+ if (m_propertyTable) {
+ unsigned totalSize = m_propertyTable->propertyStorageSize();
+ if (totalSize < static_cast<unsigned>(inlineStorageCapacity))
+ return 0;
+ return totalSize - inlineStorageCapacity;
+ }
+ return numberOfOutOfLineSlotsForLastOffset(m_offset);
+ }
+ unsigned outOfLineSizeForKnownNonFinalObject() const
+ {
+ ASSERT(m_typeInfo.type() != FinalObjectType);
+ if (m_propertyTable)
+ return m_propertyTable->propertyStorageSize();
+ return numberOfOutOfLineSlotsForLastOffset(m_offset);
+ }
+ unsigned outOfLineSize() const
+ {
+ ASSERT(structure()->classInfo() == &s_info);
+ if (m_propertyTable) {
+ unsigned totalSize = m_propertyTable->propertyStorageSize();
+ unsigned inlineCapacity = this->inlineCapacity();
+ if (totalSize < inlineCapacity)
+ return 0;
+ return totalSize - inlineCapacity;
+ }
+ return numberOfOutOfLineSlotsForLastOffset(m_offset);
+ }
+ bool hasInlineStorage() const
+ {
+ return m_typeInfo.type() == FinalObjectType;
+ }
+ unsigned inlineCapacity() const
+ {
+ if (hasInlineStorage())
+ return inlineStorageCapacity;
+ return 0;
+ }
+ unsigned inlineSizeForKnownFinalObject() const
+ {
+ ASSERT(m_typeInfo.type() == FinalObjectType);
+ unsigned result;
+ if (m_propertyTable)
+ result = m_propertyTable->propertyStorageSize();
+ else
+ result = m_offset + 1;
+ if (result > static_cast<unsigned>(inlineStorageCapacity))
+ return inlineStorageCapacity;
+ return result;
+ }
+ unsigned inlineSize() const
+ {
+ if (!hasInlineStorage())
+ return 0;
+ return inlineSizeForKnownFinalObject();
+ }
+ unsigned totalStorageSize() const
+ {
+ if (m_propertyTable)
+ return m_propertyTable->propertyStorageSize();
+ return numberOfSlotsForLastOffset(m_offset, m_typeInfo.type());
+ }
+ unsigned totalStorageCapacity() const
+ {
+ ASSERT(structure()->classInfo() == &s_info);
+ return m_outOfLineCapacity + inlineCapacity();
+ }
+
+ PropertyOffset firstValidOffset() const
+ {
+ if (hasInlineStorage())
+ return 0;
+ return inlineStorageCapacity;
+ }
+ PropertyOffset lastValidOffset() const
+ {
+ if (m_propertyTable) {
+ PropertyOffset size = m_propertyTable->propertyStorageSize();
+ if (!hasInlineStorage())
+ size += inlineStorageCapacity;
+ return size - 1;
+ }
+ return m_offset;
+ }
+ bool isValidOffset(PropertyOffset offset) const
+ {
+ return offset >= firstValidOffset()
+ && offset <= lastValidOffset();
+ }
- size_t get(JSGlobalData&, PropertyName);
- size_t get(JSGlobalData&, const UString& name);
- JS_EXPORT_PRIVATE size_t get(JSGlobalData&, PropertyName, unsigned& attributes, JSCell*& specificValue);
+ PropertyOffset get(JSGlobalData&, PropertyName);
+ PropertyOffset get(JSGlobalData&, const UString& name);
+ JS_EXPORT_PRIVATE PropertyOffset get(JSGlobalData&, PropertyName, unsigned& attributes, JSCell*& specificValue);
bool hasGetterSetterProperties() const { return m_hasGetterSetterProperties; }
bool hasReadOnlyOrGetterSetterPropertiesExcludingProto() const { return m_hasReadOnlyOrGetterSetterPropertiesExcludingProto; }
@@ -160,7 +273,12 @@ namespace JSC {
bool hasNonEnumerableProperties() const { return m_hasNonEnumerableProperties; }
- bool isEmpty() const { return m_propertyTable ? m_propertyTable->isEmpty() : m_offset == noOffset; }
+ bool isEmpty() const
+ {
+ if (m_propertyTable)
+ return m_propertyTable->isEmpty();
+ return !JSC::isValidOffset(m_offset);
+ }
JS_EXPORT_PRIVATE void despecifyDictionaryFunction(JSGlobalData&, PropertyName);
void disableSpecificFunctionTracking() { m_specificFunctionThrashCount = maxSpecificFunctionThrashCount; }
@@ -256,8 +374,8 @@ namespace JSC {
} DictionaryKind;
static Structure* toDictionaryTransition(JSGlobalData&, Structure*, DictionaryKind);
- size_t putSpecificValue(JSGlobalData&, PropertyName, unsigned attributes, JSCell* specificValue);
- size_t remove(PropertyName);
+ PropertyOffset putSpecificValue(JSGlobalData&, PropertyName, unsigned attributes, JSCell* specificValue);
+ PropertyOffset remove(PropertyName);
void createPropertyMap(unsigned keyCount = 0);
void checkConsistency();
@@ -284,7 +402,7 @@ namespace JSC {
int transitionCount() const
{
// Since the number of transitions is always the same as m_offset, we keep the size of Structure down by not storing both.
- return m_offset == noOffset ? 0 : m_offset + 1;
+ return numberOfSlotsForLastOffset(m_offset, m_typeInfo.type());
}
bool isValid(ExecState*, StructureChain* cachedPrototypeChain) const;
@@ -293,8 +411,6 @@ namespace JSC {
static const int s_maxTransitionLength = 64;
- static const int noOffset = -1;
-
static const unsigned maxSpecificFunctionThrashCount = 3;
TypeInfo m_typeInfo;
@@ -319,10 +435,10 @@ namespace JSC {
mutable InlineWatchpointSet m_transitionWatchpointSet;
- uint32_t m_propertyStorageCapacity;
+ uint32_t m_outOfLineCapacity;
// m_offset does not account for anonymous slots
- int m_offset;
+ PropertyOffset m_offset;
unsigned m_dictionaryKind : 2;
bool m_isPinnedPropertyTable : 1;
@@ -336,26 +452,26 @@ namespace JSC {
unsigned m_staticFunctionReified;
};
- inline size_t Structure::get(JSGlobalData& globalData, PropertyName propertyName)
+ inline PropertyOffset Structure::get(JSGlobalData& globalData, PropertyName propertyName)
{
ASSERT(structure()->classInfo() == &s_info);
materializePropertyMapIfNecessary(globalData);
if (!m_propertyTable)
- return notFound;
+ return invalidOffset;
PropertyMapEntry* entry = m_propertyTable->find(propertyName.uid()).first;
- return entry ? entry->offset : notFound;
+ return entry ? entry->offset : invalidOffset;
}
- inline size_t Structure::get(JSGlobalData& globalData, const UString& name)
+ inline PropertyOffset Structure::get(JSGlobalData& globalData, const UString& name)
{
ASSERT(structure()->classInfo() == &s_info);
materializePropertyMapIfNecessary(globalData);
if (!m_propertyTable)
- return notFound;
+ return invalidOffset;
PropertyMapEntry* entry = m_propertyTable->findWithString(name.impl()).first;
- return entry ? entry->offset : notFound;
+ return entry ? entry->offset : invalidOffset;
}
inline JSValue JSValue::structureOrUndefined() const
diff --git a/Source/JavaScriptCore/runtime/WeakGCMap.h b/Source/JavaScriptCore/runtime/WeakGCMap.h
index 9e8db4d60..6926165a7 100644
--- a/Source/JavaScriptCore/runtime/WeakGCMap.h
+++ b/Source/JavaScriptCore/runtime/WeakGCMap.h
@@ -75,8 +75,9 @@ public:
return HandleTypes<MappedType>::getFromSlot(const_cast<JSValue*>(&impl->jsValue()));
}
- void set(JSGlobalData&, const KeyType& key, ExternalType value)
+ void set(JSGlobalData& globalData, const KeyType& key, ExternalType value)
{
+ ASSERT_UNUSED(globalData, globalData.apiLock().currentThreadIsHoldingLock());
typename MapType::AddResult result = m_map.add(key, 0);
if (!result.isNewEntry)
WeakSet::deallocate(result.iterator->second);