X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/ba379fdc102753d6be2c4d937058fe40257329fe..14957cd040308e3eeec43d26bae5d76da13fcd85:/runtime/JSGlobalData.cpp?ds=sidebyside diff --git a/runtime/JSGlobalData.cpp b/runtime/JSGlobalData.cpp index 156d102..377e849 100644 --- a/runtime/JSGlobalData.cpp +++ b/runtime/JSGlobalData.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * 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 @@ -30,22 +30,35 @@ #include "JSGlobalData.h" #include "ArgList.h" -#include "Collector.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 "Parser.h" +#include "JSZombie.h" #include "Lexer.h" #include "Lookup.h" #include "Nodes.h" +#include "Parser.h" +#include "RegExpCache.h" +#include "RegExpObject.h" +#include "StrictEvalActivation.h" +#include +#if ENABLE(REGEXP_TRACING) +#include "RegExp.h" +#endif + #if ENABLE(JSC_MULTIPLE_THREADS) #include @@ -53,97 +66,231 @@ #if PLATFORM(MAC) #include "ProfilerServer.h" +#include #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 arrayConstructorTable; +extern JSC_CONST_HASHTABLE HashTable arrayPrototypeTable; +extern JSC_CONST_HASHTABLE HashTable booleanPrototypeTable; extern JSC_CONST_HASHTABLE HashTable jsonTable; extern JSC_CONST_HASHTABLE HashTable dateTable; +extern JSC_CONST_HASHTABLE HashTable dateConstructorTable; +extern JSC_CONST_HASHTABLE HashTable errorPrototypeTable; +extern JSC_CONST_HASHTABLE HashTable globalObjectTable; extern JSC_CONST_HASHTABLE HashTable mathTable; -extern JSC_CONST_HASHTABLE HashTable numberTable; +extern JSC_CONST_HASHTABLE HashTable numberConstructorTable; +extern JSC_CONST_HASHTABLE HashTable numberPrototypeTable; +extern JSC_CONST_HASHTABLE HashTable objectConstructorTable; +extern JSC_CONST_HASHTABLE HashTable objectPrototypeTable; extern JSC_CONST_HASHTABLE HashTable regExpTable; extern JSC_CONST_HASHTABLE HashTable regExpConstructorTable; +extern JSC_CONST_HASHTABLE HashTable regExpPrototypeTable; extern JSC_CONST_HASHTABLE HashTable stringTable; +extern JSC_CONST_HASHTABLE HashTable stringConstructorTable; + +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 -struct VPtrSet { - VPtrSet(); - - void* jsArrayVPtr; - void* jsByteArrayVPtr; - void* jsStringVPtr; - void* jsFunctionVPtr; -}; - -VPtrSet::VPtrSet() +void JSGlobalData::storeVPtrs() { - // Bizarrely, calling fastMalloc here is faster than allocating space on the stack. - void* storage = fastMalloc(sizeof(CollectorBlock)); + // Enough storage to fit a JSArray, JSByteArray, JSString, or JSFunction. + // COMPILE_ASSERTS below check that this is true. + char storage[64]; - JSCell* jsArray = new (storage) JSArray(JSArray::createStructure(jsNull())); - jsArrayVPtr = jsArray->vptr(); - jsArray->~JSCell(); + 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); - jsByteArrayVPtr = jsByteArray->vptr(); - jsByteArray->~JSCell(); + 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); - jsStringVPtr = jsString->vptr(); - jsString->~JSCell(); + CLOBBER_MEMORY(); + JSGlobalData::jsStringVPtr = jsString->vptr(); - JSCell* jsFunction = new (storage) JSFunction(JSFunction::createStructure(jsNull())); - jsFunctionVPtr = jsFunction->vptr(); - jsFunction->~JSCell(); - - fastFree(storage); + 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(bool isShared, const VPtrSet& vptrSet) - : isSharedInstance(isShared) +JSGlobalData::JSGlobalData(GlobalDataType globalDataType, ThreadStackType threadStackType) + : globalDataType(globalDataType) , clientData(0) - , arrayTable(fastNew(JSC::arrayTable)) + , 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)) - , numberTable(fastNew(JSC::numberTable)) + , 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)) - , activationStructure(JSActivation::createStructure(jsNull())) - , interruptedExecutionErrorStructure(JSObject::createStructure(jsNull())) - , staticScopeStructure(JSStaticScopeObject::createStructure(jsNull())) - , stringStructure(JSString::createStructure(jsNull())) - , notAnObjectErrorStubStructure(JSNotAnObjectErrorStub::createStructure(jsNull())) - , notAnObjectStructure(JSNotAnObject::createStructure(jsNull())) -#if USE(JSVALUE32) - , numberStructure(JSNumberCell::createStructure(jsNull())) -#endif - , jsArrayVPtr(vptrSet.jsArrayVPtr) - , jsByteArrayVPtr(vptrSet.jsByteArrayVPtr) - , jsStringVPtr(vptrSet.jsStringVPtr) - , jsFunctionVPtr(vptrSet.jsFunctionVPtr) - , identifierTable(createIdentifierTable()) + , stringConstructorTable(fastNew(JSC::stringConstructorTable)) + , identifierTable(globalDataType == Default ? wtfThreadData().currentIdentifierTable() : createIdentifierTable()) , propertyNames(new CommonIdentifiers(this)) , emptyList(new MarkedArgumentBuffer) +#if ENABLE(ASSEMBLER) + , executableAllocator(*this) + , regexAllocator(*this) +#endif , lexer(new Lexer(this)) , parser(new Parser) - , interpreter(new Interpreter) -#if ENABLE(JIT) - , jitStubs(this) -#endif + , interpreter(0) , heap(this) - , initializingLazyNumericCompareFunction(false) - , head(0) + , globalObjectCount(0) , dynamicGlobalObject(0) - , scopeNodeBeingReparsed(0) - , firstStringifierToMark(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)); + debuggerActivationStructure.set(*this, DebuggerActivation::createStructure(*this, jsNull())); + 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)); + regExpStructure.set(*this, RegExp::createStructure(*this, jsNull())); + 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 = adoptPtr(new JITThunks(this)); +#endif +} + +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(); + dummyMarkableCellStructure.clear(); + regExpStructure.clear(); + structureChainStructure.clear(); + +#if ENABLE(JSC_ZOMBIES) + zombieStructure.clear(); +#endif } JSGlobalData::~JSGlobalData() @@ -156,23 +303,43 @@ JSGlobalData::~JSGlobalData() interpreter = 0; #endif - arrayTable->deleteTable(); + arrayPrototypeTable->deleteTable(); + arrayConstructorTable->deleteTable(); + booleanPrototypeTable->deleteTable(); dateTable->deleteTable(); + dateConstructorTable->deleteTable(); + errorPrototypeTable->deleteTable(); + globalObjectTable->deleteTable(); jsonTable->deleteTable(); mathTable->deleteTable(); - numberTable->deleteTable(); + numberConstructorTable->deleteTable(); + numberPrototypeTable->deleteTable(); + objectConstructorTable->deleteTable(); + objectPrototypeTable->deleteTable(); regExpTable->deleteTable(); regExpConstructorTable->deleteTable(); + regExpPrototypeTable->deleteTable(); stringTable->deleteTable(); + stringConstructorTable->deleteTable(); - fastDelete(const_cast(arrayTable)); + 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(numberTable)); + 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)); delete parser; delete lexer; @@ -182,22 +349,29 @@ JSGlobalData::~JSGlobalData() delete emptyList; delete propertyNames; - deleteIdentifierTable(identifierTable); + if (globalDataType != Default) + deleteIdentifierTable(identifierTable); delete clientData; + delete m_regExpCache; +#if ENABLE(REGEXP_TRACING) + delete m_rtTraceList; +#endif } -PassRefPtr JSGlobalData::create(bool isShared) +PassRefPtr JSGlobalData::createContextGroup(ThreadStackType type) { - return adoptRef(new JSGlobalData(isShared, VPtrSet())); + return adoptRef(new JSGlobalData(APIContextGroup, type)); } -PassRefPtr JSGlobalData::createLeaked() +PassRefPtr JSGlobalData::create(ThreadStackType type) { - Structure::startIgnoringLeaks(); - RefPtr data = create(); - Structure::stopIgnoringLeaks(); - return data.release(); + return adoptRef(new JSGlobalData(Default, type)); +} + +PassRefPtr JSGlobalData::createLeaked(ThreadStackType type) +{ + return create(type); } bool JSGlobalData::sharedInstanceExists() @@ -209,7 +383,7 @@ JSGlobalData& JSGlobalData::sharedInstance() { JSGlobalData*& instance = sharedInstanceInternal(); if (!instance) { - instance = create(true).releaseRef(); + instance = adoptRef(new JSGlobalData(APIShared, ThreadStackTypeSmall)).leakRef(); #if ENABLE(JSC_MULTIPLE_THREADS) instance->makeUsableFromMultipleThreads(); #endif @@ -224,22 +398,144 @@ JSGlobalData*& JSGlobalData::sharedInstanceInternal() return sharedInstance; } -// FIXME: We can also detect forms like v1 < v2 ? -1 : 0, reverse comparison, etc. -const Vector& JSGlobalData::numericCompareFunction(ExecState* exec) +#if ENABLE(JIT) +NativeExecutable* JSGlobalData::getHostFunction(NativeFunction function) { - if (!lazyNumericCompareFunction.size() && !initializingLazyNumericCompareFunction) { - initializingLazyNumericCompareFunction = true; - RefPtr programNode = parser->parse(exec, 0, makeSource(UString("(function (v1, v2) { return v1 - v2; })")), 0, 0); - RefPtr functionBody = extractFunctionBody(programNode.get()); - lazyNumericCompareFunction = functionBody->bytecode(exec->scopeChain()).instructions(); - initializingLazyNumericCompareFunction = false; + 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); +} + +struct StackPreservingRecompiler { + HashSet currentlyExecutingFunctions; + void operator()(JSCell* cell) + { + if (!cell->inherits(&FunctionExecutable::s_info)) + return; + FunctionExecutable* executable = static_cast(cell); + if (currentlyExecutingFunctions.contains(executable)) + return; + executable->discardCode(); } +}; - return lazyNumericCompareFunction; +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.forEach(recompiler); + } else + recompileAllJSFunctions(); + + m_regExpCache->invalidateCode(); + heap.collectAllGarbage(); } -JSGlobalData::ClientData::~ClientData() +#if ENABLE(ASSEMBLER) +void releaseExecutableMemory(JSGlobalData& globalData) +{ + globalData.releaseExecutableMemory(); +} +#endif + +#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