blob: 5570fbbefb3a62f516ffd6f9a7fed0ec62da2ad0 [file] [log] [blame]
/*
* Copyright (C) 2016-2024 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.
*/
#include "config.h"
#include "StackFrame.h"
#include "CodeBlock.h"
#include "DebuggerPrimitives.h"
#include "FunctionExecutable.h"
#include "JSCellInlines.h"
#include "JSFunctionInlines.h"
#include <wtf/text/MakeString.h>
namespace JSC {
StackFrame::StackFrame(VM& vm, JSCell* owner, JSCell* callee)
: m_frameData(JSFrameData {
WriteBarrier<JSCell>(vm, owner, callee),
WriteBarrier<CodeBlock>(),
BytecodeIndex()
})
{
}
StackFrame::StackFrame(VM& vm, JSCell* owner, JSCell* callee, CodeBlock* codeBlock, BytecodeIndex bytecodeIndex)
: m_frameData(JSFrameData {
WriteBarrier<JSCell>(vm, owner, callee),
WriteBarrier<CodeBlock>(vm, owner, codeBlock),
bytecodeIndex
})
{
}
StackFrame::StackFrame(VM& vm, JSCell* owner, JSCell* callee, CodeBlock* codeBlock, BytecodeIndex bytecodeIndex, bool isAsyncFrame)
: m_frameData(JSFrameData {
WriteBarrier<JSCell>(vm, owner, callee),
WriteBarrier<CodeBlock>(vm, owner, codeBlock),
bytecodeIndex,
isAsyncFrame
})
{
}
StackFrame::StackFrame(VM& vm, JSCell* owner, CodeBlock* codeBlock, BytecodeIndex bytecodeIndex)
: m_frameData(JSFrameData {
WriteBarrier<JSCell>(),
WriteBarrier<CodeBlock>(vm, owner, codeBlock),
bytecodeIndex
})
{
}
StackFrame::StackFrame(Wasm::IndexOrName indexOrName)
: m_frameData(WasmFrameData { WTFMove(indexOrName), 0 })
{
}
StackFrame::StackFrame(Wasm::IndexOrName indexOrName, size_t functionIndex)
: m_frameData(WasmFrameData { WTFMove(indexOrName), functionIndex })
{
}
StackFrame::StackFrame(VM& vm, JSCell* owner, JSCell* callee, bool isAsyncFrame)
: m_frameData(JSFrameData {
WriteBarrier<JSCell>(vm, owner, callee),
WriteBarrier<CodeBlock>(),
BytecodeIndex(),
isAsyncFrame
})
{
}
bool StackFrame::hasBytecodeIndex() const
{
if (auto* jsFrame = std::get_if<JSFrameData>(&m_frameData))
return !!jsFrame->bytecodeIndex;
return false;
}
BytecodeIndex StackFrame::bytecodeIndex() const
{
ASSERT(hasBytecodeIndex());
return std::get<JSFrameData>(m_frameData).bytecodeIndex;
}
template<typename Visitor>
void StackFrame::visitAggregate(Visitor& visitor)
{
WTF::switchOn(m_frameData,
[&visitor](const JSFrameData& jsFrame) {
if (jsFrame.callee)
visitor.append(jsFrame.callee);
if (jsFrame.codeBlock)
visitor.append(jsFrame.codeBlock);
},
[](const WasmFrameData&) { }
);
}
template void StackFrame::visitAggregate(AbstractSlotVisitor&);
template void StackFrame::visitAggregate(SlotVisitor&);
bool StackFrame::isMarked(VM& vm) const
{
return WTF::switchOn(m_frameData,
[&vm](const JSFrameData& jsFrame) {
return (!jsFrame.callee || vm.heap.isMarked(jsFrame.callee.get())) && (!jsFrame.codeBlock || vm.heap.isMarked(jsFrame.codeBlock.get()));
},
[](const WasmFrameData&) { return true; }
);
}
SourceID StackFrame::sourceID() const
{
if (auto* jsFrame = std::get_if<JSFrameData>(&m_frameData)) {
if (!jsFrame->codeBlock)
return noSourceID;
return jsFrame->codeBlock->ownerExecutable()->sourceID();
}
return noSourceID;
}
static String processSourceURL(VM& vm, const JSC::StackFrame& frame, const String& sourceURL)
{
if (vm.clientData && (!protocolIsInHTTPFamily(sourceURL) && !protocolIs(sourceURL, "blob"_s))) {
String overrideURL = vm.clientData->overrideSourceURL(frame, sourceURL);
if (!overrideURL.isNull())
return overrideURL;
}
if (!sourceURL.isNull())
return sourceURL;
return emptyString();
}
String StackFrame::sourceURL(VM& vm) const
{
return WTF::switchOn(m_frameData,
[&vm, this](const JSFrameData& jsFrame) -> String {
if (isAsyncFrameWithoutCodeBlock()) {
ASSERT(jsFrame.callee);
ASSERT(!jsFrame.codeBlock);
JSFunction* calleeFn = jsDynamicCast<JSFunction*>(jsFrame.callee.get());
return processSourceURL(vm, *this, calleeFn->jsExecutable()->sourceURL());
}
if (!jsFrame.codeBlock)
return "[native code]"_s;
return processSourceURL(vm, *this, jsFrame.codeBlock->ownerExecutable()->sourceURL());
},
[](const WasmFrameData& wasmFrame) -> String {
auto moduleName = wasmFrame.functionIndexOrName.moduleName();
if (moduleName.empty())
return makeString("wasm-function["_s, wasmFrame.functionIndex, ']');
return makeString(moduleName, ":wasm-function["_s, wasmFrame.functionIndex, ']');
}
);
}
String StackFrame::sourceURLStripped(VM& vm) const
{
return WTF::switchOn(m_frameData,
[&vm, this](const JSFrameData& jsFrame) -> String {
if (isAsyncFrameWithoutCodeBlock()) {
ASSERT(jsFrame.callee);
ASSERT(!jsFrame.codeBlock);
JSFunction* calleeFn = jsDynamicCast<JSFunction*>(jsFrame.callee.get());
return processSourceURL(vm, *this, calleeFn->jsExecutable()->sourceURLStripped());
}
if (!jsFrame.codeBlock)
return "[native code]"_s;
return processSourceURL(vm, *this, jsFrame.codeBlock->ownerExecutable()->sourceURLStripped());
},
[](const WasmFrameData& wasmFrame) -> String {
auto moduleName = wasmFrame.functionIndexOrName.moduleName();
if (moduleName.empty())
return makeString("wasm-function["_s, wasmFrame.functionIndex, ']');
return makeString(moduleName, ":wasm-function["_s, wasmFrame.functionIndex, ']');
}
);
}
String StackFrame::functionName(VM& vm) const
{
return WTF::switchOn(m_frameData,
[&vm](const JSFrameData& jsFrame) -> String {
if (jsFrame.codeBlock) {
switch (jsFrame.codeBlock->codeType()) {
case EvalCode:
return "eval code"_s;
case ModuleCode:
return "module code"_s;
case GlobalCode:
return "global code"_s;
case FunctionCode:
break;
}
}
String name;
if (jsFrame.callee && jsFrame.callee->isObject())
name = getCalculatedDisplayName(vm, jsCast<JSObject*>(jsFrame.callee.get())).impl();
else if (jsFrame.codeBlock) {
if (auto* executable = jsDynamicCast<FunctionExecutable*>(jsFrame.codeBlock->ownerExecutable()))
name = executable->ecmaName().impl();
}
if (name.isNull())
return emptyString();
if (jsFrame.m_isAsyncFrame)
return makeString("async "_s, name);
return name;
},
[](const WasmFrameData& wasmFrame) -> String {
if (wasmFrame.functionIndexOrName.isEmpty() || !wasmFrame.functionIndexOrName.nameSection())
return "wasm-stub"_s;
if (wasmFrame.functionIndexOrName.isIndex())
return WTF::toString(wasmFrame.functionIndexOrName.index());
return WTF::toString(wasmFrame.functionIndexOrName.name()->span());
}
);
}
LineColumn StackFrame::computeLineAndColumn() const
{
if (auto* jsFrame = std::get_if<JSFrameData>(&m_frameData)) {
if (!jsFrame->codeBlock)
return { };
auto lineColumn = jsFrame->codeBlock->lineColumnForBytecodeIndex(jsFrame->bytecodeIndex);
ScriptExecutable* executable = jsFrame->codeBlock->ownerExecutable();
if (std::optional<int> overrideLineNumber = executable->overrideLineNumber(jsFrame->codeBlock->vm()))
lineColumn.line = overrideLineNumber.value();
return lineColumn;
}
return { };
}
String StackFrame::toString(VM& vm) const
{
String functionName = this->functionName(vm);
String sourceURL = this->sourceURLStripped(vm);
if (sourceURL.isEmpty() || !hasLineAndColumnInfo())
return makeString(functionName, '@', sourceURL);
auto lineColumn = computeLineAndColumn();
return makeString(functionName, '@', sourceURL, ':', lineColumn.line, ':', lineColumn.column);
}
} // namespace JSC