Fix various bugs with function names and source mappings involving eval and node:vm (#16212)

This commit is contained in:
Jarred Sumner
2025-01-07 20:21:50 -08:00
committed by GitHub
parent 71d3b41351
commit da2dd657b1
22 changed files with 620 additions and 540 deletions

View File

@@ -5,7 +5,11 @@
#include "config.h"
#include "ErrorStackTrace.h"
#include "JavaScriptCore/CallData.h"
#include "JavaScriptCore/CodeType.h"
#include "JavaScriptCore/Error.h"
#include "JavaScriptCore/ExecutableBase.h"
#include "JavaScriptCore/JSType.h"
#include "wtf/text/OrdinalNumber.h"
#include <JavaScriptCore/CatchScope.h>
@@ -404,6 +408,13 @@ JSCStackFrame::JSCStackFrame(JSC::VM& vm, JSC::StackVisitor& visitor)
m_callee = visitor->callee().asCell();
m_callFrame = visitor->callFrame();
if (auto* codeBlock = visitor->codeBlock()) {
auto codeType = codeBlock->codeType();
if (codeType == JSC::FunctionCode || codeType == JSC::EvalCode) {
m_isFunctionOrEval = true;
}
}
// Based on JSC's GetStackTraceFunctor (Interpreter.cpp)
if (visitor->isNativeCalleeFrame()) {
auto* nativeCallee = visitor->callee().asNativeCallee();
@@ -454,12 +465,21 @@ JSCStackFrame::JSCStackFrame(JSC::VM& vm, const JSC::StackFrame& frame)
m_codeBlock = codeBlock;
m_bytecodeIndex = frame.bytecodeIndex();
}
auto codeType = codeBlock->codeType();
if (codeType == JSC::FunctionCode || codeType == JSC::EvalCode) {
m_isFunctionOrEval = true;
}
}
if (!m_codeBlock && frame.hasLineAndColumnInfo()) {
auto lineColumn = frame.computeLineAndColumn();
m_sourcePositions = { OrdinalNumber::fromOneBasedInt(lineColumn.line), OrdinalNumber::fromOneBasedInt(lineColumn.column) };
m_sourcePositionsState = SourcePositionsState::Calculated;
auto codeType = frame.codeBlock()->codeType();
if (codeType == JSC::FunctionCode || codeType == JSC::EvalCode) {
m_isFunctionOrEval = true;
}
}
}
@@ -531,45 +551,22 @@ ALWAYS_INLINE String JSCStackFrame::retrieveSourceURL()
ALWAYS_INLINE String JSCStackFrame::retrieveFunctionName()
{
static const auto functionNameModuleCodeString = MAKE_STATIC_STRING_IMPL("module code");
static const auto functionNameGlobalCodeString = MAKE_STATIC_STRING_IMPL("global code");
if (m_isWasmFrame) {
return JSC::Wasm::makeString(m_wasmFunctionIndexOrName);
}
if (m_codeBlock) {
switch (m_codeBlock->codeType()) {
case JSC::EvalCode:
// Node returns null here.
return String();
case JSC::ModuleCode:
return String(functionNameModuleCodeString);
case JSC::FunctionCode:
break;
case JSC::GlobalCode:
return String(functionNameGlobalCodeString);
default:
ASSERT_NOT_REACHED();
if (m_callee) {
auto* calleeObject = m_callee->getObject();
if (calleeObject) {
return Zig::functionName(m_vm, calleeObject->globalObject(), calleeObject);
}
}
if (m_callee) {
if (auto* callee = m_callee->getObject()) {
// Does the code block have a user-defined name property?
JSC::JSValue name = callee->getDirect(m_vm, m_vm.propertyNames->name);
if (name && name.isString()) {
auto scope = DECLARE_CATCH_SCOPE(m_vm);
auto nameString = name.toWTFString(callee->globalObject());
if (scope.exception()) {
scope.clearException();
}
if (!nameString.isEmpty()) {
return nameString;
}
}
return JSC::getCalculatedDisplayName(m_vm, callee);
if (m_codeBlock) {
auto functionName = Zig::functionName(m_vm, m_codeBlock);
if (!functionName.isEmpty()) {
return functionName;
}
}
@@ -579,7 +576,6 @@ ALWAYS_INLINE String JSCStackFrame::retrieveFunctionName()
ALWAYS_INLINE String JSCStackFrame::retrieveTypeName()
{
JSC::JSObject* calleeObject = JSC::jsCast<JSC::JSObject*>(m_callee);
// return JSC::jsTypeStringForValue(m_globalObjectcalleeObject->toThis()
return calleeObject->className();
}
@@ -648,7 +644,7 @@ String sourceURL(JSC::CodeBlock* codeBlock)
return sourceURL(source);
}
String sourceURL(JSC::VM& vm, JSC::StackFrame& frame)
String sourceURL(JSC::VM& vm, const JSC::StackFrame& frame)
{
if (frame.isWasmFrame()) {
return "[wasm code]"_s;
@@ -694,4 +690,203 @@ String sourceURL(JSC::VM& vm, JSC::JSFunction* function)
return Zig::sourceURL(jsExecutable->source());
}
String functionName(JSC::VM& vm, JSC::CodeBlock* codeBlock)
{
auto codeType = codeBlock->codeType();
auto* executable = codeBlock->ownerExecutable();
if (!executable) {
return String();
}
if (codeType == JSC::FunctionCode) {
auto* jsExecutable = jsCast<JSC::FunctionExecutable*>(executable);
if (!jsExecutable) {
return String();
}
return jsExecutable->ecmaName().string();
}
return String();
}
String functionName(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, JSC::JSObject* object)
{
WTF::String functionName;
auto jstype = object->type();
if (jstype == JSC::ProxyObjectType) return {};
// First try the "name" property.
{
WTF::String name;
auto catchScope = DECLARE_CATCH_SCOPE(vm);
PropertySlot slot(object, PropertySlot::InternalMethodType::VMInquiry, &vm);
if (object->getOwnNonIndexPropertySlot(vm, object->structure(), vm.propertyNames->name, slot)) {
if (!slot.isAccessor()) {
JSValue functionNameValue = slot.getValue(lexicalGlobalObject, vm.propertyNames->name);
if (functionNameValue && functionNameValue.isString()) {
name = functionNameValue.toWTFString(lexicalGlobalObject);
if (!name.isEmpty()) {
return name;
}
}
}
}
if (UNLIKELY(catchScope.exception())) {
catchScope.clearException();
}
}
{
// Then try the "displayName" property (what this does internally)
auto catchScope = DECLARE_CATCH_SCOPE(vm);
functionName = JSC::getCalculatedDisplayName(vm, object);
if (UNLIKELY(catchScope.exception())) {
catchScope.clearException();
}
}
return functionName;
}
String functionName(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, const JSC::StackFrame& frame, bool isInFinalizer, unsigned int* flags)
{
WTF::String functionName;
bool isConstructor = false;
if (isInFinalizer) {
if (auto* callee = frame.callee()) {
if (auto* object = callee->getObject()) {
auto jstype = object->type();
Structure* structure = object->structure();
auto setTypeFlagsIfNecessary = [&]() {
if (flags) {
if (jstype == JSC::JSFunctionType || jstype == JSC::InternalFunctionType) {
*flags |= static_cast<unsigned int>(FunctionNameFlags::Function);
}
}
};
const auto getName = [&]() -> String {
// First try the "name" property.
{
unsigned attributes;
PropertyOffset offset = structure->getConcurrently(vm.propertyNames->name.impl(), attributes);
if (offset != invalidOffset && !(attributes & (PropertyAttribute::Accessor | PropertyAttribute::CustomAccessorOrValue))) {
JSValue name = object->getDirect(offset);
if (name && name.isString()) {
auto str = asString(name)->tryGetValueWithoutGC();
if (!str->isEmpty()) {
setTypeFlagsIfNecessary();
return str.data;
}
}
}
}
// Then try the "displayName" property.
{
unsigned attributes;
PropertyOffset offset = structure->getConcurrently(vm.propertyNames->displayName.impl(), attributes);
if (offset != invalidOffset && !(attributes & (PropertyAttribute::Accessor | PropertyAttribute::CustomAccessorOrValue))) {
JSValue name = object->getDirect(offset);
if (name && name.isString()) {
auto str = asString(name)->tryGetValueWithoutGC();
if (!str->isEmpty()) {
functionName = str.data;
if (!functionName.isEmpty()) {
setTypeFlagsIfNecessary();
return functionName;
}
}
}
}
}
// Lastly, try type-specific properties.
if (jstype == JSC::JSFunctionType) {
auto* function = jsDynamicCast<JSC::JSFunction*>(object);
if (function) {
functionName = function->nameWithoutGC(vm);
setTypeFlagsIfNecessary();
return functionName;
}
} else if (jstype == JSC::InternalFunctionType) {
auto* function = jsDynamicCast<JSC::InternalFunction*>(object);
if (function) {
functionName = function->name();
setTypeFlagsIfNecessary();
return functionName;
}
}
return functionName;
};
functionName = getName();
}
}
return functionName;
}
if (auto codeblock = frame.codeBlock()) {
if (codeblock->isConstructor()) {
isConstructor = true;
}
// We cannot run this in FinalizeUnconditionally, as we cannot call getters there
if (!isInFinalizer) {
auto codeType = codeblock->codeType();
switch (codeType) {
case JSC::CodeType::FunctionCode:
case JSC::CodeType::EvalCode: {
if (flags) {
if (codeType == JSC::CodeType::EvalCode) {
*flags |= static_cast<unsigned int>(FunctionNameFlags::Eval);
} else if (codeType == JSC::CodeType::FunctionCode) {
*flags |= static_cast<unsigned int>(FunctionNameFlags::Function);
}
}
if (auto* callee = frame.callee()) {
if (auto* object = callee->getObject()) {
functionName = Zig::functionName(vm, lexicalGlobalObject, object);
if (flags) {
if (auto* unlinkedCodeBlock = codeblock->unlinkedCodeBlock()) {
if (unlinkedCodeBlock->isBuiltinFunction()) {
*flags |= static_cast<unsigned int>(FunctionNameFlags::Builtin);
}
}
}
}
}
break;
}
default: {
break;
}
}
if (functionName.isEmpty()) {
functionName = Zig::functionName(vm, codeblock);
}
}
} else {
if (auto* callee = frame.callee()) {
if (auto* object = callee->getObject()) {
functionName = Zig::functionName(vm, lexicalGlobalObject, object);
}
}
}
if ((flags && (*flags & static_cast<unsigned int>(FunctionNameFlags::AddNewKeyword))) && isConstructor && !functionName.isEmpty()) {
return makeString("new "_s, functionName);
}
return functionName;
}
}