|  | /* | 
|  | * Copyright (C) 2008-2022 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 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 "VM.h" | 
|  |  | 
|  | #include "AccessCase.h" | 
|  | #include "AggregateError.h" | 
|  | #include "ArgList.h" | 
|  | #include "BuiltinExecutables.h" | 
|  | #include "BytecodeIntrinsicRegistry.h" | 
|  | #include "CheckpointOSRExitSideState.h" | 
|  | #include "CodeBlock.h" | 
|  | #include "CodeCache.h" | 
|  | #include "CommonIdentifiers.h" | 
|  | #include "ControlFlowProfiler.h" | 
|  | #include "CustomGetterSetter.h" | 
|  | #include "DOMAttributeGetterSetter.h" | 
|  | #include "DeferredWorkTimer.h" | 
|  | #include "Disassembler.h" | 
|  | #include "DoublePredictionFuzzerAgent.h" | 
|  | #include "ErrorInstance.h" | 
|  | #include "EvalCodeBlock.h" | 
|  | #include "Exception.h" | 
|  | #include "ExecutableToCodeBlockEdge.h" | 
|  | #include "FTLThunks.h" | 
|  | #include "FileBasedFuzzerAgent.h" | 
|  | #include "FunctionCodeBlock.h" | 
|  | #include "FunctionExecutable.h" | 
|  | #include "GetterSetter.h" | 
|  | #include "GigacageAlignedMemoryAllocator.h" | 
|  | #include "HasOwnPropertyCache.h" | 
|  | #include "Heap.h" | 
|  | #include "HeapProfiler.h" | 
|  | #include "Interpreter.h" | 
|  | #include "IntlCache.h" | 
|  | #include "JITCode.h" | 
|  | #include "JITOperationList.h" | 
|  | #include "JITSizeStatistics.h" | 
|  | #include "JITThunks.h" | 
|  | #include "JITWorklist.h" | 
|  | #include "JSAPIValueWrapper.h" | 
|  | #include "JSBigInt.h" | 
|  | #include "JSGlobalObject.h" | 
|  | #include "JSImmutableButterfly.h" | 
|  | #include "JSLock.h" | 
|  | #include "JSMap.h" | 
|  | #include "JSPromise.h" | 
|  | #include "JSPropertyNameEnumerator.h" | 
|  | #include "JSScriptFetchParameters.h" | 
|  | #include "JSScriptFetcher.h" | 
|  | #include "JSSet.h" | 
|  | #include "JSSourceCode.h" | 
|  | #include "JSTemplateObjectDescriptor.h" | 
|  | #include "LLIntData.h" | 
|  | #include "LLIntExceptions.h" | 
|  | #include "MarkedBlockInlines.h" | 
|  | #include "MinimumReservedZoneSize.h" | 
|  | #include "ModuleProgramCodeBlock.h" | 
|  | #include "ModuleProgramExecutable.h" | 
|  | #include "NarrowingNumberPredictionFuzzerAgent.h" | 
|  | #include "NativeExecutable.h" | 
|  | #include "NumberObject.h" | 
|  | #include "PredictionFileCreatingFuzzerAgent.h" | 
|  | #include "ProfilerDatabase.h" | 
|  | #include "ProgramCodeBlock.h" | 
|  | #include "ProgramExecutable.h" | 
|  | #include "PropertyTable.h" | 
|  | #include "RandomizingFuzzerAgent.h" | 
|  | #include "RegExpCache.h" | 
|  | #include "SamplingProfiler.h" | 
|  | #include "ScopedArguments.h" | 
|  | #include "ShadowChicken.h" | 
|  | #include "SimpleTypedArrayController.h" | 
|  | #include "SourceProviderCache.h" | 
|  | #include "StrongInlines.h" | 
|  | #include "StructureChain.h" | 
|  | #include "StructureInlines.h" | 
|  | #include "TestRunnerUtils.h" | 
|  | #include "ThunkGenerators.h" | 
|  | #include "TypeProfiler.h" | 
|  | #include "TypeProfilerLog.h" | 
|  | #include "VMEntryScope.h" | 
|  | #include "VMInlines.h" | 
|  | #include "VMInspector.h" | 
|  | #include "VariableEnvironment.h" | 
|  | #include "WasmWorklist.h" | 
|  | #include "Watchdog.h" | 
|  | #include "WeakGCMapInlines.h" | 
|  | #include "WideningNumberPredictionFuzzerAgent.h" | 
|  | #include <wtf/ProcessID.h> | 
|  | #include <wtf/ReadWriteLock.h> | 
|  | #include <wtf/SimpleStats.h> | 
|  | #include <wtf/StackTrace.h> | 
|  | #include <wtf/StringPrintStream.h> | 
|  | #include <wtf/Threading.h> | 
|  | #include <wtf/text/AtomStringTable.h> | 
|  |  | 
|  | #if ENABLE(C_LOOP) | 
|  | #include "CLoopStackInlines.h" | 
|  | #endif | 
|  |  | 
|  | #if ENABLE(DFG_JIT) | 
|  | #include "ConservativeRoots.h" | 
|  | #endif | 
|  |  | 
|  | #if ENABLE(REGEXP_TRACING) | 
|  | #include "RegExp.h" | 
|  | #endif | 
|  |  | 
|  | namespace JSC { | 
|  |  | 
|  | Atomic<unsigned> VM::s_numberOfIDs; | 
|  |  | 
|  | DEFINE_ALLOCATOR_WITH_HEAP_IDENTIFIER(VM); | 
|  |  | 
|  | // 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() | 
|  | { | 
|  | if (!Options::useJIT()) | 
|  | return false; | 
|  |  | 
|  | char* canUseJITString = getenv("JavaScriptCoreUseJIT"); | 
|  | if (canUseJITString && !atoi(canUseJITString)) | 
|  | return false; | 
|  |  | 
|  | ExecutableAllocator::initializeUnderlyingAllocator(); | 
|  | if (!ExecutableAllocator::singleton().isValid()) { | 
|  | if (Options::crashIfCantAllocateJITMemory()) | 
|  | CRASH(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  | #endif // ENABLE(!ASSEMBLER) | 
|  |  | 
|  | bool VM::canUseAssembler() | 
|  | { | 
|  | #if ENABLE(ASSEMBLER) | 
|  | static std::once_flag onceKey; | 
|  | static bool enabled = false; | 
|  | std::call_once(onceKey, [] { | 
|  | enabled = enableAssembler(); | 
|  | }); | 
|  | return enabled; | 
|  | #else | 
|  | return false; // interpreter only | 
|  | #endif | 
|  | } | 
|  |  | 
|  | void VM::computeCanUseJIT() | 
|  | { | 
|  | #if ENABLE(JIT) | 
|  | #if ASSERT_ENABLED | 
|  | RELEASE_ASSERT(!g_jscConfig.vm.canUseJITIsSet); | 
|  | g_jscConfig.vm.canUseJITIsSet = true; | 
|  | #endif | 
|  | g_jscConfig.vm.canUseJIT = VM::canUseAssembler() && Options::useJIT(); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | inline unsigned VM::nextID() | 
|  | { | 
|  | for (;;) { | 
|  | unsigned currentNumberOfIDs = s_numberOfIDs.load(); | 
|  | unsigned newID = currentNumberOfIDs + 1; | 
|  | if (s_numberOfIDs.compareExchangeWeak(currentNumberOfIDs, newID)) | 
|  | return newID; | 
|  | } | 
|  | } | 
|  |  | 
|  | static bool vmCreationShouldCrash = false; | 
|  |  | 
|  | VM::VM(VMType vmType, HeapType heapType, WTF::RunLoop* runLoop, bool* success) | 
|  | : m_id(nextID()) | 
|  | , m_apiLock(adoptRef(new JSLock(this))) | 
|  | , m_runLoop(runLoop ? *runLoop : WTF::RunLoop::current()) | 
|  | , m_random(Options::seedOfVMRandomForFuzzer() ? Options::seedOfVMRandomForFuzzer() : cryptographicallyRandomNumber()) | 
|  | , m_integrityRandom(*this) | 
|  | , heap(*this, heapType) | 
|  | , vmType(vmType) | 
|  | , clientData(nullptr) | 
|  | , topEntryFrame(nullptr) | 
|  | , topCallFrame(CallFrame::noCaller()) | 
|  | , deferredWorkTimer(DeferredWorkTimer::create(*this)) | 
|  | , m_atomStringTable(vmType == Default ? Thread::current().atomStringTable() : new AtomStringTable) | 
|  | , m_privateSymbolRegistry(WTF::SymbolRegistry::Type::PrivateSymbol) | 
|  | , propertyNames(nullptr) | 
|  | , emptyList(new ArgList) | 
|  | , machineCodeBytesPerBytecodeWordForBaselineJIT(makeUnique<SimpleStats>()) | 
|  | , symbolImplToSymbolMap(*this) | 
|  | , structureCache(*this) | 
|  | , interpreter(nullptr) | 
|  | , entryScope(nullptr) | 
|  | , m_regExpCache(new RegExpCache(this)) | 
|  | , m_compactVariableMap(adoptRef(*(new CompactTDZEnvironmentMap))) | 
|  | #if ENABLE(REGEXP_TRACING) | 
|  | , m_rtTraceList(new RTTraceList()) | 
|  | #endif | 
|  | #if ENABLE(GC_VALIDATION) | 
|  | , m_initializingObjectClass(0) | 
|  | #endif | 
|  | , m_stackPointerAtVMEntry(nullptr) | 
|  | , m_codeCache(makeUnique<CodeCache>()) | 
|  | , m_intlCache(makeUnique<IntlCache>()) | 
|  | , m_builtinExecutables(makeUnique<BuiltinExecutables>(*this)) | 
|  | , m_typeProfilerEnabledCount(0) | 
|  | , m_primitiveGigacageEnabled(IsWatched) | 
|  | , m_controlFlowProfilerEnabledCount(0) | 
|  | { | 
|  | if (UNLIKELY(vmCreationShouldCrash)) | 
|  | CRASH_WITH_INFO(0x4242424220202020, 0xbadbeef0badbeef, 0x1234123412341234, 0x1337133713371337); | 
|  |  | 
|  | interpreter = new Interpreter(*this); | 
|  | updateSoftReservedZoneSize(Options::softReservedZoneSize()); | 
|  | setLastStackTop(Thread::current()); | 
|  |  | 
|  | JSRunLoopTimer::Manager::shared().registerVM(*this); | 
|  |  | 
|  | // Need to be careful to keep everything consistent here | 
|  | JSLockHolder lock(this); | 
|  | AtomStringTable* existingEntryAtomStringTable = Thread::current().setCurrentAtomStringTable(m_atomStringTable); | 
|  | structureStructure.set(*this, Structure::createStructure(*this)); | 
|  | structureRareDataStructure.set(*this, StructureRareData::createStructure(*this, nullptr, jsNull())); | 
|  | stringStructure.set(*this, JSString::createStructure(*this, nullptr, jsNull())); | 
|  |  | 
|  | smallStrings.initializeCommonStrings(*this); | 
|  |  | 
|  | propertyNames = new CommonIdentifiers(*this); | 
|  | propertyNameEnumeratorStructure.set(*this, JSPropertyNameEnumerator::createStructure(*this, nullptr, jsNull())); | 
|  | getterSetterStructure.set(*this, GetterSetter::createStructure(*this, nullptr, jsNull())); | 
|  | customGetterSetterStructure.set(*this, CustomGetterSetter::createStructure(*this, nullptr, jsNull())); | 
|  | domAttributeGetterSetterStructure.set(*this, DOMAttributeGetterSetter::createStructure(*this, nullptr, jsNull())); | 
|  | scopedArgumentsTableStructure.set(*this, ScopedArgumentsTable::createStructure(*this, nullptr, jsNull())); | 
|  | apiWrapperStructure.set(*this, JSAPIValueWrapper::createStructure(*this, nullptr, jsNull())); | 
|  | nativeExecutableStructure.set(*this, NativeExecutable::createStructure(*this, nullptr, jsNull())); | 
|  | evalExecutableStructure.set(*this, EvalExecutable::createStructure(*this, nullptr, jsNull())); | 
|  | programExecutableStructure.set(*this, ProgramExecutable::createStructure(*this, nullptr, jsNull())); | 
|  | functionExecutableStructure.set(*this, FunctionExecutable::createStructure(*this, nullptr, jsNull())); | 
|  | moduleProgramExecutableStructure.set(*this, ModuleProgramExecutable::createStructure(*this, nullptr, jsNull())); | 
|  | regExpStructure.set(*this, RegExp::createStructure(*this, nullptr, jsNull())); | 
|  | symbolStructure.set(*this, Symbol::createStructure(*this, nullptr, jsNull())); | 
|  | symbolTableStructure.set(*this, SymbolTable::createStructure(*this, nullptr, jsNull())); | 
|  |  | 
|  | immutableButterflyStructures[arrayIndexFromIndexingType(CopyOnWriteArrayWithInt32) - NumberOfIndexingShapes].set(*this, JSImmutableButterfly::createStructure(*this, nullptr, jsNull(), CopyOnWriteArrayWithInt32)); | 
|  | immutableButterflyStructures[arrayIndexFromIndexingType(CopyOnWriteArrayWithDouble) - NumberOfIndexingShapes].set(*this, JSImmutableButterfly::createStructure(*this, nullptr, jsNull(), CopyOnWriteArrayWithDouble)); | 
|  | immutableButterflyStructures[arrayIndexFromIndexingType(CopyOnWriteArrayWithContiguous) - NumberOfIndexingShapes].set(*this, JSImmutableButterfly::createStructure(*this, nullptr, jsNull(), CopyOnWriteArrayWithContiguous)); | 
|  |  | 
|  | sourceCodeStructure.set(*this, JSSourceCode::createStructure(*this, nullptr, jsNull())); | 
|  | scriptFetcherStructure.set(*this, JSScriptFetcher::createStructure(*this, nullptr, jsNull())); | 
|  | scriptFetchParametersStructure.set(*this, JSScriptFetchParameters::createStructure(*this, nullptr, jsNull())); | 
|  | structureChainStructure.set(*this, StructureChain::createStructure(*this, nullptr, jsNull())); | 
|  | sparseArrayValueMapStructure.set(*this, SparseArrayValueMap::createStructure(*this, nullptr, jsNull())); | 
|  | templateObjectDescriptorStructure.set(*this, JSTemplateObjectDescriptor::createStructure(*this, nullptr, jsNull())); | 
|  | unlinkedFunctionExecutableStructure.set(*this, UnlinkedFunctionExecutable::createStructure(*this, nullptr, jsNull())); | 
|  | unlinkedProgramCodeBlockStructure.set(*this, UnlinkedProgramCodeBlock::createStructure(*this, nullptr, jsNull())); | 
|  | unlinkedEvalCodeBlockStructure.set(*this, UnlinkedEvalCodeBlock::createStructure(*this, nullptr, jsNull())); | 
|  | unlinkedFunctionCodeBlockStructure.set(*this, UnlinkedFunctionCodeBlock::createStructure(*this, nullptr, jsNull())); | 
|  | unlinkedModuleProgramCodeBlockStructure.set(*this, UnlinkedModuleProgramCodeBlock::createStructure(*this, nullptr, jsNull())); | 
|  | propertyTableStructure.set(*this, PropertyTable::createStructure(*this, nullptr, jsNull())); | 
|  | functionRareDataStructure.set(*this, FunctionRareData::createStructure(*this, nullptr, jsNull())); | 
|  | exceptionStructure.set(*this, Exception::createStructure(*this, nullptr, jsNull())); | 
|  | programCodeBlockStructure.set(*this, ProgramCodeBlock::createStructure(*this, nullptr, jsNull())); | 
|  | moduleProgramCodeBlockStructure.set(*this, ModuleProgramCodeBlock::createStructure(*this, nullptr, jsNull())); | 
|  | evalCodeBlockStructure.set(*this, EvalCodeBlock::createStructure(*this, nullptr, jsNull())); | 
|  | functionCodeBlockStructure.set(*this, FunctionCodeBlock::createStructure(*this, nullptr, jsNull())); | 
|  | hashMapBucketSetStructure.set(*this, HashMapBucket<HashMapBucketDataKey>::createStructure(*this, nullptr, jsNull())); | 
|  | hashMapBucketMapStructure.set(*this, HashMapBucket<HashMapBucketDataKeyValue>::createStructure(*this, nullptr, jsNull())); | 
|  | bigIntStructure.set(*this, JSBigInt::createStructure(*this, nullptr, jsNull())); | 
|  | executableToCodeBlockEdgeStructure.set(*this, ExecutableToCodeBlockEdge::createStructure(*this, nullptr, jsNull())); | 
|  |  | 
|  | // Eagerly initialize constant cells since the concurrent compiler can access them. | 
|  | if (Options::useJIT()) { | 
|  | sentinelMapBucket(); | 
|  | sentinelSetBucket(); | 
|  | emptyPropertyNameEnumerator(); | 
|  | } | 
|  | { | 
|  | auto* bigInt = JSBigInt::tryCreateFrom(*this, 1); | 
|  | if (bigInt) | 
|  | heapBigIntConstantOne.set(*this, bigInt); | 
|  | else { | 
|  | if (success) | 
|  | *success = false; | 
|  | else | 
|  | RELEASE_ASSERT(bigInt); | 
|  | } | 
|  | } | 
|  |  | 
|  | Thread::current().setCurrentAtomStringTable(existingEntryAtomStringTable); | 
|  |  | 
|  | Gigacage::addPrimitiveDisableCallback(primitiveGigacageDisabledCallback, this); | 
|  |  | 
|  | heap.notifyIsSafeToCollect(); | 
|  |  | 
|  | LLInt::Data::performAssertions(*this); | 
|  |  | 
|  | if (UNLIKELY(Options::useProfiler())) { | 
|  | m_perBytecodeProfiler = makeUnique<Profiler::Database>(*this); | 
|  |  | 
|  | StringPrintStream pathOut; | 
|  | const char* profilerPath = getenv("JSC_PROFILER_PATH"); | 
|  | if (profilerPath) | 
|  | pathOut.print(profilerPath, "/"); | 
|  | pathOut.print("JSCProfile-", getCurrentProcessID(), "-", m_perBytecodeProfiler->databaseID(), ".json"); | 
|  | m_perBytecodeProfiler->registerToSaveAtExit(pathOut.toCString().data()); | 
|  | } | 
|  |  | 
|  | callFrameForCatch = nullptr; | 
|  |  | 
|  | // Initialize this last, as a free way of asserting that VM initialization itself | 
|  | // won't use this. | 
|  | m_typedArrayController = adoptRef(new SimpleTypedArrayController()); | 
|  |  | 
|  | m_bytecodeIntrinsicRegistry = makeUnique<BytecodeIntrinsicRegistry>(*this); | 
|  |  | 
|  | if (Options::useTypeProfiler()) | 
|  | enableTypeProfiler(); | 
|  | if (Options::useControlFlowProfiler()) | 
|  | enableControlFlowProfiler(); | 
|  | #if ENABLE(SAMPLING_PROFILER) | 
|  | if (Options::useSamplingProfiler()) { | 
|  | setShouldBuildPCToCodeOriginMapping(); | 
|  | Ref<Stopwatch> stopwatch = Stopwatch::create(); | 
|  | stopwatch->start(); | 
|  | m_samplingProfiler = adoptRef(new SamplingProfiler(*this, WTFMove(stopwatch))); | 
|  | if (Options::samplingProfilerPath()) | 
|  | m_samplingProfiler->registerForReportAtExit(); | 
|  | m_samplingProfiler->start(); | 
|  | } | 
|  | #endif // ENABLE(SAMPLING_PROFILER) | 
|  |  | 
|  | if (Options::useRandomizingFuzzerAgent()) | 
|  | setFuzzerAgent(makeUnique<RandomizingFuzzerAgent>(*this)); | 
|  | if (Options::useDoublePredictionFuzzerAgent()) | 
|  | setFuzzerAgent(makeUnique<DoublePredictionFuzzerAgent>(*this)); | 
|  | if (Options::useFileBasedFuzzerAgent()) | 
|  | setFuzzerAgent(makeUnique<FileBasedFuzzerAgent>(*this)); | 
|  | if (Options::usePredictionFileCreatingFuzzerAgent()) | 
|  | setFuzzerAgent(makeUnique<PredictionFileCreatingFuzzerAgent>(*this)); | 
|  | if (Options::useNarrowingNumberPredictionFuzzerAgent()) | 
|  | setFuzzerAgent(makeUnique<NarrowingNumberPredictionFuzzerAgent>(*this)); | 
|  | if (Options::useWideningNumberPredictionFuzzerAgent()) | 
|  | setFuzzerAgent(makeUnique<WideningNumberPredictionFuzzerAgent>(*this)); | 
|  |  | 
|  | if (Options::alwaysGeneratePCToCodeOriginMap()) | 
|  | setShouldBuildPCToCodeOriginMapping(); | 
|  |  | 
|  | if (Options::watchdog()) { | 
|  | Watchdog& watchdog = ensureWatchdog(); | 
|  | watchdog.setTimeLimit(Seconds::fromMilliseconds(Options::watchdog())); | 
|  | } | 
|  |  | 
|  | #if ENABLE(JIT) | 
|  | // Make sure that any stubs that the JIT is going to use are initialized in non-compilation threads. | 
|  | if (Options::useJIT()) { | 
|  | jitStubs = makeUnique<JITThunks>(); | 
|  | #if ENABLE(FTL_JIT) | 
|  | ftlThunks = makeUnique<FTL::Thunks>(); | 
|  | #endif // ENABLE(FTL_JIT) | 
|  | getCTIInternalFunctionTrampolineFor(CodeForCall); | 
|  | getCTIInternalFunctionTrampolineFor(CodeForConstruct); | 
|  | m_sharedJITStubs = makeUnique<SharedJITStubSet>(); | 
|  | } | 
|  | #endif // ENABLE(JIT) | 
|  |  | 
|  | if (Options::forceDebuggerBytecodeGeneration() || Options::alwaysUseShadowChicken()) | 
|  | ensureShadowChicken(); | 
|  |  | 
|  | #if ENABLE(JIT) | 
|  | if (Options::dumpBaselineJITSizeStatistics() || Options::dumpDFGJITSizeStatistics()) | 
|  | jitSizeStatistics = makeUnique<JITSizeStatistics>(); | 
|  | #endif | 
|  |  | 
|  | VMInspector::instance().add(this); | 
|  |  | 
|  | if (!g_jscConfig.disabledFreezingForTesting) | 
|  | Config::permanentlyFreeze(); | 
|  | } | 
|  |  | 
|  | static ReadWriteLock s_destructionLock; | 
|  |  | 
|  | void waitForVMDestruction() | 
|  | { | 
|  | Locker locker { s_destructionLock.write() }; | 
|  | } | 
|  |  | 
|  | VM::~VM() | 
|  | { | 
|  | Locker destructionLocker { s_destructionLock.read() }; | 
|  |  | 
|  | Gigacage::removePrimitiveDisableCallback(primitiveGigacageDisabledCallback, this); | 
|  | deferredWorkTimer->stopRunningTasks(); | 
|  | #if ENABLE(WEBASSEMBLY) | 
|  | if (Wasm::Worklist* worklist = Wasm::existingWorklistOrNull()) | 
|  | worklist->stopAllPlansForContext(wasmContext); | 
|  | #endif | 
|  | if (UNLIKELY(m_watchdog)) | 
|  | m_watchdog->willDestroyVM(this); | 
|  | m_traps.willDestroyVM(); | 
|  | VMInspector::instance().remove(this); | 
|  |  | 
|  | // Never GC, ever again. | 
|  | heap.incrementDeferralDepth(); | 
|  |  | 
|  | #if ENABLE(SAMPLING_PROFILER) | 
|  | if (m_samplingProfiler) { | 
|  | m_samplingProfiler->reportDataToOptionFile(); | 
|  | m_samplingProfiler->shutdown(); | 
|  | } | 
|  | #endif // ENABLE(SAMPLING_PROFILER) | 
|  |  | 
|  | #if ENABLE(JIT) | 
|  | if (JITWorklist* worklist = JITWorklist::existingGlobalWorklistOrNull()) | 
|  | worklist->cancelAllPlansForVM(*this); | 
|  | #endif // ENABLE(JIT) | 
|  |  | 
|  | waitForAsynchronousDisassembly(); | 
|  |  | 
|  | // Clear this first to ensure that nobody tries to remove themselves from it. | 
|  | m_perBytecodeProfiler = nullptr; | 
|  |  | 
|  | ASSERT(currentThreadIsHoldingAPILock()); | 
|  | m_apiLock->willDestroyVM(this); | 
|  | smallStrings.setIsInitialized(false); | 
|  | heap.lastChanceToFinalize(); | 
|  |  | 
|  | JSRunLoopTimer::Manager::shared().unregisterVM(*this); | 
|  |  | 
|  | delete interpreter; | 
|  | #ifndef NDEBUG | 
|  | interpreter = reinterpret_cast<Interpreter*>(0xbbadbeef); | 
|  | #endif | 
|  |  | 
|  | delete emptyList; | 
|  |  | 
|  | delete propertyNames; | 
|  | if (vmType != Default) | 
|  | delete m_atomStringTable; | 
|  |  | 
|  | delete clientData; | 
|  | delete m_regExpCache; | 
|  |  | 
|  | #if ENABLE(REGEXP_TRACING) | 
|  | delete m_rtTraceList; | 
|  | #endif | 
|  |  | 
|  | #if ENABLE(DFG_JIT) | 
|  | for (unsigned i = 0; i < m_scratchBuffers.size(); ++i) | 
|  | VMMalloc::free(m_scratchBuffers[i]); | 
|  | #endif | 
|  |  | 
|  | #if ENABLE(JIT) | 
|  | m_sharedJITStubs = nullptr; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | void VM::primitiveGigacageDisabledCallback(void* argument) | 
|  | { | 
|  | static_cast<VM*>(argument)->primitiveGigacageDisabled(); | 
|  | } | 
|  |  | 
|  | void VM::primitiveGigacageDisabled() | 
|  | { | 
|  | if (m_apiLock->currentThreadIsHoldingLock()) { | 
|  | m_primitiveGigacageEnabled.fireAll(*this, "Primitive gigacage disabled"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // This is totally racy, and that's OK. The point is, it's up to the user to ensure that they pass the | 
|  | // uncaged buffer in a nicely synchronized manner. | 
|  | m_needToFirePrimitiveGigacageEnabled = true; | 
|  | } | 
|  |  | 
|  | void VM::setLastStackTop(const Thread& thread) | 
|  | { | 
|  | m_lastStackTop = thread.savedLastStackTop(); | 
|  | auto& stack = thread.stack(); | 
|  | RELEASE_ASSERT(stack.contains(m_lastStackTop), 0x5510, m_lastStackTop, stack.origin(), stack.end()); | 
|  | } | 
|  |  | 
|  | Ref<VM> VM::createContextGroup(HeapType heapType) | 
|  | { | 
|  | return adoptRef(*new VM(APIContextGroup, heapType)); | 
|  | } | 
|  |  | 
|  | Ref<VM> VM::create(HeapType heapType, WTF::RunLoop* runLoop) | 
|  | { | 
|  | return adoptRef(*new VM(Default, heapType, runLoop)); | 
|  | } | 
|  |  | 
|  | RefPtr<VM> VM::tryCreate(HeapType heapType, WTF::RunLoop* runLoop) | 
|  | { | 
|  | bool success = true; | 
|  | RefPtr<VM> vm = adoptRef(new VM(Default, heapType, runLoop, &success)); | 
|  | if (!success) { | 
|  | // Here, we're destructing a partially constructed VM and we know that | 
|  | // no one else can be using it at the same time. So, acquiring the lock | 
|  | // is superflous. However, we don't want to change how VMs are destructed. | 
|  | // Just going through the motion of acquiring the lock here allows us to | 
|  | // use the standard destruction process. | 
|  |  | 
|  | // VM expects us to be holding the VM lock when destructing it. Acquiring | 
|  | // the lock also puts the VM in a state (e.g. acquiring heap access) that | 
|  | // is needed for destruction. The lock will hold the last reference to | 
|  | // the VM after we nullify the refPtr below. The VM will actually be | 
|  | // destructed in JSLockHolder's destructor. | 
|  | JSLockHolder lock(vm.get()); | 
|  | vm = nullptr; | 
|  | } | 
|  | return vm; | 
|  | } | 
|  |  | 
|  | bool VM::sharedInstanceExists() | 
|  | { | 
|  | return sharedInstanceInternal(); | 
|  | } | 
|  |  | 
|  | VM& VM::sharedInstance() | 
|  | { | 
|  | GlobalJSLock globalLock; | 
|  | VM*& instance = sharedInstanceInternal(); | 
|  | if (!instance) | 
|  | instance = adoptRef(new VM(APIShared, SmallHeap)).leakRef(); | 
|  | return *instance; | 
|  | } | 
|  |  | 
|  | VM*& VM::sharedInstanceInternal() | 
|  | { | 
|  | static VM* sharedInstance; | 
|  | return sharedInstance; | 
|  | } | 
|  |  | 
|  | Watchdog& VM::ensureWatchdog() | 
|  | { | 
|  | if (!m_watchdog) { | 
|  | m_watchdog = adoptRef(new Watchdog(this)); | 
|  | ensureTerminationException(); | 
|  | } | 
|  | return *m_watchdog; | 
|  | } | 
|  |  | 
|  | HeapProfiler& VM::ensureHeapProfiler() | 
|  | { | 
|  | if (!m_heapProfiler) | 
|  | m_heapProfiler = makeUnique<HeapProfiler>(*this); | 
|  | return *m_heapProfiler; | 
|  | } | 
|  |  | 
|  | #if ENABLE(SAMPLING_PROFILER) | 
|  | SamplingProfiler& VM::ensureSamplingProfiler(Ref<Stopwatch>&& stopwatch) | 
|  | { | 
|  | if (!m_samplingProfiler) | 
|  | m_samplingProfiler = adoptRef(new SamplingProfiler(*this, WTFMove(stopwatch))); | 
|  | return *m_samplingProfiler; | 
|  | } | 
|  | #endif // ENABLE(SAMPLING_PROFILER) | 
|  |  | 
|  | static StringImpl::StaticStringImpl terminationErrorString { "JavaScript execution terminated." }; | 
|  | Exception* VM::ensureTerminationException() | 
|  | { | 
|  | if (!m_terminationException) { | 
|  | JSString* terminationError = jsNontrivialString(*this, terminationErrorString); | 
|  | m_terminationException = Exception::create(*this, terminationError, Exception::DoNotCaptureStack); | 
|  | } | 
|  | return m_terminationException; | 
|  | } | 
|  |  | 
|  | #if ENABLE(JIT) | 
|  | static ThunkGenerator thunkGeneratorForIntrinsic(Intrinsic intrinsic) | 
|  | { | 
|  | switch (intrinsic) { | 
|  | case CharCodeAtIntrinsic: | 
|  | return charCodeAtThunkGenerator; | 
|  | case CharAtIntrinsic: | 
|  | return charAtThunkGenerator; | 
|  | case StringPrototypeCodePointAtIntrinsic: | 
|  | return stringPrototypeCodePointAtThunkGenerator; | 
|  | case Clz32Intrinsic: | 
|  | return clz32ThunkGenerator; | 
|  | case FromCharCodeIntrinsic: | 
|  | return fromCharCodeThunkGenerator; | 
|  | case SqrtIntrinsic: | 
|  | return sqrtThunkGenerator; | 
|  | case AbsIntrinsic: | 
|  | return absThunkGenerator; | 
|  | case FloorIntrinsic: | 
|  | return floorThunkGenerator; | 
|  | case CeilIntrinsic: | 
|  | return ceilThunkGenerator; | 
|  | case TruncIntrinsic: | 
|  | return truncThunkGenerator; | 
|  | case RoundIntrinsic: | 
|  | return roundThunkGenerator; | 
|  | case ExpIntrinsic: | 
|  | return expThunkGenerator; | 
|  | case LogIntrinsic: | 
|  | return logThunkGenerator; | 
|  | case IMulIntrinsic: | 
|  | return imulThunkGenerator; | 
|  | case RandomIntrinsic: | 
|  | return randomThunkGenerator; | 
|  | case BoundFunctionCallIntrinsic: | 
|  | return boundFunctionCallGenerator; | 
|  | default: | 
|  | return nullptr; | 
|  | } | 
|  | } | 
|  |  | 
|  | MacroAssemblerCodeRef<JITThunkPtrTag> VM::getCTIStub(ThunkGenerator generator) | 
|  | { | 
|  | return jitStubs->ctiStub(*this, generator); | 
|  | } | 
|  |  | 
|  | #endif // ENABLE(JIT) | 
|  |  | 
|  | NativeExecutable* VM::getHostFunction(NativeFunction function, NativeFunction constructor, const String& name) | 
|  | { | 
|  | return getHostFunction(function, NoIntrinsic, constructor, nullptr, name); | 
|  | } | 
|  |  | 
|  | static Ref<NativeJITCode> jitCodeForCallTrampoline() | 
|  | { | 
|  | static NativeJITCode* result; | 
|  | static std::once_flag onceKey; | 
|  | std::call_once(onceKey, [&] { | 
|  | result = new NativeJITCode(LLInt::getCodeRef<JSEntryPtrTag>(llint_native_call_trampoline), JITType::HostCallThunk, NoIntrinsic); | 
|  | }); | 
|  | return *result; | 
|  | } | 
|  |  | 
|  | static Ref<NativeJITCode> jitCodeForConstructTrampoline() | 
|  | { | 
|  | static NativeJITCode* result; | 
|  | static std::once_flag onceKey; | 
|  | std::call_once(onceKey, [&] { | 
|  | result = new NativeJITCode(LLInt::getCodeRef<JSEntryPtrTag>(llint_native_construct_trampoline), JITType::HostCallThunk, NoIntrinsic); | 
|  | }); | 
|  | return *result; | 
|  | } | 
|  |  | 
|  | NativeExecutable* VM::getHostFunction(NativeFunction function, Intrinsic intrinsic, NativeFunction constructor, const DOMJIT::Signature* signature, const String& name) | 
|  | { | 
|  | #if ENABLE(JIT) | 
|  | if (Options::useJIT()) { | 
|  | return jitStubs->hostFunctionStub( | 
|  | *this, function, constructor, | 
|  | intrinsic != NoIntrinsic ? thunkGeneratorForIntrinsic(intrinsic) : nullptr, | 
|  | intrinsic, signature, name); | 
|  | } | 
|  | #endif // ENABLE(JIT) | 
|  | UNUSED_PARAM(intrinsic); | 
|  | UNUSED_PARAM(signature); | 
|  | return NativeExecutable::create(*this, jitCodeForCallTrampoline(), function, jitCodeForConstructTrampoline(), constructor, name); | 
|  | } | 
|  |  | 
|  | NativeExecutable* VM::getBoundFunction(bool isJSFunction, bool canConstruct) | 
|  | { | 
|  | bool slowCase = !isJSFunction; | 
|  |  | 
|  | auto getOrCreate = [&] (Weak<NativeExecutable>& slot) -> NativeExecutable* { | 
|  | if (auto* cached = slot.get()) | 
|  | return cached; | 
|  | NativeExecutable* result = getHostFunction( | 
|  | slowCase ? boundFunctionCall : boundThisNoArgsFunctionCall, | 
|  | slowCase ? NoIntrinsic : BoundFunctionCallIntrinsic, | 
|  | canConstruct ? (slowCase ? boundFunctionConstruct : boundThisNoArgsFunctionConstruct) : callHostFunctionAsConstructor, nullptr, String()); | 
|  | slot = Weak<NativeExecutable>(result); | 
|  | return result; | 
|  | }; | 
|  |  | 
|  | if (slowCase) { | 
|  | if (canConstruct) | 
|  | return getOrCreate(m_slowCanConstructBoundExecutable); | 
|  | return getOrCreate(m_slowBoundExecutable); | 
|  | } | 
|  | if (canConstruct) | 
|  | return getOrCreate(m_fastCanConstructBoundExecutable); | 
|  | return getOrCreate(m_fastBoundExecutable); | 
|  | } | 
|  |  | 
|  | MacroAssemblerCodePtr<JSEntryPtrTag> VM::getCTIInternalFunctionTrampolineFor(CodeSpecializationKind kind) | 
|  | { | 
|  | #if ENABLE(JIT) | 
|  | if (Options::useJIT()) { | 
|  | if (kind == CodeForCall) | 
|  | return jitStubs->ctiInternalFunctionCall(*this).retagged<JSEntryPtrTag>(); | 
|  | return jitStubs->ctiInternalFunctionConstruct(*this).retagged<JSEntryPtrTag>(); | 
|  | } | 
|  | #endif | 
|  | if (kind == CodeForCall) | 
|  | return LLInt::getCodePtr<JSEntryPtrTag>(llint_internal_function_call_trampoline); | 
|  | return LLInt::getCodePtr<JSEntryPtrTag>(llint_internal_function_construct_trampoline); | 
|  | } | 
|  |  | 
|  | MacroAssemblerCodeRef<JSEntryPtrTag> VM::getCTILinkCall() | 
|  | { | 
|  | #if ENABLE(JIT) | 
|  | if (Options::useJIT()) | 
|  | return getCTIStub(linkCallThunkGenerator).template retagged<JSEntryPtrTag>(); | 
|  | #endif | 
|  | return LLInt::getCodeRef<JSEntryPtrTag>(llint_link_call_trampoline); | 
|  | } | 
|  |  | 
|  | MacroAssemblerCodeRef<JSEntryPtrTag> VM::getCTIThrowExceptionFromCallSlowPath() | 
|  | { | 
|  | #if ENABLE(JIT) | 
|  | if (Options::useJIT()) | 
|  | return getCTIStub(throwExceptionFromCallSlowPathGenerator).template retagged<JSEntryPtrTag>(); | 
|  | #endif | 
|  | return LLInt::callToThrow(*this).template retagged<JSEntryPtrTag>(); | 
|  | } | 
|  |  | 
|  | MacroAssemblerCodeRef<JITStubRoutinePtrTag> VM::getCTIVirtualCall(CallMode callMode) | 
|  | { | 
|  | #if ENABLE(JIT) | 
|  | if (Options::useJIT()) | 
|  | return virtualThunkFor(*this, callMode); | 
|  | #endif | 
|  | switch (callMode) { | 
|  | case CallMode::Regular: | 
|  | return LLInt::getCodeRef<JITStubRoutinePtrTag>(llint_virtual_call_trampoline); | 
|  | case CallMode::Tail: | 
|  | return LLInt::getCodeRef<JITStubRoutinePtrTag>(llint_virtual_tail_call_trampoline); | 
|  | case CallMode::Construct: | 
|  | return LLInt::getCodeRef<JITStubRoutinePtrTag>(llint_virtual_construct_trampoline); | 
|  | } | 
|  | return LLInt::getCodeRef<JITStubRoutinePtrTag>(llint_virtual_call_trampoline); | 
|  | } | 
|  |  | 
|  | VM::ClientData::~ClientData() | 
|  | { | 
|  | } | 
|  |  | 
|  | void VM::whenIdle(Function<void()>&& callback) | 
|  | { | 
|  | if (!entryScope) { | 
|  | callback(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | entryScope->addDidPopListener(WTFMove(callback)); | 
|  | } | 
|  |  | 
|  | void VM::deleteAllLinkedCode(DeleteAllCodeEffort effort) | 
|  | { | 
|  | whenIdle([=, this] () { | 
|  | heap.deleteAllCodeBlocks(effort); | 
|  | }); | 
|  | } | 
|  |  | 
|  | void VM::deleteAllCode(DeleteAllCodeEffort effort) | 
|  | { | 
|  | whenIdle([=, this] () { | 
|  | m_codeCache->clear(); | 
|  | m_regExpCache->deleteAllCode(); | 
|  | heap.deleteAllCodeBlocks(effort); | 
|  | heap.deleteAllUnlinkedCodeBlocks(effort); | 
|  | heap.reportAbandonedObjectGraph(); | 
|  | }); | 
|  | } | 
|  |  | 
|  | void VM::shrinkFootprintWhenIdle() | 
|  | { | 
|  | whenIdle([=, this] () { | 
|  | sanitizeStackForVM(*this); | 
|  | deleteAllCode(DeleteAllCodeIfNotCollecting); | 
|  | heap.collectNow(Synchronousness::Sync, CollectionScope::Full); | 
|  | // FIXME: Consider stopping various automatic threads here. | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=185447 | 
|  | WTF::releaseFastMallocFreeMemory(); | 
|  | }); | 
|  | } | 
|  |  | 
|  | SourceProviderCache* VM::addSourceProviderCache(SourceProvider* sourceProvider) | 
|  | { | 
|  | auto addResult = sourceProviderCacheMap.add(sourceProvider, nullptr); | 
|  | if (addResult.isNewEntry) | 
|  | addResult.iterator->value = adoptRef(new SourceProviderCache); | 
|  | return addResult.iterator->value.get(); | 
|  | } | 
|  |  | 
|  | void VM::clearSourceProviderCaches() | 
|  | { | 
|  | sourceProviderCacheMap.clear(); | 
|  | } | 
|  |  | 
|  | bool VM::hasExceptionsAfterHandlingTraps() | 
|  | { | 
|  | if (UNLIKELY(traps().needHandling(VMTraps::NonDebuggerAsyncEvents))) | 
|  | m_traps.handleTraps(VMTraps::NonDebuggerAsyncEvents); | 
|  | return exception(); | 
|  | } | 
|  |  | 
|  | void VM::clearException() | 
|  | { | 
|  | #if ENABLE(EXCEPTION_SCOPE_VERIFICATION) | 
|  | m_needExceptionCheck = false; | 
|  | m_nativeStackTraceOfLastThrow = nullptr; | 
|  | m_throwingThread = nullptr; | 
|  | #endif | 
|  | m_exception = nullptr; | 
|  | traps().clearTrapBit(VMTraps::NeedExceptionHandling); | 
|  | } | 
|  |  | 
|  | void VM::setException(Exception* exception) | 
|  | { | 
|  | ASSERT(!exception || !isTerminationException(exception) || terminationInProgress()); | 
|  | m_exception = exception; | 
|  | m_lastException = exception; | 
|  | if (exception) | 
|  | traps().setTrapBit(VMTraps::NeedExceptionHandling); | 
|  | } | 
|  |  | 
|  | void VM::throwTerminationException() | 
|  | { | 
|  | ASSERT(terminationInProgress()); | 
|  | ASSERT(!m_traps.isDeferringTermination()); | 
|  | setException(terminationException()); | 
|  | if (m_executionForbiddenOnTermination) | 
|  | setExecutionForbidden(); | 
|  | } | 
|  |  | 
|  | Exception* VM::throwException(JSGlobalObject* globalObject, Exception* exceptionToThrow) | 
|  | { | 
|  | // The TerminationException should never be overridden. | 
|  | if (hasPendingTerminationException()) | 
|  | return m_exception; | 
|  |  | 
|  | // The TerminationException is not like ordinary exceptions that should be | 
|  | // reported to the debugger. The fact that the TerminationException uses the | 
|  | // exception handling mechanism is just a VM internal implementation detail. | 
|  | // It is not meaningful to report it to the debugger as an exception. | 
|  | if (isTerminationException(exceptionToThrow)) { | 
|  | // Note: we can only get here is we're just re-throwing the TerminationException | 
|  | // from C++ functions to propagate it. If we're throwing it for the first | 
|  | // time, we would have gone through VM::throwTerminationException(). | 
|  | setException(exceptionToThrow); | 
|  | return exceptionToThrow; | 
|  | } | 
|  |  | 
|  | CallFrame* throwOriginFrame = topJSCallFrame(); | 
|  | if (!throwOriginFrame) | 
|  | throwOriginFrame = globalObject->deprecatedCallFrameForDebugger(); | 
|  |  | 
|  | if (UNLIKELY(Options::breakOnThrow())) { | 
|  | CodeBlock* codeBlock = throwOriginFrame ? throwOriginFrame->codeBlock() : nullptr; | 
|  | dataLog("Throwing exception in call frame ", RawPointer(throwOriginFrame), " for code block ", codeBlock, "\n"); | 
|  | CRASH(); | 
|  | } | 
|  |  | 
|  | interpreter->notifyDebuggerOfExceptionToBeThrown(*this, globalObject, throwOriginFrame, exceptionToThrow); | 
|  |  | 
|  | setException(exceptionToThrow); | 
|  |  | 
|  | #if ENABLE(EXCEPTION_SCOPE_VERIFICATION) | 
|  | m_nativeStackTraceOfLastThrow = StackTrace::captureStackTrace(Options::unexpectedExceptionStackTraceLimit()); | 
|  | m_throwingThread = &Thread::current(); | 
|  | #endif | 
|  | return exceptionToThrow; | 
|  | } | 
|  |  | 
|  | Exception* VM::throwException(JSGlobalObject* globalObject, JSValue thrownValue) | 
|  | { | 
|  | VM& vm = *this; | 
|  | Exception* exception = jsDynamicCast<Exception*>(vm, thrownValue); | 
|  | if (!exception) | 
|  | exception = Exception::create(*this, thrownValue); | 
|  |  | 
|  | return throwException(globalObject, exception); | 
|  | } | 
|  |  | 
|  | Exception* VM::throwException(JSGlobalObject* globalObject, JSObject* error) | 
|  | { | 
|  | return throwException(globalObject, JSValue(error)); | 
|  | } | 
|  |  | 
|  | void VM::setStackPointerAtVMEntry(void* sp) | 
|  | { | 
|  | m_stackPointerAtVMEntry = sp; | 
|  | updateStackLimits(); | 
|  | } | 
|  |  | 
|  | size_t VM::updateSoftReservedZoneSize(size_t softReservedZoneSize) | 
|  | { | 
|  | size_t oldSoftReservedZoneSize = m_currentSoftReservedZoneSize; | 
|  | m_currentSoftReservedZoneSize = softReservedZoneSize; | 
|  | #if ENABLE(C_LOOP) | 
|  | interpreter->cloopStack().setSoftReservedZoneSize(softReservedZoneSize); | 
|  | #endif | 
|  |  | 
|  | updateStackLimits(); | 
|  |  | 
|  | return oldSoftReservedZoneSize; | 
|  | } | 
|  |  | 
|  | #if OS(WINDOWS) | 
|  | // 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<char*>(&stackLimit); p > stackLimit; p -= pageSize) { | 
|  | char ch = *p; | 
|  | *p = ch; | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | void VM::updateStackLimits() | 
|  | { | 
|  | #if OS(WINDOWS) | 
|  | void* lastSoftStackLimit = m_softStackLimit; | 
|  | #endif | 
|  |  | 
|  | const StackBounds& stack = Thread::current().stack(); | 
|  | size_t reservedZoneSize = Options::reservedZoneSize(); | 
|  | // We should have already ensured that Options::reservedZoneSize() >= minimumReserveZoneSize at | 
|  | // options initialization time, and the option value should not have been changed thereafter. | 
|  | // We don't have the ability to assert here that it hasn't changed, but we can at least assert | 
|  | // that the value is sane. | 
|  | RELEASE_ASSERT(reservedZoneSize >= minimumReservedZoneSize); | 
|  |  | 
|  | if (m_stackPointerAtVMEntry) { | 
|  | char* startOfStack = reinterpret_cast<char*>(m_stackPointerAtVMEntry); | 
|  | m_softStackLimit = stack.recursionLimit(startOfStack, Options::maxPerThreadStackUsage(), m_currentSoftReservedZoneSize); | 
|  | m_stackLimit = stack.recursionLimit(startOfStack, Options::maxPerThreadStackUsage(), reservedZoneSize); | 
|  | } else { | 
|  | m_softStackLimit = stack.recursionLimit(m_currentSoftReservedZoneSize); | 
|  | m_stackLimit = stack.recursionLimit(reservedZoneSize); | 
|  | } | 
|  |  | 
|  | #if OS(WINDOWS) | 
|  | // We only need to precommit stack memory dictated by the VM::m_softStackLimit limit. | 
|  | // This is because VM::m_softStackLimit applies to stack usage by LLINT asm or JIT | 
|  | // generated code which can allocate stack space that the C++ compiler does not know | 
|  | // about. As such, we have to precommit that stack memory manually. | 
|  | // | 
|  | // In contrast, we do not need to worry about VM::m_stackLimit because that limit is | 
|  | // used exclusively by C++ code, and the C++ compiler will automatically commit the | 
|  | // needed stack pages. | 
|  | if (lastSoftStackLimit != m_softStackLimit) | 
|  | preCommitStackMemory(m_softStackLimit); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | #if ENABLE(DFG_JIT) | 
|  | void VM::gatherScratchBufferRoots(ConservativeRoots& conservativeRoots) | 
|  | { | 
|  | Locker locker { m_scratchBufferLock }; | 
|  | for (auto* scratchBuffer : m_scratchBuffers) { | 
|  | if (scratchBuffer->activeLength()) { | 
|  | void* bufferStart = scratchBuffer->dataBuffer(); | 
|  | conservativeRoots.add(bufferStart, static_cast<void*>(static_cast<char*>(bufferStart) + scratchBuffer->activeLength())); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void VM::scanSideState(ConservativeRoots& roots) const | 
|  | { | 
|  | ASSERT(heap.worldIsStopped()); | 
|  | for (const auto& sideState : m_checkpointSideState) { | 
|  | static_assert(sizeof(sideState->tmps) / sizeof(JSValue) == maxNumCheckpointTmps); | 
|  | roots.add(sideState->tmps, sideState->tmps + maxNumCheckpointTmps); | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | void VM::pushCheckpointOSRSideState(std::unique_ptr<CheckpointOSRExitSideState>&& payload) | 
|  | { | 
|  | ASSERT(currentThreadIsHoldingAPILock()); | 
|  | ASSERT(payload->associatedCallFrame); | 
|  | #if ASSERT_ENABLED | 
|  | for (const auto& sideState : m_checkpointSideState) | 
|  | ASSERT(sideState->associatedCallFrame != payload->associatedCallFrame); | 
|  | #endif | 
|  | m_checkpointSideState.append(WTFMove(payload)); | 
|  |  | 
|  | #if ASSERT_ENABLED | 
|  | auto bounds = StackBounds::currentThreadStackBounds(); | 
|  | void* previousCallFrame = bounds.end(); | 
|  | for (size_t i = m_checkpointSideState.size(); i--;) { | 
|  | auto* callFrame = m_checkpointSideState[i]->associatedCallFrame; | 
|  | if (!bounds.contains(callFrame)) | 
|  | break; | 
|  | ASSERT(previousCallFrame < callFrame); | 
|  | previousCallFrame = callFrame; | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | std::unique_ptr<CheckpointOSRExitSideState> VM::popCheckpointOSRSideState(CallFrame* expectedCallFrame) | 
|  | { | 
|  | ASSERT(currentThreadIsHoldingAPILock()); | 
|  | auto sideState = m_checkpointSideState.takeLast(); | 
|  | RELEASE_ASSERT(sideState->associatedCallFrame == expectedCallFrame); | 
|  | return sideState; | 
|  | } | 
|  |  | 
|  | void VM::popAllCheckpointOSRSideStateUntil(CallFrame* target) | 
|  | { | 
|  | ASSERT(currentThreadIsHoldingAPILock()); | 
|  | auto bounds = StackBounds::currentThreadStackBounds().withSoftOrigin(target); | 
|  | ASSERT(bounds.contains(target)); | 
|  |  | 
|  | // We have to worry about migrating from another thread since there may be no checkpoints in our thread but one in the other threads. | 
|  | while (m_checkpointSideState.size() && bounds.contains(m_checkpointSideState.last()->associatedCallFrame)) | 
|  | m_checkpointSideState.takeLast(); | 
|  | m_checkpointSideState.shrinkToFit(); | 
|  | } | 
|  |  | 
|  | static void logSanitizeStack(VM& vm) | 
|  | { | 
|  | if (UNLIKELY(Options::verboseSanitizeStack())) { | 
|  | auto& stackBounds = Thread::current().stack(); | 
|  | dataLogLn("Sanitizing stack for VM = ", RawPointer(&vm), ", current stack pointer at ", RawPointer(currentStackPointer()), ", last stack top = ", RawPointer(vm.lastStackTop()), ", in stack range (", RawPointer(stackBounds.end()), ", ", RawPointer(stackBounds.origin()), "]"); | 
|  | } | 
|  | } | 
|  |  | 
|  | #if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS) | 
|  | char* VM::acquireRegExpPatternContexBuffer() | 
|  | { | 
|  | m_regExpPatternContextLock.lock(); | 
|  | ASSERT(m_regExpPatternContextLock.isLocked()); | 
|  | if (!m_regExpPatternContexBuffer) | 
|  | m_regExpPatternContexBuffer = makeUniqueArray<char>(VM::patternContextBufferSize); | 
|  | return m_regExpPatternContexBuffer.get(); | 
|  | } | 
|  |  | 
|  | void VM::releaseRegExpPatternContexBuffer() | 
|  | { | 
|  | ASSERT(m_regExpPatternContextLock.isLocked()); | 
|  |  | 
|  | m_regExpPatternContextLock.unlock(); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | #if ENABLE(REGEXP_TRACING) | 
|  | void VM::addRegExpToTrace(RegExp* regExp) | 
|  | { | 
|  | gcProtect(regExp); | 
|  | m_rtTraceList->add(regExp); | 
|  | } | 
|  |  | 
|  | void VM::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("Regular Expression                              8 Bit          16 Bit        match()    Matches    Average\n"); | 
|  | dataLogF(" <Match only / Match>                         JIT Addr      JIT Address       calls      found   String len\n"); | 
|  | dataLogF("----------------------------------------+----------------+----------------+----------+----------+-----------\n"); | 
|  |  | 
|  | unsigned reCount = 0; | 
|  |  | 
|  | for (; iter != m_rtTraceList->end(); ++iter, ++reCount) { | 
|  | (*iter)->printTraceData(); | 
|  | gcUnprotect(*iter); | 
|  | } | 
|  |  | 
|  | dataLogF("%d Regular Expressions\n", reCount); | 
|  | } | 
|  |  | 
|  | m_rtTraceList->clear(); | 
|  | } | 
|  | #else | 
|  | void VM::dumpRegExpTrace() | 
|  | { | 
|  | } | 
|  | #endif | 
|  |  | 
|  | WatchpointSet* VM::ensureWatchpointSetForImpureProperty(UniquedStringImpl* propertyName) | 
|  | { | 
|  | auto result = m_impurePropertyWatchpointSets.add(propertyName, nullptr); | 
|  | if (result.isNewEntry) | 
|  | result.iterator->value = WatchpointSet::create(IsWatched); | 
|  | return result.iterator->value.get(); | 
|  | } | 
|  |  | 
|  | void VM::addImpureProperty(UniquedStringImpl* propertyName) | 
|  | { | 
|  | if (RefPtr<WatchpointSet> watchpointSet = m_impurePropertyWatchpointSets.take(propertyName)) | 
|  | watchpointSet->fireAll(*this, "Impure property added"); | 
|  | } | 
|  |  | 
|  | template<typename Func> | 
|  | static bool enableProfilerWithRespectToCount(unsigned& counter, const Func& doEnableWork) | 
|  | { | 
|  | bool needsToRecompile = false; | 
|  | if (!counter) { | 
|  | doEnableWork(); | 
|  | needsToRecompile = true; | 
|  | } | 
|  | counter++; | 
|  |  | 
|  | return needsToRecompile; | 
|  | } | 
|  |  | 
|  | template<typename Func> | 
|  | static bool disableProfilerWithRespectToCount(unsigned& counter, const Func& 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 = makeUnique<TypeProfiler>(); | 
|  | this->m_typeProfilerLog = makeUnique<TypeProfilerLog>(*this); | 
|  | }; | 
|  |  | 
|  | 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 = makeUnique<ControlFlowProfiler>(); | 
|  | }; | 
|  |  | 
|  | 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(*this, "VM Dump Types"_s); | 
|  | typeProfiler()->dumpTypeProfilerData(*this); | 
|  | } | 
|  |  | 
|  | void VM::queueMicrotask(JSGlobalObject& globalObject, Ref<Microtask>&& task) | 
|  | { | 
|  | m_microtaskQueue.append(makeUnique<QueuedTask>(*this, &globalObject, WTFMove(task))); | 
|  | } | 
|  |  | 
|  | void VM::callPromiseRejectionCallback(Strong<JSPromise>& promise) | 
|  | { | 
|  | JSObject* callback = promise->globalObject()->unhandledRejectionCallback(); | 
|  | if (!callback) | 
|  | return; | 
|  |  | 
|  | auto scope = DECLARE_CATCH_SCOPE(*this); | 
|  |  | 
|  | auto callData = getCallData(*this, callback); | 
|  | ASSERT(callData.type != CallData::Type::None); | 
|  |  | 
|  | MarkedArgumentBuffer args; | 
|  | args.append(promise.get()); | 
|  | args.append(promise->result(*this)); | 
|  | call(promise->globalObject(), callback, callData, jsNull(), args); | 
|  | scope.clearException(); | 
|  | } | 
|  |  | 
|  | void VM::didExhaustMicrotaskQueue() | 
|  | { | 
|  | do { | 
|  | auto unhandledRejections = WTFMove(m_aboutToBeNotifiedRejectedPromises); | 
|  | for (auto& promise : unhandledRejections) { | 
|  | if (promise->isHandled(*this)) | 
|  | continue; | 
|  |  | 
|  | callPromiseRejectionCallback(promise); | 
|  | } | 
|  | } while (!m_aboutToBeNotifiedRejectedPromises.isEmpty()); | 
|  | } | 
|  |  | 
|  | void VM::promiseRejected(JSPromise* promise) | 
|  | { | 
|  | m_aboutToBeNotifiedRejectedPromises.constructAndAppend(*this, promise); | 
|  | } | 
|  |  | 
|  | void VM::drainMicrotasks() | 
|  | { | 
|  | if (UNLIKELY(executionForbidden())) | 
|  | m_microtaskQueue.clear(); | 
|  | else { | 
|  | do { | 
|  | while (!m_microtaskQueue.isEmpty()) { | 
|  | m_microtaskQueue.takeFirst()->run(); | 
|  | if (m_onEachMicrotaskTick) | 
|  | m_onEachMicrotaskTick(*this); | 
|  | } | 
|  | didExhaustMicrotaskQueue(); | 
|  | } while (!m_microtaskQueue.isEmpty()); | 
|  | } | 
|  | finalizeSynchronousJSExecution(); | 
|  | } | 
|  |  | 
|  | void QueuedTask::run() | 
|  | { | 
|  | m_microtask->run(m_globalObject.get()); | 
|  | } | 
|  |  | 
|  | void sanitizeStackForVM(VM& vm) | 
|  | { | 
|  | auto& thread = Thread::current(); | 
|  | auto& stack = thread.stack(); | 
|  | if (!vm.currentThreadIsHoldingAPILock()) | 
|  | return; // vm.lastStackTop() may not be set up correctly if JSLock is not held. | 
|  |  | 
|  | logSanitizeStack(vm); | 
|  |  | 
|  | RELEASE_ASSERT(stack.contains(vm.lastStackTop()), 0xaa10, vm.lastStackTop(), stack.origin(), stack.end()); | 
|  | #if ENABLE(C_LOOP) | 
|  | vm.interpreter->cloopStack().sanitizeStack(); | 
|  | #else | 
|  | sanitizeStackForVMImpl(&vm); | 
|  | #endif | 
|  | RELEASE_ASSERT(stack.contains(vm.lastStackTop()), 0xaa20, vm.lastStackTop(), stack.origin(), stack.end()); | 
|  | } | 
|  |  | 
|  | size_t VM::committedStackByteCount() | 
|  | { | 
|  | #if !ENABLE(C_LOOP) | 
|  | // When using the C stack, we don't know how many stack pages are actually | 
|  | // committed. So, we use the current stack usage as an estimate. | 
|  | uint8_t* current = bitwise_cast<uint8_t*>(currentStackPointer()); | 
|  | uint8_t* high = bitwise_cast<uint8_t*>(Thread::current().stack().origin()); | 
|  | return high - current; | 
|  | #else | 
|  | return CLoopStack::committedByteCount(); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | #if ENABLE(C_LOOP) | 
|  | bool VM::ensureStackCapacityForCLoop(Register* newTopOfStack) | 
|  | { | 
|  | return interpreter->cloopStack().ensureCapacityFor(newTopOfStack); | 
|  | } | 
|  |  | 
|  | bool VM::isSafeToRecurseSoftCLoop() const | 
|  | { | 
|  | return interpreter->cloopStack().isSafeToRecurse(); | 
|  | } | 
|  |  | 
|  | void* VM::currentCLoopStackPointer() const | 
|  | { | 
|  | return interpreter->cloopStack().currentStackPointer(); | 
|  | } | 
|  | #endif // ENABLE(C_LOOP) | 
|  |  | 
|  | #if ENABLE(EXCEPTION_SCOPE_VERIFICATION) | 
|  | void VM::verifyExceptionCheckNeedIsSatisfied(unsigned recursionDepth, ExceptionEventLocation& location) | 
|  | { | 
|  | if (!Options::validateExceptionChecks()) | 
|  | return; | 
|  |  | 
|  | if (UNLIKELY(m_needExceptionCheck)) { | 
|  | auto throwDepth = m_simulatedThrowPointRecursionDepth; | 
|  | auto& throwLocation = m_simulatedThrowPointLocation; | 
|  |  | 
|  | dataLog( | 
|  | "ERROR: Unchecked JS exception:\n" | 
|  | "    This scope can throw a JS exception: ", throwLocation, "\n" | 
|  | "        (ExceptionScope::m_recursionDepth was ", throwDepth, ")\n" | 
|  | "    But the exception was unchecked as of this scope: ", location, "\n" | 
|  | "        (ExceptionScope::m_recursionDepth was ", recursionDepth, ")\n" | 
|  | "\n"); | 
|  |  | 
|  | StringPrintStream out; | 
|  | std::unique_ptr<StackTrace> currentTrace = StackTrace::captureStackTrace(Options::unexpectedExceptionStackTraceLimit()); | 
|  |  | 
|  | if (Options::dumpSimulatedThrows()) { | 
|  | out.println("The simulated exception was thrown at:"); | 
|  | m_nativeStackTraceOfLastSimulatedThrow->dump(out, "    "); | 
|  | out.println(); | 
|  | } | 
|  | out.println("Unchecked exception detected at:"); | 
|  | currentTrace->dump(out, "    "); | 
|  | out.println(); | 
|  |  | 
|  | dataLog(out.toCString()); | 
|  | RELEASE_ASSERT(!m_needExceptionCheck); | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | ScratchBuffer* VM::scratchBufferForSize(size_t size) | 
|  | { | 
|  | if (!size) | 
|  | return nullptr; | 
|  |  | 
|  | Locker locker { m_scratchBufferLock }; | 
|  |  | 
|  | if (size > m_sizeOfLastScratchBuffer) { | 
|  | // Protect against a N^2 memory usage pathology by ensuring | 
|  | // that at worst, we get a geometric series, meaning that the | 
|  | // total memory usage is somewhere around | 
|  | // max(scratch buffer size) * 4. | 
|  | m_sizeOfLastScratchBuffer = size * 2; | 
|  |  | 
|  | ScratchBuffer* newBuffer = ScratchBuffer::create(m_sizeOfLastScratchBuffer); | 
|  | RELEASE_ASSERT(newBuffer); | 
|  | m_scratchBuffers.append(newBuffer); | 
|  | } | 
|  |  | 
|  | ScratchBuffer* result = m_scratchBuffers.last(); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | void VM::clearScratchBuffers() | 
|  | { | 
|  | Locker locker { m_scratchBufferLock }; | 
|  | for (auto* scratchBuffer : m_scratchBuffers) | 
|  | scratchBuffer->setActiveLength(0); | 
|  | } | 
|  |  | 
|  | void VM::ensureShadowChicken() | 
|  | { | 
|  | if (m_shadowChicken) | 
|  | return; | 
|  | m_shadowChicken = makeUnique<ShadowChicken>(); | 
|  | } | 
|  |  | 
|  | JSCell* VM::sentinelSetBucketSlow() | 
|  | { | 
|  | ASSERT(!m_sentinelSetBucket); | 
|  | auto* sentinel = JSSet::BucketType::createSentinel(*this); | 
|  | m_sentinelSetBucket.set(*this, sentinel); | 
|  | return sentinel; | 
|  | } | 
|  |  | 
|  | JSCell* VM::sentinelMapBucketSlow() | 
|  | { | 
|  | ASSERT(!m_sentinelMapBucket); | 
|  | auto* sentinel = JSMap::BucketType::createSentinel(*this); | 
|  | m_sentinelMapBucket.set(*this, sentinel); | 
|  | return sentinel; | 
|  | } | 
|  |  | 
|  | JSPropertyNameEnumerator* VM::emptyPropertyNameEnumeratorSlow() | 
|  | { | 
|  | ASSERT(!m_emptyPropertyNameEnumerator); | 
|  | PropertyNameArray propertyNames(*this, PropertyNameMode::Strings, PrivateSymbolMode::Exclude); | 
|  | auto* enumerator = JSPropertyNameEnumerator::create(*this, nullptr, 0, 0, WTFMove(propertyNames)); | 
|  | m_emptyPropertyNameEnumerator.set(*this, enumerator); | 
|  | return enumerator; | 
|  | } | 
|  |  | 
|  | JSGlobalObject* VM::deprecatedVMEntryGlobalObject(JSGlobalObject* globalObject) const | 
|  | { | 
|  | if (entryScope) | 
|  | return entryScope->globalObject(); | 
|  | return globalObject; | 
|  | } | 
|  |  | 
|  | void VM::setCrashOnVMCreation(bool shouldCrash) | 
|  | { | 
|  | vmCreationShouldCrash = shouldCrash; | 
|  | } | 
|  |  | 
|  | void VM::addLoopHintExecutionCounter(const Instruction* instruction) | 
|  | { | 
|  | Locker locker { m_loopHintExecutionCountLock }; | 
|  | auto addResult = m_loopHintExecutionCounts.add(instruction, std::pair<unsigned, std::unique_ptr<uintptr_t>>(0, nullptr)); | 
|  | if (addResult.isNewEntry) { | 
|  | auto ptr = WTF::makeUniqueWithoutFastMallocCheck<uintptr_t>(); | 
|  | *ptr = 0; | 
|  | addResult.iterator->value.second = WTFMove(ptr); | 
|  | } | 
|  | ++addResult.iterator->value.first; | 
|  | } | 
|  |  | 
|  | uintptr_t* VM::getLoopHintExecutionCounter(const Instruction* instruction) | 
|  | { | 
|  | Locker locker { m_loopHintExecutionCountLock }; | 
|  | auto iter = m_loopHintExecutionCounts.find(instruction); | 
|  | return iter->value.second.get(); | 
|  | } | 
|  |  | 
|  | void VM::removeLoopHintExecutionCounter(const Instruction* instruction) | 
|  | { | 
|  | Locker locker { m_loopHintExecutionCountLock }; | 
|  | auto iter = m_loopHintExecutionCounts.find(instruction); | 
|  | RELEASE_ASSERT(!!iter->value.first); | 
|  | --iter->value.first; | 
|  | if (!iter->value.first) | 
|  | m_loopHintExecutionCounts.remove(iter); | 
|  | } | 
|  |  | 
|  | } // namespace JSC |