X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/9dae56ea45a0f5f8136a5c93d6f3a7f99399ca73..14957cd040308e3eeec43d26bae5d76da13fcd85:/runtime/JSGlobalData.cpp diff --git a/runtime/JSGlobalData.cpp b/runtime/JSGlobalData.cpp index 10b584d..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,19 +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 @@ -50,58 +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 const HashTable arrayTable; -extern const HashTable dateTable; -extern const HashTable mathTable; -extern const HashTable numberTable; -extern const HashTable regExpTable; -extern const HashTable regExpConstructorTable; -extern const HashTable stringTable; - -JSGlobalData::JSGlobalData(bool isShared) - : initializingLazyNumericCompareFunction(false) - , interpreter(new Interpreter) - , exception(noValue()) - , arrayTable(new HashTable(JSC::arrayTable)) - , dateTable(new HashTable(JSC::dateTable)) - , mathTable(new HashTable(JSC::mathTable)) - , numberTable(new HashTable(JSC::numberTable)) - , regExpTable(new HashTable(JSC::regExpTable)) - , regExpConstructorTable(new HashTable(JSC::regExpConstructorTable)) - , stringTable(new HashTable(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(ALTERNATE_JSIMMEDIATE) - , numberStructure(JSNumberCell::createStructure(jsNull())) -#endif - , identifierTable(createIdentifierTable()) +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 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 + +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) + , 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 ArgList) - , newParserObjects(0) - , parserObjectExtraRefCounts(0) + , emptyList(new MarkedArgumentBuffer) +#if ENABLE(ASSEMBLER) + , executableAllocator(*this) + , regexAllocator(*this) +#endif , lexer(new Lexer(this)) , parser(new Parser) - , head(0) - , dynamicGlobalObject(0) - , isSharedInstance(isShared) - , clientData(0) - , scopeNodeBeingReparsed(0) + , 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)); + 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 - interpreter->initialize(this); +#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() @@ -114,20 +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(); - delete arrayTable; - delete dateTable; - delete mathTable; - delete numberTable; - delete regExpTable; - delete regExpConstructorTable; - delete stringTable; + 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)); delete parser; delete lexer; @@ -137,29 +349,29 @@ JSGlobalData::~JSGlobalData() delete emptyList; delete propertyNames; - deleteIdentifierTable(identifierTable); + if (globalDataType != Default) + deleteIdentifierTable(identifierTable); - delete newParserObjects; - delete parserObjectExtraRefCounts; - delete clientData; + delete m_regExpCache; +#if ENABLE(REGEXP_TRACING) + delete m_rtTraceList; +#endif } -PassRefPtr JSGlobalData::create() +PassRefPtr JSGlobalData::createContextGroup(ThreadStackType type) { - return adoptRef(new JSGlobalData); + return adoptRef(new JSGlobalData(APIContextGroup, type)); } -PassRefPtr JSGlobalData::createLeaked() +PassRefPtr JSGlobalData::create(ThreadStackType type) { -#ifndef NDEBUG - Structure::startIgnoringLeaks(); - RefPtr data = create(); - Structure::stopIgnoringLeaks(); - return data.release(); -#else - return create(); -#endif + return adoptRef(new JSGlobalData(Default, type)); +} + +PassRefPtr JSGlobalData::createLeaked(ThreadStackType type) +{ + return create(type); } bool JSGlobalData::sharedInstanceExists() @@ -171,7 +383,7 @@ JSGlobalData& JSGlobalData::sharedInstance() { JSGlobalData*& instance = sharedInstanceInternal(); if (!instance) { - instance = new JSGlobalData(true); + instance = adoptRef(new JSGlobalData(APIShared, ThreadStackTypeSmall)).leakRef(); #if ENABLE(JSC_MULTIPLE_THREADS) instance->makeUsableFromMultipleThreads(); #endif @@ -186,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) +{ + 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) { - 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; + 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