aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/bun-types/sqlite.d.ts1
-rw-r--r--src/bun.js/bindings/JSBundlerPlugin.cpp3
-rw-r--r--src/bun.js/bindings/Process.cpp290
-rw-r--r--src/bun.js/bindings/Process.h2
-rw-r--r--src/bun.js/bindings/ScriptExecutionContext.cpp6
-rw-r--r--src/bun.js/bindings/ScriptExecutionContext.h8
-rw-r--r--src/bun.js/bindings/bindings.cpp14
-rw-r--r--src/bun.js/bindings/exports.zig43
-rw-r--r--src/bun.js/bindings/webcore/EventEmitter.cpp7
-rw-r--r--src/bun.js/bindings/webcore/EventEmitter.h6
-rw-r--r--src/bun.js/bindings/webcore/JSEventEmitter.cpp15
-rw-r--r--src/bun.js/bindings/webcore/JSEventEmitter.h3
-rw-r--r--src/bun.js/test/pretty_format.zig42
-rw-r--r--test/js/bun/http/error-response.js8
-rw-r--r--test/js/bun/http/serve.test.ts15
-rw-r--r--test/js/node/process/call-raise.js15
-rw-r--r--test/js/node/process/process-signal-handler.fixture.js63
-rw-r--r--test/js/node/process/process.test.js26
-rw-r--r--test/js/web/console/console-log.expected.txt8
-rw-r--r--test/js/web/console/console-log.js6
20 files changed, 444 insertions, 137 deletions
diff --git a/packages/bun-types/sqlite.d.ts b/packages/bun-types/sqlite.d.ts
index e1b8adcd0..c5b1af1c8 100644
--- a/packages/bun-types/sqlite.d.ts
+++ b/packages/bun-types/sqlite.d.ts
@@ -235,6 +235,7 @@ declare module "bun:sqlite" {
ParamsType extends SQLQueryBindings | SQLQueryBindings[],
>(
sqlQuery: string,
+ params?: ParamsType
): Statement<
ReturnType,
ParamsType extends Array<any> ? ParamsType : [ParamsType]
diff --git a/src/bun.js/bindings/JSBundlerPlugin.cpp b/src/bun.js/bindings/JSBundlerPlugin.cpp
index d55c5fc2e..ec3933574 100644
--- a/src/bun.js/bindings/JSBundlerPlugin.cpp
+++ b/src/bun.js/bindings/JSBundlerPlugin.cpp
@@ -157,7 +157,8 @@ public:
JSC::LazyProperty<JSBundlerPlugin, JSC::JSFunction> setupFunction;
private:
- JSBundlerPlugin(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure, void* config, BunPluginTarget target, JSBundlerPluginAddErrorCallback addError, JSBundlerPluginOnLoadAsyncCallback onLoadAsync, JSBundlerPluginOnResolveAsyncCallback onResolveAsync)
+ JSBundlerPlugin(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure, void* config, BunPluginTarget target,
+ JSBundlerPluginAddErrorCallback addError, JSBundlerPluginOnLoadAsyncCallback onLoadAsync, JSBundlerPluginOnResolveAsyncCallback onResolveAsync)
: JSC::JSNonFinalObject(vm, structure)
, plugin(BundlerPlugin(config, target, addError, onLoadAsync, onResolveAsync))
{
diff --git a/src/bun.js/bindings/Process.cpp b/src/bun.js/bindings/Process.cpp
index 7d7bdd982..f9fb85b95 100644
--- a/src/bun.js/bindings/Process.cpp
+++ b/src/bun.js/bindings/Process.cpp
@@ -437,91 +437,215 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionChdir,
return JSC::JSValue::encode(result);
}
-// static const NeverDestroyed<String> signalNames[] = {
-// MAKE_STATIC_STRING_IMPL("SIGHUP"),
-// MAKE_STATIC_STRING_IMPL("SIGINT"),
-// MAKE_STATIC_STRING_IMPL("SIGQUIT"),
-// MAKE_STATIC_STRING_IMPL("SIGILL"),
-// MAKE_STATIC_STRING_IMPL("SIGTRAP"),
-// MAKE_STATIC_STRING_IMPL("SIGABRT"),
-// MAKE_STATIC_STRING_IMPL("SIGIOT"),
-// MAKE_STATIC_STRING_IMPL("SIGBUS"),
-// MAKE_STATIC_STRING_IMPL("SIGFPE"),
-// MAKE_STATIC_STRING_IMPL("SIGKILL"),
-// MAKE_STATIC_STRING_IMPL("SIGUSR1"),
-// MAKE_STATIC_STRING_IMPL("SIGSEGV"),
-// MAKE_STATIC_STRING_IMPL("SIGUSR2"),
-// MAKE_STATIC_STRING_IMPL("SIGPIPE"),
-// MAKE_STATIC_STRING_IMPL("SIGALRM"),
-// MAKE_STATIC_STRING_IMPL("SIGTERM"),
-// MAKE_STATIC_STRING_IMPL("SIGCHLD"),
-// MAKE_STATIC_STRING_IMPL("SIGCONT"),
-// MAKE_STATIC_STRING_IMPL("SIGSTOP"),
-// MAKE_STATIC_STRING_IMPL("SIGTSTP"),
-// MAKE_STATIC_STRING_IMPL("SIGTTIN"),
-// MAKE_STATIC_STRING_IMPL("SIGTTOU"),
-// MAKE_STATIC_STRING_IMPL("SIGURG"),
-// MAKE_STATIC_STRING_IMPL("SIGXCPU"),
-// MAKE_STATIC_STRING_IMPL("SIGXFSZ"),
-// MAKE_STATIC_STRING_IMPL("SIGVTALRM"),
-// MAKE_STATIC_STRING_IMPL("SIGPROF"),
-// MAKE_STATIC_STRING_IMPL("SIGWINCH"),
-// MAKE_STATIC_STRING_IMPL("SIGIO"),
-// MAKE_STATIC_STRING_IMPL("SIGINFO"),
-// MAKE_STATIC_STRING_IMPL("SIGSYS"),
-// };
-// static const int signalNumbers[] = {
-// SIGHUP,
-// SIGINT,
-// SIGQUIT,
-// SIGILL,
-// SIGTRAP,
-// SIGABRT,
-// SIGIOT,
-// SIGBUS,
-// SIGFPE,
-// SIGKILL,
-// SIGUSR1,
-// SIGSEGV,
-// SIGUSR2,
-// SIGPIPE,
-// SIGALRM,
-// SIGTERM,
-// SIGCHLD,
-// SIGCONT,
-// SIGSTOP,
-// SIGTSTP,
-// SIGTTIN,
-// SIGTTOU,
-// SIGURG,
-// SIGXCPU,
-// SIGXFSZ,
-// SIGVTALRM,
-// SIGPROF,
-// SIGWINCH,
-// SIGIO,
-// SIGINFO,
-// SIGSYS,
-// };
-
-// JSC_DEFINE_HOST_FUNCTION(jsFunctionProcessOn, (JSGlobalObject * globalObject, CallFrame* callFrame))
-// {
-// VM& vm = globalObject->vm();
-// auto scope = DECLARE_THROW_SCOPE(vm);
-
-// if (callFrame->argumentCount() < 2) {
-// throwVMError(globalObject, scope, "Not enough arguments"_s);
-// return JSValue::encode(jsUndefined());
-// }
-
-// String eventName = callFrame->uncheckedArgument(0).toWTFString(globalObject);
-// RETURN_IF_EXCEPTION(scope, encodedJSValue());
-// }
+static HashMap<String, int>* signalNameToNumberMap = nullptr;
+static HashMap<int, String>* signalNumberToNameMap = nullptr;
+
+// signal number to array of script execution context ids that care about the signal
+static HashMap<int, HashSet<uint32_t>>* signalToContextIdsMap = nullptr;
+static Lock signalToContextIdsMapLock;
+
+static void onDidChangeListeners(EventEmitter& eventEmitter, const Identifier& eventName, bool isAdded)
+{
+
+ static const NeverDestroyed<String> signalNames[] = {
+ MAKE_STATIC_STRING_IMPL("SIGHUP"),
+ MAKE_STATIC_STRING_IMPL("SIGINT"),
+ MAKE_STATIC_STRING_IMPL("SIGQUIT"),
+ MAKE_STATIC_STRING_IMPL("SIGILL"),
+ MAKE_STATIC_STRING_IMPL("SIGTRAP"),
+ MAKE_STATIC_STRING_IMPL("SIGABRT"),
+ MAKE_STATIC_STRING_IMPL("SIGIOT"),
+ MAKE_STATIC_STRING_IMPL("SIGBUS"),
+ MAKE_STATIC_STRING_IMPL("SIGFPE"),
+ MAKE_STATIC_STRING_IMPL("SIGKILL"),
+ MAKE_STATIC_STRING_IMPL("SIGUSR1"),
+ MAKE_STATIC_STRING_IMPL("SIGSEGV"),
+ MAKE_STATIC_STRING_IMPL("SIGUSR2"),
+ MAKE_STATIC_STRING_IMPL("SIGPIPE"),
+ MAKE_STATIC_STRING_IMPL("SIGALRM"),
+ MAKE_STATIC_STRING_IMPL("SIGTERM"),
+ MAKE_STATIC_STRING_IMPL("SIGCHLD"),
+ MAKE_STATIC_STRING_IMPL("SIGCONT"),
+ MAKE_STATIC_STRING_IMPL("SIGSTOP"),
+ MAKE_STATIC_STRING_IMPL("SIGTSTP"),
+ MAKE_STATIC_STRING_IMPL("SIGTTIN"),
+ MAKE_STATIC_STRING_IMPL("SIGTTOU"),
+ MAKE_STATIC_STRING_IMPL("SIGURG"),
+ MAKE_STATIC_STRING_IMPL("SIGXCPU"),
+ MAKE_STATIC_STRING_IMPL("SIGXFSZ"),
+ MAKE_STATIC_STRING_IMPL("SIGVTALRM"),
+ MAKE_STATIC_STRING_IMPL("SIGPROF"),
+ MAKE_STATIC_STRING_IMPL("SIGWINCH"),
+ MAKE_STATIC_STRING_IMPL("SIGIO"),
+ MAKE_STATIC_STRING_IMPL("SIGINFO"),
+ MAKE_STATIC_STRING_IMPL("SIGSYS"),
+ };
+
+ static std::once_flag signalNameToNumberMapOnceFlag;
+ std::call_once(signalNameToNumberMapOnceFlag, [] {
+ signalNameToNumberMap = new HashMap<String, int>();
+ signalNameToNumberMap->reserveInitialCapacity(31);
+ signalNameToNumberMap->add(signalNames[0], SIGHUP);
+ signalNameToNumberMap->add(signalNames[1], SIGINT);
+ signalNameToNumberMap->add(signalNames[2], SIGQUIT);
+ signalNameToNumberMap->add(signalNames[3], SIGILL);
+ signalNameToNumberMap->add(signalNames[4], SIGTRAP);
+ signalNameToNumberMap->add(signalNames[5], SIGABRT);
+ signalNameToNumberMap->add(signalNames[6], SIGIOT);
+ signalNameToNumberMap->add(signalNames[7], SIGBUS);
+ signalNameToNumberMap->add(signalNames[8], SIGFPE);
+ // signalNameToNumberMap->add(signalNames[9], SIGKILL);
+ signalNameToNumberMap->add(signalNames[10], SIGUSR1);
+ signalNameToNumberMap->add(signalNames[11], SIGSEGV);
+ signalNameToNumberMap->add(signalNames[12], SIGUSR2);
+ signalNameToNumberMap->add(signalNames[13], SIGPIPE);
+ signalNameToNumberMap->add(signalNames[14], SIGALRM);
+ signalNameToNumberMap->add(signalNames[15], SIGTERM);
+ signalNameToNumberMap->add(signalNames[16], SIGCHLD);
+ signalNameToNumberMap->add(signalNames[17], SIGCONT);
+ // signalNameToNumberMap->add(signalNames[18], SIGSTOP);
+ signalNameToNumberMap->add(signalNames[19], SIGTSTP);
+ signalNameToNumberMap->add(signalNames[20], SIGTTIN);
+ signalNameToNumberMap->add(signalNames[21], SIGTTOU);
+ signalNameToNumberMap->add(signalNames[22], SIGURG);
+ signalNameToNumberMap->add(signalNames[23], SIGXCPU);
+ signalNameToNumberMap->add(signalNames[24], SIGXFSZ);
+ signalNameToNumberMap->add(signalNames[25], SIGVTALRM);
+ signalNameToNumberMap->add(signalNames[26], SIGPROF);
+ signalNameToNumberMap->add(signalNames[27], SIGWINCH);
+ signalNameToNumberMap->add(signalNames[28], SIGIO);
+#ifdef SIGINFO
+ signalNameToNumberMap->add(signalNames[29], SIGINFO);
+#endif
+
+#ifndef SIGINFO
+ signalNameToNumberMap->add(signalNames[29], 255);
+#endif
+ signalNameToNumberMap->add(signalNames[30], SIGSYS);
+ });
+
+ static std::once_flag signalNumberToNameMapOnceFlag;
+ std::call_once(signalNumberToNameMapOnceFlag, [] {
+ signalNumberToNameMap = new HashMap<int, String>();
+ signalNumberToNameMap->reserveInitialCapacity(31);
+ signalNumberToNameMap->add(SIGHUP, signalNames[0]);
+ signalNumberToNameMap->add(SIGINT, signalNames[1]);
+ signalNumberToNameMap->add(SIGQUIT, signalNames[2]);
+ signalNumberToNameMap->add(SIGILL, signalNames[3]);
+ signalNumberToNameMap->add(SIGTRAP, signalNames[4]);
+ signalNumberToNameMap->add(SIGABRT, signalNames[5]);
+ signalNumberToNameMap->add(SIGIOT, signalNames[6]);
+ signalNumberToNameMap->add(SIGBUS, signalNames[7]);
+ signalNumberToNameMap->add(SIGFPE, signalNames[8]);
+ // signalNumberToNameMap->add(SIGKILL, signalNames[9]);
+ signalNumberToNameMap->add(SIGUSR1, signalNames[10]);
+ signalNumberToNameMap->add(SIGSEGV, signalNames[11]);
+ signalNumberToNameMap->add(SIGUSR2, signalNames[12]);
+ signalNumberToNameMap->add(SIGPIPE, signalNames[13]);
+ signalNumberToNameMap->add(SIGALRM, signalNames[14]);
+ signalNumberToNameMap->add(SIGTERM, signalNames[15]);
+ signalNumberToNameMap->add(SIGCHLD, signalNames[16]);
+ signalNumberToNameMap->add(SIGCONT, signalNames[17]);
+ // signalNumberToNameMap->add(SIGSTOP, signalNames[18]);
+ signalNumberToNameMap->add(SIGTSTP, signalNames[19]);
+ signalNumberToNameMap->add(SIGTTIN, signalNames[20]);
+ signalNumberToNameMap->add(SIGTTOU, signalNames[21]);
+ signalNumberToNameMap->add(SIGURG, signalNames[22]);
+ signalNumberToNameMap->add(SIGXCPU, signalNames[23]);
+ signalNumberToNameMap->add(SIGXFSZ, signalNames[24]);
+ signalNumberToNameMap->add(SIGVTALRM, signalNames[25]);
+ signalNumberToNameMap->add(SIGPROF, signalNames[26]);
+ signalNumberToNameMap->add(SIGWINCH, signalNames[27]);
+ signalNumberToNameMap->add(SIGIO, signalNames[28]);
+#ifdef SIGINFO
+ signalNameToNumberMap->add(signalNames[29], SIGINFO);
+#endif
+ signalNumberToNameMap->add(SIGSYS, signalNames[30]);
+ });
+
+ if (!signalToContextIdsMap) {
+ signalToContextIdsMap = new HashMap<int, HashSet<uint32_t>>();
+ }
+
+ if (isAdded) {
+ if (auto signalNumber = signalNameToNumberMap->get(eventName.string())) {
+ uint32_t contextId = eventEmitter.scriptExecutionContext()->identifier();
+ Locker lock { signalToContextIdsMapLock };
+ if (!signalToContextIdsMap->contains(signalNumber)) {
+ HashSet<uint32_t> contextIds;
+ contextIds.add(contextId);
+ signalToContextIdsMap->set(signalNumber, contextIds);
+
+ lock.unlockEarly();
+
+ struct sigaction action;
+ memset(&action, 0, sizeof(struct sigaction));
+
+ // Set the handler in the action struct
+ action.sa_handler = [](int signalNumber) {
+ if (UNLIKELY(signalNumberToNameMap->find(signalNumber) == signalNumberToNameMap->end()))
+ return;
+
+ Locker lock { signalToContextIdsMapLock };
+ if (UNLIKELY(signalToContextIdsMap->find(signalNumber) == signalToContextIdsMap->end()))
+ return;
+ auto contextIds = signalToContextIdsMap->get(signalNumber);
+
+ for (int contextId : contextIds) {
+ auto* context = ScriptExecutionContext::getScriptExecutionContext(contextId);
+ if (UNLIKELY(!context))
+ continue;
+
+ JSGlobalObject* lexicalGlobalObject = context->jsGlobalObject();
+ Zig::GlobalObject* globalObject = static_cast<Zig::GlobalObject*>(lexicalGlobalObject);
+
+ Process* process = jsCast<Process*>(globalObject->processObject());
+
+ context->postCrossThreadTask(*process, &Process::emitSignalEvent, signalNumber);
+ }
+ };
+
+ // Clear the sa_mask
+ sigemptyset(&action.sa_mask);
+ sigaddset(&action.sa_mask, signalNumber);
+ action.sa_flags = SA_RESTART;
+
+ sigaction(signalNumber, &action, nullptr);
+ } else {
+ auto contextIds = signalToContextIdsMap->get(signalNumber);
+ contextIds.add(contextId);
+ signalToContextIdsMap->set(signalNumber, contextIds);
+ }
+ }
+ } else {
+ if (auto signalNumber = signalNameToNumberMap->get(eventName.string())) {
+ uint32_t contextId = eventEmitter.scriptExecutionContext()->identifier();
+ Locker lock { signalToContextIdsMapLock };
+ if (signalToContextIdsMap->find(signalNumber) != signalToContextIdsMap->end()) {
+ HashSet<uint32_t> contextIds = signalToContextIdsMap->get(signalNumber);
+ contextIds.remove(contextId);
+ if (contextIds.isEmpty()) {
+ signal(signalNumber, SIG_DFL);
+ signalToContextIdsMap->remove(signalNumber);
+ } else {
+ signalToContextIdsMap->set(signalNumber, contextIds);
+ }
+ }
+ }
+ }
+}
+
+void Process::emitSignalEvent(int signalNumber)
+{
+ String signalName = signalNumberToNameMap->get(signalNumber);
+ Identifier signalNameIdentifier = Identifier::fromString(vm(), signalName);
+ MarkedArgumentBuffer args;
+ args.append(jsNumber(signalNumber));
+ wrapped().emitForBindings(signalNameIdentifier, args);
+}
Process::~Process()
{
- for (auto& listener : this->wrapped().eventListenerMap().entries()) {
- }
}
JSC_DEFINE_HOST_FUNCTION(Process_functionAbort, (JSGlobalObject * globalObject, CallFrame*))
@@ -1550,6 +1674,8 @@ void Process::finishCreation(JSC::VM& vm)
{
Base::finishCreation(vm);
+ this->wrapped().onDidChangeListener = &onDidChangeListeners;
+
this->cpuUsageStructure.initLater([](const JSC::LazyProperty<JSC::JSObject, JSC::Structure>::Initializer& init) {
init.set(constructCPUUsageStructure(init.vm, init.owner->globalObject()));
});
diff --git a/src/bun.js/bindings/Process.h b/src/bun.js/bindings/Process.h
index fbad9b1ff..0ee6f4243 100644
--- a/src/bun.js/bindings/Process.h
+++ b/src/bun.js/bindings/Process.h
@@ -19,6 +19,8 @@ public:
{
}
+ void emitSignalEvent(int signalNumber);
+
DECLARE_EXPORT_INFO;
static void destroy(JSC::JSCell* cell)
diff --git a/src/bun.js/bindings/ScriptExecutionContext.cpp b/src/bun.js/bindings/ScriptExecutionContext.cpp
index e8cae5e33..3262bdb5d 100644
--- a/src/bun.js/bindings/ScriptExecutionContext.cpp
+++ b/src/bun.js/bindings/ScriptExecutionContext.cpp
@@ -20,6 +20,12 @@ static HashMap<ScriptExecutionContextIdentifier, ScriptExecutionContext*>& allSc
return contexts;
}
+ScriptExecutionContext* ScriptExecutionContext::getScriptExecutionContext(ScriptExecutionContextIdentifier identifier)
+{
+ Locker locker { allScriptExecutionContextsMapLock };
+ return allScriptExecutionContextsMap().get(identifier);
+}
+
template<bool SSL, bool isServer>
static void registerHTTPContextForWebSocket(ScriptExecutionContext* script, us_socket_context_t* ctx, us_loop_t* loop)
{
diff --git a/src/bun.js/bindings/ScriptExecutionContext.h b/src/bun.js/bindings/ScriptExecutionContext.h
index 5f6c56a90..aed7977a5 100644
--- a/src/bun.js/bindings/ScriptExecutionContext.h
+++ b/src/bun.js/bindings/ScriptExecutionContext.h
@@ -96,7 +96,12 @@ public:
}
}
- const WTF::URL& url() const { return m_url; }
+ static ScriptExecutionContext* getScriptExecutionContext(ScriptExecutionContextIdentifier identifier);
+
+ const WTF::URL& url() const
+ {
+ return m_url;
+ }
bool activeDOMObjectsAreSuspended() { return false; }
bool activeDOMObjectsAreStopped() { return false; }
bool isContextThread() { return true; }
@@ -141,6 +146,7 @@ public:
auto* task = new EventLoopTask(WTFMove(lambda));
postTaskOnTimeout(task, timeout);
}
+
template<typename... Arguments>
void postCrossThreadTask(Arguments&&... arguments)
{
diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp
index 9f9b20c1e..d311072e4 100644
--- a/src/bun.js/bindings/bindings.cpp
+++ b/src/bun.js/bindings/bindings.cpp
@@ -2775,8 +2775,18 @@ void JSC__JSValue__put(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, const Z
bool JSC__JSValue__isClass(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1)
{
- JSC::JSValue value = JSC::JSValue::decode(JSValue0);
- return value.isConstructor();
+ JSValue value = JSValue::decode(JSValue0);
+ auto callData = getCallData(value);
+
+ switch (callData.type) {
+ case CallData::Type::JS:
+ return callData.js.functionExecutable->isClassConstructorFunction();
+ case CallData::Type::Native:
+ if (callData.native.isBoundFunction)
+ return false;
+ return value.isConstructor();
+ }
+ return false;
}
bool JSC__JSValue__isCell(JSC__JSValue JSValue0) { return JSC::JSValue::decode(JSValue0).isCell(); }
bool JSC__JSValue__isCustomGetterSetter(JSC__JSValue JSValue0)
diff --git a/src/bun.js/bindings/exports.zig b/src/bun.js/bindings/exports.zig
index 73a26e4be..e9e9d3a8d 100644
--- a/src/bun.js/bindings/exports.zig
+++ b/src/bun.js/bindings/exports.zig
@@ -460,7 +460,7 @@ pub const ZigStackTrace = extern struct {
var source_line_len = source_lines_iter.getLength();
if (source_line_len > 0) {
- var source_lines = try allocator.alloc(Api.SourceLine, @intCast(usize, @max(source_lines_iter.i, 0)));
+ var source_lines = try allocator.alloc(Api.SourceLine, @intCast(usize, @max(source_lines_iter.i + 1, 0)));
var source_line_buf = try allocator.alloc(u8, source_line_len);
source_lines_iter = this.sourceLineIterator();
var remain_buf = source_line_buf[0..];
@@ -468,7 +468,7 @@ pub const ZigStackTrace = extern struct {
while (source_lines_iter.next()) |source| {
const text = source.text.slice();
defer source.text.deinit();
- defer bun.copy(
+ bun.copy(
u8,
remain_buf,
text,
@@ -515,7 +515,7 @@ pub const ZigStackTrace = extern struct {
pub fn getLength(this: *SourceLineIterator) usize {
var count: usize = 0;
- for (this.trace.source_lines_ptr[0..@intCast(usize, this.i)]) |*line| {
+ for (this.trace.source_lines_ptr[0..@intCast(usize, this.i + 1)]) |*line| {
count += line.length();
}
@@ -1417,23 +1417,20 @@ pub const ZigConsoleClient = struct {
// If we check an Object has a method table and it does not
// it will crash
- const callable = js_type != .Object and value.isCallable(globalThis.vm());
-
- if (value.isClass(globalThis) and !callable) {
- return .{
- .tag = .Object,
- .cell = js_type,
- };
- }
+ if (js_type != .Object and value.isCallable(globalThis.vm())) {
+ if (value.isClass(globalThis)) {
+ return .{
+ .tag = .Class,
+ .cell = js_type,
+ };
+ }
- if (callable and js_type == .JSFunction) {
- return .{
- .tag = .Function,
- .cell = js_type,
- };
- } else if (callable and js_type == .InternalFunction) {
return .{
- .tag = .Object,
+ // TODO: we print InternalFunction as Object because we have a lot of
+ // callable namespaces and printing the contents of it is better than [Function: namespace]
+ // ideally, we would print [Function: namespace] { ... } on all functions, internal and js.
+ // what we'll do later is rid of .Function and .Class and handle the prefix in the .Object formatter
+ .tag = if (js_type == .InternalFunction) .Object else .Function,
.cell = js_type,
};
}
@@ -1756,7 +1753,7 @@ pub const ZigConsoleClient = struct {
parent: JSValue,
const enable_ansi_colors = enable_ansi_colors_;
pub fn handleFirstProperty(this: *@This(), globalThis: *JSC.JSGlobalObject, value: JSValue) void {
- if (!value.jsType().isFunction() and !value.isClass(globalThis)) {
+ if (!value.jsType().isFunction()) {
var writer = WrappedWriter(Writer){
.ctx = this.writer,
.failed = false,
@@ -2094,9 +2091,9 @@ pub const ZigConsoleClient = struct {
this.addForNewLine(printable.len);
if (printable.len == 0) {
- writer.print(comptime Output.prettyFmt("[class]", enable_ansi_colors), .{});
+ writer.print(comptime Output.prettyFmt("<cyan>[class]<r>", enable_ansi_colors), .{});
} else {
- writer.print(comptime Output.prettyFmt("[class <cyan>{}<r>]", enable_ansi_colors), .{printable});
+ writer.print(comptime Output.prettyFmt("<cyan>[class {}]<r>", enable_ansi_colors), .{printable});
}
},
.Function => {
@@ -2106,7 +2103,7 @@ pub const ZigConsoleClient = struct {
if (printable.len == 0) {
writer.print(comptime Output.prettyFmt("<cyan>[Function]<r>", enable_ansi_colors), .{});
} else {
- writer.print(comptime Output.prettyFmt("<cyan>[Function<d>:<r> <cyan>{}]<r>", enable_ansi_colors), .{printable});
+ writer.print(comptime Output.prettyFmt("<cyan>[Function: {}]<r>", enable_ansi_colors), .{printable});
}
},
.Getter => {
@@ -2802,7 +2799,7 @@ pub const ZigConsoleClient = struct {
}
if (iter.i == 0) {
- if (value.isClass(this.globalThis) and !value.isCallable(this.globalThis.vm()))
+ if (value.isClass(this.globalThis))
this.printAs(.Class, Writer, writer_, value, jsType, enable_ansi_colors)
else if (value.isCallable(this.globalThis.vm()))
this.printAs(.Function, Writer, writer_, value, jsType, enable_ansi_colors)
diff --git a/src/bun.js/bindings/webcore/EventEmitter.cpp b/src/bun.js/bindings/webcore/EventEmitter.cpp
index 0650d624c..0e273042b 100644
--- a/src/bun.js/bindings/webcore/EventEmitter.cpp
+++ b/src/bun.js/bindings/webcore/EventEmitter.cpp
@@ -35,6 +35,8 @@ bool EventEmitter::addListener(const Identifier& eventType, Ref<EventListener>&&
}
eventListenersDidChange();
+ if (this->onDidChangeListener)
+ this->onDidChangeListener(*this, eventType, true);
return true;
}
@@ -62,6 +64,9 @@ bool EventEmitter::removeListener(const Identifier& eventType, EventListener& li
if (data->eventListenerMap.remove(eventType, listener)) {
eventListenersDidChange();
+
+ if (this->onDidChangeListener)
+ this->onDidChangeListener(*this, eventType, false);
return true;
}
return false;
@@ -93,6 +98,8 @@ bool EventEmitter::removeAllListeners(const Identifier& eventType)
if (data->eventListenerMap.removeAll(eventType)) {
eventListenersDidChange();
+ if (this->onDidChangeListener)
+ this->onDidChangeListener(*this, eventType, false);
return true;
}
return false;
diff --git a/src/bun.js/bindings/webcore/EventEmitter.h b/src/bun.js/bindings/webcore/EventEmitter.h
index b46bcff5d..8db59c188 100644
--- a/src/bun.js/bindings/webcore/EventEmitter.h
+++ b/src/bun.js/bindings/webcore/EventEmitter.h
@@ -67,6 +67,8 @@ public:
bool hasActiveEventListeners(const Identifier& eventType) const;
bool hasEventListeners(JSC::VM& vm, ASCIILiteral eventType) const;
+ WTF::Function<void(EventEmitter&, const Identifier& eventName, bool isAdded)> onDidChangeListener = WTF::Function<void(EventEmitter&, const Identifier& eventName, bool isAdded)>(nullptr);
+
unsigned getMaxListeners() const { return m_maxListeners; };
void setMaxListeners(unsigned count);
@@ -101,7 +103,9 @@ private:
EventEmitterData* eventTargetData() { return &m_eventTargetData; }
EventEmitterData* eventTargetDataConcurrently() { return &m_eventTargetData; }
EventEmitterData& ensureEventEmitterData() { return m_eventTargetData; }
- void eventListenersDidChange() {}
+ void eventListenersDidChange()
+ {
+ }
void innerInvokeEventListeners(const Identifier&, SimpleEventListenerVector, const MarkedArgumentBuffer& arguments);
void invalidateEventListenerRegions();
diff --git a/src/bun.js/bindings/webcore/JSEventEmitter.cpp b/src/bun.js/bindings/webcore/JSEventEmitter.cpp
index 231ae0db4..959cbd8d7 100644
--- a/src/bun.js/bindings/webcore/JSEventEmitter.cpp
+++ b/src/bun.js/bindings/webcore/JSEventEmitter.cpp
@@ -219,7 +219,7 @@ JSC_DEFINE_CUSTOM_GETTER(jsEventEmitterConstructor, (JSGlobalObject * lexicalGlo
return JSValue::encode(JSEventEmitter::getConstructor(JSC::getVM(lexicalGlobalObject), prototype->globalObject()));
}
-static inline JSC::EncodedJSValue addListener(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSEventEmitter>::ClassParameter castedThis, bool once, bool prepend)
+inline JSC::EncodedJSValue JSEventEmitter::addListener(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, JSEventEmitter* castedThis, bool once, bool prepend)
{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto throwScope = DECLARE_THROW_SCOPE(vm);
@@ -251,7 +251,7 @@ static inline JSC::EncodedJSValue addListener(JSC::JSGlobalObject* lexicalGlobal
static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_addListenerBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSEventEmitter>::ClassParameter castedThis)
{
- return addListener(lexicalGlobalObject, callFrame, castedThis, false, false);
+ return JSEventEmitter::addListener(lexicalGlobalObject, callFrame, castedThis, false, false);
}
static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_setMaxListenersBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSEventEmitter>::ClassParameter castedThis)
@@ -280,17 +280,17 @@ static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_getMaxListener
static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_addOnceListenerBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSEventEmitter>::ClassParameter castedThis)
{
- return addListener(lexicalGlobalObject, callFrame, castedThis, true, false);
+ return JSEventEmitter::addListener(lexicalGlobalObject, callFrame, castedThis, true, false);
}
static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_prependListenerBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSEventEmitter>::ClassParameter castedThis)
{
- return addListener(lexicalGlobalObject, callFrame, castedThis, false, true);
+ return JSEventEmitter::addListener(lexicalGlobalObject, callFrame, castedThis, false, true);
}
static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_prependOnceListenerBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSEventEmitter>::ClassParameter castedThis)
{
- return addListener(lexicalGlobalObject, callFrame, castedThis, true, true);
+ return JSEventEmitter::addListener(lexicalGlobalObject, callFrame, castedThis, true, true);
}
JSC_DEFINE_HOST_FUNCTION(jsEventEmitterPrototypeFunction_addListener, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
@@ -325,6 +325,11 @@ JSC_DEFINE_HOST_FUNCTION(jsEventEmitterPrototypeFunction_prependOnceListener, (J
static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_removeListenerBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSEventEmitter>::ClassParameter castedThis)
{
+ return JSEventEmitter::removeListener(lexicalGlobalObject, callFrame, castedThis);
+}
+
+inline JSC::EncodedJSValue JSEventEmitter::removeListener(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, JSEventEmitter* castedThis)
+{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto throwScope = DECLARE_THROW_SCOPE(vm);
JSC::JSValue actualThis = callFrame->thisValue();
diff --git a/src/bun.js/bindings/webcore/JSEventEmitter.h b/src/bun.js/bindings/webcore/JSEventEmitter.h
index 855241011..30d62d792 100644
--- a/src/bun.js/bindings/webcore/JSEventEmitter.h
+++ b/src/bun.js/bindings/webcore/JSEventEmitter.h
@@ -27,6 +27,9 @@ public:
static EventEmitter* toWrapped(JSC::VM&, JSC::JSValue);
static void destroy(JSC::JSCell*);
+ static inline JSC::EncodedJSValue addListener(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, JSEventEmitter* castedThis, bool once, bool prepend);
+ static inline JSC::EncodedJSValue removeListener(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, JSEventEmitter* castedThis);
+
DECLARE_INFO;
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
diff --git a/src/bun.js/test/pretty_format.zig b/src/bun.js/test/pretty_format.zig
index 4a245c3bb..a6c6aa631 100644
--- a/src/bun.js/test/pretty_format.zig
+++ b/src/bun.js/test/pretty_format.zig
@@ -423,23 +423,20 @@ pub const JestPrettyFormat = struct {
// If we check an Object has a method table and it does not
// it will crash
- const callable = js_type != .Object and value.isCallable(globalThis.vm());
+ if (js_type != .Object and value.isCallable(globalThis.vm())) {
+ if (value.isClass(globalThis)) {
+ return .{
+ .tag = .Class,
+ .cell = js_type,
+ };
+ }
- if (value.isClass(globalThis) and !callable) {
return .{
- .tag = .Object,
- .cell = js_type,
- };
- }
-
- if (callable and js_type == .JSFunction) {
- return .{
- .tag = .Function,
- .cell = js_type,
- };
- } else if (callable and js_type == .InternalFunction) {
- return .{
- .tag = .Object,
+ // TODO: we print InternalFunction as Object because we have a lot of
+ // callable namespaces and printing the contents of it is better than [Function: namespace]
+ // ideally, we would print [Function: namespace] { ... } on all functions, internal and js.
+ // what we'll do later is rid of .Function and .Class and handle the prefix in the .Object formatter
+ .tag = if (js_type == .InternalFunction) .Object else .Function,
.cell = js_type,
};
}
@@ -750,7 +747,7 @@ pub const JestPrettyFormat = struct {
parent: JSValue,
const enable_ansi_colors = enable_ansi_colors_;
pub fn handleFirstProperty(this: *@This(), globalThis: *JSC.JSGlobalObject, value: JSValue) void {
- if (!value.jsType().isFunction() and !value.isClass(globalThis)) {
+ if (!value.jsType().isFunction()) {
var writer = WrappedWriter(Writer){
.ctx = this.writer,
.failed = false,
@@ -1126,13 +1123,20 @@ pub const JestPrettyFormat = struct {
this.addForNewLine(printable.len);
if (printable.len == 0) {
- writer.print(comptime Output.prettyFmt("[class]", enable_ansi_colors), .{});
+ writer.print(comptime Output.prettyFmt("<cyan>[class]<r>", enable_ansi_colors), .{});
} else {
- writer.print(comptime Output.prettyFmt("[class <cyan>{}<r>]", enable_ansi_colors), .{printable});
+ writer.print(comptime Output.prettyFmt("<cyan>[class {}]<r>", enable_ansi_colors), .{printable});
}
},
.Function => {
- writer.writeAll("[Function]");
+ var printable = ZigString.init(&name_buf);
+ value.getNameProperty(this.globalThis, &printable);
+
+ if (printable.len == 0) {
+ writer.print(comptime Output.prettyFmt("<cyan>[Function]<r>", enable_ansi_colors), .{});
+ } else {
+ writer.print(comptime Output.prettyFmt("<cyan>[Function: {}]<r>", enable_ansi_colors), .{printable});
+ }
},
.Array => {
const len = @truncate(u32, value.getLength(this.globalThis));
diff --git a/test/js/bun/http/error-response.js b/test/js/bun/http/error-response.js
new file mode 100644
index 000000000..3284c146b
--- /dev/null
+++ b/test/js/bun/http/error-response.js
@@ -0,0 +1,8 @@
+const s = Bun.serve({
+ fetch(req, res) {
+ s.stop(true);
+ throw new Error("1");
+ },
+ port: 0,
+});
+fetch(`http://${s.hostname}:${s.port}`).then(res => console.log(res.status));
diff --git a/test/js/bun/http/serve.test.ts b/test/js/bun/http/serve.test.ts
index 7182ba68d..bba35c085 100644
--- a/test/js/bun/http/serve.test.ts
+++ b/test/js/bun/http/serve.test.ts
@@ -2,8 +2,10 @@ import { file, gc, Serve, serve, Server } from "bun";
import { afterEach, describe, it, expect, afterAll } from "bun:test";
import { readFileSync, writeFileSync } from "fs";
import { resolve } from "path";
+import { bunExe, bunEnv } from "harness";
import { renderToReadableStream } from "react-dom/server";
import app_jsx from "./app.jsx";
+import { spawn } from "child_process";
type Handler = (req: Request) => Response;
afterEach(() => gc(true));
@@ -980,6 +982,19 @@ describe("should support Content-Range with Bun.file()", () => {
}
});
+it("formats error responses correctly", async () => {
+ const c = spawn(bunExe(), ["./error-response.js"], { cwd: import.meta.dir, env: bunEnv });
+
+ var output = "";
+ c.stderr.on("data", chunk => {
+ output += chunk.toString();
+ });
+ c.stderr.on("end", () => {
+ expect(output).toContain('throw new Error("1");');
+ c.kill();
+ });
+});
+
it("request body and signal life cycle", async () => {
{
const headers = {
diff --git a/test/js/node/process/call-raise.js b/test/js/node/process/call-raise.js
new file mode 100644
index 000000000..898906759
--- /dev/null
+++ b/test/js/node/process/call-raise.js
@@ -0,0 +1,15 @@
+import { dlopen } from "bun:ffi";
+
+var lazyRaise;
+export function raise(signal) {
+ if (!lazyRaise) {
+ const suffix = process.platform === "darwin" ? "dylib" : "so.6";
+ lazyRaise = dlopen(`libc.${suffix}`, {
+ raise: {
+ args: ["int"],
+ returns: "int",
+ },
+ }).symbols.raise;
+ }
+ lazyRaise(signal);
+}
diff --git a/test/js/node/process/process-signal-handler.fixture.js b/test/js/node/process/process-signal-handler.fixture.js
new file mode 100644
index 000000000..de5a78bda
--- /dev/null
+++ b/test/js/node/process/process-signal-handler.fixture.js
@@ -0,0 +1,63 @@
+import os from "os";
+import { raise } from "./call-raise";
+
+var counter = 0;
+function done() {
+ counter++;
+ if (counter === 2) {
+ setTimeout(() => {
+ if (counter !== 2) {
+ console.log(counter);
+ console.log("FAIL");
+ process.exit(1);
+ }
+
+ console.log("PASS");
+ process.exit(0);
+ }, 1);
+ }
+}
+
+var counter2 = 0;
+function done2() {
+ counter2++;
+ if (counter2 === 2) {
+ setTimeout(() => {
+ if (counter2 !== 2) {
+ console.log(counter2);
+ console.log("FAIL");
+ process.exit(1);
+ }
+
+ console.log("PASS");
+ process.exit(0);
+ }, 1);
+ }
+}
+
+const SIGUSR1 = os.constants.signals.SIGUSR1;
+const SIGUSR2 = os.constants.signals.SIGUSR2;
+
+switch (process.argv.at(-1)) {
+ case "SIGUSR1": {
+ process.on("SIGUSR1", () => {
+ done();
+ });
+ process.on("SIGUSR1", () => {
+ done();
+ });
+ raise(SIGUSR1);
+ break;
+ }
+ case "SIGUSR2": {
+ process.on("SIGUSR2", () => {
+ done2();
+ });
+ process.emit("SIGUSR2");
+ raise(SIGUSR2);
+ break;
+ }
+ default: {
+ throw new Error("Unknown argument: " + process.argv.at(-1));
+ }
+}
diff --git a/test/js/node/process/process.test.js b/test/js/node/process/process.test.js
index e038383de..51825b2b4 100644
--- a/test/js/node/process/process.test.js
+++ b/test/js/node/process/process.test.js
@@ -1,6 +1,6 @@
-import { resolveSync, spawnSync, which } from "bun";
+import { spawnSync, which } from "bun";
import { describe, expect, it } from "bun:test";
-import { existsSync, readFileSync, realpathSync } from "fs";
+import { existsSync, readFileSync } from "fs";
import { bunEnv, bunExe } from "harness";
import { basename, join, resolve } from "path";
@@ -381,6 +381,28 @@ it("process.getuid", () => {
expect(typeof process.getuid()).toBe("number");
});
+describe("signal", () => {
+ const fixture = join(import.meta.dir, "./process-signal-handler.fixture.js");
+ it("simple case works", async () => {
+ const child = Bun.spawn({
+ cmd: [bunExe(), fixture, "SIGUSR1"],
+ env: bunEnv,
+ });
+
+ expect(await child.exited).toBe(0);
+ expect(await new Response(child.stdout).text()).toBe("PASS\n");
+ });
+ it("process.emit will call signal events", async () => {
+ const child = Bun.spawn({
+ cmd: [bunExe(), fixture, "SIGUSR2"],
+ env: bunEnv,
+ });
+
+ expect(await child.exited).toBe(0);
+ expect(await new Response(child.stdout).text()).toBe("PASS\n");
+ });
+});
+
const undefinedStubs = [
"_debugEnd",
"_debugProcess",
diff --git a/test/js/web/console/console-log.expected.txt b/test/js/web/console/console-log.expected.txt
index 97191c8be..332322665 100644
--- a/test/js/web/console/console-log.expected.txt
+++ b/test/js/web/console/console-log.expected.txt
@@ -1,4 +1,6 @@
Hello World!
+0
+-0
123
-123
123.567
@@ -7,6 +9,8 @@ true
false
null
undefined
+Infinity
+-Infinity
Symbol(Symbol Description)
2000-06-27T02:24:34.304Z
[ 123, 456, 789 ]
@@ -29,7 +33,9 @@ Symbol(Symbol Description)
}
Promise { <pending> }
[Function]
-[Function: Foo]
+[Function]
+[class Foo]
+[class]
{}
[Function: foooo]
/FooRegex/
diff --git a/test/js/web/console/console-log.js b/test/js/web/console/console-log.js
index e23a3e9cb..4db40aaac 100644
--- a/test/js/web/console/console-log.js
+++ b/test/js/web/console/console-log.js
@@ -1,4 +1,6 @@
console.log("Hello World!");
+console.log(0);
+console.log(-0);
console.log(123);
console.log(-123);
console.log(123.567);
@@ -7,6 +9,8 @@ console.log(true);
console.log(false);
console.log(null);
console.log(undefined);
+console.log(Infinity);
+console.log(-Infinity);
console.log(Symbol("Symbol Description"));
console.log(new Date(Math.pow(2, 34) * 56));
console.log([123, 456, 789]);
@@ -27,7 +31,9 @@ console.log(new Promise(() => {}));
class Foo {}
console.log(() => {});
+console.log(function () {});
console.log(Foo);
+console.log(class {});
console.log(new Foo());
console.log(function foooo() {});