]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - interpreter/JSStackInlines.h
JavaScriptCore-1218.tar.gz
[apple/javascriptcore.git] / interpreter / JSStackInlines.h
diff --git a/interpreter/JSStackInlines.h b/interpreter/JSStackInlines.h
new file mode 100644 (file)
index 0000000..d316b59
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * 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"
+
+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