diff options
Diffstat (limited to 'Source/JavaScriptCore/runtime')
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); |