X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/93a3786624b2768d89bfa27e46598dc64e2fb70a..ed1e77d3adeb83d26fd1dfb16dd84cabdcefd250:/runtime/VM.cpp?ds=inline diff --git a/runtime/VM.cpp b/runtime/VM.cpp index 33589b8..76741dd 100644 --- a/runtime/VM.cpp +++ b/runtime/VM.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2011, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2011, 2013-2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,7 +10,7 @@ * 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 + * 3. Neither the name of Apple 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. * @@ -30,41 +30,72 @@ #include "VM.h" #include "ArgList.h" +#include "ArityCheckFailReturnThunks.h" +#include "ArrayBufferNeuteringWatchpoint.h" +#include "BuiltinExecutables.h" +#include "CodeBlock.h" #include "CodeCache.h" #include "CommonIdentifiers.h" +#include "CommonSlowPaths.h" +#include "CustomGetterSetter.h" #include "DFGLongLivedState.h" -#include "DebuggerActivation.h" +#include "DFGWorklist.h" +#include "Disassembler.h" +#include "ErrorInstance.h" +#include "Exception.h" +#include "FTLThunks.h" #include "FunctionConstructor.h" #include "GCActivityCallback.h" #include "GetterSetter.h" #include "Heap.h" +#include "HeapIterationScope.h" #include "HostCallReturnValue.h" +#include "Identifier.h" #include "IncrementalSweeper.h" #include "Interpreter.h" -#include "JSActivation.h" +#include "JITCode.h" #include "JSAPIValueWrapper.h" #include "JSArray.h" +#include "JSCInlines.h" #include "JSFunction.h" +#include "JSGlobalObjectFunctions.h" +#include "JSLexicalEnvironment.h" #include "JSLock.h" #include "JSNameScope.h" #include "JSNotAnObject.h" -#include "JSPropertyNameIterator.h" +#include "JSPromiseDeferred.h" +#include "JSPropertyNameEnumerator.h" +#include "JSTemplateRegistryKey.h" #include "JSWithScope.h" #include "Lexer.h" #include "Lookup.h" +#include "MapData.h" #include "Nodes.h" -#include "ParserArena.h" +#include "Parser.h" +#include "ProfilerDatabase.h" +#include "PropertyMapHashTable.h" #include "RegExpCache.h" #include "RegExpObject.h" +#include "RuntimeType.h" +#include "SimpleTypedArrayController.h" #include "SourceProviderCache.h" +#include "StackVisitor.h" #include "StrictEvalActivation.h" #include "StrongInlines.h" +#include "StructureInlines.h" +#include "TypeProfiler.h" +#include "TypeProfilerLog.h" #include "UnlinkedCodeBlock.h" +#include "WeakGCMapInlines.h" +#include "WeakMapData.h" +#include #include #include #include #include #include +#include +#include #if ENABLE(DFG_JIT) #include "ConservativeRoots.h" @@ -82,24 +113,6 @@ 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 privateNamePrototypeTable; -extern const HashTable regExpTable; -extern const HashTable regExpConstructorTable; -extern const HashTable regExpPrototypeTable; -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. @@ -116,20 +129,6 @@ static bool enableAssembler(ExecutableAllocator& executableAllocator) 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 canUseJITKeyRetain = adoptCF(CFStringCreateWithCString(0 , "JavaScriptCoreUseJIT", kCFStringEncodingMacRoman)); - CFStringRef canUseJITKey = canUseJITKeyRetain.get(); -#else - CFStringRef canUseJITKey = CFSTR("JavaScriptCoreUseJIT"); -#endif // COMPILER(GCC) && !COMPILER(CLANG) - RetainPtr canUseJIT = adoptCF(CFPreferencesCopyAppValue(canUseJITKey, kCFPreferencesCurrentApplication)); - if (canUseJIT) - return kCFBooleanTrue == canUseJIT.get(); -#endif - #if USE(CF) || OS(UNIX) char* canUseJITString = getenv("JavaScriptCoreUseJIT"); return !canUseJITString || atoi(canUseJITString); @@ -147,43 +146,22 @@ VM::VM(VMType vmType, HeapType heapType) , heap(this, heapType) , vmType(vmType) , clientData(0) + , topVMEntryFrame(nullptr) , 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)) - , privateNamePrototypeTable(fastNew(JSC::privateNamePrototypeTable)) - , regExpTable(fastNew(JSC::regExpTable)) - , regExpConstructorTable(fastNew(JSC::regExpConstructorTable)) - , regExpPrototypeTable(fastNew(JSC::regExpPrototypeTable)) - , stringConstructorTable(fastNew(JSC::stringConstructorTable)) - , identifierTable(vmType == Default ? wtfThreadData().currentIdentifierTable() : createIdentifierTable()) - , propertyNames(new CommonIdentifiers(this)) + , m_atomicStringTable(vmType == Default ? wtfThreadData().atomicStringTable() : new AtomicStringTable) + , propertyNames(nullptr) , emptyList(new MarkedArgumentBuffer) - , parserArena(adoptPtr(new ParserArena)) - , keywords(adoptPtr(new Keywords(this))) + , stringCache(*this) + , prototypeMap(*this) + , keywords(std::make_unique(*this)) , interpreter(0) - , jsArrayClassInfo(&JSArray::s_info) - , jsFinalObjectClassInfo(&JSFinalObject::s_info) -#if ENABLE(DFG_JIT) + , jsArrayClassInfo(JSArray::info()) + , jsFinalObjectClassInfo(JSFinalObject::info()) , sizeOfLastScratchBuffer(0) -#endif - , dynamicGlobalObject(0) - , m_enabledProfiler(0) + , entryScope(0) , m_regExpCache(new RegExpCache(this)) #if ENABLE(REGEXP_TRACING) , m_rtTraceList(new RTTraceList()) -#endif -#ifndef NDEBUG - , exclusiveThread(0) #endif , m_newStringsSinceLastHashCons(0) #if ENABLE(ASSEMBLER) @@ -197,23 +175,44 @@ VM::VM(VMType vmType, HeapType heapType) #endif #if ENABLE(GC_VALIDATION) , m_initializingObjectClass(0) +#endif + , m_stackPointerAtVMEntry(0) + , m_stackLimit(0) +#if !ENABLE(JIT) + , m_jsStackLimit(0) +#endif +#if ENABLE(FTL_JIT) + , m_ftlStackLimit(0) + , m_largestFTLStackSize(0) #endif , m_inDefineOwnProperty(false) - , m_codeCache(CodeCache::create(CodeCache::GlobalCodeCache)) + , m_codeCache(std::make_unique()) + , m_enabledProfiler(nullptr) + , m_builtinExecutables(std::make_unique(*this)) + , m_typeProfilerEnabledCount(0) + , m_controlFlowProfilerEnabledCount(0) { interpreter = new Interpreter(*this); + StackBounds stack = wtfThreadData().stack(); + updateReservedZoneSize(Options::reservedZoneSize()); +#if !ENABLE(JIT) + interpreter->stack().setReservedZoneSize(Options::reservedZoneSize()); +#endif + setLastStackTop(stack.origin()); // Need to be careful to keep everything consistent here JSLockHolder lock(this); - IdentifierTable* existingEntryIdentifierTable = wtfThreadData().setCurrentIdentifierTable(identifierTable); + AtomicStringTable* existingEntryAtomicStringTable = wtfThreadData().setCurrentAtomicStringTable(m_atomicStringTable); + propertyNames = new CommonIdentifiers(this); structureStructure.set(*this, Structure::createStructure(*this)); structureRareDataStructure.set(*this, StructureRareData::createStructure(*this, 0, jsNull())); - debuggerActivationStructure.set(*this, DebuggerActivation::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())); + propertyNameEnumeratorStructure.set(*this, JSPropertyNameEnumerator::createStructure(*this, 0, jsNull())); getterSetterStructure.set(*this, GetterSetter::createStructure(*this, 0, jsNull())); + customGetterSetterStructure.set(*this, CustomGetterSetter::createStructure(*this, 0, jsNull())); + scopedArgumentsTableStructure.set(*this, ScopedArgumentsTable::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())); @@ -222,23 +221,38 @@ VM::VM(VMType vmType, HeapType heapType) 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())); + symbolStructure.set(*this, Symbol::createStructure(*this, 0, jsNull())); + symbolTableStructure.set(*this, SymbolTable::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())); + templateRegistryKeyStructure.set(*this, JSTemplateRegistryKey::createStructure(*this, 0, jsNull())); + arrayBufferNeuteringWatchpointStructure.set(*this, ArrayBufferNeuteringWatchpoint::createStructure(*this)); 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())); propertyTableStructure.set(*this, PropertyTable::createStructure(*this, 0, jsNull())); + weakMapDataStructure.set(*this, WeakMapData::createStructure(*this, 0, jsNull())); + inferredValueStructure.set(*this, InferredValue::createStructure(*this, 0, jsNull())); + functionRareDataStructure.set(*this, FunctionRareData::createStructure(*this, 0, jsNull())); + exceptionStructure.set(*this, Exception::createStructure(*this, 0, jsNull())); +#if ENABLE(PROMISES) + promiseDeferredStructure.set(*this, JSPromiseDeferred::createStructure(*this, 0, jsNull())); +#endif + iterationTerminator.set(*this, JSFinalObject::create(*this, JSFinalObject::createStructure(*this, 0, jsNull(), 1))); smallStrings.initializeCommonStrings(*this); - wtfThreadData().setCurrentIdentifierTable(existingEntryIdentifierTable); + wtfThreadData().setCurrentAtomicStringTable(existingEntryAtomicStringTable); #if ENABLE(JIT) - jitStubs = adoptPtr(new JITThunks()); - performPlatformSpecificJITAssertions(this); + jitStubs = std::make_unique(); + arityCheckFailReturnThunks = std::make_unique(); #endif + arityCheckData = std::make_unique(); + +#if ENABLE(FTL_JIT) + ftlThunks = std::make_unique(); +#endif // ENABLE(FTL_JIT) interpreter->initialize(this->canUseJIT()); @@ -247,33 +261,56 @@ VM::VM(VMType vmType, HeapType heapType) #endif heap.notifyIsSafeToCollect(); - + LLInt::Data::performAssertions(*this); if (Options::enableProfiler()) { - m_perBytecodeProfiler = adoptPtr(new Profiler::Database(*this)); + m_perBytecodeProfiler = std::make_unique(*this); StringPrintStream pathOut; -#if !OS(WINCE) const char* profilerPath = getenv("JSC_PROFILER_PATH"); if (profilerPath) pathOut.print(profilerPath, "/"); -#endif pathOut.print("JSCProfile-", getCurrentProcessID(), "-", m_perBytecodeProfiler->databaseID(), ".json"); m_perBytecodeProfiler->registerToSaveAtExit(pathOut.toCString().data()); } #if ENABLE(DFG_JIT) if (canUseJIT()) - m_dfgState = adoptPtr(new DFG::LongLivedState()); + dfgState = std::make_unique(); #endif + + // Initialize this last, as a free way of asserting that VM initialization itself + // won't use this. + m_typedArrayController = adoptRef(new SimpleTypedArrayController()); + + if (Options::enableTypeProfiler()) + enableTypeProfiler(); + if (Options::enableControlFlowProfiler()) + enableControlFlowProfiler(); } VM::~VM() { - // Clear this first to ensure that nobody tries to remove themselves from it. - m_perBytecodeProfiler.clear(); + // Never GC, ever again. + heap.incrementDeferralDepth(); + +#if ENABLE(DFG_JIT) + // Make sure concurrent compilations are done, but don't install them, since there is + // no point to doing so. + for (unsigned i = DFG::numberOfWorklists(); i--;) { + if (DFG::Worklist* worklist = DFG::worklistForIndexOrNull(i)) { + worklist->waitUntilAllPlansForVMAreReady(*this); + worklist->removeAllReadyPlansForVM(*this); + } + } +#endif // ENABLE(DFG_JIT) + waitForAsynchronousDisassembly(); + + // Clear this first to ensure that nobody tries to remove themselves from it. + m_perBytecodeProfiler = nullptr; + ASSERT(m_apiLock->currentThreadIsHoldingLock()); m_apiLock->willDestroyVM(this); heap.lastChanceToFinalize(); @@ -283,47 +320,11 @@ VM::~VM() interpreter = reinterpret_cast(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(); - privateNamePrototypeTable->deleteTable(); - regExpTable->deleteTable(); - regExpConstructorTable->deleteTable(); - regExpPrototypeTable->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(privateNamePrototypeTable)); - fastDelete(const_cast(regExpTable)); - fastDelete(const_cast(regExpConstructorTable)); - fastDelete(const_cast(regExpPrototypeTable)); - fastDelete(const_cast(stringConstructorTable)); - delete emptyList; delete propertyNames; if (vmType != Default) - deleteIdentifierTable(identifierTable); + delete m_atomicStringTable; delete clientData; delete m_regExpCache; @@ -337,17 +338,17 @@ VM::~VM() #endif } -PassRefPtr VM::createContextGroup(HeapType heapType) +Ref VM::createContextGroup(HeapType heapType) { - return adoptRef(new VM(APIContextGroup, heapType)); + return adoptRef(*new VM(APIContextGroup, heapType)); } -PassRefPtr VM::create(HeapType heapType) +Ref VM::create(HeapType heapType) { - return adoptRef(new VM(Default, heapType)); + return adoptRef(*new VM(Default, heapType)); } -PassRefPtr VM::createLeaked(HeapType heapType) +Ref VM::createLeaked(HeapType heapType) { return create(heapType); } @@ -361,10 +362,8 @@ VM& VM::sharedInstance() { GlobalJSLock globalLock; VM*& instance = sharedInstanceInternal(); - if (!instance) { + if (!instance) instance = adoptRef(new VM(APIShared, SmallHeap)).leakRef(); - instance->makeUsableFromMultipleThreads(); - } return *instance; } @@ -382,6 +381,8 @@ static ThunkGenerator thunkGeneratorForIntrinsic(Intrinsic intrinsic) return charCodeAtThunkGenerator; case CharAtIntrinsic: return charAtThunkGenerator; + case Clz32Intrinsic: + return clz32ThunkGenerator; case FromCharCodeIntrinsic: return fromCharCodeThunkGenerator; case SqrtIntrinsic: @@ -418,10 +419,15 @@ NativeExecutable* VM::getHostFunction(NativeFunction function, Intrinsic intrins } #else // !ENABLE(JIT) + NativeExecutable* VM::getHostFunction(NativeFunction function, NativeFunction constructor) { - return NativeExecutable::create(*this, function, constructor); + return NativeExecutable::create(*this, + adoptRef(new NativeJITCode(MacroAssemblerCodeRef::createLLIntCodeRef(llint_native_call_trampoline), JITCode::HostCallThunk)), function, + adoptRef(new NativeJITCode(MacroAssemblerCodeRef::createLLIntCodeRef(llint_native_construct_trampoline), JITCode::HostCallThunk)), constructor, + NoIntrinsic); } + #endif // !ENABLE(JIT) VM::ClientData::~ClientData() @@ -432,7 +438,7 @@ void VM::resetDateCache() { localTimeOffsetCache.reset(); cachedDateString = String(); - cachedDateStringValue = QNaN; + cachedDateStringValue = std::numeric_limits::quiet_NaN(); dateInstanceCache.reset(); } @@ -446,10 +452,23 @@ void VM::stopSampling() interpreter->stopSampling(); } +void VM::prepareToDiscardCode() +{ +#if ENABLE(DFG_JIT) + for (unsigned i = DFG::numberOfWorklists(); i--;) { + if (DFG::Worklist* worklist = DFG::worklistForIndexOrNull(i)) + worklist->completeAllPlansForVM(*this); + } +#endif // ENABLE(DFG_JIT) +} + void VM::discardAllCode() { + prepareToDiscardCode(); m_codeCache->clear(); + m_regExpCache->invalidateCode(); heap.deleteAllCompiledCode(); + heap.deleteAllUnlinkedFunctionCode(); heap.reportAbandonedObjectGraph(); } @@ -463,7 +482,7 @@ void VM::dumpSampleData(ExecState* exec) SourceProviderCache* VM::addSourceProviderCache(SourceProvider* sourceProvider) { - SourceProviderCacheMap::AddResult addResult = sourceProviderCacheMap.add(sourceProvider, 0); + auto addResult = sourceProviderCacheMap.add(sourceProvider, nullptr); if (addResult.isNewEntry) addResult.iterator->value = adoptRef(new SourceProviderCache); return addResult.iterator->value.get(); @@ -476,60 +495,167 @@ void VM::clearSourceProviderCaches() struct StackPreservingRecompiler : public MarkedBlock::VoidFunctor { HashSet currentlyExecutingFunctions; - void operator()(JSCell* cell) + inline void visit(JSCell* cell) { - if (!cell->inherits(&FunctionExecutable::s_info)) + if (!cell->inherits(FunctionExecutable::info())) return; FunctionExecutable* executable = jsCast(cell); if (currentlyExecutingFunctions.contains(executable)) return; - executable->clearCodeIfNotCompiling(); + executable->clearCode(); + } + IterationStatus operator()(JSCell* cell) + { + visit(cell); + return IterationStatus::Continue; } }; void VM::releaseExecutableMemory() { - if (dynamicGlobalObject) { + prepareToDiscardCode(); + + if (entryScope) { StackPreservingRecompiler recompiler; + HeapIterationScope iterationScope(heap); HashSet roots; - heap.canonicalizeCellLivenessData(); 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)) + if (cell->inherits(ScriptExecutable::info())) executable = static_cast(*ptr); - else if (cell->inherits(&JSFunction::s_info)) { + else if (cell->inherits(JSFunction::info())) { JSFunction* function = jsCast(*ptr); if (function->isHostFunction()) continue; executable = function->jsExecutable(); } else continue; - ASSERT(executable->inherits(&ScriptExecutable::s_info)); + ASSERT(executable->inherits(ScriptExecutable::info())); executable->unlinkCalls(); - if (executable->inherits(&FunctionExecutable::s_info)) + if (executable->inherits(FunctionExecutable::info())) recompiler.currentlyExecutingFunctions.add(static_cast(executable)); } - heap.objectSpace().forEachLiveCell(recompiler); + heap.objectSpace().forEachLiveCell(iterationScope, recompiler); } m_regExpCache->invalidateCode(); heap.collectAllGarbage(); } -void VM::clearExceptionStack() +void VM::throwException(ExecState* exec, Exception* exception) { - m_exceptionStack = RefCountedArray(); -} + if (Options::breakOnThrow()) { + dataLog("In call frame ", RawPointer(exec), " for code block ", *exec->codeBlock(), "\n"); + CRASH(); + } -#if ENABLE(ASSEMBLER) + ASSERT(exec == topCallFrame || exec == exec->lexicalGlobalObject()->globalExec() || exec == exec->vmEntryGlobalObject()->globalExec()); + setException(exception); +} + +JSValue VM::throwException(ExecState* exec, JSValue thrownValue) +{ + Exception* exception = jsDynamicCast(thrownValue); + if (!exception) + exception = Exception::create(*this, thrownValue); + + throwException(exec, exception); + return JSValue(exception); +} + +JSObject* VM::throwException(ExecState* exec, JSObject* error) +{ + return asObject(throwException(exec, JSValue(error))); +} + +void VM::setStackPointerAtVMEntry(void* sp) +{ + m_stackPointerAtVMEntry = sp; + updateStackLimit(); +} + +size_t VM::updateReservedZoneSize(size_t reservedZoneSize) +{ + size_t oldReservedZoneSize = m_reservedZoneSize; + m_reservedZoneSize = reservedZoneSize; + + updateStackLimit(); + + return oldReservedZoneSize; +} + +#if PLATFORM(WIN) +// On Windows the reserved stack space consists of committed memory, a guard page, and uncommitted memory, +// where the guard page is a barrier between committed and uncommitted memory. +// When data from the guard page is read or written, the guard page is moved, and memory is committed. +// This is how the system grows the stack. +// When using the C stack on Windows we need to precommit the needed stack space. +// Otherwise we might crash later if we access uncommitted stack memory. +// This can happen if we allocate stack space larger than the page guard size (4K). +// The system does not get the chance to move the guard page, and commit more memory, +// and we crash if uncommitted memory is accessed. +// The MSVC compiler fixes this by inserting a call to the _chkstk() function, +// when needed, see http://support.microsoft.com/kb/100775. +// By touching every page up to the stack limit with a dummy operation, +// we force the system to move the guard page, and commit memory. + +static void preCommitStackMemory(void* stackLimit) +{ + const int pageSize = 4096; + for (volatile char* p = reinterpret_cast(&stackLimit); p > stackLimit; p -= pageSize) { + char ch = *p; + *p = ch; + } +} +#endif + +inline void VM::updateStackLimit() +{ +#if PLATFORM(WIN) + void* lastStackLimit = m_stackLimit; +#endif + + if (m_stackPointerAtVMEntry) { + ASSERT(wtfThreadData().stack().isGrowingDownward()); + char* startOfStack = reinterpret_cast(m_stackPointerAtVMEntry); +#if ENABLE(FTL_JIT) + m_stackLimit = wtfThreadData().stack().recursionLimit(startOfStack, Options::maxPerThreadStackUsage(), m_reservedZoneSize + m_largestFTLStackSize); + m_ftlStackLimit = wtfThreadData().stack().recursionLimit(startOfStack, Options::maxPerThreadStackUsage(), m_reservedZoneSize + 2 * m_largestFTLStackSize); +#else + m_stackLimit = wtfThreadData().stack().recursionLimit(startOfStack, Options::maxPerThreadStackUsage(), m_reservedZoneSize); +#endif + } else { +#if ENABLE(FTL_JIT) + m_stackLimit = wtfThreadData().stack().recursionLimit(m_reservedZoneSize + m_largestFTLStackSize); + m_ftlStackLimit = wtfThreadData().stack().recursionLimit(m_reservedZoneSize + 2 * m_largestFTLStackSize); +#else + m_stackLimit = wtfThreadData().stack().recursionLimit(m_reservedZoneSize); +#endif + } + +#if PLATFORM(WIN) + if (lastStackLimit != m_stackLimit) + preCommitStackMemory(m_stackLimit); +#endif +} + +#if ENABLE(FTL_JIT) +void VM::updateFTLLargestStackSize(size_t stackSize) +{ + if (stackSize > m_largestFTLStackSize) { + m_largestFTLStackSize = stackSize; + updateStackLimit(); + } +} +#endif + void releaseExecutableMemory(VM& vm) { vm.releaseExecutableMemory(); } -#endif #if ENABLE(DFG_JIT) void VM::gatherConservativeRoots(ConservativeRoots& conservativeRoots) @@ -544,9 +670,22 @@ void VM::gatherConservativeRoots(ConservativeRoots& conservativeRoots) } #endif +void logSanitizeStack(VM* vm) +{ + if (Options::verboseSanitizeStack() && vm->topCallFrame) { + int dummy; + dataLog( + "Sanitizing stack with top call frame at ", RawPointer(vm->topCallFrame), + ", current stack pointer at ", RawPointer(&dummy), ", in ", + pointerDump(vm->topCallFrame->codeBlock()), " and last code origin = ", + vm->topCallFrame->codeOrigin(), "\n"); + } +} + #if ENABLE(REGEXP_TRACING) void VM::addRegExpToTrace(RegExp* regExp) { + gcProtect(regExp); m_rtTraceList->add(regExp); } @@ -557,14 +696,16 @@ void VM::dumpRegExpTrace() if (iter != m_rtTraceList->end()) { dataLogF("\nRegExp Tracing\n"); - dataLogF(" match() matches\n"); - dataLogF("Regular Expression JIT Address calls found\n"); - dataLogF("----------------------------------------+----------------+----------+----------\n"); + dataLogF("Regular Expression 8 Bit 16 Bit match() Matches Average\n"); + dataLogF(" JIT Addr JIT Address calls found String len\n"); + dataLogF("----------------------------------------+----------------+----------------+----------+----------+-----------\n"); unsigned reCount = 0; - for (; iter != m_rtTraceList->end(); ++iter, ++reCount) + for (; iter != m_rtTraceList->end(); ++iter, ++reCount) { (*iter)->printTraceData(); + gcUnprotect(*iter); + } dataLogF("%d Regular Expressions\n", reCount); } @@ -577,4 +718,120 @@ void VM::dumpRegExpTrace() } #endif +void VM::registerWatchpointForImpureProperty(const Identifier& propertyName, Watchpoint* watchpoint) +{ + auto result = m_impurePropertyWatchpointSets.add(propertyName.string(), nullptr); + if (result.isNewEntry) + result.iterator->value = adoptRef(new WatchpointSet(IsWatched)); + result.iterator->value->add(watchpoint); +} + +void VM::addImpureProperty(const String& propertyName) +{ + if (RefPtr watchpointSet = m_impurePropertyWatchpointSets.take(propertyName)) + watchpointSet->fireAll("Impure property added"); +} + +class SetEnabledProfilerFunctor { +public: + bool operator()(CodeBlock* codeBlock) + { + if (JITCode::isOptimizingJIT(codeBlock->jitType())) + codeBlock->jettison(Profiler::JettisonDueToLegacyProfiler); + return false; + } +}; + +void VM::setEnabledProfiler(LegacyProfiler* profiler) +{ + m_enabledProfiler = profiler; + if (m_enabledProfiler) { + prepareToDiscardCode(); + SetEnabledProfilerFunctor functor; + heap.forEachCodeBlock(functor); + } +} + +static bool enableProfilerWithRespectToCount(unsigned& counter, std::function doEnableWork) +{ + bool needsToRecompile = false; + if (!counter) { + doEnableWork(); + needsToRecompile = true; + } + counter++; + + return needsToRecompile; +} + +static bool disableProfilerWithRespectToCount(unsigned& counter, std::function doDisableWork) +{ + RELEASE_ASSERT(counter > 0); + bool needsToRecompile = false; + counter--; + if (!counter) { + doDisableWork(); + needsToRecompile = true; + } + + return needsToRecompile; +} + +bool VM::enableTypeProfiler() +{ + auto enableTypeProfiler = [this] () { + this->m_typeProfiler = std::make_unique(); + this->m_typeProfilerLog = std::make_unique(); + }; + + return enableProfilerWithRespectToCount(m_typeProfilerEnabledCount, enableTypeProfiler); +} + +bool VM::disableTypeProfiler() +{ + auto disableTypeProfiler = [this] () { + this->m_typeProfiler.reset(nullptr); + this->m_typeProfilerLog.reset(nullptr); + }; + + return disableProfilerWithRespectToCount(m_typeProfilerEnabledCount, disableTypeProfiler); +} + +bool VM::enableControlFlowProfiler() +{ + auto enableControlFlowProfiler = [this] () { + this->m_controlFlowProfiler = std::make_unique(); + }; + + return enableProfilerWithRespectToCount(m_controlFlowProfilerEnabledCount, enableControlFlowProfiler); +} + +bool VM::disableControlFlowProfiler() +{ + auto disableControlFlowProfiler = [this] () { + this->m_controlFlowProfiler.reset(nullptr); + }; + + return disableProfilerWithRespectToCount(m_controlFlowProfilerEnabledCount, disableControlFlowProfiler); +} + +void VM::dumpTypeProfilerData() +{ + if (!typeProfiler()) + return; + + typeProfilerLog()->processLogEntries(ASCIILiteral("VM Dump Types")); + typeProfiler()->dumpTypeProfilerData(*this); +} + +void sanitizeStackForVM(VM* vm) +{ + logSanitizeStack(vm); +#if !ENABLE(JIT) + vm->interpreter->stack().sanitizeStack(); +#else + sanitizeStackForVMImpl(vm); +#endif +} + } // namespace JSC