Make topCallFrame reliable.
authormark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 23 Oct 2012 07:12:29 +0000 (07:12 +0000)
committermark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 23 Oct 2012 07:12:29 +0000 (07:12 +0000)
https://bugs.webkit.org/show_bug.cgi?id=98928.

Reviewed by Geoffrey Garen.

Source/JavaScriptCore:

- 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):

LayoutTests:

- Re-baseline some tests to match the new stack dump results.

* http/tests/inspector/console-resource-errors-expected.txt:
* http/tests/inspector/stacktraces/csp-injected-content-warning-contains-stacktrace-expected.txt:
* http/tests/inspector/stacktraces/csp-inline-warning-contains-stacktrace-expected.txt:

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

17 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/inspector/console-resource-errors-expected.txt
LayoutTests/http/tests/inspector/stacktraces/csp-injected-content-warning-contains-stacktrace-expected.txt
LayoutTests/http/tests/inspector/stacktraces/csp-inline-warning-contains-stacktrace-expected.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecode/SamplingTool.h
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/interpreter/CallFrame.h
Source/JavaScriptCore/interpreter/CallFrameClosure.h
Source/JavaScriptCore/interpreter/Interpreter.cpp
Source/JavaScriptCore/interpreter/Interpreter.h
Source/JavaScriptCore/interpreter/JSStack.cpp
Source/JavaScriptCore/interpreter/JSStack.h
Source/JavaScriptCore/interpreter/JSStackInlines.h [new file with mode: 0644]
Source/JavaScriptCore/jit/JITStubs.cpp
Source/JavaScriptCore/runtime/JSGlobalData.cpp

index 03010c5..93bc9aa 100644 (file)
@@ -1,3 +1,16 @@
+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.
+
+        - Re-baseline some tests to match the new stack dump results.
+
+        * http/tests/inspector/console-resource-errors-expected.txt:
+        * http/tests/inspector/stacktraces/csp-injected-content-warning-contains-stacktrace-expected.txt:
+        * http/tests/inspector/stacktraces/csp-inline-warning-contains-stacktrace-expected.txt:
+
 2012-10-22  Zan Dobersek  <zandobersek@gmail.com>
 
         Unreviewed GTK gardening.
index d271731..f14da96 100644 (file)
@@ -4,5 +4,5 @@ Tests that errors to load a resource cause error messages to be logged to consol
 GET http://127.0.0.1:8000/inspector/missing.css 404 (Not Found) console-resource-errors-iframe.html:3 console-message console-error-level
 GET http://127.0.0.1:8000/inspector/non-existent-iframe.html 404 (Not Found) non-existent-iframe.html:1 console-message console-error-level
 GET http://127.0.0.1:8000/inspector/non-existent-script.js 404 (Not Found) console-resource-errors-iframe.html:4 console-message console-error-level
-GET http://127.0.0.1:8000/inspector/non-existent-xhr 404 (Not Found) [native code]:1send [native code]:1loadXHR console-resource-errors.html:18performActions console-resource-errors.html:10eval codeeval [native code]:1evaluate [native code]:1_evaluateOn_evaluateAndWrapevaluate(anonymous function) [native code]:1 console-message console-error-level
+GET http://127.0.0.1:8000/inspector/non-existent-xhr 404 (Not Found) [native code]:1send [native code]:1loadXHR console-resource-errors.html:18performActions console-resource-errors.html:10eval codeeval [native code]:1evaluate [native code]:1_evaluateOn_evaluateAndWrapevaluate console-message console-error-level
 
index 5f0bca8..471d17d 100644 (file)
@@ -14,5 +14,4 @@ Stack Trace:
     4) (internal script) / _evaluateOn / (line number) / (column number)
     5) (internal script) / _evaluateAndWrap / (line number) / (column number)
     6) (internal script) / evaluate / (line number) / (column number)
-    7) [native code] /  / 0 / 0
 
index 8b64913..ca46c98 100644 (file)
@@ -14,5 +14,4 @@ Stack Trace:
     5) (internal script) / _evaluateOn / (line number) / (column number)
     6) (internal script) / _evaluateAndWrap / (line number) / (column number)
     7) (internal script) / evaluate / (line number) / (column number)
-    8) [native code] /  / 0 / 0
 
index b74d768..c096c4b 100644 (file)
@@ -1,3 +1,78 @@
+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
index 8f90c3e..6c9df62 100644 (file)
@@ -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)
index 11c2c1c..13e0438 100644 (file)
@@ -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;
 }
 
index 3796cc7..aadcdb0 100644 (file)
@@ -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);
     }
index 7aa49a9..2b0ea3a 100644 (file)
@@ -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;
     };
 
index 157d1b3..010c965 100644 (file)
@@ -34,7 +34,6 @@ struct CallFrameClosure {
     JSFunction* function;
     FunctionExecutable* functionExecutable;
     JSGlobalData* globalData;
-    Register* oldEnd;
     JSScope* scope;
     int parameterCountIncludingThis;
     int argumentCountIncludingThis;
index 41cce5c..0d475b4 100644 (file)
@@ -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"
@@ -207,33 +208,6 @@ static int depth(CodeBlock* codeBlock, JSScope* sc)
     return sc->localDepth();
 }
 
-ALWAYS_INLINE CallFrame* Interpreter::slideRegisterWindowForCall(CodeBlock* newCodeBlock, JSStack* stack, CallFrame* callFrame, size_t registerOffset, int argumentCountIncludingThis)
-{
-    // 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;
-
-    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);
-
-    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();
-
-    return newCallFrame;
-}
-
 JSValue eval(CallFrame* callFrame)
 {
     if (!callFrame->argumentCount())
@@ -282,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)
@@ -363,8 +337,9 @@ CallFrame* loadVarargs(CallFrame* callFrame, JSStack* stack, JSValue thisValue,
     return newCallFrame;
 }
 
-Interpreter::Interpreter()
+Interpreter::Interpreter(JSGlobalData& globalData)
     : m_sampleEntryDepth(0)
+    , m_stack(globalData)
     , m_errorHandlingModeReentry(0)
 #if !ASSERT_DISABLED
     , m_initialized(false)
@@ -799,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;
@@ -865,10 +827,12 @@ 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;
@@ -880,7 +844,7 @@ JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, J
     // 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();
@@ -989,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:
@@ -1013,24 +973,25 @@ failedJSONP:
 #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)
     }
 
-    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;
@@ -1039,93 +1000,72 @@ JSValue Interpreter::executeCall(CallFrame* callFrame, JSObject* function, CallT
     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));
-
-    newCallFrame->setThisValue(thisValue);
-    for (size_t i = 0; i < args.size(); ++i)
-        newCallFrame->setArgument(i, args.at(i));
+    bool isJSCall = (callType == CallTypeJS);
+    JSScope* scope;
+    CodeBlock* newCodeBlock;
+    size_t argsCount = 1 + args.size(); // implicit "this" parameter
 
-    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);
 
+        // 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)
-        }
-
-        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;
@@ -1134,85 +1074,64 @@ JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* construc
     if (!nativeStack.isSafeToRecurse(policy.requiredCapacity()))
         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))
-        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);
 
+        // 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)
+        } 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());
@@ -1221,9 +1140,10 @@ 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;
@@ -1234,50 +1154,60 @@ CallFrameClosure Interpreter::prepareForRepeatCall(FunctionExecutable* functionE
         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())) {
-        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();
+
+    size_t argsCount = argumentCountIncludingThis;
+
+    CallFrame* newCallFrame = m_stack.pushFrame(callFrame, newCodeBlock, scope, argsCount, function);  
+    if (UNLIKELY(!newCallFrame)) {
+        throwStackOverflowError(callFrame);
+        return CallFrameClosure();
+    }
 
-    newCallFrame = slideRegisterWindowForCall(codeBlock, &m_stack, newCallFrame, 0, argumentCountIncludingThis);
     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 };
+
+    // 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());
@@ -1285,32 +1215,35 @@ JSValue Interpreter::execute(CallFrameClosure& closure)
 #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)
     }
 
-    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;
     const StackBounds& nativeStack = wtfThreadData().stack();
@@ -1318,6 +1251,7 @@ JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue
     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));
@@ -1340,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);
@@ -1357,22 +1291,19 @@ 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());
@@ -1380,14 +1311,14 @@ JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue
 #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)
     }
 
-    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);
 }
 
index 0c76d48..c3bca1a 100644 (file)
@@ -48,6 +48,7 @@ namespace JSC {
     class EvalExecutable;
     class ExecutableBase;
     class FunctionExecutable;
+    class JSGlobalData;
     class JSGlobalObject;
     class LLIntOffsetsExtractor;
     class ProgramExecutable;
@@ -185,7 +186,7 @@ namespace JSC {
             Interpreter& m_interpreter;
         };
 
-        Interpreter();
+        Interpreter(JSGlobalData &);
         ~Interpreter();
         
         void initialize(bool canUseJIT);
@@ -219,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;
@@ -260,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*);
@@ -291,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);
 
index da7650f..f5f9e37 100644 (file)
@@ -28,6 +28,7 @@
 
 #include "config.h"
 #include "JSStack.h"
+#include "JSStackInlines.h"
 
 #include "ConservativeRoots.h"
 #include "Interpreter.h"
@@ -42,8 +43,9 @@ static Mutex& stackStatisticsMutex()
     return staticMutex;
 }    
 
-JSStack::JSStack(size_t capacity)
+JSStack::JSStack(JSGlobalData& globalData, size_t capacity)
     : m_end(0)
+    , m_topCallFrame(globalData.topCallFrame)
 {
     ASSERT(capacity && isPageAligned(capacity));
 
@@ -52,6 +54,8 @@ JSStack::JSStack(size_t capacity)
     m_commitEnd = static_cast<Register*>(m_reservation.base());
 
     disableErrorStackReserve();
+
+    m_topCallFrame = 0;
 }
 
 JSStack::~JSStack()
@@ -89,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()
@@ -138,8 +142,10 @@ void JSStack::disableErrorStackReserve()
     // 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)
+    if (m_end > m_useableEnd) {
+        ASSERT(m_topCallFrame->frameExtent() <= m_useableEnd);
         shrink(m_useableEnd);
+    }
 }
 
 } // namespace JSC
index 43c2a9a..3beb59e 100644 (file)
 #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,12 +87,28 @@ 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();
 
-    private:
-        friend class LLIntOffsetsExtractor;
+#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:
         Register* reservationEnd() const
         {
             char* base = static_cast<char*>(m_reservation.base());
@@ -95,13 +116,25 @@ namespace JSC {
             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;
+
+        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 (file)
index 0000000..25b7dcf
--- /dev/null
@@ -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
index a16b328..ba8c76c 100644 (file)
@@ -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);
     }
 
index 631a497..5fb682b 100644 (file)
@@ -197,7 +197,7 @@ JSGlobalData::JSGlobalData(GlobalDataType globalDataType, HeapType heapType)
 #endif
     , m_inDefineOwnProperty(false)
 {
-    interpreter = new Interpreter;
+    interpreter = new Interpreter(*this);
 
     // Need to be careful to keep everything consistent here
     JSLockHolder lock(this);