| /* |
| * Copyright (C) 2008 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. |
| * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of |
| * its contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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. |
| */ |
| |
| #include "config.h" |
| #include "JSGlobalData.h" |
| |
| #include "ArgList.h" |
| #include "Heap.h" |
| #include "CommonIdentifiers.h" |
| #include "FunctionConstructor.h" |
| #include "GetterSetter.h" |
| #include "Interpreter.h" |
| #include "JSActivation.h" |
| #include "JSAPIValueWrapper.h" |
| #include "JSArray.h" |
| #include "JSByteArray.h" |
| #include "JSClassRef.h" |
| #include "JSFunction.h" |
| #include "JSLock.h" |
| #include "JSNotAnObject.h" |
| #include "JSPropertyNameIterator.h" |
| #include "JSStaticScopeObject.h" |
| #include "JSZombie.h" |
| #include "Lexer.h" |
| #include "Lookup.h" |
| #include "Nodes.h" |
| #include "Parser.h" |
| #include "RegExpCache.h" |
| #include "StrictEvalActivation.h" |
| #include <wtf/WTFThreadData.h> |
| #if ENABLE(REGEXP_TRACING) |
| #include "RegExp.h" |
| #endif |
| |
| |
| #if ENABLE(JSC_MULTIPLE_THREADS) |
| #include <wtf/Threading.h> |
| #endif |
| |
| #if PLATFORM(MAC) |
| #include "ProfilerServer.h" |
| #include <CoreFoundation/CoreFoundation.h> |
| #endif |
| |
| using namespace WTF; |
| |
| namespace { |
| |
| using namespace JSC; |
| |
| class Recompiler { |
| public: |
| void operator()(JSCell*); |
| }; |
| |
| inline void Recompiler::operator()(JSCell* cell) |
| { |
| if (!cell->inherits(&JSFunction::s_info)) |
| return; |
| JSFunction* function = asFunction(cell); |
| if (function->executable()->isHostFunction()) |
| return; |
| function->jsExecutable()->discardCode(); |
| } |
| |
| } // namespace |
| |
| namespace JSC { |
| |
| extern JSC_CONST_HASHTABLE HashTable arrayTable; |
| extern JSC_CONST_HASHTABLE HashTable jsonTable; |
| extern JSC_CONST_HASHTABLE HashTable dateTable; |
| extern JSC_CONST_HASHTABLE HashTable mathTable; |
| extern JSC_CONST_HASHTABLE HashTable numberTable; |
| extern JSC_CONST_HASHTABLE HashTable objectConstructorTable; |
| extern JSC_CONST_HASHTABLE HashTable regExpTable; |
| extern JSC_CONST_HASHTABLE HashTable regExpConstructorTable; |
| extern JSC_CONST_HASHTABLE HashTable stringTable; |
| |
| void* JSGlobalData::jsArrayVPtr; |
| void* JSGlobalData::jsByteArrayVPtr; |
| void* JSGlobalData::jsStringVPtr; |
| void* JSGlobalData::jsFunctionVPtr; |
| |
| #if COMPILER(GCC) |
| // Work around for gcc trying to coalesce our reads of the various cell vptrs |
| #define CLOBBER_MEMORY() do { \ |
| asm volatile ("" : : : "memory"); \ |
| } while (false) |
| #else |
| #define CLOBBER_MEMORY() do { } while (false) |
| #endif |
| |
| void JSGlobalData::storeVPtrs() |
| { |
| // Enough storage to fit a JSArray, JSByteArray, JSString, or JSFunction. |
| // COMPILE_ASSERTS below check that this is true. |
| char storage[64]; |
| |
| COMPILE_ASSERT(sizeof(JSArray) <= sizeof(storage), sizeof_JSArray_must_be_less_than_storage); |
| JSCell* jsArray = new (storage) JSArray(JSArray::VPtrStealingHack); |
| CLOBBER_MEMORY(); |
| JSGlobalData::jsArrayVPtr = jsArray->vptr(); |
| |
| COMPILE_ASSERT(sizeof(JSByteArray) <= sizeof(storage), sizeof_JSByteArray_must_be_less_than_storage); |
| JSCell* jsByteArray = new (storage) JSByteArray(JSByteArray::VPtrStealingHack); |
| CLOBBER_MEMORY(); |
| JSGlobalData::jsByteArrayVPtr = jsByteArray->vptr(); |
| |
| COMPILE_ASSERT(sizeof(JSString) <= sizeof(storage), sizeof_JSString_must_be_less_than_storage); |
| JSCell* jsString = new (storage) JSString(JSString::VPtrStealingHack); |
| CLOBBER_MEMORY(); |
| JSGlobalData::jsStringVPtr = jsString->vptr(); |
| |
| COMPILE_ASSERT(sizeof(JSFunction) <= sizeof(storage), sizeof_JSFunction_must_be_less_than_storage); |
| JSCell* jsFunction = new (storage) JSFunction(JSCell::VPtrStealingHack); |
| CLOBBER_MEMORY(); |
| JSGlobalData::jsFunctionVPtr = jsFunction->vptr(); |
| } |
| |
| JSGlobalData::JSGlobalData(GlobalDataType globalDataType, ThreadStackType threadStackType) |
| : globalDataType(globalDataType) |
| , clientData(0) |
| , arrayTable(fastNew<HashTable>(JSC::arrayTable)) |
| , dateTable(fastNew<HashTable>(JSC::dateTable)) |
| , jsonTable(fastNew<HashTable>(JSC::jsonTable)) |
| , mathTable(fastNew<HashTable>(JSC::mathTable)) |
| , numberTable(fastNew<HashTable>(JSC::numberTable)) |
| , objectConstructorTable(fastNew<HashTable>(JSC::objectConstructorTable)) |
| , regExpTable(fastNew<HashTable>(JSC::regExpTable)) |
| , regExpConstructorTable(fastNew<HashTable>(JSC::regExpConstructorTable)) |
| , stringTable(fastNew<HashTable>(JSC::stringTable)) |
| , identifierTable(globalDataType == Default ? wtfThreadData().currentIdentifierTable() : createIdentifierTable()) |
| , propertyNames(new CommonIdentifiers(this)) |
| , emptyList(new MarkedArgumentBuffer) |
| , lexer(new Lexer(this)) |
| , parser(new Parser) |
| , interpreter(0) |
| , heap(this) |
| , globalObjectCount(0) |
| , dynamicGlobalObject(0) |
| , cachedUTCOffset(NaN) |
| , maxReentryDepth(threadStackType == ThreadStackTypeSmall ? MaxSmallThreadReentryDepth : MaxLargeThreadReentryDepth) |
| , m_regExpCache(new RegExpCache(this)) |
| #if ENABLE(REGEXP_TRACING) |
| , m_rtTraceList(new RTTraceList()) |
| #endif |
| #ifndef NDEBUG |
| , exclusiveThread(0) |
| #endif |
| { |
| interpreter = new Interpreter(*this); |
| if (globalDataType == Default) |
| m_stack = wtfThreadData().stack(); |
| |
| // Need to be careful to keep everything consistent here |
| IdentifierTable* existingEntryIdentifierTable = wtfThreadData().setCurrentIdentifierTable(identifierTable); |
| JSLock lock(SilenceAssertionsOnly); |
| structureStructure.set(*this, Structure::createStructure(*this)); |
| activationStructure.set(*this, JSActivation::createStructure(*this, jsNull())); |
| interruptedExecutionErrorStructure.set(*this, JSNonFinalObject::createStructure(*this, jsNull())); |
| terminatedExecutionErrorStructure.set(*this, JSNonFinalObject::createStructure(*this, jsNull())); |
| staticScopeStructure.set(*this, JSStaticScopeObject::createStructure(*this, jsNull())); |
| strictEvalActivationStructure.set(*this, StrictEvalActivation::createStructure(*this, jsNull())); |
| stringStructure.set(*this, JSString::createStructure(*this, jsNull())); |
| notAnObjectStructure.set(*this, JSNotAnObject::createStructure(*this, jsNull())); |
| propertyNameIteratorStructure.set(*this, JSPropertyNameIterator::createStructure(*this, jsNull())); |
| getterSetterStructure.set(*this, GetterSetter::createStructure(*this, jsNull())); |
| apiWrapperStructure.set(*this, JSAPIValueWrapper::createStructure(*this, jsNull())); |
| scopeChainNodeStructure.set(*this, ScopeChainNode::createStructure(*this, jsNull())); |
| executableStructure.set(*this, ExecutableBase::createStructure(*this, jsNull())); |
| nativeExecutableStructure.set(*this, NativeExecutable::createStructure(*this, jsNull())); |
| evalExecutableStructure.set(*this, EvalExecutable::createStructure(*this, jsNull())); |
| programExecutableStructure.set(*this, ProgramExecutable::createStructure(*this, jsNull())); |
| functionExecutableStructure.set(*this, FunctionExecutable::createStructure(*this, jsNull())); |
| dummyMarkableCellStructure.set(*this, JSCell::createDummyStructure(*this)); |
| structureChainStructure.set(*this, StructureChain::createStructure(*this, jsNull())); |
| |
| #if ENABLE(JSC_ZOMBIES) |
| zombieStructure.set(*this, JSZombie::createStructure(*this, jsNull())); |
| #endif |
| |
| wtfThreadData().setCurrentIdentifierTable(existingEntryIdentifierTable); |
| |
| #if PLATFORM(MAC) |
| startProfilerServerIfNeeded(); |
| #endif |
| #if ENABLE(JIT) && ENABLE(INTERPRETER) |
| #if USE(CF) |
| CFStringRef canUseJITKey = CFStringCreateWithCString(0 , "JavaScriptCoreUseJIT", kCFStringEncodingMacRoman); |
| CFBooleanRef canUseJIT = (CFBooleanRef)CFPreferencesCopyAppValue(canUseJITKey, kCFPreferencesCurrentApplication); |
| if (canUseJIT) { |
| m_canUseJIT = kCFBooleanTrue == canUseJIT; |
| CFRelease(canUseJIT); |
| } else { |
| char* canUseJITString = getenv("JavaScriptCoreUseJIT"); |
| m_canUseJIT = !canUseJITString || atoi(canUseJITString); |
| } |
| CFRelease(canUseJITKey); |
| #elif OS(UNIX) |
| char* canUseJITString = getenv("JavaScriptCoreUseJIT"); |
| m_canUseJIT = !canUseJITString || atoi(canUseJITString); |
| #else |
| m_canUseJIT = true; |
| #endif |
| #endif |
| #if ENABLE(JIT) |
| #if ENABLE(INTERPRETER) |
| if (m_canUseJIT) |
| m_canUseJIT = executableAllocator.isValid(); |
| #endif |
| jitStubs = new JITThunks(this); |
| #endif |
| } |
| |
| void JSGlobalData::clearBuiltinStructures() |
| { |
| structureStructure.clear(); |
| activationStructure.clear(); |
| interruptedExecutionErrorStructure.clear(); |
| terminatedExecutionErrorStructure.clear(); |
| staticScopeStructure.clear(); |
| strictEvalActivationStructure.clear(); |
| stringStructure.clear(); |
| notAnObjectStructure.clear(); |
| propertyNameIteratorStructure.clear(); |
| getterSetterStructure.clear(); |
| apiWrapperStructure.clear(); |
| scopeChainNodeStructure.clear(); |
| executableStructure.clear(); |
| nativeExecutableStructure.clear(); |
| evalExecutableStructure.clear(); |
| programExecutableStructure.clear(); |
| functionExecutableStructure.clear(); |
| dummyMarkableCellStructure.clear(); |
| structureChainStructure.clear(); |
| |
| #if ENABLE(JSC_ZOMBIES) |
| zombieStructure.clear(); |
| #endif |
| } |
| |
| JSGlobalData::~JSGlobalData() |
| { |
| // By the time this is destroyed, heap.destroy() must already have been called. |
| |
| delete interpreter; |
| #ifndef NDEBUG |
| // Zeroing out to make the behavior more predictable when someone attempts to use a deleted instance. |
| interpreter = 0; |
| #endif |
| |
| arrayTable->deleteTable(); |
| dateTable->deleteTable(); |
| jsonTable->deleteTable(); |
| mathTable->deleteTable(); |
| numberTable->deleteTable(); |
| objectConstructorTable->deleteTable(); |
| regExpTable->deleteTable(); |
| regExpConstructorTable->deleteTable(); |
| stringTable->deleteTable(); |
| |
| fastDelete(const_cast<HashTable*>(arrayTable)); |
| fastDelete(const_cast<HashTable*>(dateTable)); |
| fastDelete(const_cast<HashTable*>(jsonTable)); |
| fastDelete(const_cast<HashTable*>(mathTable)); |
| fastDelete(const_cast<HashTable*>(numberTable)); |
| fastDelete(const_cast<HashTable*>(objectConstructorTable)); |
| fastDelete(const_cast<HashTable*>(regExpTable)); |
| fastDelete(const_cast<HashTable*>(regExpConstructorTable)); |
| fastDelete(const_cast<HashTable*>(stringTable)); |
| |
| delete parser; |
| delete lexer; |
| |
| deleteAllValues(opaqueJSClassData); |
| |
| delete emptyList; |
| |
| delete propertyNames; |
| if (globalDataType != Default) |
| deleteIdentifierTable(identifierTable); |
| |
| delete clientData; |
| delete m_regExpCache; |
| #if ENABLE(REGEXP_TRACING) |
| delete m_rtTraceList; |
| #endif |
| } |
| |
| PassRefPtr<JSGlobalData> JSGlobalData::createContextGroup(ThreadStackType type) |
| { |
| return adoptRef(new JSGlobalData(APIContextGroup, type)); |
| } |
| |
| PassRefPtr<JSGlobalData> JSGlobalData::create(ThreadStackType type) |
| { |
| return adoptRef(new JSGlobalData(Default, type)); |
| } |
| |
| PassRefPtr<JSGlobalData> JSGlobalData::createLeaked(ThreadStackType type) |
| { |
| return create(type); |
| } |
| |
| bool JSGlobalData::sharedInstanceExists() |
| { |
| return sharedInstanceInternal(); |
| } |
| |
| JSGlobalData& JSGlobalData::sharedInstance() |
| { |
| JSGlobalData*& instance = sharedInstanceInternal(); |
| if (!instance) { |
| instance = adoptRef(new JSGlobalData(APIShared, ThreadStackTypeSmall)).leakRef(); |
| #if ENABLE(JSC_MULTIPLE_THREADS) |
| instance->makeUsableFromMultipleThreads(); |
| #endif |
| } |
| return *instance; |
| } |
| |
| JSGlobalData*& JSGlobalData::sharedInstanceInternal() |
| { |
| ASSERT(JSLock::currentThreadIsHoldingLock()); |
| static JSGlobalData* sharedInstance; |
| return sharedInstance; |
| } |
| |
| #if ENABLE(JIT) |
| NativeExecutable* JSGlobalData::getHostFunction(NativeFunction function) |
| { |
| return jitStubs->hostFunctionStub(this, function); |
| } |
| NativeExecutable* JSGlobalData::getHostFunction(NativeFunction function, ThunkGenerator generator) |
| { |
| return jitStubs->hostFunctionStub(this, function, generator); |
| } |
| #else |
| NativeExecutable* JSGlobalData::getHostFunction(NativeFunction function) |
| { |
| return NativeExecutable::create(*this, function, callHostFunctionAsConstructor); |
| } |
| #endif |
| |
| JSGlobalData::ClientData::~ClientData() |
| { |
| } |
| |
| void JSGlobalData::resetDateCache() |
| { |
| cachedUTCOffset = NaN; |
| dstOffsetCache.reset(); |
| cachedDateString = UString(); |
| cachedDateStringValue = NaN; |
| dateInstanceCache.reset(); |
| } |
| |
| void JSGlobalData::startSampling() |
| { |
| interpreter->startSampling(); |
| } |
| |
| void JSGlobalData::stopSampling() |
| { |
| interpreter->stopSampling(); |
| } |
| |
| void JSGlobalData::dumpSampleData(ExecState* exec) |
| { |
| interpreter->dumpSampleData(exec); |
| } |
| |
| void JSGlobalData::recompileAllJSFunctions() |
| { |
| // If JavaScript is running, it's not safe to recompile, since we'll end |
| // up throwing away code that is live on the stack. |
| ASSERT(!dynamicGlobalObject); |
| |
| Recompiler recompiler; |
| heap.forEach(recompiler); |
| } |
| |
| #if ENABLE(REGEXP_TRACING) |
| void JSGlobalData::addRegExpToTrace(PassRefPtr<RegExp> regExp) |
| { |
| m_rtTraceList->add(regExp); |
| } |
| |
| void JSGlobalData::dumpRegExpTrace() |
| { |
| // The first RegExp object is ignored. It is create by the RegExpPrototype ctor and not used. |
| RTTraceList::iterator iter = ++m_rtTraceList->begin(); |
| |
| if (iter != m_rtTraceList->end()) { |
| printf("\nRegExp Tracing\n"); |
| printf(" match() matches\n"); |
| printf("Regular Expression JIT Address calls found\n"); |
| printf("----------------------------------------+----------------+----------+----------\n"); |
| |
| unsigned reCount = 0; |
| |
| for (; iter != m_rtTraceList->end(); ++iter, ++reCount) |
| (*iter)->printTraceData(); |
| |
| printf("%d Regular Expressions\n", reCount); |
| } |
| |
| m_rtTraceList->clear(); |
| } |
| #else |
| void JSGlobalData::dumpRegExpTrace() |
| { |
| } |
| #endif |
| |
| } // namespace JSC |