blob: 25b7dcf5ae879825de93b0a8cafdb9e8212f1a26 [file] [log] [blame]
/*
* 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