/* * Copyright (C) 2008, 2011 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 "DebuggerActivation.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 "Lexer.h" #include "Lookup.h" #include "Nodes.h" #include "ParserArena.h" #include "RegExpCache.h" #include "RegExpObject.h" #include "StrictEvalActivation.h" #include "StrongInlines.h" #include #include #if ENABLE(REGEXP_TRACING) #include "RegExp.h" #endif #if PLATFORM(MAC) #include #endif using namespace WTF; namespace { using namespace JSC; class Recompiler : public MarkedBlock::VoidFunctor { public: void operator()(JSCell*); }; inline void Recompiler::operator()(JSCell* cell) { if (!cell->inherits(&JSFunction::s_info)) return; JSFunction* function = asFunction(cell); if (!function->executable() || function->executable()->isHostFunction()) return; function->jsExecutable()->discardCode(); } } // namespace namespace JSC { extern const HashTable arrayConstructorTable; extern const HashTable arrayPrototypeTable; extern const HashTable booleanPrototypeTable; extern const HashTable jsonTable; extern const HashTable dateTable; extern const HashTable dateConstructorTable; extern const HashTable errorPrototypeTable; extern const HashTable globalObjectTable; extern const HashTable mathTable; extern const HashTable numberConstructorTable; extern const HashTable numberPrototypeTable; extern const HashTable objectConstructorTable; extern const HashTable objectPrototypeTable; extern const HashTable regExpTable; extern const HashTable regExpConstructorTable; extern const HashTable regExpPrototypeTable; extern const HashTable stringTable; extern const HashTable stringConstructorTable; JSGlobalData::JSGlobalData(GlobalDataType globalDataType, ThreadStackType threadStackType, HeapSize heapSize) : globalDataType(globalDataType) , clientData(0) , topCallFrame(CallFrame::noCaller()) , arrayConstructorTable(fastNew(JSC::arrayConstructorTable)) , arrayPrototypeTable(fastNew(JSC::arrayPrototypeTable)) , booleanPrototypeTable(fastNew(JSC::booleanPrototypeTable)) , dateTable(fastNew(JSC::dateTable)) , dateConstructorTable(fastNew(JSC::dateConstructorTable)) , errorPrototypeTable(fastNew(JSC::errorPrototypeTable)) , globalObjectTable(fastNew(JSC::globalObjectTable)) , jsonTable(fastNew(JSC::jsonTable)) , mathTable(fastNew(JSC::mathTable)) , numberConstructorTable(fastNew(JSC::numberConstructorTable)) , numberPrototypeTable(fastNew(JSC::numberPrototypeTable)) , objectConstructorTable(fastNew(JSC::objectConstructorTable)) , objectPrototypeTable(fastNew(JSC::objectPrototypeTable)) , regExpTable(fastNew(JSC::regExpTable)) , regExpConstructorTable(fastNew(JSC::regExpConstructorTable)) , regExpPrototypeTable(fastNew(JSC::regExpPrototypeTable)) , stringTable(fastNew(JSC::stringTable)) , stringConstructorTable(fastNew(JSC::stringConstructorTable)) , identifierTable(globalDataType == Default ? wtfThreadData().currentIdentifierTable() : createIdentifierTable()) , propertyNames(new CommonIdentifiers(this)) , emptyList(new MarkedArgumentBuffer) #if ENABLE(ASSEMBLER) , executableAllocator(*this) #endif , parserArena(adoptPtr(new ParserArena)) , keywords(adoptPtr(new Keywords(this))) , interpreter(0) , heap(this, heapSize) #if ENABLE(DFG_JIT) , sizeOfLastScratchBuffer(0) #endif , dynamicGlobalObject(0) , cachedUTCOffset(std::numeric_limits::quiet_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 #if CPU(X86) && ENABLE(JIT) , m_timeoutCount(512) #endif #if ENABLE(GC_VALIDATION) , m_isInitializingObject(false) #endif { interpreter = new Interpreter; 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)); debuggerActivationStructure.set(*this, DebuggerActivation::createStructure(*this, 0, jsNull())); activationStructure.set(*this, JSActivation::createStructure(*this, 0, jsNull())); interruptedExecutionErrorStructure.set(*this, InterruptedExecutionError::createStructure(*this, 0, jsNull())); terminatedExecutionErrorStructure.set(*this, TerminatedExecutionError::createStructure(*this, 0, jsNull())); staticScopeStructure.set(*this, JSStaticScopeObject::createStructure(*this, 0, jsNull())); strictEvalActivationStructure.set(*this, StrictEvalActivation::createStructure(*this, 0, jsNull())); stringStructure.set(*this, JSString::createStructure(*this, 0, jsNull())); notAnObjectStructure.set(*this, JSNotAnObject::createStructure(*this, 0, jsNull())); propertyNameIteratorStructure.set(*this, JSPropertyNameIterator::createStructure(*this, 0, jsNull())); getterSetterStructure.set(*this, GetterSetter::createStructure(*this, 0, jsNull())); apiWrapperStructure.set(*this, JSAPIValueWrapper::createStructure(*this, 0, jsNull())); scopeChainNodeStructure.set(*this, ScopeChainNode::createStructure(*this, 0, jsNull())); executableStructure.set(*this, ExecutableBase::createStructure(*this, 0, jsNull())); nativeExecutableStructure.set(*this, NativeExecutable::createStructure(*this, 0, jsNull())); evalExecutableStructure.set(*this, EvalExecutable::createStructure(*this, 0, jsNull())); programExecutableStructure.set(*this, ProgramExecutable::createStructure(*this, 0, jsNull())); functionExecutableStructure.set(*this, FunctionExecutable::createStructure(*this, 0, jsNull())); regExpStructure.set(*this, RegExp::createStructure(*this, 0, jsNull())); structureChainStructure.set(*this, StructureChain::createStructure(*this, 0, jsNull())); wtfThreadData().setCurrentIdentifierTable(existingEntryIdentifierTable); #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 = adoptPtr(new JITThunks(this)); #endif interpreter->initialize(this->canUseJIT()); heap.notifyIsSafeToCollect(); } void JSGlobalData::clearBuiltinStructures() { structureStructure.clear(); debuggerActivationStructure.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(); regExpStructure.clear(); structureChainStructure.clear(); } 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 arrayPrototypeTable->deleteTable(); arrayConstructorTable->deleteTable(); booleanPrototypeTable->deleteTable(); dateTable->deleteTable(); dateConstructorTable->deleteTable(); errorPrototypeTable->deleteTable(); globalObjectTable->deleteTable(); jsonTable->deleteTable(); mathTable->deleteTable(); numberConstructorTable->deleteTable(); numberPrototypeTable->deleteTable(); objectConstructorTable->deleteTable(); objectPrototypeTable->deleteTable(); regExpTable->deleteTable(); regExpConstructorTable->deleteTable(); regExpPrototypeTable->deleteTable(); stringTable->deleteTable(); stringConstructorTable->deleteTable(); fastDelete(const_cast(arrayConstructorTable)); fastDelete(const_cast(arrayPrototypeTable)); fastDelete(const_cast(booleanPrototypeTable)); fastDelete(const_cast(dateTable)); fastDelete(const_cast(dateConstructorTable)); fastDelete(const_cast(errorPrototypeTable)); fastDelete(const_cast(globalObjectTable)); fastDelete(const_cast(jsonTable)); fastDelete(const_cast(mathTable)); fastDelete(const_cast(numberConstructorTable)); fastDelete(const_cast(numberPrototypeTable)); fastDelete(const_cast(objectConstructorTable)); fastDelete(const_cast(objectPrototypeTable)); fastDelete(const_cast(regExpTable)); fastDelete(const_cast(regExpConstructorTable)); fastDelete(const_cast(regExpPrototypeTable)); fastDelete(const_cast(stringTable)); fastDelete(const_cast(stringConstructorTable)); opaqueJSClassData.clear(); delete emptyList; delete propertyNames; if (globalDataType != Default) deleteIdentifierTable(identifierTable); delete clientData; delete m_regExpCache; #if ENABLE(REGEXP_TRACING) delete m_rtTraceList; #endif #if ENABLE(DFG_JIT) for (unsigned i = 0; i < scratchBuffers.size(); ++i) fastFree(scratchBuffers[i]); #endif } PassRefPtr JSGlobalData::createContextGroup(ThreadStackType type, HeapSize heapSize) { return adoptRef(new JSGlobalData(APIContextGroup, type, heapSize)); } PassRefPtr JSGlobalData::create(ThreadStackType type, HeapSize heapSize) { return adoptRef(new JSGlobalData(Default, type, heapSize)); } PassRefPtr JSGlobalData::createLeaked(ThreadStackType type, HeapSize heapSize) { return create(type, heapSize); } bool JSGlobalData::sharedInstanceExists() { return sharedInstanceInternal(); } JSGlobalData& JSGlobalData::sharedInstance() { JSGlobalData*& instance = sharedInstanceInternal(); if (!instance) { instance = adoptRef(new JSGlobalData(APIShared, ThreadStackTypeSmall, SmallHeap)).leakRef(); instance->makeUsableFromMultipleThreads(); } return *instance; } JSGlobalData*& JSGlobalData::sharedInstanceInternal() { ASSERT(JSLock::currentThreadIsHoldingLock()); static JSGlobalData* sharedInstance; return sharedInstance; } #if ENABLE(JIT) static ThunkGenerator thunkGeneratorForIntrinsic(Intrinsic intrinsic) { switch (intrinsic) { case CharCodeAtIntrinsic: return charCodeAtThunkGenerator; case CharAtIntrinsic: return charAtThunkGenerator; case FromCharCodeIntrinsic: return fromCharCodeThunkGenerator; case SqrtIntrinsic: return sqrtThunkGenerator; case PowIntrinsic: return powThunkGenerator; case AbsIntrinsic: return absThunkGenerator; case FloorIntrinsic: return floorThunkGenerator; case CeilIntrinsic: return ceilThunkGenerator; case RoundIntrinsic: return roundThunkGenerator; case ExpIntrinsic: return expThunkGenerator; case LogIntrinsic: return logThunkGenerator; default: return 0; } } NativeExecutable* JSGlobalData::getHostFunction(NativeFunction function, NativeFunction constructor) { #if ENABLE(INTERPRETER) if (!canUseJIT()) return NativeExecutable::create(*this, function, constructor); #endif return jitStubs->hostFunctionStub(this, function, constructor); } NativeExecutable* JSGlobalData::getHostFunction(NativeFunction function, Intrinsic intrinsic) { ASSERT(canUseJIT()); return jitStubs->hostFunctionStub(this, function, intrinsic != NoIntrinsic ? thunkGeneratorForIntrinsic(intrinsic) : 0, intrinsic); } #else NativeExecutable* JSGlobalData::getHostFunction(NativeFunction function, NativeFunction constructor) { return NativeExecutable::create(*this, function, constructor); } #endif JSGlobalData::ClientData::~ClientData() { } void JSGlobalData::resetDateCache() { cachedUTCOffset = std::numeric_limits::quiet_NaN(); dstOffsetCache.reset(); cachedDateString = UString(); cachedDateStringValue = std::numeric_limits::quiet_NaN(); dateInstanceCache.reset(); } void JSGlobalData::startSampling() { interpreter->startSampling(); } void JSGlobalData::stopSampling() { interpreter->stopSampling(); } void JSGlobalData::dumpSampleData(ExecState* exec) { interpreter->dumpSampleData(exec); #if ENABLE(ASSEMBLER) ExecutableAllocator::dumpProfile(); #endif } 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); heap.objectSpace().forEachCell(); } struct StackPreservingRecompiler : public MarkedBlock::VoidFunctor { HashSet currentlyExecutingFunctions; void operator()(JSCell* cell) { if (!cell->inherits(&FunctionExecutable::s_info)) return; FunctionExecutable* executable = jsCast(cell); if (currentlyExecutingFunctions.contains(executable)) return; executable->discardCode(); } }; void JSGlobalData::releaseExecutableMemory() { if (dynamicGlobalObject) { StackPreservingRecompiler recompiler; HashSet roots; heap.getConservativeRegisterRoots(roots); HashSet::iterator end = roots.end(); for (HashSet::iterator ptr = roots.begin(); ptr != end; ++ptr) { ScriptExecutable* executable = 0; JSCell* cell = *ptr; if (cell->inherits(&ScriptExecutable::s_info)) executable = static_cast(*ptr); else if (cell->inherits(&JSFunction::s_info)) { JSFunction* function = asFunction(*ptr); if (function->isHostFunction()) continue; executable = function->jsExecutable(); } else continue; ASSERT(executable->inherits(&ScriptExecutable::s_info)); executable->unlinkCalls(); if (executable->inherits(&FunctionExecutable::s_info)) recompiler.currentlyExecutingFunctions.add(static_cast(executable)); } heap.objectSpace().forEachCell(recompiler); } m_regExpCache->invalidateCode(); heap.collectAllGarbage(); } void releaseExecutableMemory(JSGlobalData& globalData) { globalData.releaseExecutableMemory(); } #if ENABLE(REGEXP_TRACING) void JSGlobalData::addRegExpToTrace(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