aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bun.js/bindings/Process.cpp189
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.h2
-rw-r--r--src/bun.js/bindings/sqlite/JSSQLStatement.cpp86
-rw-r--r--src/bun.js/bindings/sqlite/JSSQLStatement.h15
-rw-r--r--src/bun.js/javascript.zig50
-rw-r--r--src/bun.js/node/types.zig4
-rw-r--r--src/bun_js.zig14
7 files changed, 285 insertions, 75 deletions
diff --git a/src/bun.js/bindings/Process.cpp b/src/bun.js/bindings/Process.cpp
index 6320deaf1..1d6b5d33a 100644
--- a/src/bun.js/bindings/Process.cpp
+++ b/src/bun.js/bindings/Process.cpp
@@ -42,6 +42,35 @@ static JSC_DECLARE_CUSTOM_GETTER(Process_getPID);
static JSC_DECLARE_CUSTOM_GETTER(Process_getPPID);
static JSC_DECLARE_HOST_FUNCTION(Process_functionCwd);
+static bool processIsExiting = false;
+
+extern "C" uint8_t Bun__getExitCode(void*);
+extern "C" uint8_t Bun__setExitCode(void*, uint8_t);
+extern "C" void* Bun__getVM();
+extern "C" Zig::GlobalObject* Bun__getDefaultGlobal();
+
+static void dispatchExitInternal(JSC::JSGlobalObject* globalObject, Process* process, int exitCode)
+{
+
+ if (processIsExiting)
+ return;
+ processIsExiting = true;
+ auto& emitter = process->wrapped();
+ auto& vm = globalObject->vm();
+
+ if (vm.hasTerminationRequest() || vm.hasExceptionsAfterHandlingTraps())
+ return;
+
+ auto event = Identifier::fromString(vm, "exit"_s);
+ if (!emitter.hasEventListeners(event)) {
+ return;
+ }
+ process->putDirect(vm, Identifier::fromString(vm, "_exiting"_s), jsBoolean(true), 0);
+
+ MarkedArgumentBuffer arguments;
+ arguments.append(jsNumber(exitCode));
+ emitter.emit(event, arguments);
+}
static JSValue constructStdioWriteStream(JSC::JSGlobalObject* globalObject, int fd)
{
@@ -324,6 +353,29 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionUmask,
extern "C" uint64_t Bun__readOriginTimer(void*);
extern "C" double Bun__readOriginTimerStart(void*);
+// https://github.com/nodejs/node/blob/1936160c31afc9780e4365de033789f39b7cbc0c/src/api/hooks.cc#L49
+extern "C" void Process__dispatchOnBeforeExit(Zig::GlobalObject* globalObject, uint8_t exitCode)
+{
+ if (!globalObject->hasProcessObject()) {
+ return;
+ }
+
+ auto* process = jsCast<Process*>(globalObject->processObject());
+ MarkedArgumentBuffer arguments;
+ arguments.append(jsNumber(exitCode));
+ process->wrapped().emit(Identifier::fromString(globalObject->vm(), "beforeExit"_s), arguments);
+}
+
+extern "C" void Process__dispatchOnExit(Zig::GlobalObject* globalObject, uint8_t exitCode)
+{
+ if (!globalObject->hasProcessObject()) {
+ return;
+ }
+
+ auto* process = jsCast<Process*>(globalObject->processObject());
+ dispatchExitInternal(globalObject, process, exitCode);
+}
+
JSC_DEFINE_HOST_FUNCTION(Process_functionUptime,
(JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
@@ -336,14 +388,38 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionUptime,
JSC_DEFINE_HOST_FUNCTION(Process_functionExit,
(JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
- if (callFrame->argumentCount() == 0) {
- // TODO: exitCode
- Bun__Process__exit(globalObject, 0);
+ auto throwScope = DECLARE_THROW_SCOPE(globalObject->vm());
+ uint8_t exitCode = 0;
+ JSValue arg0 = callFrame->argument(0);
+ if (arg0.isNumber()) {
+ if (!arg0.isInt32()) {
+ throwRangeError(globalObject, throwScope, "The \"code\" argument must be an integer"_s);
+ return JSC::JSValue::encode(JSC::JSValue {});
+ }
+
+ int extiCode32 = arg0.toInt32(globalObject);
+ RETURN_IF_EXCEPTION(throwScope, JSC::JSValue::encode(JSC::JSValue {}));
+
+ if (extiCode32 < 0 || extiCode32 > 127) {
+ throwRangeError(globalObject, throwScope, "The \"code\" argument must be an integer between 0 and 127"_s);
+ return JSC::JSValue::encode(JSC::JSValue {});
+ }
+
+ exitCode = static_cast<uint8_t>(extiCode32);
+ } else if (!arg0.isUndefinedOrNull()) {
+ throwTypeError(globalObject, throwScope, "The \"code\" argument must be an integer"_s);
+ return JSC::JSValue::encode(JSC::JSValue {});
} else {
- Bun__Process__exit(globalObject, callFrame->argument(0).toInt32(globalObject));
+ exitCode = Bun__getExitCode(Bun__getVM());
+ }
+
+ auto* zigGlobal = jsDynamicCast<Zig::GlobalObject*>(globalObject);
+ if (UNLIKELY(!zigGlobal)) {
+ zigGlobal = Bun__getDefaultGlobal();
}
- return JSC::JSValue::encode(JSC::jsUndefined());
+ Process__dispatchOnExit(zigGlobal, exitCode);
+ Bun__Process__exit(zigGlobal, exitCode);
}
extern "C" uint64_t Bun__readOriginTimer(void*);
@@ -391,18 +467,15 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionHRTime,
array->setIndexQuickly(vm, 1, JSC::jsNumber(nanoseconds));
return JSC::JSValue::encode(JSC::JSValue(array));
}
-static JSC_DECLARE_HOST_FUNCTION(Process_functionHRTimeBigInt);
-static JSC_DEFINE_HOST_FUNCTION(Process_functionHRTimeBigInt,
+JSC_DEFINE_HOST_FUNCTION(Process_functionHRTimeBigInt,
(JSC::JSGlobalObject * globalObject_, JSC::CallFrame* callFrame))
{
Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(globalObject_);
return JSC::JSValue::encode(JSValue(JSC::JSBigInt::createFrom(globalObject, Bun__readOriginTimer(globalObject->bunVM()))));
}
-static JSC_DECLARE_HOST_FUNCTION(Process_functionChdir);
-
-static JSC_DEFINE_HOST_FUNCTION(Process_functionChdir,
+JSC_DEFINE_HOST_FUNCTION(Process_functionChdir,
(JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
auto scope = DECLARE_THROW_SCOPE(globalObject->vm());
@@ -611,6 +684,46 @@ JSC_DEFINE_CUSTOM_GETTER(Process_lazyExecArgvGetter, (JSC::JSGlobalObject * glob
return ret;
}
+JSC_DEFINE_CUSTOM_GETTER(Process__getExitCode, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName name))
+{
+ Process* process = jsDynamicCast<Process*>(JSValue::decode(thisValue));
+ if (!process) {
+ return JSValue::encode(jsUndefined());
+ }
+
+ return JSValue::encode(jsNumber(Bun__getExitCode(jsCast<Zig::GlobalObject*>(process->globalObject())->bunVM())));
+}
+JSC_DEFINE_CUSTOM_SETTER(Process__setExitCode, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue value, JSC::PropertyName))
+{
+ Process* process = jsDynamicCast<Process*>(JSValue::decode(thisValue));
+ if (!process) {
+ return false;
+ }
+
+ auto throwScope = DECLARE_THROW_SCOPE(process->vm());
+ JSValue exitCode = JSValue::decode(value);
+ if (!exitCode.isNumber()) {
+ throwTypeError(lexicalGlobalObject, throwScope, "exitCode must be a number"_s);
+ return false;
+ }
+
+ if (!exitCode.isInt32()) {
+ throwRangeError(lexicalGlobalObject, throwScope, "The \"code\" argument must be an integer"_s);
+ return JSC::JSValue::encode(JSC::JSValue {});
+ }
+
+ int exitCodeInt = exitCode.toInt32(lexicalGlobalObject);
+ RETURN_IF_EXCEPTION(throwScope, false);
+ if (exitCodeInt < 0 || exitCodeInt > 127) {
+ throwRangeError(lexicalGlobalObject, throwScope, "exitCode must be between 0 and 127"_s);
+ return false;
+ }
+
+ void* ptr = jsCast<Zig::GlobalObject*>(process->globalObject())->bunVM();
+ Bun__setExitCode(ptr, static_cast<uint8_t>(exitCodeInt));
+ return true;
+}
+
JSC_DEFINE_CUSTOM_GETTER(Process_lazyExecPathGetter, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName name))
{
JSC::JSObject* thisObject = JSValue::decode(thisValue).getObject();
@@ -677,39 +790,42 @@ void Process::finishCreation(JSC::VM& vm)
vm, clientData->builtinNames().versionsPublicName(),
JSC::CustomGetterSetter::create(vm, Process_getVersionsLazy, Process_setVersionsLazy), 0);
// this should be transpiled out, but just incase
- this->putDirect(this->vm(), JSC::Identifier::fromString(this->vm(), "browser"_s),
- JSC::JSValue(false));
+ this->putDirect(vm, JSC::Identifier::fromString(vm, "browser"_s),
+ JSC::JSValue(false), PropertyAttribute::DontEnum | 0);
- this->putDirect(this->vm(), JSC::Identifier::fromString(this->vm(), "exitCode"_s),
- JSC::JSValue(JSC::jsNumber(0)));
+ this->putDirectCustomAccessor(vm, JSC::Identifier::fromString(vm, "exitCode"_s),
+ JSC::CustomGetterSetter::create(vm,
+ Process__getExitCode,
+ Process__setExitCode),
+ 0);
- this->putDirect(this->vm(), clientData->builtinNames().versionPublicName(),
- JSC::jsString(this->vm(), makeString("v", REPORTED_NODE_VERSION)));
+ this->putDirect(vm, clientData->builtinNames().versionPublicName(),
+ JSC::jsString(vm, makeString("v", REPORTED_NODE_VERSION)));
// this gives some way of identifying at runtime whether the SSR is happening in node or not.
// this should probably be renamed to what the name of the bundler is, instead of "notNodeJS"
// but it must be something that won't evaluate to truthy in Node.js
- this->putDirect(this->vm(), JSC::Identifier::fromString(this->vm(), "isBun"_s), JSC::JSValue(true));
+ this->putDirect(vm, JSC::Identifier::fromString(vm, "isBun"_s), JSC::JSValue(true));
#if defined(__APPLE__)
- this->putDirect(this->vm(), JSC::Identifier::fromString(this->vm(), "platform"_s),
- JSC::jsString(this->vm(), makeAtomString("darwin")));
+ this->putDirect(vm, JSC::Identifier::fromString(vm, "platform"_s),
+ JSC::jsString(vm, makeAtomString("darwin")));
#else
- this->putDirect(this->vm(), JSC::Identifier::fromString(this->vm(), "platform"_s),
- JSC::jsString(this->vm(), makeAtomString("linux")));
+ this->putDirect(vm, JSC::Identifier::fromString(vm, "platform"_s),
+ JSC::jsString(vm, makeAtomString("linux")));
#endif
#if defined(__x86_64__)
- this->putDirect(this->vm(), JSC::Identifier::fromString(this->vm(), "arch"_s),
- JSC::jsString(this->vm(), makeAtomString("x64")));
+ this->putDirect(vm, JSC::Identifier::fromString(vm, "arch"_s),
+ JSC::jsString(vm, makeAtomString("x64")));
#elif defined(__i386__)
- this->putDirect(this->vm(), JSC::Identifier::fromString(this->vm(), "arch"_s),
- JSC::jsString(this->vm(), makeAtomString("x86")));
+ this->putDirect(vm, JSC::Identifier::fromString(vm, "arch"_s),
+ JSC::jsString(vm, makeAtomString("x86")));
#elif defined(__arm__)
- this->putDirect(this->vm(), JSC::Identifier::fromString(this->vm(), "arch"_s),
- JSC::jsString(this->vm(), makeAtomString("arm")));
+ this->putDirect(vm, JSC::Identifier::fromString(vm, "arch"_s),
+ JSC::jsString(vm, makeAtomString("arm")));
#elif defined(__aarch64__)
- this->putDirect(this->vm(), JSC::Identifier::fromString(this->vm(), "arch"_s),
- JSC::jsString(this->vm(), makeAtomString("arm64")));
+ this->putDirect(vm, JSC::Identifier::fromString(vm, "arch"_s),
+ JSC::jsString(vm, makeAtomString("arm64")));
#endif
JSC::JSFunction* hrtime = JSC::JSFunction::create(vm, globalObject, 0,
@@ -719,7 +835,7 @@ void Process::finishCreation(JSC::VM& vm)
MAKE_STATIC_STRING_IMPL("bigint"), Process_functionHRTimeBigInt, ImplementationVisibility::Public);
hrtime->putDirect(vm, JSC::Identifier::fromString(vm, "bigint"_s), hrtimeBigInt);
- this->putDirect(this->vm(), JSC::Identifier::fromString(this->vm(), "hrtime"_s), hrtime);
+ this->putDirect(vm, JSC::Identifier::fromString(vm, "hrtime"_s), hrtime);
this->putDirectCustomAccessor(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "release"_s)),
JSC::CustomGetterSetter::create(vm, Process_getterRelease, Process_setterRelease), 0);
@@ -733,7 +849,10 @@ void Process::finishCreation(JSC::VM& vm)
this->putDirectCustomAccessor(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "stdin"_s)),
JSC::CustomGetterSetter::create(vm, Process_lazyStdinGetter, Process_defaultSetter), 0);
- this->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(this->vm(), "abort"_s),
+ this->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "abort"_s),
+ 0, Process_functionAbort, ImplementationVisibility::Public, NoIntrinsic, 0);
+
+ this->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "abort"_s),
0, Process_functionAbort, ImplementationVisibility::Public, NoIntrinsic, 0);
this->putDirectCustomAccessor(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "argv0"_s)),
@@ -745,13 +864,13 @@ void Process::finishCreation(JSC::VM& vm)
this->putDirectCustomAccessor(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "execArgv"_s)),
JSC::CustomGetterSetter::create(vm, Process_lazyExecArgvGetter, Process_defaultSetter), 0);
- this->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(this->vm(), "uptime"_s),
+ this->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "uptime"_s),
0, Process_functionUptime, ImplementationVisibility::Public, NoIntrinsic, 0);
- this->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(this->vm(), "umask"_s),
+ this->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "umask"_s),
1, Process_functionUmask, ImplementationVisibility::Public, NoIntrinsic, 0);
- this->putDirectBuiltinFunction(vm, globalObject, JSC::Identifier::fromString(this->vm(), "binding"_s),
+ this->putDirectBuiltinFunction(vm, globalObject, JSC::Identifier::fromString(vm, "binding"_s),
processObjectInternalsBindingCodeGenerator(vm),
0);
@@ -788,7 +907,7 @@ void Process::finishCreation(JSC::VM& vm)
config->putDirect(vm, JSC::Identifier::fromString(vm, "variables"_s), variables, 0);
this->putDirect(vm, JSC::Identifier::fromString(vm, "config"_s), config, 0);
- this->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(this->vm(), "emitWarning"_s),
+ this->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "emitWarning"_s),
1, Process_emitWarning, ImplementationVisibility::Public, NoIntrinsic, 0);
JSC::JSFunction* requireDotMainFunction = JSFunction::create(
diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h
index da6ba92a0..f44212da1 100644
--- a/src/bun.js/bindings/ZigGlobalObject.h
+++ b/src/bun.js/bindings/ZigGlobalObject.h
@@ -270,6 +270,8 @@ public:
JSWeakMap* vmModuleContextMap() { return m_vmModuleContextMap.getInitializedOnMainThread(this); }
+ bool hasProcessObject() const { return m_processObject.isInitialized(); }
+
JSC::JSObject* processObject()
{
return m_processObject.getInitializedOnMainThread(this);
diff --git a/src/bun.js/bindings/sqlite/JSSQLStatement.cpp b/src/bun.js/bindings/sqlite/JSSQLStatement.cpp
index a6855fd19..61ac91ba7 100644
--- a/src/bun.js/bindings/sqlite/JSSQLStatement.cpp
+++ b/src/bun.js/bindings/sqlite/JSSQLStatement.cpp
@@ -107,6 +107,50 @@ static JSC_DECLARE_HOST_FUNCTION(jsSQLStatementDeserialize);
return JSValue::encode(jsUndefined()); \
}
+class VersionSqlite3 {
+public:
+ explicit VersionSqlite3(sqlite3* db)
+ : db(db)
+ , version(0)
+ {
+ }
+ sqlite3* db;
+ std::atomic<uint64_t> version;
+};
+
+class SQLiteSingleton {
+public:
+ Vector<VersionSqlite3*> databases;
+ Vector<std::atomic<uint64_t>> schema_versions;
+};
+
+static SQLiteSingleton* _instance = nullptr;
+
+static Vector<VersionSqlite3*>& databases()
+{
+ if (!_instance) {
+ _instance = new SQLiteSingleton();
+ _instance->databases = Vector<VersionSqlite3*>();
+ _instance->databases.reserveInitialCapacity(4);
+ _instance->schema_versions = Vector<std::atomic<uint64_t>>();
+ }
+
+ return _instance->databases;
+}
+
+extern "C" void Bun__closeAllSQLiteDatabasesForTermination()
+{
+ if (!_instance) {
+ return;
+ }
+ auto& dbs = _instance->databases;
+
+ for (auto& db : dbs) {
+ if (db->db)
+ sqlite3_close_v2(db->db);
+ }
+}
+
namespace WebCore {
using namespace JSC;
@@ -272,10 +316,6 @@ void JSSQLStatement::destroy(JSC::JSCell* cell)
void JSSQLStatementConstructor::destroy(JSC::JSCell* cell)
{
- JSSQLStatementConstructor* thisObject = static_cast<JSSQLStatementConstructor*>(cell);
- for (auto version_db : thisObject->databases) {
- delete version_db;
- }
}
static inline bool rebindValue(JSC::JSGlobalObject* lexicalGlobalObject, sqlite3_stmt* stmt, int i, JSC::JSValue value, JSC::ThrowScope& scope, bool clone)
@@ -547,8 +587,8 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementDeserialize, (JSC::JSGlobalObject * lexic
return JSValue::encode(JSC::jsUndefined());
}
- auto count = thisObject->databases.size();
- thisObject->databases.append(new VersionSqlite3(db));
+ auto count = databases().size();
+ databases().append(new VersionSqlite3(db));
RELEASE_AND_RETURN(scope, JSValue::encode(jsNumber(count)));
}
@@ -565,12 +605,12 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementSerialize, (JSC::JSGlobalObject * lexical
}
int32_t dbIndex = callFrame->argument(0).toInt32(lexicalGlobalObject);
- if (UNLIKELY(dbIndex < 0 || dbIndex >= thisObject->databases.size())) {
+ if (UNLIKELY(dbIndex < 0 || dbIndex >= databases().size())) {
throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Invalid database handle"_s));
return JSValue::encode(JSC::jsUndefined());
}
- sqlite3* db = thisObject->databases[dbIndex]->db;
+ sqlite3* db = databases()[dbIndex]->db;
if (UNLIKELY(!db)) {
throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Can't do this on a closed database"_s));
return JSValue::encode(JSC::jsUndefined());
@@ -606,7 +646,7 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementLoadExtensionFunction, (JSC::JSGlobalObje
}
int32_t dbIndex = callFrame->argument(0).toInt32(lexicalGlobalObject);
- if (UNLIKELY(dbIndex < 0 || dbIndex >= thisObject->databases.size())) {
+ if (UNLIKELY(dbIndex < 0 || dbIndex >= databases().size())) {
throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Invalid database handle"_s));
return JSValue::encode(JSC::jsUndefined());
}
@@ -620,7 +660,7 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementLoadExtensionFunction, (JSC::JSGlobalObje
auto extensionString = extension.toWTFString(lexicalGlobalObject);
RETURN_IF_EXCEPTION(scope, {});
- sqlite3* db = thisObject->databases[dbIndex]->db;
+ sqlite3* db = databases()[dbIndex]->db;
if (UNLIKELY(!db)) {
throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Can't do this on a closed database"_s));
return JSValue::encode(JSC::jsUndefined());
@@ -661,11 +701,11 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementExecuteFunction, (JSC::JSGlobalObject * l
}
int32_t handle = callFrame->argument(0).toInt32(lexicalGlobalObject);
- if (thisObject->databases.size() < handle) {
+ if (databases().size() < handle) {
throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Invalid database handle"_s));
return JSValue::encode(JSC::jsUndefined());
}
- sqlite3* db = thisObject->databases[handle]->db;
+ sqlite3* db = databases()[handle]->db;
if (UNLIKELY(!db)) {
throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Database has closed"_s));
@@ -724,7 +764,7 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementExecuteFunction, (JSC::JSGlobalObject * l
rc = sqlite3_step(statement);
if (!sqlite3_stmt_readonly(statement)) {
- thisObject->databases[handle]->version++;
+ databases()[handle]->version++;
}
while (rc == SQLITE_ROW) {
@@ -765,12 +805,12 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementIsInTransactionFunction, (JSC::JSGlobalOb
int32_t handle = dbNumber.toInt32(lexicalGlobalObject);
- if (handle < 0 || handle > thisObject->databases.size()) {
+ if (handle < 0 || handle > databases().size()) {
throwException(lexicalGlobalObject, scope, createRangeError(lexicalGlobalObject, "Invalid database handle"_s));
return JSValue::encode(JSC::jsUndefined());
}
- sqlite3* db = thisObject->databases[handle]->db;
+ sqlite3* db = databases()[handle]->db;
if (UNLIKELY(!db)) {
throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Database has closed"_s));
@@ -803,12 +843,12 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementPrepareStatementFunction, (JSC::JSGlobalO
}
int32_t handle = dbNumber.toInt32(lexicalGlobalObject);
- if (handle < 0 || handle > thisObject->databases.size()) {
+ if (handle < 0 || handle > databases().size()) {
throwException(lexicalGlobalObject, scope, createRangeError(lexicalGlobalObject, "Invalid database handle"_s));
return JSValue::encode(JSC::jsUndefined());
}
- sqlite3* db = thisObject->databases[handle]->db;
+ sqlite3* db = databases()[handle]->db;
if (!db) {
throwException(lexicalGlobalObject, scope, createRangeError(lexicalGlobalObject, "Cannot use a closed database"_s));
return JSValue::encode(JSC::jsUndefined());
@@ -848,7 +888,7 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementPrepareStatementFunction, (JSC::JSGlobalO
auto* structure = JSSQLStatement::createStructure(vm, lexicalGlobalObject, lexicalGlobalObject->objectPrototype());
// auto* structure = JSSQLStatement::createStructure(vm, globalObject(), thisObject->getDirect(vm, vm.propertyNames->prototype));
JSSQLStatement* sqlStatement = JSSQLStatement::create(
- structure, reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject), statement, thisObject->databases[handle]);
+ structure, reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject), statement, databases()[handle]);
if (bindings.isObject()) {
auto* castedThis = sqlStatement;
DO_REBIND(bindings)
@@ -924,8 +964,8 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementOpenStatementFunction, (JSC::JSGlobalObje
status = sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 1, NULL);
assert(status == SQLITE_OK);
- auto count = constructor->databases.size();
- constructor->databases.append(new VersionSqlite3(db));
+ auto count = databases().size();
+ databases().append(new VersionSqlite3(db));
RELEASE_AND_RETURN(scope, JSValue::encode(jsNumber(count)));
}
@@ -956,12 +996,12 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementCloseStatementFunction, (JSC::JSGlobalObj
int dbIndex = dbNumber.toInt32(lexicalGlobalObject);
- if (dbIndex < 0 || dbIndex >= constructor->databases.size()) {
+ if (dbIndex < 0 || dbIndex >= databases().size()) {
throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Invalid database handle"_s));
return JSValue::encode(jsUndefined());
}
- sqlite3* db = constructor->databases[dbIndex]->db;
+ sqlite3* db = databases()[dbIndex]->db;
// no-op if already closed
if (!db) {
return JSValue::encode(jsUndefined());
@@ -973,7 +1013,7 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementCloseStatementFunction, (JSC::JSGlobalObj
return JSValue::encode(jsUndefined());
}
- constructor->databases[dbIndex]->db = nullptr;
+ databases()[dbIndex]->db = nullptr;
return JSValue::encode(jsUndefined());
}
diff --git a/src/bun.js/bindings/sqlite/JSSQLStatement.h b/src/bun.js/bindings/sqlite/JSSQLStatement.h
index e63b99fbb..8566fcdd9 100644
--- a/src/bun.js/bindings/sqlite/JSSQLStatement.h
+++ b/src/bun.js/bindings/sqlite/JSSQLStatement.h
@@ -47,17 +47,6 @@
namespace WebCore {
-class VersionSqlite3 {
-public:
- explicit VersionSqlite3(sqlite3* db)
- : db(db)
- , version(0)
- {
- }
- sqlite3* db;
- std::atomic<uint64_t> version;
-};
-
class JSSQLStatementConstructor final : public JSC::JSFunction {
public:
using Base = JSC::JSFunction;
@@ -82,13 +71,9 @@ public:
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
}
- Vector<VersionSqlite3*> databases;
- Vector<std::atomic<uint64_t>> schema_versions;
-
private:
JSSQLStatementConstructor(JSC::VM& vm, NativeExecutable* native, JSGlobalObject* globalObject, JSC::Structure* structure)
: Base(vm, native, globalObject, structure)
- , databases()
{
}
diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig
index b696c6cf2..7d2435823 100644
--- a/src/bun.js/javascript.zig
+++ b/src/bun.js/javascript.zig
@@ -334,6 +334,33 @@ pub export fn Bun__onDidAppendPlugin(jsc_vm: *VirtualMachine, globalObject: *JSG
jsc_vm.bundler.linker.plugin_runner = &jsc_vm.plugin_runner.?;
}
+pub const ExitHandler = struct {
+ exit_code: u8 = 0,
+
+ pub export fn Bun__getExitCode(vm: *VirtualMachine) u8 {
+ return vm.exit_handler.exit_code;
+ }
+
+ pub export fn Bun__setExitCode(vm: *VirtualMachine, code: u8) void {
+ vm.exit_handler.exit_code = code;
+ }
+
+ extern fn Process__dispatchOnBeforeExit(*JSC.JSGlobalObject, code: u8) void;
+ extern fn Process__dispatchOnExit(*JSC.JSGlobalObject, code: u8) void;
+ extern fn Bun__closeAllSQLiteDatabasesForTermination() void;
+
+ pub fn dispatchOnExit(this: *ExitHandler) void {
+ var vm = @fieldParentPtr(VirtualMachine, "exit_handler", this);
+ Process__dispatchOnExit(vm.global, this.exit_code);
+ Bun__closeAllSQLiteDatabasesForTermination();
+ }
+
+ pub fn dispatchOnBeforeExit(this: *ExitHandler) void {
+ var vm = @fieldParentPtr(VirtualMachine, "exit_handler", this);
+ Process__dispatchOnBeforeExit(vm.global, this.exit_code);
+ }
+};
+
/// TODO: rename this to ScriptExecutionContext
/// This is the shared global state for a single JS instance execution
/// Today, Bun is one VM per thread, so the name "VirtualMachine" sort of makes sense
@@ -376,6 +403,7 @@ pub const VirtualMachine = struct {
plugin_runner: ?PluginRunner = null,
is_main_thread: bool = false,
last_reported_error_for_dedupe: JSValue = .zero,
+ exit_handler: ExitHandler = .{},
/// Do not access this field directly
/// It exists in the VirtualMachine struct so that
@@ -620,7 +648,29 @@ pub const VirtualMachine = struct {
loop.run();
}
+ pub fn onBeforeExit(this: *VirtualMachine) void {
+ this.exit_handler.dispatchOnBeforeExit();
+ var dispatch = false;
+ while (true) {
+ while (this.eventLoop().tasks.count > 0 or this.active_tasks > 0 or this.uws_event_loop.?.active > 0) : (dispatch = true) {
+ this.tick();
+ this.eventLoop().autoTickActive();
+ }
+
+ if (dispatch) {
+ this.exit_handler.dispatchOnBeforeExit();
+ dispatch = false;
+
+ if (this.eventLoop().tasks.count > 0 or this.active_tasks > 0 or this.uws_event_loop.?.active > 0) continue;
+ }
+
+ break;
+ }
+ }
+
pub fn onExit(this: *VirtualMachine) void {
+ this.exit_handler.dispatchOnExit();
+
var rare_data = this.rare_data orelse return;
var hook = rare_data.cleanup_hook orelse return;
hook.execute();
diff --git a/src/bun.js/node/types.zig b/src/bun.js/node/types.zig
index 96d04636e..553b292d6 100644
--- a/src/bun.js/node/types.zig
+++ b/src/bun.js/node/types.zig
@@ -2202,7 +2202,9 @@ pub const Process = struct {
}
}
- pub fn exit(_: *JSC.JSGlobalObject, code: i32) callconv(.C) void {
+ pub fn exit(globalObject: *JSC.JSGlobalObject, code: i32) callconv(.C) void {
+ globalObject.bunVM().onExit();
+
std.os.exit(@truncate(u8, @intCast(u32, @max(code, 0))));
}
diff --git a/src/bun_js.zig b/src/bun_js.zig
index 63ffe0611..72b7f8de9 100644
--- a/src/bun_js.zig
+++ b/src/bun_js.zig
@@ -248,6 +248,8 @@ pub const Run = struct {
vm.eventLoop().tick();
vm.eventLoop().tickPossiblyForever();
} else {
+ vm.exit_handler.exit_code = 1;
+ vm.onExit();
Global.exit(1);
}
}
@@ -279,6 +281,8 @@ pub const Run = struct {
vm.eventLoop().tick();
vm.eventLoop().tickPossiblyForever();
} else {
+ vm.exit_handler.exit_code = 1;
+ vm.onExit();
Global.exit(1);
}
}
@@ -315,6 +319,8 @@ pub const Run = struct {
vm.eventLoop().autoTickActive();
}
+ vm.onBeforeExit();
+
if (this.vm.pending_internal_promise.status(vm.global.vm()) == .Rejected and prev_promise != this.vm.pending_internal_promise) {
prev_promise = this.vm.pending_internal_promise;
vm.onUnhandledError(this.vm.global, this.vm.pending_internal_promise.result(vm.global.vm()));
@@ -332,6 +338,8 @@ pub const Run = struct {
vm.tick();
vm.eventLoop().autoTickActive();
}
+
+ vm.onBeforeExit();
}
if (vm.log.msgs.items.len > 0) {
@@ -347,10 +355,14 @@ pub const Run = struct {
vm.onUnhandledRejection = &onUnhandledRejectionBeforeClose;
vm.global.handleRejectedPromises();
+ if (this.any_unhandled and this.vm.exit_handler.exit_code == 0) {
+ this.vm.exit_handler.exit_code = 1;
+ }
+ const exit_code = this.vm.exit_handler.exit_code;
vm.onExit();
if (!JSC.is_bindgen) JSC.napi.fixDeadCodeElimination();
- Global.exit(@intFromBool(this.any_unhandled));
+ Global.exit(exit_code);
}
};