diff options
author | Simon Hausmann <simon.hausmann@digia.com> | 2012-10-23 10:25:11 +0200 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@digia.com> | 2012-10-23 10:25:11 +0200 |
commit | 5ea819f80c6840c492386bfafbffb059c7e2091f (patch) | |
tree | 42ad0b1d82eff090d14278a088ea0f4840a0f938 /Source/JavaScriptCore | |
parent | 43a42f108af6bcbd91f2672731c3047c26213af1 (diff) | |
download | qtwebkit-5ea819f80c6840c492386bfafbffb059c7e2091f.tar.gz |
Imported WebKit commit 20434eb8eb95065803473139d8794e98a7672f75 (http://svn.webkit.org/repository/webkit/trunk@132191)
New snapshot that should fix build with latest qtbase and the QPlastiqueStyle removal
Diffstat (limited to 'Source/JavaScriptCore')
30 files changed, 967 insertions, 365 deletions
diff --git a/Source/JavaScriptCore/API/JSContextRef.cpp b/Source/JavaScriptCore/API/JSContextRef.cpp index e2a102948..162c825aa 100644 --- a/Source/JavaScriptCore/API/JSContextRef.cpp +++ b/Source/JavaScriptCore/API/JSContextRef.cpp @@ -54,7 +54,7 @@ using namespace JSC; JSContextGroupRef JSContextGroupCreate() { initializeThreading(); - return toRef(JSGlobalData::createContextGroup(ThreadStackTypeSmall).leakRef()); + return toRef(JSGlobalData::createContextGroup().leakRef()); } JSContextGroupRef JSContextGroupRetain(JSContextGroupRef group) @@ -89,7 +89,7 @@ JSGlobalContextRef JSGlobalContextCreateInGroup(JSContextGroupRef group, JSClass { initializeThreading(); - RefPtr<JSGlobalData> globalData = group ? PassRefPtr<JSGlobalData>(toJS(group)) : JSGlobalData::createContextGroup(ThreadStackTypeSmall); + RefPtr<JSGlobalData> globalData = group ? PassRefPtr<JSGlobalData>(toJS(group)) : JSGlobalData::createContextGroup(); APIEntryShim entryShim(globalData.get(), false); globalData->makeUsableFromMultipleThreads(); diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog index c6b5ce758..c096c4bd0 100644 --- a/Source/JavaScriptCore/ChangeLog +++ b/Source/JavaScriptCore/ChangeLog @@ -1,3 +1,186 @@ +2012-10-23 Mark Lam <mark.lam@apple.com> + + Make topCallFrame reliable. + https://bugs.webkit.org/show_bug.cgi?id=98928. + + Reviewed by Geoffrey Garen. + + - VM entry points and the GC now uses topCallFrame. + - The callerFrame value in CallFrames are now always the previous + frame on the stack, except for the first frame which has a + callerFrame of 0 (not counting the HostCallFrameFlag). + Hence, we can now traverse every frame on the stack all the way + back to the first frame. + - GlobalExec's will no longer be used as the callerFrame values in + call frames. + - Added fences and traps for debugging the JSStack in debug builds. + + * bytecode/SamplingTool.h: + (SamplingTool): + (JSC::SamplingTool::CallRecord::CallRecord): + * dfg/DFGOperations.cpp: + - Fixed 2 DFG helper functions to flush topCallFrame as expected. + * dfg/DFGSpeculativeJIT.h: + (JSC::DFG::SpeculativeJIT::prepareForExternalCall): + * interpreter/CallFrame.h: + (JSC::ExecState::callerFrameNoFlags): + (ExecState): + (JSC::ExecState::argIndexForRegister): + (JSC::ExecState::getArgumentUnsafe): + * interpreter/CallFrameClosure.h: + (CallFrameClosure): + * interpreter/Interpreter.cpp: + (JSC): + (JSC::eval): + (JSC::Interpreter::Interpreter): + (JSC::Interpreter::throwException): + (JSC::Interpreter::execute): + (JSC::Interpreter::executeCall): + (JSC::Interpreter::executeConstruct): + (JSC::Interpreter::prepareForRepeatCall): + (JSC::Interpreter::endRepeatCall): + * interpreter/Interpreter.h: + (JSC): + (Interpreter): + * interpreter/JSStack.cpp: + (JSC::JSStack::JSStack): + (JSC::JSStack::gatherConservativeRoots): + (JSC::JSStack::disableErrorStackReserve): + * interpreter/JSStack.h: + (JSC): + (JSStack): + (JSC::JSStack::installFence): + (JSC::JSStack::validateFence): + (JSC::JSStack::installTrapsAfterFrame): + * interpreter/JSStackInlines.h: Added. + (JSC): + (JSC::JSStack::getTopOfFrame): + (JSC::JSStack::getTopOfStack): + (JSC::JSStack::getStartOfFrame): + (JSC::JSStack::pushFrame): + (JSC::JSStack::popFrame): + (JSC::JSStack::generateFenceValue): + (JSC::JSStack::installFence): + (JSC::JSStack::validateFence): + (JSC::JSStack::installTrapsAfterFrame): + * jit/JITStubs.cpp: + (JSC::jitCompileFor): + (JSC::lazyLinkFor): + - Set frame->codeBlock to 0 for both the above because they are called + with partially intitialized frames (cb uninitialized), but may + trigger a GC. + (JSC::DEFINE_STUB_FUNCTION): + * runtime/JSGlobalData.cpp: + (JSC::JSGlobalData::JSGlobalData): + +2012-10-22 Filip Pizlo <fpizlo@apple.com> + + DFG::Array::Undecided should be called DFG::Array::SelectUsingPredictions + https://bugs.webkit.org/show_bug.cgi?id=100052 + + Reviewed by Oliver Hunt. + + No functional change, just renaming. It's a clearer name that more accurately + reflects the meaning, and it eliminates the namespace confusion that will happen + with the Undecided indexing type in https://bugs.webkit.org/show_bug.cgi?id=98606 + + * dfg/DFGAbstractState.cpp: + (JSC::DFG::AbstractState::execute): + * dfg/DFGArrayMode.cpp: + (JSC::DFG::fromObserved): + (JSC::DFG::refineArrayMode): + (JSC::DFG::modeAlreadyChecked): + (JSC::DFG::modeToString): + * dfg/DFGArrayMode.h: + (JSC::DFG::canCSEStorage): + (JSC::DFG::modeIsSpecific): + (JSC::DFG::modeSupportsLength): + (JSC::DFG::benefitsFromStructureCheck): + * dfg/DFGFixupPhase.cpp: + (JSC::DFG::FixupPhase::fixupNode): + (JSC::DFG::FixupPhase::blessArrayOperation): + * dfg/DFGSpeculativeJIT.cpp: + (JSC::DFG::SpeculativeJIT::arrayify): + * dfg/DFGSpeculativeJIT32_64.cpp: + (JSC::DFG::SpeculativeJIT::compile): + * dfg/DFGSpeculativeJIT64.cpp: + (JSC::DFG::SpeculativeJIT::compile): + +2012-10-22 Mark Lam <mark.lam@apple.com> + + Change stack recursion checks to be based on stack availability. + https://bugs.webkit.org/show_bug.cgi?id=99872. + + Reviewed by Filip Pizlo and Geoffrey Garen. + + - Remove m_reentryDepth, ThreadStackType which are now obsolete. + - Replaced the reentryDepth checks with a StackBounds check. + - Added the Interpreter::StackPolicy class to compute a reasonable + stack capacity requirement given the native stack that the + interpreter is executing on at that time. + - Reserved an amount of JSStack space for the use of error handling + and enable its use (using Interpreter::ErrorHandlingMode) when + we're about to throw or report an exception. + - Interpreter::StackPolicy also allows more native stack space + to be used when in ErrorHandlingMode. This is needed in the case + of native stack overflows. + - Fixed the parser so that it throws a StackOverflowError instead of + a SyntaxError when it encounters a stack overflow. + + * API/JSContextRef.cpp: + (JSContextGroupCreate): + (JSGlobalContextCreateInGroup): + * JavaScriptCore.order: + * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def: + * interpreter/Interpreter.cpp: + (JSC::Interpreter::ErrorHandlingMode::ErrorHandlingMode): + (JSC): + (JSC::Interpreter::ErrorHandlingMode::~ErrorHandlingMode): + (JSC::Interpreter::StackPolicy::StackPolicy): + (JSC::Interpreter::Interpreter): + (JSC::Interpreter::execute): + (JSC::Interpreter::executeCall): + (JSC::Interpreter::executeConstruct): + (JSC::Interpreter::prepareForRepeatCall): + * interpreter/Interpreter.h: + (JSC): + (Interpreter): + (ErrorHandlingMode): + (StackPolicy): + (JSC::Interpreter::StackPolicy::requiredCapacity): + * interpreter/JSStack.cpp: + (JSC): + (JSC::JSStack::JSStack): + (JSC::JSStack::growSlowCase): + (JSC::JSStack::enableErrorStackReserve): + (JSC::JSStack::disableErrorStackReserve): + * interpreter/JSStack.h: + (JSStack): + (JSC::JSStack::reservationEnd): + (JSC): + * jsc.cpp: + (jscmain): + * parser/Parser.cpp: + (JSC::::Parser): + * parser/Parser.h: + (Parser): + (JSC::::parse): + * runtime/ExceptionHelpers.cpp: + (JSC::throwStackOverflowError): + * runtime/JSGlobalData.cpp: + (JSC::JSGlobalData::JSGlobalData): + (JSC::JSGlobalData::createContextGroup): + (JSC::JSGlobalData::create): + (JSC::JSGlobalData::createLeaked): + (JSC::JSGlobalData::sharedInstance): + * runtime/JSGlobalData.h: + (JSC): + (JSGlobalData): + * runtime/StringRecursionChecker.h: + (JSC::StringRecursionChecker::performCheck): + * testRegExp.cpp: + (realMain): + 2012-10-20 Martin Robinson <mrobinson@igalia.com> Fix 'make dist' for the GTK+ port diff --git a/Source/JavaScriptCore/JavaScriptCore.order b/Source/JavaScriptCore/JavaScriptCore.order index aa3d0ef32..6a1946684 100644 --- a/Source/JavaScriptCore/JavaScriptCore.order +++ b/Source/JavaScriptCore/JavaScriptCore.order @@ -120,7 +120,7 @@ _JSGlobalContextCreate __ZN3JSC6JSLock4lockENS_14JSLockBehaviorE __ZN3JSCL17createJSLockCountEv __ZN3JSC12JSGlobalData14sharedInstanceEv -__ZN3JSC12JSGlobalDataC2ENS0_14GlobalDataTypeENS_15ThreadStackTypeE +__ZN3JSC12JSGlobalDataC2ENS0_14GlobalDataTypeENS_8HeapTypeE __ZN3JSC21createIdentifierTableEv __ZN3JSC17CommonIdentifiersC1EPNS_12JSGlobalDataE __ZN3JSC17CommonIdentifiersC2EPNS_12JSGlobalDataE @@ -440,7 +440,7 @@ __ZN3WTF6strtodEPKcPPc __ZN3WTF10StringImpl22containsOnlyWhitespaceEv __ZN3WTF10StringImpl11reverseFindEPS0_j __ZN3WTF17equalIgnoringCaseEPNS_10StringImplES1_ -__ZN3JSC12JSGlobalData12createLeakedENS_15ThreadStackTypeE +__ZN3JSC12JSGlobalData12createLeakedENS_8HeapTypeE __ZN3JSC24JSObjectWithGlobalObjectC2ERNS_12JSGlobalDataEPNS_14JSGlobalObjectEPNS_9StructureE __ZN3JSC8evaluateEPNS_9ExecStateEPNS_14ScopeChainNodeERKNS_10SourceCodeENS_7JSValueE __ZN3JSC17ProgramExecutableC1EPNS_9ExecStateERKNS_10SourceCodeE @@ -1707,7 +1707,7 @@ __ZN3JSC8JSParser21parseDoWhileStatementINS_13SyntaxCheckerEEENT_9StatementERS3_ __ZN3JSCL16mathProtoFuncMinEPNS_9ExecStateE __ZN3JSCL21arrayProtoFuncReverseEPNS_9ExecStateE __ZN3JSC17ProgramExecutable13visitChildrenERNS_9MarkStackE -__ZN3JSC12JSGlobalData18createContextGroupENS_15ThreadStackTypeE +__ZN3JSC12JSGlobalData18createContextGroupENS_8HeapTypeE __ZN3JSC12JSGlobalData22clearBuiltinStructuresEv __ZN3JSC4Heap7destroyEv __ZN3JSC9JITThunks22clearHostFunctionStubsEv @@ -1842,7 +1842,7 @@ _JSObjectHasProperty _JSObjectGetPrototype __ZN3JSC8JSObject15unwrappedObjectEv __ZN3JSC11createErrorEPNS_9ExecStateERKNS_7UStringE -__ZN3JSC12JSGlobalData6createENS_15ThreadStackTypeE +__ZN3JSC12JSGlobalData6createENS_8HeapTypeE __ZN3JSC8JSObject17putDirectFunctionEPNS_9ExecStateEPNS_10JSFunctionEj __ZN3JSC12JSGlobalData13startSamplingEv __ZN3JSC11Interpreter13startSamplingEv diff --git a/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def b/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def index a386b4c2b..b23100547 100755 --- a/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def +++ b/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def @@ -10,6 +10,8 @@ EXPORTS ??0DropAllLocks@JSLock@JSC@@QAE@PAVExecState@2@@Z ??0DropAllLocks@JSLock@JSC@@QAE@PAVJSGlobalData@2@@Z ??0DynamicGlobalObjectScope@JSC@@QAE@AAVJSGlobalData@1@PAVJSGlobalObject@1@@Z + ??0ErrorHandlingMode@Interpreter@JSC@@QAE@PAVExecState@2@@Z + ??1ErrorHandlingMode@Interpreter@JSC@@QAE@XZ ??0InternalFunction@JSC@@IAE@PAVJSGlobalObject@1@PAVStructure@1@@Z ??0JSGlobalObject@JSC@@IAE@AAVJSGlobalData@1@PAVStructure@1@PBUGlobalObjectMethodTable@1@@Z ??0JSLockHolder@JSC@@QAE@AAVJSGlobalData@1@@Z @@ -118,13 +120,13 @@ EXPORTS ?convertUTF8ToUTF16@Unicode@WTF@@YA?AW4ConversionResult@12@PAPBDPBDPAPA_WPA_WPA_N_N@Z ?copyBackingStore@JSObject@JSC@@SAXPAVJSCell@2@AAVCopyVisitor@2@@Z ?create@JSFunction@JSC@@SAPAV12@PAVExecState@2@PAVJSGlobalObject@2@HABVString@WTF@@P6I_J0@ZW4Intrinsic@2@3@Z - ?create@JSGlobalData@JSC@@SA?AV?$PassRefPtr@VJSGlobalData@JSC@@@WTF@@W4ThreadStackType@2@W4HeapType@2@@Z + ?create@JSGlobalData@JSC@@SA?AV?$PassRefPtr@VJSGlobalData@JSC@@@WTF@@W4HeapType@2@@Z ?create@OpaqueJSString@@SA?AV?$PassRefPtr@UOpaqueJSString@@@WTF@@ABVString@3@@Z ?create@RegExp@JSC@@SAPAV12@AAVJSGlobalData@2@ABVString@WTF@@W4RegExpFlags@2@@Z ?createEmptyString@SmallStrings@JSC@@AAEXPAVJSGlobalData@2@@Z ?createError@JSC@@YAPAVJSObject@1@PAVExecState@1@ABVString@WTF@@@Z ?createInterruptedExecutionException@JSC@@YAPAVJSObject@1@PAVJSGlobalData@1@@Z - ?createLeaked@JSGlobalData@JSC@@SA?AV?$PassRefPtr@VJSGlobalData@JSC@@@WTF@@W4ThreadStackType@2@W4HeapType@2@@Z + ?createLeaked@JSGlobalData@JSC@@SA?AV?$PassRefPtr@VJSGlobalData@JSC@@@WTF@@W4HeapType@2@@Z ?createNotEnoughArgumentsError@JSC@@YAPAVJSObject@1@PAVExecState@1@@Z ?createRangeError@JSC@@YAPAVJSObject@1@PAVExecState@1@ABVString@WTF@@@Z ?createReferenceError@JSC@@YAPAVJSObject@1@PAVExecState@1@ABVString@WTF@@@Z diff --git a/Source/JavaScriptCore/bytecode/SamplingTool.h b/Source/JavaScriptCore/bytecode/SamplingTool.h index 8f90c3e17..6c9df62ee 100644 --- a/Source/JavaScriptCore/bytecode/SamplingTool.h +++ b/Source/JavaScriptCore/bytecode/SamplingTool.h @@ -230,17 +230,18 @@ namespace JSC { class SamplingTool { public: friend struct CallRecord; - friend class HostCallRecord; #if ENABLE(OPCODE_SAMPLING) class CallRecord { WTF_MAKE_NONCOPYABLE(CallRecord); public: - CallRecord(SamplingTool* samplingTool) + CallRecord(SamplingTool* samplingTool, bool isHostCall = false) : m_samplingTool(samplingTool) , m_savedSample(samplingTool->m_sample) , m_savedCodeBlock(samplingTool->m_codeBlock) { + if (isHostcall) + samplingTool->m_sample |= 0x1; } ~CallRecord() @@ -254,32 +255,15 @@ namespace JSC { intptr_t m_savedSample; CodeBlock* m_savedCodeBlock; }; - - class HostCallRecord : public CallRecord { - public: - HostCallRecord(SamplingTool* samplingTool) - : CallRecord(samplingTool) - { - samplingTool->m_sample |= 0x1; - } - }; #else class CallRecord { WTF_MAKE_NONCOPYABLE(CallRecord); public: - CallRecord(SamplingTool*) + CallRecord(SamplingTool*, bool = false) { } }; - - class HostCallRecord : public CallRecord { - public: - HostCallRecord(SamplingTool* samplingTool) - : CallRecord(samplingTool) - { - } - }; -#endif +#endif SamplingTool(Interpreter* interpreter) : m_interpreter(interpreter) diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp index 928788bf3..58ff7d23c 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp +++ b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp @@ -840,7 +840,7 @@ bool AbstractState::execute(unsigned indexInBlock) case GetByVal: { node.setCanExit(true); switch (node.arrayMode()) { - case Array::Undecided: + case Array::SelectUsingPredictions: case Array::Unprofiled: ASSERT_NOT_REACHED(); break; diff --git a/Source/JavaScriptCore/dfg/DFGArrayMode.cpp b/Source/JavaScriptCore/dfg/DFGArrayMode.cpp index 623e9d743..a3aafde01 100644 --- a/Source/JavaScriptCore/dfg/DFGArrayMode.cpp +++ b/Source/JavaScriptCore/dfg/DFGArrayMode.cpp @@ -40,7 +40,7 @@ Array::Mode fromObserved(ArrayProfile* profile, Array::Action action, bool makeS case asArrayModes(NonArray): if (action == Array::Write && !profile->mayInterceptIndexedAccesses()) return Array::ToContiguous; // FIXME: we don't know whether to go to contiguous or array storage. We're making a static guess here. In future we should use exit profiling for this. - return Array::Undecided; + return Array::SelectUsingPredictions; case asArrayModes(NonArrayWithContiguous): return makeSafe ? Array::ContiguousOutOfBounds : (profile->mayStoreToHole() ? Array::ContiguousToTail : Array::Contiguous); case asArrayModes(ArrayWithContiguous): @@ -71,23 +71,23 @@ Array::Mode fromObserved(ArrayProfile* profile, Array::Action action, bool makeS case asArrayModes(NonArray) | asArrayModes(NonArrayWithContiguous): if (action == Array::Write && !profile->mayInterceptIndexedAccesses()) return Array::ToContiguous; - return Array::Undecided; + return Array::SelectUsingPredictions; case asArrayModes(NonArray) | asArrayModes(NonArrayWithContiguous) | asArrayModes(NonArrayWithArrayStorage): case asArrayModes(NonArray) | asArrayModes(NonArrayWithArrayStorage): if (action == Array::Write && !profile->mayInterceptIndexedAccesses()) return Array::ToArrayStorage; - return Array::Undecided; + return Array::SelectUsingPredictions; case asArrayModes(NonArray) | asArrayModes(NonArrayWithSlowPutArrayStorage): case asArrayModes(NonArray) | asArrayModes(NonArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage): if (action == Array::Write && !profile->mayInterceptIndexedAccesses()) return Array::ToSlowPutArrayStorage; - return Array::Undecided; + return Array::SelectUsingPredictions; default: // We know that this is possibly a kind of array for which, though there is no // useful data in the array profile, we may be able to extract useful data from // the value profiles of the inputs. Hence, we leave it as undecided, and let // the predictions propagator decide later. - return Array::Undecided; + return Array::SelectUsingPredictions; } } @@ -110,7 +110,7 @@ Array::Mode refineArrayMode(Array::Mode arrayMode, SpeculatedType base, Speculat return Array::ForceExit; } - if (arrayMode != Array::Undecided) + if (arrayMode != Array::SelectUsingPredictions) return arrayMode; if (isStringSpeculation(base)) @@ -250,7 +250,7 @@ bool modeAlreadyChecked(AbstractValue& value, Array::Mode arrayMode) case Array::Float64Array: return isFloat64ArraySpeculation(value.m_type); - case Array::Undecided: + case Array::SelectUsingPredictions: case Array::Unprofiled: break; } @@ -262,8 +262,8 @@ bool modeAlreadyChecked(AbstractValue& value, Array::Mode arrayMode) const char* modeToString(Array::Mode mode) { switch (mode) { - case Array::Undecided: - return "Undecided"; + case Array::SelectUsingPredictions: + return "SelectUsingPredictions"; case Array::Unprofiled: return "Unprofiled"; case Array::Generic: diff --git a/Source/JavaScriptCore/dfg/DFGArrayMode.h b/Source/JavaScriptCore/dfg/DFGArrayMode.h index f7ac92733..a1cd74114 100644 --- a/Source/JavaScriptCore/dfg/DFGArrayMode.h +++ b/Source/JavaScriptCore/dfg/DFGArrayMode.h @@ -47,7 +47,7 @@ enum Action { }; enum Mode { - Undecided, // Implies that we need predictions to decide. We will never get to the backend in this mode. + SelectUsingPredictions, // Implies that we need predictions to decide. We will never get to the backend in this mode. Unprofiled, // Implies that array profiling didn't see anything. But that could be because the operands didn't comply with basic type assumptions (base is cell, property is int). This either becomes Generic or ForceExit depending on value profiling. ForceExit, // Implies that we have no idea how to execute this operation, so we should just give up. Generic, @@ -266,7 +266,7 @@ inline bool mayStoreToHole(Array::Mode arrayMode) inline bool canCSEStorage(Array::Mode arrayMode) { switch (arrayMode) { - case Array::Undecided: + case Array::SelectUsingPredictions: case Array::Unprofiled: case Array::ForceExit: case Array::Generic: @@ -299,7 +299,7 @@ inline Array::Mode modeForPut(Array::Mode arrayMode) inline bool modeIsSpecific(Array::Mode mode) { switch (mode) { - case Array::Undecided: + case Array::SelectUsingPredictions: case Array::Unprofiled: case Array::ForceExit: case Array::Generic: @@ -312,7 +312,7 @@ inline bool modeIsSpecific(Array::Mode mode) inline bool modeSupportsLength(Array::Mode mode) { switch (mode) { - case Array::Undecided: + case Array::SelectUsingPredictions: case Array::Unprofiled: case Array::ForceExit: case Array::Generic: @@ -329,7 +329,7 @@ inline bool benefitsFromStructureCheck(Array::Mode mode) { switch (mode) { case ALL_EFFECTFUL_MODES: - case Array::Undecided: + case Array::SelectUsingPredictions: case Array::Unprofiled: case Array::ForceExit: case Array::Generic: diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp index 49212730c..5dcfe08a5 100644 --- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp @@ -86,7 +86,7 @@ private: ArrayProfile* arrayProfile = m_graph.baselineCodeBlockFor(nodePtr->codeOrigin)->getArrayProfile( nodePtr->codeOrigin.bytecodeIndex); - Array::Mode arrayMode = Array::Undecided; + Array::Mode arrayMode = Array::SelectUsingPredictions; if (arrayProfile) { arrayProfile->computeUpdatedPrediction(); arrayMode = refineArrayMode( @@ -436,7 +436,7 @@ private: return; } - case Array::Undecided: + case Array::SelectUsingPredictions: case Array::Unprofiled: ASSERT_NOT_REACHED(); return; diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp index 11c2c1cef..13e04388c 100644 --- a/Source/JavaScriptCore/dfg/DFGOperations.cpp +++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp @@ -1165,20 +1165,24 @@ JSCell* DFG_OPERATION operationCreateActivation(ExecState* exec) JSCell* DFG_OPERATION operationCreateArguments(ExecState* exec) { + JSGlobalData& globalData = exec->globalData(); + NativeCallFrameTracer tracer(&globalData, exec); // NB: This needs to be exceedingly careful with top call frame tracking, since it // may be called from OSR exit, while the state of the call stack is bizarre. - Arguments* result = Arguments::create(exec->globalData(), exec); - ASSERT(!exec->globalData().exception); + Arguments* result = Arguments::create(globalData, exec); + ASSERT(!globalData.exception); return result; } JSCell* DFG_OPERATION operationCreateInlinedArguments( ExecState* exec, InlineCallFrame* inlineCallFrame) { + JSGlobalData& globalData = exec->globalData(); + NativeCallFrameTracer tracer(&globalData, exec); // NB: This needs to be exceedingly careful with top call frame tracking, since it // may be called from OSR exit, while the state of the call stack is bizarre. - Arguments* result = Arguments::create(exec->globalData(), exec, inlineCallFrame); - ASSERT(!exec->globalData().exception); + Arguments* result = Arguments::create(globalData, exec, inlineCallFrame); + ASSERT(!globalData.exception); return result; } diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp index a9b91d046..7cb028388 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp @@ -497,7 +497,7 @@ void SpeculativeJIT::arrayify(Node& node, GPRReg baseReg, GPRReg propertyReg) break; default: CRASH(); - desiredArrayMode = Array::Undecided; + desiredArrayMode = Array::ForceExit; break; } diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h index 3796cc704..aadcdb06b 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h @@ -1925,6 +1925,15 @@ public: #if !defined(NDEBUG) && !CPU(ARM) void prepareForExternalCall() { + // We're about to call out to a "native" helper function. The helper + // function is expected to set topCallFrame itself with the ExecState + // that is passed to it. + // + // We explicitly trash topCallFrame here so that we'll know if some of + // the helper functions are not setting topCallFrame when they should + // be doing so. Note: the previous value in topcallFrame was not valid + // anyway since it was not being updated by JIT'ed code by design. + for (unsigned i = 0; i < sizeof(void*) / 4; i++) m_jit.store32(TrustedImm32(0xbadbeef), reinterpret_cast<char*>(&m_jit.globalData()->topCallFrame) + i * 4); } diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp index 453851ba3..ab089ba36 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp @@ -2653,7 +2653,7 @@ void SpeculativeJIT::compile(Node& node) case GetByVal: { switch (node.arrayMode()) { - case Array::Undecided: + case Array::SelectUsingPredictions: case Array::ForceExit: ASSERT_NOT_REACHED(); terminateSpeculativeExecution(InadequateCoverage, JSValueRegs(), NoNode); @@ -2833,7 +2833,7 @@ void SpeculativeJIT::compile(Node& node) bool alreadyHandled = false; switch (arrayMode) { - case Array::Undecided: + case Array::SelectUsingPredictions: case Array::ForceExit: ASSERT_NOT_REACHED(); terminateSpeculativeExecution(InadequateCoverage, JSValueRegs(), NoNode); diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp index 42ab40341..f11fd1663 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp @@ -2680,7 +2680,7 @@ void SpeculativeJIT::compile(Node& node) case GetByVal: { switch (node.arrayMode()) { - case Array::Undecided: + case Array::SelectUsingPredictions: case Array::ForceExit: ASSERT_NOT_REACHED(); terminateSpeculativeExecution(InadequateCoverage, JSValueRegs(), NoNode); @@ -2840,7 +2840,7 @@ void SpeculativeJIT::compile(Node& node) bool alreadyHandled = false; switch (arrayMode) { - case Array::Undecided: + case Array::SelectUsingPredictions: case Array::ForceExit: ASSERT_NOT_REACHED(); terminateSpeculativeExecution(InadequateCoverage, JSValueRegs(), NoNode); diff --git a/Source/JavaScriptCore/interpreter/CallFrame.h b/Source/JavaScriptCore/interpreter/CallFrame.h index 7aa49a9b0..2b0ea3aac 100644 --- a/Source/JavaScriptCore/interpreter/CallFrame.h +++ b/Source/JavaScriptCore/interpreter/CallFrame.h @@ -256,6 +256,7 @@ namespace JSC { CodeBlock* someCodeBlockForPossiblyInlinedCode() { return codeBlock(); } #endif + CallFrame* callerFrameNoFlags() { return callerFrame()->removeHostCallFrameFlag(); } // Call this to get the true call frame (accounted for inlining and any // other optimizations), when you have entered into VM code through one @@ -281,6 +282,36 @@ namespace JSC { ExecState(); ~ExecState(); + // The following are for internal use in debugging and verification + // code only and not meant as an API for general usage: + + size_t argIndexForRegister(Register* reg) + { + // The register at 'offset' number of slots from the frame pointer + // i.e. + // reg = frame[offset]; + // ==> reg = frame + offset; + // ==> offset = reg - frame; + int offset = reg - this->registers(); + + // The offset is defined (based on argumentOffset()) to be: + // offset = s_firstArgumentOffset - argIndex; + // Hence: + // argIndex = s_firstArgumentOffset - offset; + size_t argIndex = s_firstArgumentOffset - offset; + return argIndex; + } + + JSValue getArgumentUnsafe(size_t argIndex) + { + // User beware! This method does not verify that there is a valid + // argument at the specified argIndex. This is used for debugging + // and verification code only. The caller is expected to know what + // he/she is doing when calling this method. + return this[argumentOffset(argIndex)].jsValue(); + } + + friend class JSStack; friend class VMInspector; }; diff --git a/Source/JavaScriptCore/interpreter/CallFrameClosure.h b/Source/JavaScriptCore/interpreter/CallFrameClosure.h index 157d1b3b9..010c9655b 100644 --- a/Source/JavaScriptCore/interpreter/CallFrameClosure.h +++ b/Source/JavaScriptCore/interpreter/CallFrameClosure.h @@ -34,7 +34,6 @@ struct CallFrameClosure { JSFunction* function; FunctionExecutable* functionExecutable; JSGlobalData* globalData; - Register* oldEnd; JSScope* scope; int parameterCountIncludingThis; int argumentCountIncludingThis; diff --git a/Source/JavaScriptCore/interpreter/Interpreter.cpp b/Source/JavaScriptCore/interpreter/Interpreter.cpp index 8c09019da..0d475b416 100644 --- a/Source/JavaScriptCore/interpreter/Interpreter.cpp +++ b/Source/JavaScriptCore/interpreter/Interpreter.cpp @@ -48,6 +48,7 @@ #include "JSNameScope.h" #include "JSNotAnObject.h" #include "JSPropertyNameIterator.h" +#include "JSStackInlines.h" #include "JSString.h" #include "JSWithScope.h" #include "LLIntCLoop.h" @@ -67,6 +68,7 @@ #include <stdio.h> #include <wtf/StackStats.h> #include <wtf/Threading.h> +#include <wtf/WTFThreadData.h> #include <wtf/text/StringBuilder.h> #if ENABLE(JIT) @@ -79,41 +81,131 @@ using namespace std; namespace JSC { -static CallFrame* getCallerInfo(JSGlobalData*, CallFrame*, int& lineNumber, unsigned& bytecodeOffset); - -// Returns the depth of the scope chain within a given call frame. -static int depth(CodeBlock* codeBlock, JSScope* sc) +Interpreter::ErrorHandlingMode::ErrorHandlingMode(ExecState *exec) + : m_interpreter(*exec->interpreter()) { - if (!codeBlock->needsFullScopeChain()) - return 0; - return sc->localDepth(); + if (!m_interpreter.m_errorHandlingModeReentry) + m_interpreter.stack().enableErrorStackReserve(); + m_interpreter.m_errorHandlingModeReentry++; } -ALWAYS_INLINE CallFrame* Interpreter::slideRegisterWindowForCall(CodeBlock* newCodeBlock, JSStack* stack, CallFrame* callFrame, size_t registerOffset, int argumentCountIncludingThis) +Interpreter::ErrorHandlingMode::~ErrorHandlingMode() { - // This ensures enough space for the worst case scenario of zero arguments passed by the caller. - if (!stack->grow(callFrame->registers() + registerOffset + newCodeBlock->numParameters() + newCodeBlock->m_numCalleeRegisters)) - return 0; + m_interpreter.m_errorHandlingModeReentry--; + ASSERT(m_interpreter.m_errorHandlingModeReentry >= 0); + if (!m_interpreter.m_errorHandlingModeReentry) + m_interpreter.stack().disableErrorStackReserve(); +} - if (argumentCountIncludingThis >= newCodeBlock->numParameters()) { - Register* newCallFrame = callFrame->registers() + registerOffset; - return CallFrame::create(newCallFrame); - } - // Too few arguments -- copy arguments, then fill in missing arguments with undefined. - size_t delta = newCodeBlock->numParameters() - argumentCountIncludingThis; - CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + registerOffset + delta); +// The Interpreter::StackPolicy class is used to compute a stack capacity +// requirement to ensure that we have enough room on the native stack for: +// 1. the max cummulative stack used by the interpreter and all code +// paths sub of it up till leaf functions. +// 2. the max cummulative stack used by the interpreter before it reaches +// the next checkpoint (execute...() function) in the interpreter. +// +// The interpreter can be run on different threads and hence, different +// native stacks (with different sizes) before exiting out of the first +// frame. Hence, the required capacity needs to be re-computed on every +// entry into the interpreter. +// +// Currently the requiredStack is computed based on a policy. See comments +// in StackPolicy::StackPolicy() for details. + +Interpreter::StackPolicy::StackPolicy(Interpreter& interpreter, const StackBounds& stack) + : m_interpreter(interpreter) +{ + int size = stack.size(); + + const int DEFAULT_REQUIRED_STACK = 1024 * 1024; + const int DEFAULT_MINIMUM_USEABLE_STACK = 128 * 1024; + const int DEFAULT_ERROR_MODE_REQUIRED_STACK = 32 * 1024; + + // Here's the policy in a nutshell: + // + // 1. If we have a large stack, let JS use as much stack as possible + // but require that we have at least DEFAULT_REQUIRED_STACK capacity + // remaining on the stack: + // + // stack grows this way --> + // --------------------------------------------------------- + // | ... | <-- DEFAULT_REQUIRED_STACK --> | ... + // --------------------------------------------------------- + // ^ ^ + // start current sp + // + // 2. In event that we're re-entering the interpreter to handle + // exceptions (in error mode), we'll be a little more generous and + // require less stack capacity for the interpreter to be re-entered. + // + // This is needed because we may have just detected an eminent stack + // overflow based on the normally computed required stack capacity. + // However, the normal required capacity far exceeds what is needed + // for exception handling work. Hence, in error mode, we only require + // DEFAULT_ERROR_MODE_REQUIRED_STACK capacity. + // + // stack grows this way --> + // ----------------------------------------------------------------- + // | ... | <-- DEFAULT_ERROR_MODE_REQUIRED_STACK --> | ... + // ----------------------------------------------------------------- + // ^ ^ + // start current sp + // + // This smaller requried capacity also means that we won't re-trigger + // a stack overflow for processing the exception caused by the original + // StackOverflowError. + // + // 3. If the stack is not large enough, give JS at least a minimum + // amount of useable stack: + // + // stack grows this way --> + // -------------------------------------------------------------------- + // | <-- DEFAULT_MINIMUM_USEABLE_STACK --> | <-- requiredCapacity --> | + // -------------------------------------------------------------------- + // ^ ^ + // start current sp + // + // The minimum useable capacity is DEFAULT_MINIMUM_USEABLE_STACK. + // In this case, the requiredCapacity is whatever is left of the + // total stack capacity after we have give JS its minimum stack + // i.e. requiredCapcity can even be 0 if there's not enough stack. + + + // Policy 1: Normal mode: required = DEFAULT_REQUIRED_STACK. + // Policy 2: Erro mode: required = DEFAULT_ERROR_MODE_REQUIRED_STACK. + int requiredCapacity = !m_interpreter.m_errorHandlingModeReentry ? + DEFAULT_REQUIRED_STACK : DEFAULT_ERROR_MODE_REQUIRED_STACK; + + int useableStack = size - requiredCapacity; + + // Policy 3: Ensure the useable stack is not too small: + if (useableStack < DEFAULT_MINIMUM_USEABLE_STACK) + useableStack = DEFAULT_MINIMUM_USEABLE_STACK; + + // Sanity check: Make sure we do not use more space than the stack's + // total capacity: + if (useableStack > size) + useableStack = size; + + // Re-compute the requiredCapacity based on the adjusted useable stack + // size: + // interpreter stack checks: + requiredCapacity = size - useableStack; + ASSERT((requiredCapacity >= 0) && (requiredCapacity < size)); + + m_requiredCapacity = requiredCapacity; +} - Register* dst = &newCallFrame->uncheckedR(CallFrame::thisArgumentOffset()); - Register* end = dst - argumentCountIncludingThis; - for ( ; dst != end; --dst) - *dst = *(dst - delta); - end -= delta; - for ( ; dst != end; --dst) - *dst = jsUndefined(); +static CallFrame* getCallerInfo(JSGlobalData*, CallFrame*, int& lineNumber, unsigned& bytecodeOffset); - return newCallFrame; +// Returns the depth of the scope chain within a given call frame. +static int depth(CodeBlock* codeBlock, JSScope* sc) +{ + if (!codeBlock->needsFullScopeChain()) + return 0; + return sc->localDepth(); } JSValue eval(CallFrame* callFrame) @@ -164,7 +256,7 @@ JSValue eval(CallFrame* callFrame) JSValue thisValue = callerFrame->thisValue(); ASSERT(isValidThisObject(thisValue, callFrame)); Interpreter* interpreter = callFrame->globalData().interpreter; - return interpreter->execute(eval, callFrame, thisValue, callerScopeChain, callFrame->registers() - interpreter->stack().begin() + 1 + JSStack::CallFrameHeaderSize); + return interpreter->execute(eval, callFrame, thisValue, callerScopeChain); } CallFrame* loadVarargs(CallFrame* callFrame, JSStack* stack, JSValue thisValue, JSValue arguments, int firstFreeRegister) @@ -245,9 +337,10 @@ CallFrame* loadVarargs(CallFrame* callFrame, JSStack* stack, JSValue thisValue, return newCallFrame; } -Interpreter::Interpreter() +Interpreter::Interpreter(JSGlobalData& globalData) : m_sampleEntryDepth(0) - , m_reentryDepth(0) + , m_stack(globalData) + , m_errorHandlingModeReentry(0) #if !ASSERT_DISABLED , m_initialized(false) #endif @@ -681,26 +774,13 @@ NEVER_INLINE HandlerInfo* Interpreter::throwException(CallFrame*& callFrame, JSV if (!unwindCallFrame(callFrame, exceptionValue, bytecodeOffset, codeBlock)) { if (Profiler* profiler = callFrame->globalData().enabledProfiler()) profiler->exceptionUnwind(callFrame); - callFrame->globalData().topCallFrame = callFrame; return 0; } } - callFrame->globalData().topCallFrame = callFrame; if (Profiler* profiler = callFrame->globalData().enabledProfiler()) profiler->exceptionUnwind(callFrame); - // Shrink the JS stack, in case stack overflow made it huge. - Register* highWaterMark = 0; - for (CallFrame* callerFrame = callFrame; callerFrame; callerFrame = callerFrame->callerFrame()->removeHostCallFrameFlag()) { - CodeBlock* codeBlock = callerFrame->codeBlock(); - if (!codeBlock) - continue; - Register* callerHighWaterMark = callerFrame->registers() + codeBlock->m_numCalleeRegisters; - highWaterMark = max(highWaterMark, callerHighWaterMark); - } - m_stack.shrink(highWaterMark); - // Unwind the scope chain within the exception handler's call frame. JSScope* scope = callFrame->scope(); int scopeDelta = 0; @@ -747,20 +827,24 @@ JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, J SamplingScope samplingScope(this); JSScope* scope = callFrame->scope(); + JSGlobalData& globalData = *scope->globalData(); + ASSERT(isValidThisObject(thisObj, callFrame)); - ASSERT(!scope->globalData()->exception); - ASSERT(!callFrame->globalData().isCollectorBusy()); - if (callFrame->globalData().isCollectorBusy()) + ASSERT(!globalData.exception); + ASSERT(!globalData.isCollectorBusy()); + if (globalData.isCollectorBusy()) CRASH(); StackStats::CheckPoint stackCheckPoint; - if (m_reentryDepth >= MaxSmallThreadReentryDepth && m_reentryDepth >= callFrame->globalData().maxReentryDepth) + const StackBounds& nativeStack = wtfThreadData().stack(); + StackPolicy policy(*this, nativeStack); + if (!nativeStack.isSafeToRecurse(policy.requiredCapacity())) return checkedReturn(throwStackOverflowError(callFrame)); // First check if the "program" is actually just a JSON object. If so, // we'll handle the JSON object here. Else, we'll handle real JS code // below at failedJSONP. - DynamicGlobalObjectScope globalObjectScope(*scope->globalData(), scope->globalObject()); + DynamicGlobalObjectScope globalObjectScope(globalData, scope->globalObject()); Vector<JSONPData> JSONPData; bool parseResult; const String programSource = program->source().toString(); @@ -869,20 +953,16 @@ failedJSONP: return checkedReturn(throwError(callFrame, error)); CodeBlock* codeBlock = &program->generatedBytecode(); - // Reserve stack space for this invocation: - Register* oldEnd = m_stack.end(); - Register* newEnd = oldEnd + codeBlock->numParameters() + JSStack::CallFrameHeaderSize + codeBlock->m_numCalleeRegisters; - if (!m_stack.grow(newEnd)) - return checkedReturn(throwStackOverflowError(callFrame)); - // Push the call frame for this invocation: - CallFrame* newCallFrame = CallFrame::create(oldEnd + codeBlock->numParameters() + JSStack::CallFrameHeaderSize); ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'. - newCallFrame->init(codeBlock, 0, scope, CallFrame::noCaller(), codeBlock->numParameters(), 0); + CallFrame* newCallFrame = m_stack.pushFrame(callFrame, codeBlock, scope, 1, 0); + if (UNLIKELY(!newCallFrame)) + return checkedReturn(throwStackOverflowError(callFrame)); + + // Set the arguments for the callee: newCallFrame->setThisValue(thisObj); - TopCallFrameSetter topCallFrame(callFrame->globalData(), newCallFrame); - if (Profiler* profiler = callFrame->globalData().enabledProfiler()) + if (Profiler* profiler = globalData.enabledProfiler()) profiler->willExecute(callFrame, program->sourceURL(), program->lineNo()); // Execute the code: @@ -890,213 +970,168 @@ failedJSONP: { SamplingTool::CallRecord callRecord(m_sampler.get()); - m_reentryDepth++; #if ENABLE(LLINT_C_LOOP) result = LLInt::CLoop::execute(newCallFrame, llint_program_prologue); #elif ENABLE(JIT) - result = program->generatedJITCode().execute(&m_stack, newCallFrame, scope->globalData()); + result = program->generatedJITCode().execute(&m_stack, newCallFrame, &globalData); #endif // ENABLE(JIT) - - m_reentryDepth--; } - if (Profiler* profiler = callFrame->globalData().enabledProfiler()) + if (Profiler* profiler = globalData.enabledProfiler()) profiler->didExecute(callFrame, program->sourceURL(), program->lineNo()); - m_stack.shrink(oldEnd); + m_stack.popFrame(newCallFrame); return checkedReturn(result); } JSValue Interpreter::executeCall(CallFrame* callFrame, JSObject* function, CallType callType, const CallData& callData, JSValue thisValue, const ArgList& args) { + JSGlobalData& globalData = callFrame->globalData(); ASSERT(isValidThisObject(thisValue, callFrame)); ASSERT(!callFrame->hadException()); - ASSERT(!callFrame->globalData().isCollectorBusy()); - if (callFrame->globalData().isCollectorBusy()) + ASSERT(!globalData.isCollectorBusy()); + if (globalData.isCollectorBusy()) return jsNull(); StackStats::CheckPoint stackCheckPoint; - if (m_reentryDepth >= MaxSmallThreadReentryDepth && m_reentryDepth >= callFrame->globalData().maxReentryDepth) + const StackBounds& nativeStack = wtfThreadData().stack(); + StackPolicy policy(*this, nativeStack); + if (!nativeStack.isSafeToRecurse(policy.requiredCapacity())) return checkedReturn(throwStackOverflowError(callFrame)); - Register* oldEnd = m_stack.end(); - ASSERT(callFrame->frameExtent() <= oldEnd || callFrame == callFrame->scope()->globalObject()->globalExec()); - int argCount = 1 + args.size(); // implicit "this" parameter - size_t registerOffset = argCount + JSStack::CallFrameHeaderSize; - - CallFrame* newCallFrame = CallFrame::create(oldEnd + registerOffset); - if (!m_stack.grow(newCallFrame->registers())) - return checkedReturn(throwStackOverflowError(callFrame)); + bool isJSCall = (callType == CallTypeJS); + JSScope* scope; + CodeBlock* newCodeBlock; + size_t argsCount = 1 + args.size(); // implicit "this" parameter - newCallFrame->setThisValue(thisValue); - for (size_t i = 0; i < args.size(); ++i) - newCallFrame->setArgument(i, args.at(i)); - - if (callType == CallTypeJS) { - JSScope* callDataScope = callData.js.scope; - - DynamicGlobalObjectScope globalObjectScope(*callDataScope->globalData(), callDataScope->globalObject()); + if (isJSCall) + scope = callData.js.scope; + else { + ASSERT(callType == CallTypeHost); + scope = callFrame->scope(); + } + DynamicGlobalObjectScope globalObjectScope(globalData, scope->globalObject()); - JSObject* compileError = callData.js.functionExecutable->compileForCall(callFrame, callDataScope); + if (isJSCall) { + // Compile the callee: + JSObject* compileError = callData.js.functionExecutable->compileForCall(callFrame, scope); if (UNLIKELY(!!compileError)) { - m_stack.shrink(oldEnd); return checkedReturn(throwError(callFrame, compileError)); } + newCodeBlock = &callData.js.functionExecutable->generatedBytecodeForCall(); + ASSERT(!!newCodeBlock); + } else + newCodeBlock = 0; - CodeBlock* newCodeBlock = &callData.js.functionExecutable->generatedBytecodeForCall(); - newCallFrame = slideRegisterWindowForCall(newCodeBlock, &m_stack, newCallFrame, 0, argCount); - if (UNLIKELY(!newCallFrame)) { - m_stack.shrink(oldEnd); - return checkedReturn(throwStackOverflowError(callFrame)); - } - - newCallFrame->init(newCodeBlock, 0, callDataScope, callFrame->addHostCallFrameFlag(), argCount, function); + CallFrame* newCallFrame = m_stack.pushFrame(callFrame, newCodeBlock, scope, argsCount, function); + if (UNLIKELY(!newCallFrame)) + return checkedReturn(throwStackOverflowError(callFrame)); - TopCallFrameSetter topCallFrame(callFrame->globalData(), newCallFrame); + // Set the arguments for the callee: + newCallFrame->setThisValue(thisValue); + for (size_t i = 0; i < args.size(); ++i) + newCallFrame->setArgument(i, args.at(i)); - if (Profiler* profiler = callFrame->globalData().enabledProfiler()) - profiler->willExecute(callFrame, function); + if (Profiler* profiler = globalData.enabledProfiler()) + profiler->willExecute(callFrame, function); - JSValue result; - { - SamplingTool::CallRecord callRecord(m_sampler.get()); + JSValue result; + { + SamplingTool::CallRecord callRecord(m_sampler.get(), !isJSCall); - m_reentryDepth++; + // Execute the code: + if (isJSCall) { #if ENABLE(LLINT_C_LOOP) result = LLInt::CLoop::execute(newCallFrame, llint_function_for_call_prologue); #elif ENABLE(JIT) - result = callData.js.functionExecutable->generatedJITCodeForCall().execute(&m_stack, newCallFrame, callDataScope->globalData()); + result = callData.js.functionExecutable->generatedJITCodeForCall().execute(&m_stack, newCallFrame, &globalData); #endif // ENABLE(JIT) - - m_reentryDepth--; - } - - if (Profiler* profiler = callFrame->globalData().enabledProfiler()) - profiler->didExecute(callFrame, function); - - m_stack.shrink(oldEnd); - return checkedReturn(result); - } - - ASSERT(callType == CallTypeHost); - JSScope* scope = callFrame->scope(); - newCallFrame->init(0, 0, scope, callFrame->addHostCallFrameFlag(), argCount, function); - - TopCallFrameSetter topCallFrame(callFrame->globalData(), newCallFrame); - - DynamicGlobalObjectScope globalObjectScope(*scope->globalData(), scope->globalObject()); - - if (Profiler* profiler = callFrame->globalData().enabledProfiler()) - profiler->willExecute(callFrame, function); - - JSValue result; - { - SamplingTool::HostCallRecord callRecord(m_sampler.get()); - result = JSValue::decode(callData.native.function(newCallFrame)); + } else + result = JSValue::decode(callData.native.function(newCallFrame)); } - if (Profiler* profiler = callFrame->globalData().enabledProfiler()) + if (Profiler* profiler = globalData.enabledProfiler()) profiler->didExecute(callFrame, function); - m_stack.shrink(oldEnd); + m_stack.popFrame(newCallFrame); return checkedReturn(result); } JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* constructor, ConstructType constructType, const ConstructData& constructData, const ArgList& args) { + JSGlobalData& globalData = callFrame->globalData(); ASSERT(!callFrame->hadException()); - ASSERT(!callFrame->globalData().isCollectorBusy()); + ASSERT(!globalData.isCollectorBusy()); // We throw in this case because we have to return something "valid" but we're // already in an invalid state. - if (callFrame->globalData().isCollectorBusy()) + if (globalData.isCollectorBusy()) return checkedReturn(throwStackOverflowError(callFrame)); StackStats::CheckPoint stackCheckPoint; - if (m_reentryDepth >= MaxSmallThreadReentryDepth && m_reentryDepth >= callFrame->globalData().maxReentryDepth) - return checkedReturn(throwStackOverflowError(callFrame)); - - Register* oldEnd = m_stack.end(); - int argCount = 1 + args.size(); // implicit "this" parameter - size_t registerOffset = argCount + JSStack::CallFrameHeaderSize; - - if (!m_stack.grow(oldEnd + registerOffset)) + const StackBounds& nativeStack = wtfThreadData().stack(); + StackPolicy policy(*this, nativeStack); + if (!nativeStack.isSafeToRecurse(policy.requiredCapacity())) return checkedReturn(throwStackOverflowError(callFrame)); - CallFrame* newCallFrame = CallFrame::create(oldEnd + registerOffset); - newCallFrame->setThisValue(jsUndefined()); - for (size_t i = 0; i < args.size(); ++i) - newCallFrame->setArgument(i, args.at(i)); + bool isJSConstruct = (constructType == ConstructTypeJS); + JSScope* scope; + CodeBlock* newCodeBlock; + size_t argsCount = 1 + args.size(); // implicit "this" parameter - if (constructType == ConstructTypeJS) { - JSScope* constructDataScope = constructData.js.scope; + if (isJSConstruct) + scope = constructData.js.scope; + else { + ASSERT(constructType == ConstructTypeHost); + scope = callFrame->scope(); + } - DynamicGlobalObjectScope globalObjectScope(*constructDataScope->globalData(), constructDataScope->globalObject()); + DynamicGlobalObjectScope globalObjectScope(globalData, scope->globalObject()); - JSObject* compileError = constructData.js.functionExecutable->compileForConstruct(callFrame, constructDataScope); + if (isJSConstruct) { + // Compile the callee: + JSObject* compileError = constructData.js.functionExecutable->compileForConstruct(callFrame, scope); if (UNLIKELY(!!compileError)) { - m_stack.shrink(oldEnd); return checkedReturn(throwError(callFrame, compileError)); } + newCodeBlock = &constructData.js.functionExecutable->generatedBytecodeForConstruct(); + ASSERT(!!newCodeBlock); + } else + newCodeBlock = 0; - CodeBlock* newCodeBlock = &constructData.js.functionExecutable->generatedBytecodeForConstruct(); - newCallFrame = slideRegisterWindowForCall(newCodeBlock, &m_stack, newCallFrame, 0, argCount); - if (UNLIKELY(!newCallFrame)) { - m_stack.shrink(oldEnd); - return checkedReturn(throwStackOverflowError(callFrame)); - } - - newCallFrame->init(newCodeBlock, 0, constructDataScope, callFrame->addHostCallFrameFlag(), argCount, constructor); + CallFrame* newCallFrame = m_stack.pushFrame(callFrame, newCodeBlock, scope, argsCount, constructor); + if (UNLIKELY(!newCallFrame)) + return checkedReturn(throwStackOverflowError(callFrame)); - TopCallFrameSetter topCallFrame(callFrame->globalData(), newCallFrame); + // Set the arguments for the callee: + newCallFrame->setThisValue(jsUndefined()); + for (size_t i = 0; i < args.size(); ++i) + newCallFrame->setArgument(i, args.at(i)); - if (Profiler* profiler = callFrame->globalData().enabledProfiler()) - profiler->willExecute(callFrame, constructor); + if (Profiler* profiler = globalData.enabledProfiler()) + profiler->willExecute(callFrame, constructor); - JSValue result; - { - SamplingTool::CallRecord callRecord(m_sampler.get()); + JSValue result; + { + SamplingTool::CallRecord callRecord(m_sampler.get(), !isJSConstruct); - m_reentryDepth++; + // Execute the code. + if (isJSConstruct) { #if ENABLE(LLINT_C_LOOP) result = LLInt::CLoop::execute(newCallFrame, llint_function_for_construct_prologue); #elif ENABLE(JIT) - result = constructData.js.functionExecutable->generatedJITCodeForConstruct().execute(&m_stack, newCallFrame, constructDataScope->globalData()); + result = constructData.js.functionExecutable->generatedJITCodeForConstruct().execute(&m_stack, newCallFrame, &globalData); #endif // ENABLE(JIT) - m_reentryDepth--; + } else { + result = JSValue::decode(constructData.native.function(newCallFrame)); } - - if (Profiler* profiler = callFrame->globalData().enabledProfiler()) - profiler->didExecute(callFrame, constructor); - - m_stack.shrink(oldEnd); - if (callFrame->hadException()) - return 0; - ASSERT(result.isObject()); - return checkedReturn(asObject(result)); - } - - ASSERT(constructType == ConstructTypeHost); - JSScope* scope = callFrame->scope(); - newCallFrame->init(0, 0, scope, callFrame->addHostCallFrameFlag(), argCount, constructor); - - TopCallFrameSetter topCallFrame(callFrame->globalData(), newCallFrame); - - DynamicGlobalObjectScope globalObjectScope(*scope->globalData(), scope->globalObject()); - - if (Profiler* profiler = callFrame->globalData().enabledProfiler()) - profiler->willExecute(callFrame, constructor); - - JSValue result; - { - SamplingTool::HostCallRecord callRecord(m_sampler.get()); - result = JSValue::decode(constructData.native.function(newCallFrame)); } - if (Profiler* profiler = callFrame->globalData().enabledProfiler()) + if (Profiler* profiler = globalData.enabledProfiler()) profiler->didExecute(callFrame, constructor); - m_stack.shrink(oldEnd); + m_stack.popFrame(newCallFrame); + if (callFrame->hadException()) return 0; ASSERT(result.isObject()); @@ -1105,101 +1140,118 @@ JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* construc CallFrameClosure Interpreter::prepareForRepeatCall(FunctionExecutable* functionExecutable, CallFrame* callFrame, JSFunction* function, int argumentCountIncludingThis, JSScope* scope) { - ASSERT(!scope->globalData()->exception); + JSGlobalData& globalData = *scope->globalData(); + ASSERT(!globalData.exception); - if (callFrame->globalData().isCollectorBusy()) + if (globalData.isCollectorBusy()) return CallFrameClosure(); StackStats::CheckPoint stackCheckPoint; - if (m_reentryDepth >= MaxSmallThreadReentryDepth && m_reentryDepth >= callFrame->globalData().maxReentryDepth) { - throwStackOverflowError(callFrame); - return CallFrameClosure(); - } - - Register* oldEnd = m_stack.end(); - size_t registerOffset = argumentCountIncludingThis + JSStack::CallFrameHeaderSize; - - CallFrame* newCallFrame = CallFrame::create(oldEnd + registerOffset); - if (!m_stack.grow(newCallFrame->registers())) { + const StackBounds& nativeStack = wtfThreadData().stack(); + StackPolicy policy(*this, nativeStack); + if (!nativeStack.isSafeToRecurse(policy.requiredCapacity())) { throwStackOverflowError(callFrame); return CallFrameClosure(); } + // Compile the callee: JSObject* error = functionExecutable->compileForCall(callFrame, scope); if (error) { throwError(callFrame, error); - m_stack.shrink(oldEnd); return CallFrameClosure(); } - CodeBlock* codeBlock = &functionExecutable->generatedBytecodeForCall(); + CodeBlock* newCodeBlock = &functionExecutable->generatedBytecodeForCall(); - newCallFrame = slideRegisterWindowForCall(codeBlock, &m_stack, newCallFrame, 0, argumentCountIncludingThis); + size_t argsCount = argumentCountIncludingThis; + + CallFrame* newCallFrame = m_stack.pushFrame(callFrame, newCodeBlock, scope, argsCount, function); if (UNLIKELY(!newCallFrame)) { throwStackOverflowError(callFrame); - m_stack.shrink(oldEnd); return CallFrameClosure(); } - newCallFrame->init(codeBlock, 0, scope, callFrame->addHostCallFrameFlag(), argumentCountIncludingThis, function); - scope->globalData()->topCallFrame = newCallFrame; - CallFrameClosure result = { callFrame, newCallFrame, function, functionExecutable, scope->globalData(), oldEnd, scope, codeBlock->numParameters(), argumentCountIncludingThis }; + + if (UNLIKELY(!newCallFrame)) { + throwStackOverflowError(callFrame); + return CallFrameClosure(); + } + + // Return the successful closure: + CallFrameClosure result = { callFrame, newCallFrame, function, functionExecutable, &globalData, scope, newCodeBlock->numParameters(), argumentCountIncludingThis }; return result; } JSValue Interpreter::execute(CallFrameClosure& closure) { + JSGlobalData& globalData = *closure.globalData; SamplingScope samplingScope(this); - ASSERT(!closure.oldCallFrame->globalData().isCollectorBusy()); - if (closure.oldCallFrame->globalData().isCollectorBusy()) + ASSERT(!globalData.isCollectorBusy()); + if (globalData.isCollectorBusy()) return jsNull(); StackStats::CheckPoint stackCheckPoint; + m_stack.validateFence(closure.newCallFrame, "BEFORE"); closure.resetCallFrame(); - if (Profiler* profiler = closure.oldCallFrame->globalData().enabledProfiler()) + m_stack.validateFence(closure.newCallFrame, "STEP 1"); + + if (Profiler* profiler = globalData.enabledProfiler()) profiler->willExecute(closure.oldCallFrame, closure.function); - TopCallFrameSetter topCallFrame(*closure.globalData, closure.newCallFrame); + // The code execution below may push more frames and point the topCallFrame + // to those newer frames, or it may pop to the top frame to the caller of + // the current repeat frame, or it may leave the top frame pointing to the + // current repeat frame. + // + // Hence, we need to preserve the topCallFrame here ourselves before + // repeating this call on a second callback function. + TopCallFrameSetter topCallFrame(globalData, closure.newCallFrame); + + // Execute the code: JSValue result; { SamplingTool::CallRecord callRecord(m_sampler.get()); - m_reentryDepth++; #if ENABLE(LLINT_C_LOOP) result = LLInt::CLoop::execute(closure.newCallFrame, llint_function_for_call_prologue); #elif ENABLE(JIT) - result = closure.functionExecutable->generatedJITCodeForCall().execute(&m_stack, closure.newCallFrame, closure.globalData); + result = closure.functionExecutable->generatedJITCodeForCall().execute(&m_stack, closure.newCallFrame, &globalData); #endif // ENABLE(JIT) - m_reentryDepth--; } - if (Profiler* profiler = closure.oldCallFrame->globalData().enabledProfiler()) + if (Profiler* profiler = globalData.enabledProfiler()) profiler->didExecute(closure.oldCallFrame, closure.function); + + m_stack.validateFence(closure.newCallFrame, "AFTER"); return checkedReturn(result); } void Interpreter::endRepeatCall(CallFrameClosure& closure) { - closure.globalData->topCallFrame = closure.oldCallFrame; - m_stack.shrink(closure.oldEnd); + m_stack.popFrame(closure.newCallFrame); } -JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue thisValue, JSScope* scope, int globalRegisterOffset) +JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue thisValue, JSScope* scope) { + JSGlobalData& globalData = *scope->globalData(); SamplingScope samplingScope(this); + ASSERT(scope->globalData() == &callFrame->globalData()); ASSERT(isValidThisObject(thisValue, callFrame)); - ASSERT(!scope->globalData()->exception); - ASSERT(!callFrame->globalData().isCollectorBusy()); - if (callFrame->globalData().isCollectorBusy()) + ASSERT(!globalData.exception); + ASSERT(!globalData.isCollectorBusy()); + if (globalData.isCollectorBusy()) return jsNull(); - DynamicGlobalObjectScope globalObjectScope(*scope->globalData(), scope->globalObject()); + DynamicGlobalObjectScope globalObjectScope(globalData, scope->globalObject()); StackStats::CheckPoint stackCheckPoint; - if (m_reentryDepth >= MaxSmallThreadReentryDepth && m_reentryDepth >= callFrame->globalData().maxReentryDepth) + const StackBounds& nativeStack = wtfThreadData().stack(); + StackPolicy policy(*this, nativeStack); + if (!nativeStack.isSafeToRecurse(policy.requiredCapacity())) return checkedReturn(throwStackOverflowError(callFrame)); + // Compile the callee: JSObject* compileError = eval->compile(callFrame, scope); if (UNLIKELY(!!compileError)) return checkedReturn(throwError(callFrame, compileError)); @@ -1222,7 +1274,7 @@ JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue variableObject = scope; } // Scope for BatchedTransitionOptimizer - BatchedTransitionOptimizer optimizer(callFrame->globalData(), variableObject); + BatchedTransitionOptimizer optimizer(globalData, variableObject); for (unsigned i = 0; i < numVariables; ++i) { const Identifier& ident = codeBlock->variable(i); @@ -1239,40 +1291,34 @@ JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue } } - Register* oldEnd = m_stack.end(); - Register* newEnd = m_stack.begin() + globalRegisterOffset + codeBlock->m_numCalleeRegisters; - if (!m_stack.grow(newEnd)) + // Push the frame: + ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'. + CallFrame* newCallFrame = m_stack.pushFrame(callFrame, codeBlock, scope, 1, 0); + if (UNLIKELY(!newCallFrame)) return checkedReturn(throwStackOverflowError(callFrame)); - CallFrame* newCallFrame = CallFrame::create(m_stack.begin() + globalRegisterOffset); - - ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'. - newCallFrame->init(codeBlock, 0, scope, callFrame->addHostCallFrameFlag(), codeBlock->numParameters(), 0); + // Set the arguments for the callee: newCallFrame->setThisValue(thisValue); - TopCallFrameSetter topCallFrame(callFrame->globalData(), newCallFrame); - - if (Profiler* profiler = callFrame->globalData().enabledProfiler()) + if (Profiler* profiler = globalData.enabledProfiler()) profiler->willExecute(callFrame, eval->sourceURL(), eval->lineNo()); + // Execute the code: JSValue result; { SamplingTool::CallRecord callRecord(m_sampler.get()); - - m_reentryDepth++; #if ENABLE(LLINT_C_LOOP) result = LLInt::CLoop::execute(newCallFrame, llint_eval_prologue); #elif ENABLE(JIT) - result = eval->generatedJITCode().execute(&m_stack, newCallFrame, scope->globalData()); + result = eval->generatedJITCode().execute(&m_stack, newCallFrame, &globalData); #endif // ENABLE(JIT) - m_reentryDepth--; } - if (Profiler* profiler = callFrame->globalData().enabledProfiler()) + if (Profiler* profiler = globalData.enabledProfiler()) profiler->didExecute(callFrame, eval->sourceURL(), eval->lineNo()); - m_stack.shrink(oldEnd); + m_stack.popFrame(newCallFrame); return checkedReturn(result); } diff --git a/Source/JavaScriptCore/interpreter/Interpreter.h b/Source/JavaScriptCore/interpreter/Interpreter.h index 11c6f078a..c3bca1ad7 100644 --- a/Source/JavaScriptCore/interpreter/Interpreter.h +++ b/Source/JavaScriptCore/interpreter/Interpreter.h @@ -48,6 +48,7 @@ namespace JSC { class EvalExecutable; class ExecutableBase; class FunctionExecutable; + class JSGlobalData; class JSGlobalObject; class LLIntOffsetsExtractor; class ProgramExecutable; @@ -170,21 +171,22 @@ namespace JSC { } }; - // We use a smaller reentrancy limit on iPhone because of the high amount of - // stack space required on the web thread. -#if PLATFORM(IOS) - enum { MaxLargeThreadReentryDepth = 64, MaxSmallThreadReentryDepth = 16 }; -#else - enum { MaxLargeThreadReentryDepth = 256, MaxSmallThreadReentryDepth = 16 }; -#endif // PLATFORM(IOS) - class Interpreter { WTF_MAKE_FAST_ALLOCATED; friend class CachedCall; friend class LLIntOffsetsExtractor; friend class JIT; + public: - Interpreter(); + class ErrorHandlingMode { + public: + JS_EXPORT_PRIVATE ErrorHandlingMode(ExecState*); + JS_EXPORT_PRIVATE ~ErrorHandlingMode(); + private: + Interpreter& m_interpreter; + }; + + Interpreter(JSGlobalData &); ~Interpreter(); void initialize(bool canUseJIT); @@ -218,7 +220,6 @@ namespace JSC { JSValue executeCall(CallFrame*, JSObject* function, CallType, const CallData&, JSValue thisValue, const ArgList&); JSObject* executeConstruct(CallFrame*, JSObject* function, ConstructType, const ConstructData&, const ArgList&); JSValue execute(EvalExecutable*, CallFrame*, JSValue thisValue, JSScope*); - JSValue execute(EvalExecutable*, CallFrame*, JSValue thisValue, JSScope*, int globalRegisterOffset); JSValue retrieveArgumentsFromVMCode(CallFrame*, JSFunction*) const; JSValue retrieveCallerFromVMCode(CallFrame*, JSFunction*) const; @@ -241,6 +242,16 @@ namespace JSC { JS_EXPORT_PRIVATE void dumpCallFrame(CallFrame*); private: + class StackPolicy { + public: + StackPolicy(Interpreter&, const StackBounds&); + inline size_t requiredCapacity() { return m_requiredCapacity; } + + private: + Interpreter& m_interpreter; + size_t m_requiredCapacity; + }; + enum ExecutionFlag { Normal, InitializeAndReturn }; CallFrameClosure prepareForRepeatCall(FunctionExecutable*, CallFrame*, JSFunction*, int argumentCountIncludingThis, JSScope*); @@ -249,8 +260,6 @@ namespace JSC { NEVER_INLINE bool unwindCallFrame(CallFrame*&, JSValue, unsigned& bytecodeOffset, CodeBlock*&); - static ALWAYS_INLINE CallFrame* slideRegisterWindowForCall(CodeBlock*, JSStack*, CallFrame*, size_t registerOffset, int argc); - static CallFrame* findFunctionCallFrameFromVMCode(CallFrame*, JSFunction*); void dumpRegisters(CallFrame*); @@ -261,9 +270,8 @@ namespace JSC { int m_sampleEntryDepth; OwnPtr<SamplingTool> m_sampler; - int m_reentryDepth; - JSStack m_stack; + int m_errorHandlingModeReentry; #if ENABLE(COMPUTED_GOTO_OPCODES) && ENABLE(LLINT) Opcode* m_opcodeTable; // Maps OpcodeID => Opcode for compiling @@ -281,11 +289,6 @@ namespace JSC { return !thisValue.isObject() || thisValue.toThisObject(exec) == thisValue; } - inline JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue thisValue, JSScope* scope) - { - return execute(eval, callFrame, thisValue, scope, m_stack.size() + 1 + JSStack::CallFrameHeaderSize); - } - JSValue eval(CallFrame*); CallFrame* loadVarargs(CallFrame*, JSStack*, JSValue thisValue, JSValue arguments, int firstFreeRegister); diff --git a/Source/JavaScriptCore/interpreter/JSStack.cpp b/Source/JavaScriptCore/interpreter/JSStack.cpp index 5dd708a48..f5f9e3763 100644 --- a/Source/JavaScriptCore/interpreter/JSStack.cpp +++ b/Source/JavaScriptCore/interpreter/JSStack.cpp @@ -28,6 +28,7 @@ #include "config.h" #include "JSStack.h" +#include "JSStackInlines.h" #include "ConservativeRoots.h" #include "Interpreter.h" @@ -41,7 +42,22 @@ static Mutex& stackStatisticsMutex() DEFINE_STATIC_LOCAL(Mutex, staticMutex, ()); return staticMutex; } - + +JSStack::JSStack(JSGlobalData& globalData, size_t capacity) + : m_end(0) + , m_topCallFrame(globalData.topCallFrame) +{ + ASSERT(capacity && isPageAligned(capacity)); + + m_reservation = PageReservation::reserve(roundUpAllocationSize(capacity * sizeof(Register), commitSize), OSAllocator::JSVMStackPages); + m_end = static_cast<Register*>(m_reservation.base()); + m_commitEnd = static_cast<Register*>(m_reservation.base()); + + disableErrorStackReserve(); + + m_topCallFrame = 0; +} + JSStack::~JSStack() { void* base = m_reservation.base(); @@ -52,15 +68,22 @@ JSStack::~JSStack() bool JSStack::growSlowCase(Register* newEnd) { + // If we have already committed enough memory to satisfy this request, + // just update the end pointer and return. if (newEnd <= m_commitEnd) { m_end = newEnd; return true; } + // Compute the chunk size of additional memory to commit, and see if we + // have it is still within our budget. If not, we'll fail to grow and + // return false. long delta = roundUpAllocationSize(reinterpret_cast<char*>(newEnd) - reinterpret_cast<char*>(m_commitEnd), commitSize); - if (reinterpret_cast<char*>(m_commitEnd) + delta > static_cast<char*>(m_reservation.base()) + m_reservation.size()) + if (reinterpret_cast<char*>(m_commitEnd) + delta > reinterpret_cast<char*>(m_useableEnd)) return false; + // Otherwise, the growth is still within our budget. Go ahead and commit + // it and return true. m_reservation.commit(m_commitEnd, delta); addToCommittedByteCount(delta); m_commitEnd = reinterpret_cast_ptr<Register*>(reinterpret_cast<char*>(m_commitEnd) + delta); @@ -70,12 +93,12 @@ bool JSStack::growSlowCase(Register* newEnd) void JSStack::gatherConservativeRoots(ConservativeRoots& conservativeRoots) { - conservativeRoots.add(begin(), end()); + conservativeRoots.add(begin(), getTopOfStack()); } void JSStack::gatherConservativeRoots(ConservativeRoots& conservativeRoots, JITStubRoutineSet& jitStubRoutines, DFGCodeBlocks& dfgCodeBlocks) { - conservativeRoots.add(begin(), end(), jitStubRoutines, dfgCodeBlocks); + conservativeRoots.add(begin(), getTopOfStack(), jitStubRoutines, dfgCodeBlocks); } void JSStack::releaseExcessCapacity() @@ -104,4 +127,25 @@ void JSStack::addToCommittedByteCount(long byteCount) committedBytesCount += byteCount; } +void JSStack::enableErrorStackReserve() +{ + m_useableEnd = reservationEnd(); +} + +void JSStack::disableErrorStackReserve() +{ + char* useableEnd = reinterpret_cast<char*>(reservationEnd()) - commitSize; + m_useableEnd = reinterpret_cast<Register*>(useableEnd); + + // By the time we get here, we are guaranteed to be destructing the last + // Interpreter::ErrorHandlingMode that enabled this reserve in the first + // place. That means the stack space beyond m_useableEnd before we + // enabled the reserve was not previously in use. Hence, it is safe to + // shrink back to that m_useableEnd. + if (m_end > m_useableEnd) { + ASSERT(m_topCallFrame->frameExtent() <= m_useableEnd); + shrink(m_useableEnd); + } +} + } // namespace JSC diff --git a/Source/JavaScriptCore/interpreter/JSStack.h b/Source/JavaScriptCore/interpreter/JSStack.h index 86fa40be7..3beb59ebf 100644 --- a/Source/JavaScriptCore/interpreter/JSStack.h +++ b/Source/JavaScriptCore/interpreter/JSStack.h @@ -35,11 +35,17 @@ #include <wtf/PageReservation.h> #include <wtf/VMTags.h> +#if !defined(NDEBUG) && !defined(ENABLE_DEBUG_JSSTACK) +#define ENABLE_DEBUG_JSSTACK 1 +#endif + namespace JSC { class ConservativeRoots; class DFGCodeBlocks; + class ExecState; class JITStubRoutineSet; + class JSGlobalData; class LLIntOffsetsExtractor; class JSStack { @@ -61,7 +67,7 @@ namespace JSC { // Allow 8k of excess registers before we start trying to reap the stack static const ptrdiff_t maxExcessCapacity = 8 * 1024; - JSStack(size_t capacity = defaultCapacity); + JSStack(JSGlobalData&, size_t capacity = defaultCapacity); ~JSStack(); void gatherConservativeRoots(ConservativeRoots&); @@ -72,7 +78,6 @@ namespace JSC { size_t size() const { return end() - begin(); } bool grow(Register*); - void shrink(Register*); static size_t committedByteCount(); static void initializeThreading(); @@ -82,26 +87,55 @@ namespace JSC { return &m_end; } + Register* getTopOfFrame(CallFrame*); + Register* getStartOfFrame(CallFrame*); + Register* getTopOfStack(); + + CallFrame* pushFrame(CallFrame* callerFrame, class CodeBlock*, + JSScope*, int argsCount, JSObject* callee); + + void popFrame(CallFrame*); + + void enableErrorStackReserve(); + void disableErrorStackReserve(); + +#if ENABLE(DEBUG_JSSTACK) + void installFence(CallFrame*, const char *function = "", int lineNo = 0); + void validateFence(CallFrame*, const char *function = "", int lineNo = 0); + static const int FenceSize = 4; +#else // !ENABLE(DEBUG_JSSTACK) + void installFence(CallFrame*, const char* = "", int = 0) { } + void validateFence(CallFrame*, const char* = "", int = 0) { } +#endif // !ENABLE(DEBUG_JSSTACK) + private: - friend class LLIntOffsetsExtractor; + Register* reservationEnd() const + { + char* base = static_cast<char*>(m_reservation.base()); + char* reservationEnd = base + m_reservation.size(); + return reinterpret_cast<Register*>(reservationEnd); + } + +#if ENABLE(DEBUG_JSSTACK) + static JSValue generateFenceValue(size_t argIndex); + void installTrapsAfterFrame(CallFrame*); +#else + void installTrapsAfterFrame(CallFrame*) { } +#endif bool growSlowCase(Register*); + void shrink(Register*); void releaseExcessCapacity(); void addToCommittedByteCount(long); + Register* m_end; Register* m_commitEnd; + Register* m_useableEnd; PageReservation m_reservation; - }; + CallFrame*& m_topCallFrame; - inline JSStack::JSStack(size_t capacity) - : m_end(0) - { - ASSERT(capacity && isPageAligned(capacity)); - - m_reservation = PageReservation::reserve(roundUpAllocationSize(capacity * sizeof(Register), commitSize), OSAllocator::JSVMStackPages); - m_end = static_cast<Register*>(m_reservation.base()); - m_commitEnd = static_cast<Register*>(m_reservation.base()); - } + friend class LLIntOffsetsExtractor; + }; inline void JSStack::shrink(Register* newEnd) { diff --git a/Source/JavaScriptCore/interpreter/JSStackInlines.h b/Source/JavaScriptCore/interpreter/JSStackInlines.h new file mode 100644 index 000000000..25b7dcf5a --- /dev/null +++ b/Source/JavaScriptCore/interpreter/JSStackInlines.h @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2012 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR + * 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. + */ + +#ifndef JSStackInlines_h +#define JSStackInlines_h + +#include "CallFrame.h" +#include "CodeBlock.h" +#include "JSStack.h" +#include <wtf/UnusedParam.h> + +namespace JSC { + +inline Register* JSStack::getTopOfFrame(CallFrame* frame) +{ + if (UNLIKELY(!frame)) + return begin(); + return frame->frameExtent(); +} + +inline Register* JSStack::getTopOfStack() +{ + return getTopOfFrame(m_topCallFrame); +} + +inline Register* JSStack::getStartOfFrame(CallFrame* frame) +{ + CallFrame* callerFrame = frame->callerFrameNoFlags(); + return getTopOfFrame(callerFrame); +} + +inline CallFrame* JSStack::pushFrame(CallFrame* callerFrame, + class CodeBlock* codeBlock, JSScope* scope, int argsCount, JSObject* callee) +{ + ASSERT(!!scope); + Register* oldEnd = getTopOfStack(); + + // Ensure that we have enough space for the parameters: + size_t paddedArgsCount = argsCount; + if (codeBlock) { + size_t numParameters = codeBlock->numParameters(); + if (paddedArgsCount < numParameters) + paddedArgsCount = numParameters; + } + + Register* newCallFrameSlot = oldEnd + paddedArgsCount + JSStack::CallFrameHeaderSize; +#if ENABLE(DEBUG_JSSTACK) + newCallFrameSlot += JSStack::FenceSize; +#endif + Register* newEnd = newCallFrameSlot; + if (!!codeBlock) + newEnd += codeBlock->m_numCalleeRegisters; + + // Ensure that we have the needed stack capacity to push the new frame: + if (!grow(newEnd)) + return 0; + + // Compute the address of the new frame for this invocation: + CallFrame* newCallFrame = CallFrame::create(newCallFrameSlot); + ASSERT(!!newCallFrame); + + // The caller frame should always be the real previous frame on the stack, + // and not a potential GlobalExec that was passed in. Point callerFrame to + // the top frame on the stack. + callerFrame = m_topCallFrame; + + // Initialize the frame header: + newCallFrame->init(codeBlock, 0, scope, + callerFrame->addHostCallFrameFlag(), argsCount, callee); + + ASSERT(!!newCallFrame->scope()); + + // Pad additional args if needed: + // Note: we need to subtract 1 from argsCount and paddedArgsCount to + // exclude the this pointer. + for (size_t i = argsCount-1; i < paddedArgsCount-1; ++i) + newCallFrame->setArgument(i, jsUndefined()); + + installFence(newCallFrame, __FUNCTION__, __LINE__); + validateFence(newCallFrame, __FUNCTION__, __LINE__); + installTrapsAfterFrame(newCallFrame); + + // Push the new frame: + m_topCallFrame = newCallFrame; + + return newCallFrame; +} + +inline void JSStack::popFrame(CallFrame* frame) +{ + validateFence(frame, __FUNCTION__, __LINE__); + CallFrame* callerFrame = frame->callerFrameNoFlags(); + + // Pop to the caller: + m_topCallFrame = callerFrame; + + // If we are popping the very first frame from the stack i.e. no more + // frames before this, then we can now safely shrink the stack. In + // this case, we're shrinking all the way to the beginning since there + // are no more frames on the stack. + if (!callerFrame) + shrink(begin()); + + installTrapsAfterFrame(callerFrame); +} + + +#if ENABLE(DEBUG_JSSTACK) +inline JSValue JSStack::generateFenceValue(size_t argIndex) +{ + unsigned fenceBits = 0xfacebad0 | ((argIndex+1) & 0xf); + JSValue fenceValue = JSValue(fenceBits); + return fenceValue; +} + +// The JSStack fences mechanism works as follows: +// 1. A fence is a number (JSStack::FenceSize) of JSValues that are initialized +// with values generated by JSStack::generateFenceValue(). +// 2. When pushFrame() is called, the fence is installed after the max extent +// of the previous topCallFrame and the last arg of the new frame: +// +// | ... | +// |--------------------------------------| +// | Frame Header of previous frame | +// |--------------------------------------| +// topCallFrame --> | | +// | Locals of previous frame | +// |--------------------------------------| +// | *** the Fence *** | +// |--------------------------------------| +// | Args of new frame | +// |--------------------------------------| +// | Frame Header of new frame | +// |--------------------------------------| +// frame --> | Locals of new frame | +// | | +// +// 3. In popFrame() and elsewhere, we can call JSStack::validateFence() to +// assert that the fence contains the values we expect. + +inline void JSStack::installFence(CallFrame* frame, const char *function, int lineNo) +{ + UNUSED_PARAM(function); + UNUSED_PARAM(lineNo); + Register* startOfFrame = getStartOfFrame(frame); + + // The last argIndex is at: + size_t maxIndex = frame->argIndexForRegister(startOfFrame) + 1; + size_t startIndex = maxIndex - FenceSize; + for (size_t i = startIndex; i < maxIndex; ++i) { + JSValue fenceValue = generateFenceValue(i); + frame->setArgument(i, fenceValue); + } +} + +inline void JSStack::validateFence(CallFrame* frame, const char *function, int lineNo) +{ + UNUSED_PARAM(function); + UNUSED_PARAM(lineNo); + ASSERT(!!frame->scope()); + Register* startOfFrame = getStartOfFrame(frame); + size_t maxIndex = frame->argIndexForRegister(startOfFrame) + 1; + size_t startIndex = maxIndex - FenceSize; + for (size_t i = startIndex; i < maxIndex; ++i) { + JSValue fenceValue = generateFenceValue(i); + JSValue actualValue = frame->getArgumentUnsafe(i); + ASSERT(fenceValue == actualValue); + } +} + +// When debugging the JSStack, we install bad values after the extent of the +// topCallFrame at the end of pushFrame() and popFrame(). The intention is +// to trigger crashes in the event that memory in this supposedly unused +// region is read and consumed without proper initialization. After the trap +// words are installed, the stack looks like this: +// +// | ... | +// |-----------------------------| +// | Frame Header of frame | +// |-----------------------------| +// topCallFrame --> | | +// | Locals of frame | +// |-----------------------------| +// | *** Trap words *** | +// |-----------------------------| +// | Unused space ... | +// | ... | + +inline void JSStack::installTrapsAfterFrame(CallFrame* frame) +{ + Register* topOfFrame = getTopOfFrame(frame); + const int sizeOfTrap = 64; + int32_t* startOfTrap = reinterpret_cast<int32_t*>(topOfFrame); + int32_t* endOfTrap = startOfTrap + sizeOfTrap; + int32_t* endOfCommitedMemory = reinterpret_cast<int32_t*>(m_commitEnd); + + // Make sure we're not exceeding the amount of available memory to write to: + if (endOfTrap > endOfCommitedMemory) + endOfTrap = endOfCommitedMemory; + + // Lay the traps: + int32_t* p = startOfTrap; + while (p < endOfTrap) + *p++ = 0xabadcafe; // A bad word to trigger a crash if deref'ed. +} +#endif // ENABLE(DEBUG_JSSTACK) + +} // namespace JSC + +#endif // JSStackInlines_h diff --git a/Source/JavaScriptCore/jit/JITStubs.cpp b/Source/JavaScriptCore/jit/JITStubs.cpp index a16b328ad..ba8c76cfb 100644 --- a/Source/JavaScriptCore/jit/JITStubs.cpp +++ b/Source/JavaScriptCore/jit/JITStubs.cpp @@ -2142,6 +2142,23 @@ DEFINE_STUB_FUNCTION(JSObject*, op_new_func) inline void* jitCompileFor(CallFrame* callFrame, CodeSpecializationKind kind) { + // This function is called by cti_op_call_jitCompile() and + // cti_op_construct_jitCompile() JIT glue trampolines to compile the + // callee function that we want to call. Both cti glue trampolines are + // called by JIT'ed code which has pushed a frame and initialized most of + // the frame content except for the codeBlock. + // + // Normally, the prologue of the callee is supposed to set the frame's cb + // pointer to the cb of the callee. But in this case, the callee code does + // not exist yet until it is compiled below. The compilation process will + // allocate memory which may trigger a GC. The GC, in turn, will scan the + // JSStack, and will expect the frame's cb to either be valid or 0. If + // we don't initialize it, the GC will be accessing invalid memory and may + // crash. + // + // Hence, we should nullify it here before proceeding with the compilation. + callFrame->setCodeBlock(0); + JSFunction* function = jsCast<JSFunction*>(callFrame->callee()); ASSERT(!function->isHostFunction()); FunctionExecutable* executable = function->jsExecutable(); @@ -2222,6 +2239,23 @@ inline void* lazyLinkFor(CallFrame* callFrame, CodeSpecializationKind kind) CodeBlock* codeBlock = 0; CallLinkInfo* callLinkInfo = &callFrame->callerFrame()->codeBlock()->getCallLinkInfo(callFrame->returnPC()); + // This function is called by cti_vm_lazyLinkCall() and + // cti_lazyLinkConstruct JIT glue trampolines to link the callee function + // that we want to call. Both cti glue trampolines are called by JIT'ed + // code which has pushed a frame and initialized most of the frame content + // except for the codeBlock. + // + // Normally, the prologue of the callee is supposed to set the frame's cb + // field to the cb of the callee. But in this case, the callee may not + // exist yet, and if not, it will be generated in the compilation below. + // The compilation will allocate memory which may trigger a GC. The GC, in + // turn, will scan the JSStack, and will expect the frame's cb to be valid + // or 0. If we don't initialize it, the GC will be accessing invalid + // memory and may crash. + // + // Hence, we should nullify it here before proceeding with the compilation. + callFrame->setCodeBlock(0); + if (executable->isHostFunction()) codePtr = executable->generatedJITCodeFor(kind).addressForCall(); else { @@ -2298,7 +2332,7 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_call_NotJSFunction) EncodedJSValue returnValue; { - SamplingTool::HostCallRecord callRecord(CTI_SAMPLER); + SamplingTool::CallRecord callRecord(CTI_SAMPLER, true); returnValue = callData.native.function(callFrame); } @@ -2424,7 +2458,7 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_construct_NotJSConstruct) EncodedJSValue returnValue; { - SamplingTool::HostCallRecord callRecord(CTI_SAMPLER); + SamplingTool::CallRecord callRecord(CTI_SAMPLER, true); returnValue = constructData.native.function(callFrame); } diff --git a/Source/JavaScriptCore/jsc.cpp b/Source/JavaScriptCore/jsc.cpp index 06dad2701..b8cf49da6 100644 --- a/Source/JavaScriptCore/jsc.cpp +++ b/Source/JavaScriptCore/jsc.cpp @@ -746,7 +746,7 @@ int jscmain(int argc, char** argv) // Note that the options parsing can affect JSGlobalData creation, and thus // comes first. CommandLine options(argc, argv); - RefPtr<JSGlobalData> globalData = JSGlobalData::create(ThreadStackTypeLarge, LargeHeap); + RefPtr<JSGlobalData> globalData = JSGlobalData::create(LargeHeap); JSLockHolder lock(globalData.get()); int result; diff --git a/Source/JavaScriptCore/parser/Parser.cpp b/Source/JavaScriptCore/parser/Parser.cpp index 0f734d3a1..049a51939 100644 --- a/Source/JavaScriptCore/parser/Parser.cpp +++ b/Source/JavaScriptCore/parser/Parser.cpp @@ -39,6 +39,7 @@ #define failWithToken(tok) do { if (!m_error) updateErrorMessage(tok); return 0; } while (0) #define failWithMessage(msg) do { if (!m_error) updateErrorMessage(msg); return 0; } while (0) #define failWithNameAndMessage(before, name, after) do { if (!m_error) updateErrorWithNameAndMessage(before, name, after); return 0; } while (0) +#define failWithStackOverflow() do { m_error = true; m_hasStackOverflow = true; return 0; } while (0) #define failIfFalse(cond) do { if (!(cond)) fail(); } while (0) #define failIfFalseWithMessage(cond, msg) do { if (!(cond)) failWithMessage(msg); } while (0) #define failIfFalseWithNameAndMessage(cond, before, name, msg) do { if (!(cond)) failWithNameAndMessage(before, name, msg); } while (0) @@ -54,7 +55,7 @@ #define consumeOrFail(tokenType) do { if (!consume(tokenType)) failWithToken(tokenType); } while (0) #define consumeOrFailWithFlags(tokenType, flags) do { if (!consume(tokenType, flags)) failWithToken(tokenType); } while (0) #define matchOrFail(tokenType) do { if (!match(tokenType)) failWithToken(tokenType); } while (0) -#define failIfStackOverflow() do { failIfFalseWithMessage(canRecurse(), "Code nested too deeply."); } while (0) +#define failIfStackOverflow() do { if (!canRecurse()) failWithStackOverflow(); } while (0) using namespace std; @@ -65,6 +66,7 @@ Parser<LexerType>::Parser(JSGlobalData* globalData, const SourceCode& source, Fu : m_globalData(globalData) , m_source(&source) , m_stack(wtfThreadData().stack()) + , m_hasStackOverflow(false) , m_error(false) , m_errorMessage("Parse error") , m_allowsIn(true) diff --git a/Source/JavaScriptCore/parser/Parser.h b/Source/JavaScriptCore/parser/Parser.h index dc42d36ba..3b0316f81 100644 --- a/Source/JavaScriptCore/parser/Parser.h +++ b/Source/JavaScriptCore/parser/Parser.h @@ -896,6 +896,7 @@ private: OwnPtr<LexerType> m_lexer; StackBounds m_stack; + bool m_hasStackOverflow; bool m_error; String m_errorMessage; JSToken m_token; @@ -987,7 +988,7 @@ PassRefPtr<ParsedNode> Parser<LexerType>::parse(JSGlobalObject* lexicalGlobalObj // we ran out of stack while parsing. If we see an error while parsing eval or program // code we assume that it was a syntax error since running out of stack is much less // likely, and we are currently unable to distinguish between the two cases. - if (isFunctionBodyNode(static_cast<ParsedNode*>(0))) + if (isFunctionBodyNode(static_cast<ParsedNode*>(0)) || m_hasStackOverflow) *exception = createStackOverflowError(lexicalGlobalObject); else if (isEvalNode<ParsedNode>()) *exception = createSyntaxError(lexicalGlobalObject, errMsg); diff --git a/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp b/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp index 38c525268..a3281b6d6 100644 --- a/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp +++ b/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp @@ -163,6 +163,7 @@ JSObject* throwOutOfMemoryError(ExecState* exec) JSObject* throwStackOverflowError(ExecState* exec) { + Interpreter::ErrorHandlingMode mode(exec); return throwError(exec, createStackOverflowError(exec)); } diff --git a/Source/JavaScriptCore/runtime/JSGlobalData.cpp b/Source/JavaScriptCore/runtime/JSGlobalData.cpp index bc3d00067..5fb682bdb 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalData.cpp +++ b/Source/JavaScriptCore/runtime/JSGlobalData.cpp @@ -130,7 +130,7 @@ static bool enableAssembler(ExecutableAllocator& executableAllocator) } #endif // ENABLE(!ASSEMBLER) -JSGlobalData::JSGlobalData(GlobalDataType globalDataType, ThreadStackType threadStackType, HeapType heapType) +JSGlobalData::JSGlobalData(GlobalDataType globalDataType, HeapType heapType) : #if ENABLE(ASSEMBLER) executableAllocator(*this), @@ -171,7 +171,6 @@ JSGlobalData::JSGlobalData(GlobalDataType globalDataType, ThreadStackType thread #endif , dynamicGlobalObject(0) , cachedUTCOffset(std::numeric_limits<double>::quiet_NaN()) - , maxReentryDepth(threadStackType == ThreadStackTypeSmall ? MaxSmallThreadReentryDepth : MaxLargeThreadReentryDepth) , m_enabledProfiler(0) , m_regExpCache(new RegExpCache(this)) #if ENABLE(REGEXP_TRACING) @@ -198,7 +197,7 @@ JSGlobalData::JSGlobalData(GlobalDataType globalDataType, ThreadStackType thread #endif , m_inDefineOwnProperty(false) { - interpreter = new Interpreter; + interpreter = new Interpreter(*this); // Need to be careful to keep everything consistent here JSLockHolder lock(this); @@ -310,19 +309,19 @@ JSGlobalData::~JSGlobalData() #endif } -PassRefPtr<JSGlobalData> JSGlobalData::createContextGroup(ThreadStackType type, HeapType heapType) +PassRefPtr<JSGlobalData> JSGlobalData::createContextGroup(HeapType heapType) { - return adoptRef(new JSGlobalData(APIContextGroup, type, heapType)); + return adoptRef(new JSGlobalData(APIContextGroup, heapType)); } -PassRefPtr<JSGlobalData> JSGlobalData::create(ThreadStackType type, HeapType heapType) +PassRefPtr<JSGlobalData> JSGlobalData::create(HeapType heapType) { - return adoptRef(new JSGlobalData(Default, type, heapType)); + return adoptRef(new JSGlobalData(Default, heapType)); } -PassRefPtr<JSGlobalData> JSGlobalData::createLeaked(ThreadStackType type, HeapType heapType) +PassRefPtr<JSGlobalData> JSGlobalData::createLeaked(HeapType heapType) { - return create(type, heapType); + return create(heapType); } bool JSGlobalData::sharedInstanceExists() @@ -335,7 +334,7 @@ JSGlobalData& JSGlobalData::sharedInstance() GlobalJSLock globalLock; JSGlobalData*& instance = sharedInstanceInternal(); if (!instance) { - instance = adoptRef(new JSGlobalData(APIShared, ThreadStackTypeSmall, SmallHeap)).leakRef(); + instance = adoptRef(new JSGlobalData(APIShared, SmallHeap)).leakRef(); instance->makeUsableFromMultipleThreads(); } return *instance; diff --git a/Source/JavaScriptCore/runtime/JSGlobalData.h b/Source/JavaScriptCore/runtime/JSGlobalData.h index 6cc0aad8d..e97c0a015 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalData.h +++ b/Source/JavaScriptCore/runtime/JSGlobalData.h @@ -104,11 +104,6 @@ namespace JSC { double increment; }; - enum ThreadStackType { - ThreadStackTypeLarge, - ThreadStackTypeSmall - }; - #if ENABLE(DFG_JIT) class ConservativeRoots; @@ -164,9 +159,9 @@ namespace JSC { static bool sharedInstanceExists(); JS_EXPORT_PRIVATE static JSGlobalData& sharedInstance(); - JS_EXPORT_PRIVATE static PassRefPtr<JSGlobalData> create(ThreadStackType, HeapType = SmallHeap); - JS_EXPORT_PRIVATE static PassRefPtr<JSGlobalData> createLeaked(ThreadStackType, HeapType = SmallHeap); - static PassRefPtr<JSGlobalData> createContextGroup(ThreadStackType, HeapType = SmallHeap); + JS_EXPORT_PRIVATE static PassRefPtr<JSGlobalData> create(HeapType = SmallHeap); + JS_EXPORT_PRIVATE static PassRefPtr<JSGlobalData> createLeaked(HeapType = SmallHeap); + static PassRefPtr<JSGlobalData> createContextGroup(HeapType = SmallHeap); JS_EXPORT_PRIVATE ~JSGlobalData(); void makeUsableFromMultipleThreads() { heap.machineThreads().makeUsableFromMultipleThreads(); } @@ -346,8 +341,6 @@ namespace JSC { String cachedDateString; double cachedDateStringValue; - int maxReentryDepth; - Profiler* m_enabledProfiler; RegExpCache* m_regExpCache; BumpPointerAllocator m_regExpAllocator; @@ -447,7 +440,7 @@ namespace JSC { private: friend class LLIntOffsetsExtractor; - JSGlobalData(GlobalDataType, ThreadStackType, HeapType); + JSGlobalData(GlobalDataType, HeapType); static JSGlobalData*& sharedInstanceInternal(); void createNativeThunk(); #if ENABLE(ASSEMBLER) diff --git a/Source/JavaScriptCore/runtime/StringRecursionChecker.h b/Source/JavaScriptCore/runtime/StringRecursionChecker.h index 831e25b46..a1b4a51fe 100644 --- a/Source/JavaScriptCore/runtime/StringRecursionChecker.h +++ b/Source/JavaScriptCore/runtime/StringRecursionChecker.h @@ -22,6 +22,7 @@ #include "Interpreter.h" #include <wtf/StackStats.h> +#include <wtf/WTFThreadData.h> namespace JSC { @@ -48,8 +49,8 @@ private: inline JSValue StringRecursionChecker::performCheck() { - int size = m_exec->globalData().stringRecursionCheckVisitedObjects.size(); - if (size >= MaxSmallThreadReentryDepth && size >= m_exec->globalData().maxReentryDepth) + const StackBounds& nativeStack = wtfThreadData().stack(); + if (!nativeStack.isSafeToRecurse()) return throwStackOverflowError(); bool alreadyVisited = !m_exec->globalData().stringRecursionCheckVisitedObjects.add(m_thisObject).isNewEntry; if (alreadyVisited) diff --git a/Source/JavaScriptCore/testRegExp.cpp b/Source/JavaScriptCore/testRegExp.cpp index 60e6c4650..527c9359d 100644 --- a/Source/JavaScriptCore/testRegExp.cpp +++ b/Source/JavaScriptCore/testRegExp.cpp @@ -498,7 +498,7 @@ static void parseArguments(int argc, char** argv, CommandLine& options) int realMain(int argc, char** argv) { - RefPtr<JSGlobalData> globalData = JSGlobalData::create(ThreadStackTypeLarge, LargeHeap); + RefPtr<JSGlobalData> globalData = JSGlobalData::create(LargeHeap); JSLockHolder lock(globalData.get()); CommandLine options; |