mirror of
https://github.com/oven-sh/bun
synced 2026-02-16 22:01:47 +00:00
the tests don't pass
This commit is contained in:
@@ -268,18 +268,11 @@ JSCStackTrace JSCStackTrace::captureCurrentJSStackTrace(Zig::GlobalObject* globa
|
||||
bool belowCaller = false;
|
||||
int32_t skipFrames = 0;
|
||||
|
||||
WTF::String callerName {};
|
||||
if (JSC::JSFunction* callerFunction = JSC::jsDynamicCast<JSC::JSFunction*>(caller)) {
|
||||
callerName = callerFunction->name(vm);
|
||||
if (!callerFunction->name(vm).isEmpty() || callerFunction->isHostOrBuiltinFunction()) {
|
||||
callerName = callerFunction->name(vm);
|
||||
} else {
|
||||
callerName = callerFunction->jsExecutable()->name().string();
|
||||
WTF::String callerName;
|
||||
if (caller)
|
||||
if (auto* object = caller.getObject()) {
|
||||
callerName = Zig::functionName(vm, globalObject, object);
|
||||
}
|
||||
}
|
||||
if (JSC::InternalFunction* callerFunctionInternal = JSC::jsDynamicCast<JSC::InternalFunction*>(caller)) {
|
||||
callerName = callerFunctionInternal->name();
|
||||
}
|
||||
|
||||
if (!callerName.isEmpty()) {
|
||||
JSC::StackVisitor::visit(callFrame, vm, [&](JSC::StackVisitor& visitor) -> WTF::IterationStatus {
|
||||
|
||||
134
src/bun.js/bindings/NodeUtilModule.cpp
Normal file
134
src/bun.js/bindings/NodeUtilModule.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
#include "GeneratedJS2Native.h"
|
||||
#include "root.h"
|
||||
|
||||
#include "ErrorStackTrace.h"
|
||||
#include "ErrorCode.h"
|
||||
#include <JavaScriptCore/JSArray.h>
|
||||
#include <JavaScriptCore/JSCInlines.h>
|
||||
#include <JavaScriptCore/JSObject.h>
|
||||
#include <JavaScriptCore/JSString.h>
|
||||
#include <JavaScriptCore/ObjectConstructor.h>
|
||||
#include <cmath>
|
||||
|
||||
namespace Bun {
|
||||
using namespace JSC;
|
||||
using namespace ERR;
|
||||
JSC_DEFINE_HOST_FUNCTION(jsFunctionUtilGetCallSites, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
JSC::JSValue firstArg = callFrame->argument(0);
|
||||
JSC::JSValue secondArg = callFrame->argument(1);
|
||||
|
||||
size_t frameLimit = 10; // Default frame limit
|
||||
|
||||
if (secondArg.isUndefined() && firstArg.isObject()) {
|
||||
secondArg = firstArg;
|
||||
} else if (!firstArg.isUndefined()) {
|
||||
if (!firstArg.isNumber()) {
|
||||
return ERR::INVALID_ARG_TYPE(scope, globalObject, "frameCount"_s, "number"_s, firstArg);
|
||||
}
|
||||
int64_t frameCount = firstArg.toInt32(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
if (frameCount < 1 || frameCount > 200) {
|
||||
return ERR::OUT_OF_RANGE(scope, globalObject, "frameCount"_s, "number"_s, firstArg);
|
||||
}
|
||||
frameLimit = frameCount;
|
||||
}
|
||||
|
||||
// We don't do anything with the sourceMap option but we do the validation still.
|
||||
if (!secondArg.isUndefined()) {
|
||||
auto* optionsObj = secondArg.getObject();
|
||||
if (!optionsObj || JSC::isJSArray(optionsObj)) {
|
||||
return ERR::INVALID_ARG_TYPE(scope, globalObject, "options"_s, "object"_s, secondArg);
|
||||
}
|
||||
|
||||
// Validate sourceMap option if present
|
||||
JSC::JSValue sourceMapValue = optionsObj->get(globalObject, JSC::Identifier::fromString(vm, "sourceMap"_s));
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
if (!sourceMapValue.isUndefined() && !sourceMapValue.isBoolean()) {
|
||||
return ERR::INVALID_ARG_TYPE(scope, globalObject, "options.sourceMap"_s, "boolean"_s, sourceMapValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Create array to store call sites
|
||||
JSC::JSArray* callSites = JSC::constructEmptyArray(globalObject, nullptr);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
// Get the stack trace
|
||||
Zig::JSCStackTrace stackTrace = Zig::JSCStackTrace::captureCurrentJSStackTrace(
|
||||
jsCast<Zig::GlobalObject*>(globalObject),
|
||||
callFrame,
|
||||
frameLimit + 1, // Add 1 to account for the current frame
|
||||
jsUndefined());
|
||||
|
||||
// Convert stack frames to call site objects
|
||||
Identifier functionNameProperty = Identifier::fromString(vm, "functionName"_s);
|
||||
Identifier scriptNameProperty = Identifier::fromString(vm, "scriptName"_s);
|
||||
Identifier lineNumberProperty = Identifier::fromString(vm, "lineNumber"_s);
|
||||
Identifier columnProperty = vm.propertyNames->column;
|
||||
auto createFirstCallSite = [&]() -> JSObject* {
|
||||
auto* callSite = JSC::constructEmptyObject(vm, globalObject->nullPrototypeObjectStructure());
|
||||
auto& frame = stackTrace.frames()[0];
|
||||
// Set functionName
|
||||
JSC::JSString* functionName = frame.functionName();
|
||||
callSite->putDirect(vm, functionNameProperty, functionName ? functionName : jsEmptyString(vm));
|
||||
|
||||
// Set scriptName (sourceURL)
|
||||
JSC::JSString* scriptName = frame.sourceURL();
|
||||
callSite->putDirect(vm, scriptNameProperty, scriptName ? scriptName : jsEmptyString(vm));
|
||||
|
||||
// Get line and column numbers
|
||||
if (auto* positions = frame.getSourcePositions()) {
|
||||
// Line number (1-based)
|
||||
callSite->putDirect(vm, lineNumberProperty, JSC::jsNumber(positions->line.oneBasedInt()));
|
||||
|
||||
// Column number (1-based)
|
||||
callSite->putDirect(vm, columnProperty, JSC::jsNumber(positions->column.oneBasedInt()));
|
||||
} else {
|
||||
// If no position info available, use 0
|
||||
callSite->putDirect(vm, lineNumberProperty, JSC::jsNumber(0));
|
||||
callSite->putDirect(vm, columnProperty, JSC::jsNumber(0));
|
||||
}
|
||||
|
||||
return callSite;
|
||||
};
|
||||
|
||||
switch (stackTrace.frames().size()) {
|
||||
case 0:
|
||||
break;
|
||||
case 1: {
|
||||
auto callSite = createFirstCallSite();
|
||||
callSites->push(globalObject, callSite);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
JSC::Structure* structure = nullptr;
|
||||
|
||||
auto* firstCallSite = createFirstCallSite();
|
||||
structure = firstCallSite->structure();
|
||||
|
||||
for (unsigned i = 1; i < stackTrace.frames().size(); ++i) {
|
||||
auto& frame = stackTrace.frames()[i];
|
||||
auto* callSite = JSC::constructEmptyObject(vm, structure);
|
||||
JSC::JSString* functionName = frame.functionName();
|
||||
|
||||
JSC::JSString* scriptName = frame.sourceURL();
|
||||
callSite->putDirectOffset(vm, 0, functionName ? functionName : jsEmptyString(vm));
|
||||
callSite->putDirectOffset(vm, 1, scriptName ? scriptName : jsEmptyString(vm));
|
||||
if (auto* positions = frame.getSourcePositions()) {
|
||||
callSite->putDirectOffset(vm, 2, JSC::jsNumber(positions->line.oneBasedInt()));
|
||||
callSite->putDirectOffset(vm, 3, JSC::jsNumber(positions->column.oneBasedInt()));
|
||||
} else {
|
||||
callSite->putDirectOffset(vm, 2, JSC::jsNumber(0));
|
||||
callSite->putDirectOffset(vm, 3, JSC::jsNumber(0));
|
||||
}
|
||||
callSites->push(globalObject, callSite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return JSC::JSValue::encode(callSites);
|
||||
}
|
||||
}
|
||||
7
src/bun.js/bindings/NodeUtilModule.h
Normal file
7
src/bun.js/bindings/NodeUtilModule.h
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
|
||||
namespace Bun {
|
||||
|
||||
JSC_DECLARE_HOST_FUNCTION(jsFunctionUtilGetCallSites);
|
||||
|
||||
}
|
||||
195
test/js/node/test/parallel/test-util-getcallsites.js
Normal file
195
test/js/node/test/parallel/test-util-getcallsites.js
Normal file
@@ -0,0 +1,195 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
|
||||
const fixtures = require('../common/fixtures');
|
||||
const file = fixtures.path('get-call-sites.js');
|
||||
|
||||
const { getCallSites } = require('node:util');
|
||||
const { spawnSync } = require('node:child_process');
|
||||
const assert = require('node:assert');
|
||||
|
||||
function main() {
|
||||
|
||||
{
|
||||
const callSites = getCallSites();
|
||||
assert.ok(callSites.length > 1);
|
||||
assert.match(
|
||||
callSites[0].scriptName,
|
||||
/test-util-getcallsites/,
|
||||
'node:util should be ignored'
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
const callSites = getCallSites(3);
|
||||
assert.strictEqual(callSites.length, 3);
|
||||
assert.match(
|
||||
callSites[0].scriptName,
|
||||
/test-util-getcallsites/,
|
||||
'node:util should be ignored'
|
||||
);
|
||||
}
|
||||
|
||||
// Guarantee dot-left numbers are ignored
|
||||
{
|
||||
const callSites = getCallSites(3.6);
|
||||
assert.strictEqual(callSites.length, 3);
|
||||
}
|
||||
|
||||
{
|
||||
const callSites = getCallSites(3.4);
|
||||
assert.strictEqual(callSites.length, 3);
|
||||
}
|
||||
|
||||
{
|
||||
assert.throws(
|
||||
() => {
|
||||
// Max than kDefaultMaxCallStackSizeToCapture
|
||||
getCallSites(201);
|
||||
},
|
||||
common.expectsError({
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
})
|
||||
);
|
||||
assert.throws(
|
||||
() => {
|
||||
getCallSites(-1);
|
||||
},
|
||||
common.expectsError({
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
})
|
||||
);
|
||||
assert.throws(
|
||||
() => {
|
||||
getCallSites([]);
|
||||
},
|
||||
common.expectsError({
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
})
|
||||
);
|
||||
assert.throws(
|
||||
() => {
|
||||
getCallSites({}, {});
|
||||
},
|
||||
common.expectsError({
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
})
|
||||
);
|
||||
assert.throws(
|
||||
() => {
|
||||
getCallSites(10, 10);
|
||||
},
|
||||
common.expectsError({
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
const callSites = getCallSites(1);
|
||||
assert.strictEqual(callSites.length, 1);
|
||||
assert.match(
|
||||
callSites[0].scriptName,
|
||||
/test-util-getcallsites/,
|
||||
'node:util should be ignored'
|
||||
);
|
||||
}
|
||||
|
||||
// Guarantee [eval] will appear on stacktraces when using -e
|
||||
{
|
||||
const { status, stderr, stdout } = spawnSync(process.execPath, [
|
||||
'-e',
|
||||
`const util = require('util');
|
||||
const assert = require('assert');
|
||||
assert.ok(util.getCallSites().length > 1);
|
||||
process.stdout.write(util.getCallSites()[0].scriptName);
|
||||
`,
|
||||
]);
|
||||
assert.strictEqual(status, 0, stderr.toString());
|
||||
assert.strictEqual(stdout.toString(), '[eval]');
|
||||
}
|
||||
|
||||
// Guarantee the stacktrace[0] is the filename
|
||||
{
|
||||
const { status, stderr, stdout } = spawnSync(process.execPath, [file]);
|
||||
assert.strictEqual(status, 0, stderr.toString());
|
||||
assert.strictEqual(stdout.toString(), file);
|
||||
}
|
||||
|
||||
// Error.stackTraceLimit should not influence callsite size
|
||||
{
|
||||
const originalStackTraceLimit = Error.stackTraceLimit;
|
||||
Error.stackTraceLimit = 0;
|
||||
const callSites = getCallSites();
|
||||
assert.notStrictEqual(callSites.length, 0);
|
||||
Error.stackTraceLimit = originalStackTraceLimit;
|
||||
}
|
||||
|
||||
{
|
||||
const { status, stderr, stdout } = spawnSync(process.execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-transform-types',
|
||||
fixtures.path('typescript/ts/test-get-callsite.ts'),
|
||||
]);
|
||||
|
||||
const output = stdout.toString();
|
||||
assert.strictEqual(stderr.toString(), '');
|
||||
assert.match(output, /lineNumber: 8/);
|
||||
assert.match(output, /column: 18/);
|
||||
assert.match(output, /test-get-callsite\.ts/);
|
||||
assert.strictEqual(status, 0);
|
||||
}
|
||||
|
||||
{
|
||||
const { status, stderr, stdout } = spawnSync(process.execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-transform-types',
|
||||
'--no-enable-source-maps',
|
||||
fixtures.path('typescript/ts/test-get-callsite.ts'),
|
||||
]);
|
||||
|
||||
const output = stdout.toString();
|
||||
assert.strictEqual(stderr.toString(), '');
|
||||
// Line should be wrong when sourcemaps are disable
|
||||
assert.match(output, /lineNumber: 2/);
|
||||
assert.match(output, /column: 18/);
|
||||
assert.match(output, /test-get-callsite\.ts/);
|
||||
assert.strictEqual(status, 0);
|
||||
}
|
||||
|
||||
{
|
||||
// Source maps should be disabled when options.sourceMap is false
|
||||
const { status, stderr, stdout } = spawnSync(process.execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-transform-types',
|
||||
fixtures.path('typescript/ts/test-get-callsite-explicit.ts'),
|
||||
]);
|
||||
|
||||
const output = stdout.toString();
|
||||
assert.strictEqual(stderr.toString(), '');
|
||||
assert.match(output, /lineNumber: 2/);
|
||||
assert.match(output, /column: 18/);
|
||||
assert.match(output, /test-get-callsite-explicit\.ts/);
|
||||
assert.strictEqual(status, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function wrapLevelFour() {
|
||||
return main();
|
||||
}
|
||||
|
||||
function wrapLevelThree() {
|
||||
return wrapLevelFour();
|
||||
}
|
||||
|
||||
function wrapLevelTwo() {
|
||||
return wrapLevelThree();
|
||||
}
|
||||
|
||||
function wrapLevelOne() {
|
||||
return wrapLevelTwo();
|
||||
}
|
||||
|
||||
wrapLevelOne();
|
||||
Reference in New Issue
Block a user