2008-10-05 Maciej Stachowiak <mjs@apple.com>
authormjs@apple.com <mjs@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 6 Oct 2008 02:48:08 +0000 (02:48 +0000)
committermjs@apple.com <mjs@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 6 Oct 2008 02:48:08 +0000 (02:48 +0000)
        Reviewed by Oliver Hunt.

        - fixed "REGRESSION (r37297): fast/js/deep-recursion-test takes too long and times out"
        https://bugs.webkit.org/show_bug.cgi?id=21375

        The problem is that dynamicGlobalObject had become O(N) in number
        of call frames, but unwinding the stack for an exception called it
        for every call frame, resulting in O(N^2) behavior for an
        exception thrown from inside deep recursion.

        Instead of doing it that way, stash the dynamic global object in JSGlobalData.

        * JavaScriptCore.exp:
        * VM/Machine.cpp:
        (JSC::DynamicGlobalObjectScope::DynamicGlobalObjectScope): Helper class to temporarily
        store and later restore a dynamicGlobalObject in JSGlobalData.
        (JSC::DynamicGlobalObjectScope::~DynamicGlobalObjectScope):
        (JSC::Machine::execute): In each version, establish a DynamicGlobalObjectScope.
        For ProgramNode, always establish set new dynamicGlobalObject, for FunctionBody and Eval,
        only if none is currently set.
        * VM/Machine.h:
        * kjs/ExecState.h:
        * kjs/JSGlobalData.cpp:
        (JSC::JSGlobalData::JSGlobalData): Ininitalize new dynamicGlobalObject field to 0.
        * kjs/JSGlobalData.h:
        * kjs/JSGlobalObject.h:
        (JSC::ExecState::dynamicGlobalObject): Moved here from ExecState for benefit of inlining.
        Return lexical global object if this is a globalExec(), otherwise look in JSGlobalData
        for the one stashed there.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@37323 268f45cc-cd09-0410-ab3c-d52691b4dbfc

JavaScriptCore/ChangeLog
JavaScriptCore/JavaScriptCore.exp
JavaScriptCore/VM/Machine.cpp
JavaScriptCore/VM/Machine.h
JavaScriptCore/kjs/ExecState.h
JavaScriptCore/kjs/JSGlobalData.cpp
JavaScriptCore/kjs/JSGlobalData.h
JavaScriptCore/kjs/JSGlobalObject.h

index b5981d3a5fb16c8a55fa18b859a87b0655afc942..6102e2ce5354a304bcced84bd970ac409ea2a5ab 100644 (file)
@@ -1,3 +1,35 @@
+2008-10-05  Maciej Stachowiak  <mjs@apple.com>
+
+        Reviewed by Oliver Hunt.
+        
+        - fixed "REGRESSION (r37297): fast/js/deep-recursion-test takes too long and times out"
+        https://bugs.webkit.org/show_bug.cgi?id=21375
+        
+        The problem is that dynamicGlobalObject had become O(N) in number
+        of call frames, but unwinding the stack for an exception called it
+        for every call frame, resulting in O(N^2) behavior for an
+        exception thrown from inside deep recursion.
+
+        Instead of doing it that way, stash the dynamic global object in JSGlobalData.
+        
+        * JavaScriptCore.exp:
+        * VM/Machine.cpp:
+        (JSC::DynamicGlobalObjectScope::DynamicGlobalObjectScope): Helper class to temporarily
+        store and later restore a dynamicGlobalObject in JSGlobalData.
+        (JSC::DynamicGlobalObjectScope::~DynamicGlobalObjectScope):
+        (JSC::Machine::execute): In each version, establish a DynamicGlobalObjectScope.
+        For ProgramNode, always establish set new dynamicGlobalObject, for FunctionBody and Eval,
+        only if none is currently set.
+        * VM/Machine.h:
+        * kjs/ExecState.h:
+        * kjs/JSGlobalData.cpp:
+        (JSC::JSGlobalData::JSGlobalData): Ininitalize new dynamicGlobalObject field to 0.
+        * kjs/JSGlobalData.h:
+        * kjs/JSGlobalObject.h:
+        (JSC::ExecState::dynamicGlobalObject): Moved here from ExecState for benefit of inlining.
+        Return lexical global object if this is a globalExec(), otherwise look in JSGlobalData
+        for the one stashed there.
+
 2008-10-05  Sam Weinig  <sam@webkit.org>
 
         Reviewed by Maciej Stachowiak.
index ea7a9f343f72377fa8897ce7760d340ff496eebf..30e3b918df2ddc9c74eef5a6dcc95e9ea75dd2a8 100644 (file)
@@ -206,7 +206,6 @@ __ZN3JSC7ArgList10slowAppendEPNS_7JSValueE
 __ZN3JSC7CStringD1Ev
 __ZN3JSC7CStringaSERKS0_
 __ZN3JSC7JSArray4infoE
-__ZN3JSC7Machine14firstCallFrameEPKNS_8RegisterE
 __ZN3JSC7Profile10restoreAllEv
 __ZN3JSC7Profile5focusEPKNS_11ProfileNodeE
 __ZN3JSC7Profile7excludeEPKNS_11ProfileNodeE
index 6ee4f89b816869b2097df6db27cd0984657e856f..ce14cb887f33940aa771485617f44c00e3daf8b1 100644 (file)
@@ -868,6 +868,25 @@ NEVER_INLINE Instruction* Machine::throwException(ExecState* exec, JSValue*& exc
     return handlerVPC;
 }
 
+class DynamicGlobalObjectScope {
+public:
+    DynamicGlobalObjectScope(ExecState* exec, JSGlobalObject* dynamicGlobalObject) 
+        : m_exec(exec)
+        , m_savedGlobalObject(exec->globalData().dynamicGlobalObject)
+    {
+        exec->globalData().dynamicGlobalObject = dynamicGlobalObject;
+    }
+
+    ~DynamicGlobalObjectScope()
+    {
+        m_exec->globalData().dynamicGlobalObject = m_savedGlobalObject;
+    }
+
+private:
+    ExecState* m_exec;
+    JSGlobalObject* m_savedGlobalObject;
+};
+
 JSValue* Machine::execute(ProgramNode* programNode, ExecState* exec, ScopeChainNode* scopeChain, JSObject* thisObj, JSValue** exception)
 {
     ASSERT(!exec->hadException());
@@ -886,6 +905,8 @@ JSValue* Machine::execute(ProgramNode* programNode, ExecState* exec, ScopeChainN
         return jsNull();
     }
 
+    DynamicGlobalObjectScope globalObjectScope(exec, scopeChain->globalObject());
+
     JSGlobalObject* lastGlobalObject = m_registerFile.globalObject();
     JSGlobalObject* globalObject = exec->dynamicGlobalObject();
     globalObject->copyGlobalsTo(m_registerFile);
@@ -920,6 +941,7 @@ JSValue* Machine::execute(ProgramNode* programNode, ExecState* exec, ScopeChainN
         lastGlobalObject->copyGlobalsTo(m_registerFile);
 
     m_registerFile.shrink(oldEnd);
+
     return result;
 }
 
@@ -940,6 +962,8 @@ JSValue* Machine::execute(FunctionBodyNode* functionBodyNode, ExecState* exec, J
         return jsNull();
     }
 
+    DynamicGlobalObjectScope globalObjectScope(exec, exec->globalData().dynamicGlobalObject ? exec->globalData().dynamicGlobalObject : scopeChain->globalObject());
+
     Register* argv = oldEnd;
     size_t dst = 0;
     argv[dst] = thisObj;
@@ -992,6 +1016,8 @@ JSValue* Machine::execute(EvalNode* evalNode, ExecState* exec, JSObject* thisObj
         return jsNull();
     }
 
+    DynamicGlobalObjectScope globalObjectScope(exec, exec->globalData().dynamicGlobalObject ? exec->globalData().dynamicGlobalObject : scopeChain->globalObject());
+
     EvalCodeBlock* codeBlock = &evalNode->byteCode(scopeChain);
 
     JSVariableObject* variableObject;
@@ -3948,14 +3974,6 @@ void Machine::retrieveLastCaller(ExecState* exec, int& lineNumber, intptr_t& sou
     function = caller;
 }
 
-const Register* Machine::firstCallFrame(const Register* callFrame)
-{
-    const Register* first = 0;
-    for (const Register* frame = callFrame; frame; frame = stripHostCallFrameBit(frame[RegisterFile::CallerRegisters].r()))
-        first = frame;
-    return first;
-}
-
 Register* Machine::callFrame(ExecState* exec, InternalFunction* function) const
 {
     for (Register* r = exec->registers(); r; r = stripHostCallFrameBit(r[RegisterFile::CallerRegisters].r()))
index de9af1b773aa042b8b609c78eb6117221a3cd30f..6fd3b845219e3609e0c2a1a3d989af69fb35e7db 100644 (file)
@@ -103,7 +103,6 @@ namespace JSC {
         JSValue* retrieveCaller(ExecState*, InternalFunction*) const;
         void retrieveLastCaller(ExecState* exec, int& lineNumber, intptr_t& sourceID, UString& sourceURL, JSValue*& function) const;
         
-        static const Register* firstCallFrame(const Register* callFrame);
         static ScopeChainNode* scopeChain(const Register* r) { return r[RegisterFile::ScopeChain].scopeChain(); }
         static CodeBlock* codeBlock(const Register* r) { return r[RegisterFile::CodeBlock].codeBlock(); }
 
index e46bf784b47522515886fc27579f81860a2d739c..35f8ea6cd5504339964e8ece04169cca1508f1bc 100644 (file)
@@ -43,10 +43,7 @@ namespace JSC  {
         Register* registers() { return this; }
 
         // Global object in which execution began.
-        JSGlobalObject* dynamicGlobalObject()
-        {
-            return Machine::scopeChain(Machine::firstCallFrame(this))->globalObject();
-        }
+        JSGlobalObject* dynamicGlobalObject();
 
         // Global object in which the currently executing code was defined.
         // Differs from dynamicGlobalObject() during function calls across web browser frames.
index e3fb49854544eda5e10da86be86a5a57ad25c532..9e5654ab740478e956abd0b06cfa3604edd407f0 100644 (file)
@@ -91,6 +91,7 @@ JSGlobalData::JSGlobalData(bool isShared)
     , lexer(new Lexer(this))
     , parser(new Parser)
     , head(0)
+    , dynamicGlobalObject(0)
     , isSharedInstance(isShared)
     , clientData(0)
     , heap(this)
index 27877157373d9118bf8877024cb27dca67f1fbc5..3033fed5d55d2b267f72024159b2ec531e169771 100644 (file)
@@ -98,6 +98,7 @@ namespace JSC {
         Parser* parser;
 
         JSGlobalObject* head;
+        JSGlobalObject* dynamicGlobalObject;
 
         bool isSharedInstance;
 
index 002887bd22599290f2b3a2d689f05020fc0e3b5d..f9f525743773e1a6879a290d6e40c932bea0baa0 100644 (file)
@@ -330,6 +330,17 @@ namespace JSC {
         return exec->lexicalGlobalObject()->numberPrototype();
     }
 
+    inline JSGlobalObject* ExecState::dynamicGlobalObject()
+    {
+        if (this == lexicalGlobalObject()->globalExec())
+            return lexicalGlobalObject();
+
+        // For any ExecState that's not a globalExec, the 
+        // dynamic global object must be set since code is running
+        ASSERT(globalData().dynamicGlobalObject);
+        return globalData().dynamicGlobalObject;
+    }
+    
 } // namespace JSC
 
 #endif // JSGlobalObject_h