blob: 126cde43476db7edd77619e32cbfb299f8807915 [file] [log] [blame]
/*
* 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 "CodeCache.h"
#include "CommonIdentifiers.h"
#include "DebuggerActivation.h"
#include "FunctionConstructor.h"
#include "GCActivityCallback.h"
#include "GetterSetter.h"
#include "Heap.h"
#include "HostCallReturnValue.h"
#include "IncrementalSweeper.h"
#include "Interpreter.h"
#include "JSActivation.h"
#include "JSAPIValueWrapper.h"
#include "JSArray.h"
#include "JSClassRef.h"
#include "JSFunction.h"
#include "JSLock.h"
#include "JSNameScope.h"
#include "JSNotAnObject.h"
#include "JSPropertyNameIterator.h"
#include "JSWithScope.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 "UnlinkedCodeBlock.h"
#include <wtf/RetainPtr.h>
#include <wtf/Threading.h>
#include <wtf/WTFThreadData.h>
#if ENABLE(DFG_JIT)
#include "ConservativeRoots.h"
#endif
#if ENABLE(REGEXP_TRACING)
#include "RegExp.h"
#endif
#if USE(CF)
#include <CoreFoundation/CoreFoundation.h>
#endif
using namespace WTF;
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;
JS_EXPORTDATA extern const HashTable objectConstructorTable;
extern const HashTable objectPrototypeTable;
extern const HashTable privateNamePrototypeTable;
extern const HashTable regExpTable;
extern const HashTable regExpConstructorTable;
extern const HashTable regExpPrototypeTable;
extern const HashTable stringTable;
extern const HashTable stringConstructorTable;
// Note: Platform.h will enforce that ENABLE(ASSEMBLER) is true if either
// ENABLE(JIT) or ENABLE(YARR_JIT) or both are enabled. The code below
// just checks for ENABLE(JIT) or ENABLE(YARR_JIT) with this premise in mind.
#if ENABLE(ASSEMBLER)
static bool enableAssembler(ExecutableAllocator& executableAllocator)
{
if (!executableAllocator.isValid() || (!Options::useJIT() && !Options::useRegExpJIT()))
return false;
#if USE(CF)
#if COMPILER(GCC) && !COMPILER(CLANG)
// FIXME: remove this once the EWS have been upgraded to LLVM.
// Work around a bug of GCC with strict-aliasing.
RetainPtr<CFStringRef> canUseJITKeyRetain(AdoptCF, CFStringCreateWithCString(0 , "JavaScriptCoreUseJIT", kCFStringEncodingMacRoman));
CFStringRef canUseJITKey = canUseJITKeyRetain.get();
#else
CFStringRef canUseJITKey = CFSTR("JavaScriptCoreUseJIT");
#endif // COMPILER(GCC) && !COMPILER(CLANG)
RetainPtr<CFBooleanRef> canUseJIT(AdoptCF, (CFBooleanRef)CFPreferencesCopyAppValue(canUseJITKey, kCFPreferencesCurrentApplication));
if (canUseJIT)
return kCFBooleanTrue == canUseJIT.get();
#endif
#if USE(CF) || OS(UNIX)
char* canUseJITString = getenv("JavaScriptCoreUseJIT");
return !canUseJITString || atoi(canUseJITString);
#else
return true;
#endif
}
#endif // ENABLE(!ASSEMBLER)
JSGlobalData::JSGlobalData(GlobalDataType globalDataType, HeapType heapType)
:
#if ENABLE(ASSEMBLER)
executableAllocator(*this),
#endif
heap(this, heapType)
, globalDataType(globalDataType)
, clientData(0)
, topCallFrame(CallFrame::noCaller())
, arrayConstructorTable(fastNew<HashTable>(JSC::arrayConstructorTable))
, arrayPrototypeTable(fastNew<HashTable>(JSC::arrayPrototypeTable))
, booleanPrototypeTable(fastNew<HashTable>(JSC::booleanPrototypeTable))
, dateTable(fastNew<HashTable>(JSC::dateTable))
, dateConstructorTable(fastNew<HashTable>(JSC::dateConstructorTable))
, errorPrototypeTable(fastNew<HashTable>(JSC::errorPrototypeTable))
, globalObjectTable(fastNew<HashTable>(JSC::globalObjectTable))
, jsonTable(fastNew<HashTable>(JSC::jsonTable))
, mathTable(fastNew<HashTable>(JSC::mathTable))
, numberConstructorTable(fastNew<HashTable>(JSC::numberConstructorTable))
, numberPrototypeTable(fastNew<HashTable>(JSC::numberPrototypeTable))
, objectConstructorTable(fastNew<HashTable>(JSC::objectConstructorTable))
, objectPrototypeTable(fastNew<HashTable>(JSC::objectPrototypeTable))
, privateNamePrototypeTable(fastNew<HashTable>(JSC::privateNamePrototypeTable))
, regExpTable(fastNew<HashTable>(JSC::regExpTable))
, regExpConstructorTable(fastNew<HashTable>(JSC::regExpConstructorTable))
, regExpPrototypeTable(fastNew<HashTable>(JSC::regExpPrototypeTable))
, stringTable(fastNew<HashTable>(JSC::stringTable))
, stringConstructorTable(fastNew<HashTable>(JSC::stringConstructorTable))
, identifierTable(globalDataType == Default ? wtfThreadData().currentIdentifierTable() : createIdentifierTable())
, propertyNames(new CommonIdentifiers(this))
, emptyList(new MarkedArgumentBuffer)
, parserArena(adoptPtr(new ParserArena))
, keywords(adoptPtr(new Keywords(this)))
, interpreter(0)
, jsArrayClassInfo(JSArray::s_classinfo())
, jsFinalObjectClassInfo(JSFinalObject::s_classinfo())
#if ENABLE(DFG_JIT)
, sizeOfLastScratchBuffer(0)
#endif
, dynamicGlobalObject(0)
, cachedUTCOffset(QNaN)
, m_enabledProfiler(0)
, 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
, m_newStringsSinceLastHashConst(0)
#if ENABLE(ASSEMBLER)
, m_canUseAssembler(enableAssembler(executableAllocator))
#endif
#if ENABLE(JIT)
, m_canUseJIT(m_canUseAssembler && Options::useJIT())
#endif
#if ENABLE(YARR_JIT)
, m_canUseRegExpJIT(m_canUseAssembler && Options::useRegExpJIT())
#endif
#if ENABLE(GC_VALIDATION)
, m_initializingObjectClass(0)
#endif
, m_inDefineOwnProperty(false)
, m_codeCache(CodeCache::create())
{
interpreter = new Interpreter(*this);
// Need to be careful to keep everything consistent here
JSLockHolder lock(this);
IdentifierTable* existingEntryIdentifierTable = wtfThreadData().setCurrentIdentifierTable(identifierTable);
structureStructure.set(*this, Structure::createStructure(*this));
debuggerActivationStructure.set(*this, DebuggerActivation::createStructure(*this, 0, jsNull()));
interruptedExecutionErrorStructure.set(*this, InterruptedExecutionError::createStructure(*this, 0, jsNull()));
terminatedExecutionErrorStructure.set(*this, TerminatedExecutionError::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()));
JSScopeStructure.set(*this, JSScope::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()));
sharedSymbolTableStructure.set(*this, SharedSymbolTable::createStructure(*this, 0, jsNull()));
structureChainStructure.set(*this, StructureChain::createStructure(*this, 0, jsNull()));
sparseArrayValueMapStructure.set(*this, SparseArrayValueMap::createStructure(*this, 0, jsNull()));
withScopeStructure.set(*this, JSWithScope::createStructure(*this, 0, jsNull()));
unlinkedFunctionExecutableStructure.set(*this, UnlinkedFunctionExecutable::createStructure(*this, 0, jsNull()));
unlinkedProgramCodeBlockStructure.set(*this, UnlinkedProgramCodeBlock::createStructure(*this, 0, jsNull()));
unlinkedEvalCodeBlockStructure.set(*this, UnlinkedEvalCodeBlock::createStructure(*this, 0, jsNull()));
unlinkedFunctionCodeBlockStructure.set(*this, UnlinkedFunctionCodeBlock::createStructure(*this, 0, jsNull()));
wtfThreadData().setCurrentIdentifierTable(existingEntryIdentifierTable);
#if ENABLE(JIT)
jitStubs = adoptPtr(new JITThunks(this));
#endif
interpreter->initialize(this->canUseJIT());
#if ENABLE(JIT)
initializeHostCallReturnValue(); // This is needed to convince the linker not to drop host call return support.
#endif
heap.notifyIsSafeToCollect();
LLInt::Data::performAssertions(*this);
if (Options::enableProfiler())
m_perBytecodeProfiler = adoptPtr(new Profiler::Database(*this));
}
JSGlobalData::~JSGlobalData()
{
// Clear this first to ensure that nobody tries to remove themselves from it.
m_perBytecodeProfiler.clear();
ASSERT(!m_apiLock.currentThreadIsHoldingLock());
heap.didStartVMShutdown();
delete interpreter;
#ifndef NDEBUG
interpreter = reinterpret_cast<Interpreter*>(0xbbadbeef);
#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();
privateNamePrototypeTable->deleteTable();
regExpTable->deleteTable();
regExpConstructorTable->deleteTable();
regExpPrototypeTable->deleteTable();
stringTable->deleteTable();
stringConstructorTable->deleteTable();
fastDelete(const_cast<HashTable*>(arrayConstructorTable));
fastDelete(const_cast<HashTable*>(arrayPrototypeTable));
fastDelete(const_cast<HashTable*>(booleanPrototypeTable));
fastDelete(const_cast<HashTable*>(dateTable));
fastDelete(const_cast<HashTable*>(dateConstructorTable));
fastDelete(const_cast<HashTable*>(errorPrototypeTable));
fastDelete(const_cast<HashTable*>(globalObjectTable));
fastDelete(const_cast<HashTable*>(jsonTable));
fastDelete(const_cast<HashTable*>(mathTable));
fastDelete(const_cast<HashTable*>(numberConstructorTable));
fastDelete(const_cast<HashTable*>(numberPrototypeTable));
fastDelete(const_cast<HashTable*>(objectConstructorTable));
fastDelete(const_cast<HashTable*>(objectPrototypeTable));
fastDelete(const_cast<HashTable*>(privateNamePrototypeTable));
fastDelete(const_cast<HashTable*>(regExpTable));
fastDelete(const_cast<HashTable*>(regExpConstructorTable));
fastDelete(const_cast<HashTable*>(regExpPrototypeTable));
fastDelete(const_cast<HashTable*>(stringTable));
fastDelete(const_cast<HashTable*>(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> JSGlobalData::createContextGroup(HeapType heapType)
{
return adoptRef(new JSGlobalData(APIContextGroup, heapType));
}
PassRefPtr<JSGlobalData> JSGlobalData::create(HeapType heapType)
{
return adoptRef(new JSGlobalData(Default, heapType));
}
PassRefPtr<JSGlobalData> JSGlobalData::createLeaked(HeapType heapType)
{
return create(heapType);
}
bool JSGlobalData::sharedInstanceExists()
{
return sharedInstanceInternal();
}
#if !defined(__LB_SHELL__) && !OS(STARBOARD)
JSGlobalData& JSGlobalData::sharedInstance()
{
GlobalJSLock globalLock;
JSGlobalData*& instance = sharedInstanceInternal();
if (!instance) {
instance = adoptRef(new JSGlobalData(APIShared, SmallHeap)).leakRef();
instance->makeUsableFromMultipleThreads();
}
return *instance;
}
#endif
JSGlobalData*& JSGlobalData::sharedInstanceInternal()
{
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)
{
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 // !ENABLE(JIT)
NativeExecutable* JSGlobalData::getHostFunction(NativeFunction function, NativeFunction constructor)
{
return NativeExecutable::create(*this, function, constructor);
}
#endif // !ENABLE(JIT)
JSGlobalData::ClientData::~ClientData()
{
}
void JSGlobalData::resetDateCache()
{
cachedUTCOffset = QNaN;
dstOffsetCache.reset();
cachedDateString = String();
cachedDateStringValue = QNaN;
dateInstanceCache.reset();
}
void JSGlobalData::startSampling()
{
interpreter->startSampling();
}
void JSGlobalData::stopSampling()
{
interpreter->stopSampling();
}
void JSGlobalData::discardAllCode()
{
m_codeCache->clear();
heap.deleteAllCompiledCode();
heap.reportAbandonedObjectGraph();
}
void JSGlobalData::dumpSampleData(ExecState* exec)
{
interpreter->dumpSampleData(exec);
#if ENABLE(ASSEMBLER)
ExecutableAllocator::dumpProfile();
#endif
}
struct StackPreservingRecompiler : public MarkedBlock::VoidFunctor {
HashSet<FunctionExecutable*> currentlyExecutingFunctions;
void operator()(JSCell* cell)
{
if (!cell->inherits(FunctionExecutable::s_classinfo()))
return;
FunctionExecutable* executable = jsCast<FunctionExecutable*>(cell);
if (currentlyExecutingFunctions.contains(executable))
return;
executable->clearCodeIfNotCompiling();
}
};
void JSGlobalData::releaseExecutableMemory()
{
if (dynamicGlobalObject) {
StackPreservingRecompiler recompiler;
HashSet<JSCell*> roots;
heap.getConservativeRegisterRoots(roots);
HashSet<JSCell*>::iterator end = roots.end();
for (HashSet<JSCell*>::iterator ptr = roots.begin(); ptr != end; ++ptr) {
ScriptExecutable* executable = 0;
JSCell* cell = *ptr;
if (cell->inherits(ScriptExecutable::s_classinfo()))
executable = static_cast<ScriptExecutable*>(*ptr);
else if (cell->inherits(JSFunction::s_classinfo())) {
JSFunction* function = jsCast<JSFunction*>(*ptr);
if (function->isHostFunction())
continue;
executable = function->jsExecutable();
} else
continue;
ASSERT(executable->inherits(ScriptExecutable::s_classinfo()));
executable->unlinkCalls();
if (executable->inherits(FunctionExecutable::s_classinfo()))
recompiler.currentlyExecutingFunctions.add(static_cast<FunctionExecutable*>(executable));
}
heap.objectSpace().forEachLiveCell<StackPreservingRecompiler>(recompiler);
}
m_regExpCache->invalidateCode();
heap.collectAllGarbage();
}
void releaseExecutableMemory(JSGlobalData& globalData)
{
globalData.releaseExecutableMemory();
}
#if ENABLE(DFG_JIT)
void JSGlobalData::gatherConservativeRoots(ConservativeRoots& conservativeRoots)
{
for (size_t i = 0; i < scratchBuffers.size(); i++) {
ScratchBuffer* scratchBuffer = scratchBuffers[i];
if (scratchBuffer->activeLength()) {
void* bufferStart = scratchBuffer->dataBuffer();
conservativeRoots.add(bufferStart, static_cast<void*>(static_cast<char*>(bufferStart) + scratchBuffer->activeLength()));
}
}
}
#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()) {
dataLogF("\nRegExp Tracing\n");
dataLogF(" match() matches\n");
dataLogF("Regular Expression JIT Address calls found\n");
dataLogF("----------------------------------------+----------------+----------+----------\n");
unsigned reCount = 0;
for (; iter != m_rtTraceList->end(); ++iter, ++reCount)
(*iter)->printTraceData();
dataLogF("%d Regular Expressions\n", reCount);
}
m_rtTraceList->clear();
}
#else
void JSGlobalData::dumpRegExpTrace()
{
}
#endif
} // namespace JSC