From ca42c820d29400d8bd93f493064db029f6d4420d Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Sun, 9 Jul 2023 16:33:49 -0700 Subject: Implement nearly all of `process` object methods and properties (#3581) * Add special case * Make process object load faster * Fix openStdin --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> --- src/bun.js/bindings/Process.cpp | 960 ++++++++++++++++++++++------------------ 1 file changed, 532 insertions(+), 428 deletions(-) (limited to 'src/bun.js/bindings/Process.cpp') diff --git a/src/bun.js/bindings/Process.cpp b/src/bun.js/bindings/Process.cpp index 1d6b5d33a..8d94594cc 100644 --- a/src/bun.js/bindings/Process.cpp +++ b/src/bun.js/bindings/Process.cpp @@ -11,13 +11,24 @@ #include #include "ZigConsoleClient.h" #include +#include #pragma mark - Node.js Process +#if !defined(_MSC_VER) +#include // setuid, getuid +#endif + namespace Zig { using namespace JSC; #define REPORTED_NODE_VERSION "18.15.0" +#define processObjectBindingCodeGenerator processObjectInternalsBindingCodeGenerator +#define processObjectMainModuleCodeGenerator moduleMainCodeGenerator + +#if !defined(BUN_WEBKIT_VERSION) +#define BUN_WEBKIT_VERSION "unknown" +#endif using JSGlobalObject = JSC::JSGlobalObject; using Exception = JSC::Exception; @@ -31,23 +42,20 @@ using JSObject = JSC::JSObject; using JSNonFinalObject = JSC::JSNonFinalObject; namespace JSCastingHelpers = JSC::JSCastingHelpers; -static JSC_DECLARE_CUSTOM_SETTER(Process_setTitle); -static JSC_DECLARE_CUSTOM_GETTER(Process_getArgv); -static JSC_DECLARE_CUSTOM_SETTER(Process_setArgv); -static JSC_DECLARE_CUSTOM_GETTER(Process_getTitle); -static JSC_DECLARE_CUSTOM_GETTER(Process_getVersionsLazy); -static JSC_DECLARE_CUSTOM_SETTER(Process_setVersionsLazy); - -static JSC_DECLARE_CUSTOM_GETTER(Process_getPID); -static JSC_DECLARE_CUSTOM_GETTER(Process_getPPID); - -static JSC_DECLARE_HOST_FUNCTION(Process_functionCwd); +JSC_DECLARE_CUSTOM_SETTER(Process_setTitle); +JSC_DECLARE_CUSTOM_GETTER(Process_getArgv); +JSC_DECLARE_CUSTOM_SETTER(Process_setArgv); +JSC_DECLARE_CUSTOM_GETTER(Process_getTitle); +JSC_DECLARE_CUSTOM_GETTER(Process_getPID); +JSC_DECLARE_CUSTOM_GETTER(Process_getPPID); +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(); +extern "C" const char* Bun__githubURL; static void dispatchExitInternal(JSC::JSGlobalObject* globalObject, Process* process, int exitCode) { @@ -72,107 +80,6 @@ static void dispatchExitInternal(JSC::JSGlobalObject* globalObject, Process* pro emitter.emit(event, arguments); } -static JSValue constructStdioWriteStream(JSC::JSGlobalObject* globalObject, int fd) -{ - auto& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - auto* thisObject = reinterpret_cast(globalObject); - JSC::JSFunction* getStdioWriteStream = JSC::JSFunction::create(vm, processObjectInternalsGetStdioWriteStreamCodeGenerator(vm), globalObject); - JSC::MarkedArgumentBuffer args; - WTF::String process = WTF::String("node:process"_s); - JSC::JSValue requireFunction = Zig::ImportMetaObject::createRequireFunction( - vm, - globalObject, - process); - - args.append(JSC::jsNumber(fd)); - args.append(requireFunction); - - auto clientData = WebCore::clientData(vm); - JSC::CallData callData = JSC::getCallData(getStdioWriteStream); - - NakedPtr returnedException = nullptr; - auto result = JSC::call(globalObject, getStdioWriteStream, callData, globalObject->globalThis(), args, returnedException); - RETURN_IF_EXCEPTION(scope, {}); - - if (returnedException) { - throwException(globalObject, scope, returnedException.get()); - return {}; - } - - return result; -} - -JSC_DEFINE_CUSTOM_GETTER( - Process_lazyStdinGetter, - (JSGlobalObject * globalObject, EncodedJSValue thisValue, PropertyName property)) -{ - auto& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - JSC::JSValue value = JSC::JSValue::decode(thisValue); - if (!value || value.isUndefinedOrNull() || !value.isObject()) - return JSValue::encode(jsUndefined()); - - auto* thisObject = reinterpret_cast(globalObject); - JSC::JSFunction* getStdioWriteStream = JSC::JSFunction::create(vm, processObjectInternalsGetStdinStreamCodeGenerator(vm), globalObject); - JSC::MarkedArgumentBuffer args; - WTF::String process = WTF::String("node:process"_s); - JSC::JSValue requireFunction = Zig::ImportMetaObject::createRequireFunction( - vm, - globalObject, - process); - - args.append(JSC::jsNumber(STDIN_FILENO)); - args.append(requireFunction); - args.append(thisObject->get(globalObject, PropertyName(JSC::Identifier::fromString(vm, "Bun"_s)))); - - auto clientData = WebCore::clientData(vm); - JSC::CallData callData = JSC::getCallData(getStdioWriteStream); - - NakedPtr returnedException = nullptr; - auto result = JSC::call(globalObject, getStdioWriteStream, callData, globalObject->globalThis(), args, returnedException); - RETURN_IF_EXCEPTION(scope, {}); - - if (UNLIKELY(returnedException)) { - throwException(globalObject, scope, returnedException.get()); - return {}; - } - - if (LIKELY(result)) - value.getObject()->putDirect(vm, property, result, 0); - - return JSValue::encode(result); -} - -JSC_DEFINE_CUSTOM_GETTER( - Process_lazyStdoutGetter, - (JSGlobalObject * globalObject, EncodedJSValue thisValue, PropertyName property)) -{ - JSValue value = JSValue::decode(thisValue); - auto& vm = globalObject->vm(); - JSC::JSObject* thisObject = value.toObject(globalObject); - JSC::JSValue stream = constructStdioWriteStream(globalObject, 1); - - if (stream) - thisObject->putDirect(vm, property, stream, 0); - - return JSValue::encode(stream); -} - -JSC_DEFINE_CUSTOM_GETTER( - Process_lazyStderrGetter, (JSGlobalObject * globalObject, EncodedJSValue thisValue, PropertyName property)) -{ - JSValue value = JSValue::decode(thisValue); - auto& vm = globalObject->vm(); - JSC::JSObject* thisObject = value.toObject(globalObject); - JSC::JSValue stream = constructStdioWriteStream(globalObject, 2); - - if (stream) - thisObject->putDirect(vm, property, stream, 0); - - return JSValue::encode(stream); -} - JSC_DEFINE_CUSTOM_SETTER(Process_defaultSetter, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue value, JSC::PropertyName propertyName)) @@ -420,6 +327,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionExit, Process__dispatchOnExit(zigGlobal, exitCode); Bun__Process__exit(zigGlobal, exitCode); + __builtin_unreachable(); } extern "C" uint64_t Bun__readOriginTimer(void*); @@ -497,34 +405,6 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionChdir, return JSC::JSValue::encode(result); } -extern "C" const char* Bun__githubURL; - -JSC_DEFINE_CUSTOM_GETTER(Process_getterRelease, (JSGlobalObject * globalObject, EncodedJSValue thisValue, PropertyName)) -{ - auto& vm = globalObject->vm(); - - auto* release = JSC::constructEmptyObject(globalObject); - release->putDirect(vm, Identifier::fromString(vm, "name"_s), jsString(vm, WTF::String("bun"_s)), 0); - release->putDirect(vm, Identifier::fromString(vm, "lts"_s), jsBoolean(false), 0); - release->putDirect(vm, Identifier::fromString(vm, "sourceUrl"_s), jsString(vm, WTF::String(Bun__githubURL, strlen(Bun__githubURL))), 0); - release->putDirect(vm, Identifier::fromString(vm, "headersUrl"_s), jsEmptyString(vm), 0); - release->putDirect(vm, Identifier::fromString(vm, "libUrl"_s), jsEmptyString(vm), 0); - - return JSValue::encode(release); -} - -JSC_DEFINE_CUSTOM_SETTER(Process_setterRelease, - (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, - JSC::EncodedJSValue value, JSC::PropertyName)) -{ - JSC::VM& vm = globalObject->vm(); - - JSC::JSObject* thisObject = JSC::jsDynamicCast(JSValue::decode(thisValue)); - thisObject->putDirect(vm, JSC::Identifier::fromString(vm, "release"_s), JSValue::decode(value), 0); - - return true; -} - // static const NeverDestroyed signalNames[] = { // MAKE_STATIC_STRING_IMPL("SIGHUP"), // MAKE_STATIC_STRING_IMPL("SIGINT"), @@ -660,31 +540,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_emitWarning, (JSGlobalObject * lexicalGlobalObj return JSValue::encode(jsUndefined()); } -JSC_DEFINE_CUSTOM_GETTER(Process_lazyArgv0Getter, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName name)) -{ - JSC::JSObject* thisObject = JSValue::decode(thisValue).getObject(); - EncodedJSValue ret = Bun__Process__getArgv0(globalObject); - - if (LIKELY(thisObject)) { - thisObject->putDirect(globalObject->vm(), name, JSValue::decode(ret), 0); - } - - return ret; -} - -JSC_DEFINE_CUSTOM_GETTER(Process_lazyExecArgvGetter, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName name)) -{ - JSC::JSObject* thisObject = JSValue::decode(thisValue).getObject(); - EncodedJSValue ret = Bun__Process__getExecArgv(globalObject); - - if (LIKELY(thisObject)) { - thisObject->putDirect(globalObject->vm(), name, JSValue::decode(ret), 0); - } - - return ret; -} - -JSC_DEFINE_CUSTOM_GETTER(Process__getExitCode, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName name)) +JSC_DEFINE_CUSTOM_GETTER(processExitCode, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName name)) { Process* process = jsDynamicCast(JSValue::decode(thisValue)); if (!process) { @@ -693,7 +549,7 @@ JSC_DEFINE_CUSTOM_GETTER(Process__getExitCode, (JSC::JSGlobalObject * lexicalGlo return JSValue::encode(jsNumber(Bun__getExitCode(jsCast(process->globalObject())->bunVM()))); } -JSC_DEFINE_CUSTOM_SETTER(Process__setExitCode, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue value, JSC::PropertyName)) +JSC_DEFINE_CUSTOM_SETTER(setProcessExitCode, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue value, JSC::PropertyName)) { Process* process = jsDynamicCast(JSValue::decode(thisValue)); if (!process) { @@ -724,158 +580,56 @@ JSC_DEFINE_CUSTOM_SETTER(Process__setExitCode, (JSC::JSGlobalObject * lexicalGlo return true; } -JSC_DEFINE_CUSTOM_GETTER(Process_lazyExecPathGetter, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName name)) +static JSValue constructVersions(VM& vm, JSObject* processObject) { - JSC::JSObject* thisObject = JSValue::decode(thisValue).getObject(); - EncodedJSValue ret = Bun__Process__getExecPath(globalObject); - - if (LIKELY(thisObject)) { - thisObject->putDirect(globalObject->vm(), name, JSValue::decode(ret), 0); - } - - return ret; -} - -void Process::finishCreation(JSC::VM& vm) -{ - Base::finishCreation(vm); - auto clientData = WebCore::clientData(vm); - auto* globalObject = reinterpret_cast(this->globalObject()); - - putDirectCustomAccessor(vm, clientData->builtinNames().pidPublicName(), - JSC::CustomGetterSetter::create(vm, Process_getPID, nullptr), - static_cast(JSC::PropertyAttribute::CustomValue)); - - putDirectCustomAccessor(vm, clientData->builtinNames().ppidPublicName(), - JSC::CustomGetterSetter::create(vm, Process_getPPID, nullptr), - static_cast(JSC::PropertyAttribute::CustomValue)); - - putDirectCustomAccessor(vm, JSC::Identifier::fromString(vm, "title"_s), - JSC::CustomGetterSetter::create(vm, Process_getTitle, Process_setTitle), - static_cast(JSC::PropertyAttribute::CustomValue)); - - putDirectCustomAccessor(vm, clientData->builtinNames().argvPublicName(), - JSC::CustomGetterSetter::create(vm, Process_getArgv, Process_setArgv), - static_cast(JSC::PropertyAttribute::CustomValue)); - - putDirect(vm, JSC::Identifier::fromString(vm, "revision"_s), - JSC::jsString(vm, makeAtomString(Bun__version_sha)), 0); - - this->putDirect(vm, clientData->builtinNames().nextTickPublicName(), - JSC::JSFunction::create(vm, globalObject, 1, - MAKE_STATIC_STRING_IMPL("nextTick"), Process_functionNextTick, ImplementationVisibility::Public), - PropertyAttribute::Function | 0); - - this->putDirect(vm, JSC::Identifier::fromString(vm, "dlopen"_s), - JSC::JSFunction::create(vm, globalObject, 1, - MAKE_STATIC_STRING_IMPL("dlopen"), Process_functionDlopen, ImplementationVisibility::Public), - PropertyAttribute::Function | 0); - - this->putDirect(vm, clientData->builtinNames().cwdPublicName(), - JSC::JSFunction::create(vm, globalObject, 0, - MAKE_STATIC_STRING_IMPL("cwd"), Process_functionCwd, ImplementationVisibility::Public), - PropertyAttribute::Function | 0); - - this->putDirect(vm, clientData->builtinNames().chdirPublicName(), - JSC::JSFunction::create(vm, globalObject, 0, - MAKE_STATIC_STRING_IMPL("chdir"), Process_functionChdir, ImplementationVisibility::Public), - PropertyAttribute::Function | 0); - - this->putDirect(vm, JSC::Identifier::fromString(vm, "exit"_s), - JSC::JSFunction::create(vm, globalObject, 0, - MAKE_STATIC_STRING_IMPL("exit"), Process_functionExit, ImplementationVisibility::Public), - PropertyAttribute::Function | 0); - - putDirectCustomAccessor( - vm, clientData->builtinNames().versionsPublicName(), - JSC::CustomGetterSetter::create(vm, Process_getVersionsLazy, Process_setVersionsLazy), 0); - // this should be transpiled out, but just incase - this->putDirect(vm, JSC::Identifier::fromString(vm, "browser"_s), - JSC::JSValue(false), PropertyAttribute::DontEnum | 0); - - this->putDirectCustomAccessor(vm, JSC::Identifier::fromString(vm, "exitCode"_s), - JSC::CustomGetterSetter::create(vm, - Process__getExitCode, - Process__setExitCode), - 0); - - 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(vm, JSC::Identifier::fromString(vm, "isBun"_s), JSC::JSValue(true)); -#if defined(__APPLE__) - this->putDirect(vm, JSC::Identifier::fromString(vm, "platform"_s), - JSC::jsString(vm, makeAtomString("darwin"))); -#else - this->putDirect(vm, JSC::Identifier::fromString(vm, "platform"_s), - JSC::jsString(vm, makeAtomString("linux"))); -#endif - -#if defined(__x86_64__) - this->putDirect(vm, JSC::Identifier::fromString(vm, "arch"_s), - JSC::jsString(vm, makeAtomString("x64"))); -#elif defined(__i386__) - this->putDirect(vm, JSC::Identifier::fromString(vm, "arch"_s), - JSC::jsString(vm, makeAtomString("x86"))); -#elif defined(__arm__) - this->putDirect(vm, JSC::Identifier::fromString(vm, "arch"_s), - JSC::jsString(vm, makeAtomString("arm"))); -#elif defined(__aarch64__) - this->putDirect(vm, JSC::Identifier::fromString(vm, "arch"_s), - JSC::jsString(vm, makeAtomString("arm64"))); -#endif - - JSC::JSFunction* hrtime = JSC::JSFunction::create(vm, globalObject, 0, - MAKE_STATIC_STRING_IMPL("hrtime"), Process_functionHRTime, ImplementationVisibility::Public); - - JSC::JSFunction* hrtimeBigInt = JSC::JSFunction::create(vm, globalObject, 0, - MAKE_STATIC_STRING_IMPL("bigint"), Process_functionHRTimeBigInt, ImplementationVisibility::Public); - - hrtime->putDirect(vm, JSC::Identifier::fromString(vm, "bigint"_s), hrtimeBigInt); - 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); - - this->putDirectCustomAccessor(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "stdout"_s)), - JSC::CustomGetterSetter::create(vm, Process_lazyStdoutGetter, Process_defaultSetter), 0); - - this->putDirectCustomAccessor(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "stderr"_s)), - JSC::CustomGetterSetter::create(vm, Process_lazyStderrGetter, Process_defaultSetter), 0); - - 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(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)), - JSC::CustomGetterSetter::create(vm, Process_lazyArgv0Getter, Process_defaultSetter), 0); - - this->putDirectCustomAccessor(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "execPath"_s)), - JSC::CustomGetterSetter::create(vm, Process_lazyExecPathGetter, Process_defaultSetter), 0); - - this->putDirectCustomAccessor(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "execArgv"_s)), - JSC::CustomGetterSetter::create(vm, Process_lazyExecArgvGetter, Process_defaultSetter), 0); + auto* globalObject = processObject->globalObject(); + JSC::JSObject* object = JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), 19); - this->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "uptime"_s), - 0, Process_functionUptime, ImplementationVisibility::Public, NoIntrinsic, 0); + object->putDirect(vm, JSC::Identifier::fromString(vm, "node"_s), + JSC::JSValue(JSC::jsOwnedString(vm, makeAtomString(REPORTED_NODE_VERSION)))); + object->putDirect( + vm, JSC::Identifier::fromString(vm, "bun"_s), + JSC::JSValue(JSC::jsOwnedString(vm, makeAtomString(Bun__version + 1 /* prefix with v */)))); + object->putDirect(vm, JSC::Identifier::fromString(vm, "webkit"_s), + JSC::JSValue(JSC::jsOwnedString(vm, makeAtomString(BUN_WEBKIT_VERSION)))); + object->putDirect(vm, JSC::Identifier::fromString(vm, "boringssl"_s), + JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_boringssl))), 0); + object->putDirect(vm, JSC::Identifier::fromString(vm, "libarchive"_s), + JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_libarchive))), 0); + object->putDirect(vm, JSC::Identifier::fromString(vm, "mimalloc"_s), + JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_mimalloc))), 0); + object->putDirect(vm, JSC::Identifier::fromString(vm, "picohttpparser"_s), + JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_picohttpparser))), 0); + object->putDirect(vm, JSC::Identifier::fromString(vm, "uwebsockets"_s), + JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_uws))), 0); + object->putDirect(vm, JSC::Identifier::fromString(vm, "webkit"_s), + JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_webkit))), 0); + object->putDirect(vm, JSC::Identifier::fromString(vm, "zig"_s), + JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_zig))), 0); + object->putDirect(vm, JSC::Identifier::fromString(vm, "zlib"_s), + JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_zlib))), 0); + object->putDirect(vm, JSC::Identifier::fromString(vm, "tinycc"_s), + JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_tinycc))), 0); + object->putDirect(vm, JSC::Identifier::fromString(vm, "lolhtml"_s), + JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_lolhtml))), 0); + object->putDirect(vm, JSC::Identifier::fromString(vm, "ares"_s), + JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_c_ares))), 0); + object->putDirect(vm, JSC::Identifier::fromString(vm, "usockets"_s), + JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_usockets))), 0); - this->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "umask"_s), - 1, Process_functionUmask, ImplementationVisibility::Public, NoIntrinsic, 0); + object->putDirect(vm, JSC::Identifier::fromString(vm, "v8"_s), JSValue(JSC::jsString(vm, makeString("10.8.168.20-node.8"_s))), 0); + object->putDirect(vm, JSC::Identifier::fromString(vm, "uv"_s), JSValue(JSC::jsString(vm, makeString("1.44.2"_s))), 0); + object->putDirect(vm, JSC::Identifier::fromString(vm, "napi"_s), JSValue(JSC::jsString(vm, makeString("8"_s))), 0); - this->putDirectBuiltinFunction(vm, globalObject, JSC::Identifier::fromString(vm, "binding"_s), - processObjectInternalsBindingCodeGenerator(vm), - 0); + object->putDirect(vm, JSC::Identifier::fromString(vm, "modules"_s), + JSC::JSValue(JSC::jsString(vm, makeAtomString("108")))); - this->putDirect(vm, vm.propertyNames->toStringTagSymbol, jsString(vm, String("process"_s)), 0); + return object; +} +static JSValue constructProcessConfigObject(VM& vm, JSObject* processObject) +{ + auto* globalObject = processObject->globalObject(); // target_defaults: // { cflags: [], // default_configuration: 'Release', @@ -905,179 +659,455 @@ void Process::finishCreation(JSC::VM& vm) JSC::jsNumber(1), 0); config->putDirect(vm, JSC::Identifier::fromString(vm, "target_defaults"_s), JSC::constructEmptyObject(globalObject), 0); 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(vm, "emitWarning"_s), - 1, Process_emitWarning, ImplementationVisibility::Public, NoIntrinsic, 0); - - JSC::JSFunction* requireDotMainFunction = JSFunction::create( - vm, - moduleMainCodeGenerator(vm), - globalObject->globalScope()); - // https://nodejs.org/api/process.html#processmainmodule - this->putDirect( - vm, - JSC::Identifier::fromString(vm, "mainModule"_s), - JSC::GetterSetter::create(vm, globalObject, requireDotMainFunction, JSValue()), - PropertyAttribute::Builtin | PropertyAttribute::Accessor | PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum | 0); + + return config; } -const JSC::ClassInfo Process::s_info = { "Process"_s, &Base::s_info, nullptr, nullptr, - CREATE_METHOD_TABLE(Process) }; +static JSValue constructProcessReleaseObject(VM& vm, JSObject* processObject) +{ + auto* globalObject = processObject->globalObject(); + auto* release = JSC::constructEmptyObject(globalObject); + release->putDirect(vm, Identifier::fromString(vm, "name"_s), jsString(vm, WTF::String("bun"_s)), 0); + release->putDirect(vm, Identifier::fromString(vm, "lts"_s), jsBoolean(false), 0); + release->putDirect(vm, Identifier::fromString(vm, "sourceUrl"_s), jsString(vm, WTF::String(Bun__githubURL, strlen(Bun__githubURL))), 0); + release->putDirect(vm, Identifier::fromString(vm, "headersUrl"_s), jsEmptyString(vm), 0); + release->putDirect(vm, Identifier::fromString(vm, "libUrl"_s), jsEmptyString(vm), 0); + + return release; +} -JSC_DEFINE_CUSTOM_GETTER(Process_getTitle, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName)) +static JSValue constructProcessHrtimeObject(VM& vm, JSObject* processObject) { - ZigString str; - Bun__Process__getTitle(globalObject, &str); - return JSValue::encode(Zig::toJSStringValue(str, globalObject)); + auto* globalObject = processObject->globalObject(); + JSC::JSFunction* hrtime = JSC::JSFunction::create(vm, globalObject, 0, + MAKE_STATIC_STRING_IMPL("hrtime"), Process_functionHRTime, ImplementationVisibility::Public); + + JSC::JSFunction* hrtimeBigInt = JSC::JSFunction::create(vm, globalObject, 0, + MAKE_STATIC_STRING_IMPL("bigint"), Process_functionHRTimeBigInt, ImplementationVisibility::Public); + + hrtime->putDirect(vm, JSC::Identifier::fromString(vm, "bigint"_s), hrtimeBigInt); + + return hrtime; } -JSC_DEFINE_CUSTOM_SETTER(Process_setTitle, - (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, - JSC::EncodedJSValue value, JSC::PropertyName)) +static JSValue constructStdioWriteStream(JSC::JSGlobalObject* globalObject, int fd) { - JSC::VM& vm = globalObject->vm(); + auto& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + JSC::JSFunction* getStdioWriteStream = JSC::JSFunction::create(vm, processObjectInternalsGetStdioWriteStreamCodeGenerator(vm), globalObject); + JSC::MarkedArgumentBuffer args; + args.append(JSC::jsNumber(fd)); - JSC::JSObject* thisObject = JSC::jsDynamicCast(JSValue::decode(thisValue)); - JSC::JSString* jsString = JSC::jsDynamicCast(JSValue::decode(value)); - if (!thisObject || !jsString) { - return false; + auto clientData = WebCore::clientData(vm); + JSC::CallData callData = JSC::getCallData(getStdioWriteStream); + + NakedPtr returnedException = nullptr; + auto result = JSC::call(globalObject, getStdioWriteStream, callData, globalObject->globalThis(), args, returnedException); + RETURN_IF_EXCEPTION(scope, {}); + + if (returnedException) { + throwException(globalObject, scope, returnedException.get()); + return {}; } - ZigString str = Zig::toZigString(jsString, globalObject); - Bun__Process__setTitle(globalObject, &str); + return result; +} - return true; +static JSValue constructStdout(VM& vm, JSObject* processObject) +{ + auto* globalObject = Bun__getDefaultGlobal(); + return constructStdioWriteStream(globalObject, 1); } -JSC_DEFINE_CUSTOM_GETTER(Process_getArgv, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName)) +static JSValue constructStderr(VM& vm, JSObject* processObject) { - JSC::VM& vm = globalObject->vm(); + auto* globalObject = Bun__getDefaultGlobal(); + return constructStdioWriteStream(globalObject, 2); +} - Zig::Process* thisObject = JSC::jsDynamicCast(JSValue::decode(thisValue)); - if (!thisObject) { - return JSValue::encode(JSC::jsUndefined()); - } +static JSValue constructStdin(VM& vm, JSObject* processObject) +{ + auto* globalObject = Bun__getDefaultGlobal(); + auto scope = DECLARE_THROW_SCOPE(vm); + auto* thisObject = reinterpret_cast(globalObject); + JSC::JSFunction* getStdioWriteStream = JSC::JSFunction::create(vm, processObjectInternalsGetStdinStreamCodeGenerator(vm), globalObject); + JSC::MarkedArgumentBuffer args; + args.append(JSC::jsNumber(STDIN_FILENO)); - JSC::EncodedJSValue argv_ = Bun__Process__getArgv(globalObject); auto clientData = WebCore::clientData(vm); + JSC::CallData callData = JSC::getCallData(getStdioWriteStream); + + NakedPtr returnedException = nullptr; + auto result = JSC::call(globalObject, getStdioWriteStream, callData, globalObject, args, returnedException); + RETURN_IF_EXCEPTION(scope, {}); - thisObject->putDirect(vm, clientData->builtinNames().argvPublicName(), - JSC::JSValue::decode(argv_), 0); + if (UNLIKELY(returnedException)) { + throwException(globalObject, scope, returnedException.get()); + return {}; + } - return argv_; + RELEASE_AND_RETURN(scope, result); } -JSC_DEFINE_CUSTOM_SETTER(Process_setArgv, - (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, - JSC::EncodedJSValue value, JSC::PropertyName)) +static JSValue constructPid(VM& vm, JSObject* processObject) { - JSC::VM& vm = globalObject->vm(); + return jsNumber(getpid()); +} - JSC::JSObject* thisObject = JSC::jsDynamicCast(JSValue::decode(thisValue)); - if (!thisObject) { - return false; - } +static JSValue constructPpid(VM& vm, JSObject* processObject) +{ + return jsNumber(getppid()); +} - auto clientData = WebCore::clientData(vm); +static JSValue constructArgv0(VM& vm, JSObject* processObject) +{ + auto* globalObject = processObject->globalObject(); + return JSValue::decode(Bun__Process__getArgv0(globalObject)); +} - return thisObject->putDirect(vm, clientData->builtinNames().argvPublicName(), - JSC::JSValue::decode(value)); +static JSValue constructExecArgv(VM& vm, JSObject* processObject) +{ + auto* globalObject = processObject->globalObject(); + return JSValue::decode(Bun__Process__getExecArgv(globalObject)); } -JSC_DEFINE_CUSTOM_GETTER(Process_getPID, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName)) +static JSValue constructExecPath(VM& vm, JSObject* processObject) { - return JSC::JSValue::encode(JSC::JSValue(getpid())); + auto* globalObject = processObject->globalObject(); + return JSValue::decode(Bun__Process__getExecPath(globalObject)); } -JSC_DEFINE_CUSTOM_GETTER(Process_getPPID, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName)) +static JSValue constructArgv(VM& vm, JSObject* processObject) { - return JSC::JSValue::encode(JSC::JSValue(getppid())); + auto* globalObject = processObject->globalObject(); + return JSValue::decode(Bun__Process__getArgv(globalObject)); } -#if !defined(BUN_WEBKIT_VERSION) -#define BUN_WEBKIT_VERSION "unknown" +static JSValue constructArch(VM& vm, JSObject* processObject) +{ +#if defined(__x86_64__) + return JSC::jsString(vm, makeAtomString("x64")); +#elif defined(__i386__) + return JSC::jsString(vm, makeAtomString("x86")); +#elif defined(__arm__) + return JSC::jsString(vm, makeAtomString("arm")); +#elif defined(__aarch64__) + return JSC::jsString(vm, makeAtomString("arm64")); +#else +#error "Unknown architecture" #endif +} -JSC_DEFINE_CUSTOM_GETTER(Process_getVersionsLazy, - (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, - JSC::PropertyName)) +static JSValue constructPlatform(VM& vm, JSObject* processObject) { - JSC::VM& vm = globalObject->vm(); - auto clientData = WebCore::clientData(vm); +#if defined(__APPLE__) + return JSC::jsString(vm, makeAtomString("darwin")); +#elif defined(__linux__) + return JSC::jsString(vm, makeAtomString("linux")); +#else +#error "Unknown platform" +#endif +} - Zig::Process* thisObject = JSC::jsDynamicCast(JSValue::decode(thisValue)); - if (!thisObject) { - return JSValue::encode(JSC::jsUndefined()); +static JSValue constructBrowser(VM& vm, JSObject* processObject) +{ + return jsBoolean(false); +} + +static JSValue constructVersion(VM& vm, JSObject* processObject) +{ + return JSC::jsString(vm, makeString("v", REPORTED_NODE_VERSION)); +} + +static JSValue constructIsBun(VM& vm, JSObject* processObject) +{ + return jsBoolean(true); +} + +static JSValue constructRevision(VM& vm, JSObject* processObject) +{ + return JSC::jsString(vm, makeAtomString(Bun__version_sha)); +} + +static JSValue constructEnv(VM& vm, JSObject* processObject) +{ + auto* globalObject = jsCast(processObject->globalObject()); + return globalObject->processEnvObject(); +} + +JSC_DEFINE_HOST_FUNCTION(Process_functiongetuid, (JSGlobalObject * globalObject, CallFrame* callFrame)) +{ + return JSValue::encode(jsNumber(getuid())); +} + +JSC_DEFINE_HOST_FUNCTION(Process_functiongeteuid, (JSGlobalObject * globalObject, CallFrame* callFrame)) +{ + return JSValue::encode(jsNumber(geteuid())); +} + +JSC_DEFINE_HOST_FUNCTION(Process_functiongetegid, (JSGlobalObject * globalObject, CallFrame* callFrame)) +{ + return JSValue::encode(jsNumber(getegid())); +} + +JSC_DEFINE_HOST_FUNCTION(Process_functiongetgid, (JSGlobalObject * globalObject, CallFrame* callFrame)) +{ + return JSValue::encode(jsNumber(getgid())); +} + +JSC_DEFINE_HOST_FUNCTION(Process_functiongetgroups, (JSGlobalObject * globalObject, CallFrame* callFrame)) +{ + auto& vm = globalObject->vm(); + int ngroups = getgroups(0, nullptr); + auto throwScope = DECLARE_THROW_SCOPE(vm); + if (ngroups == -1) { + SystemError error; + error.errno_ = errno; + error.syscall = Bun::toString("getgroups"_s); + throwException(globalObject, throwScope, JSValue::decode(SystemError__toErrorInstance(&error, globalObject))); + return JSValue::encode(jsUndefined()); } - auto scope = DECLARE_THROW_SCOPE(vm); - JSC::JSObject* object = JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), 19); + gid_t egid = getegid(); + JSArray* groups = constructEmptyArray(globalObject, nullptr, static_cast(ngroups)); + Vector groupVector(ngroups); + getgroups(1, &egid); + bool needsEgid = true; + for (unsigned i = 0; i < ngroups; i++) { + auto current = groupVector[i]; + if (current == needsEgid) { + needsEgid = false; + } - object->putDirect(vm, JSC::Identifier::fromString(vm, "node"_s), - JSC::JSValue(JSC::jsOwnedString(vm, makeAtomString(REPORTED_NODE_VERSION)))); - object->putDirect( - vm, JSC::Identifier::fromString(vm, "bun"_s), - JSC::JSValue(JSC::jsOwnedString(vm, makeAtomString(Bun__version + 1 /* prefix with v */)))); - object->putDirect(vm, JSC::Identifier::fromString(vm, "webkit"_s), - JSC::JSValue(JSC::jsOwnedString(vm, makeAtomString(BUN_WEBKIT_VERSION)))); - object->putDirect(vm, JSC::Identifier::fromString(vm, "boringssl"_s), - JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_boringssl))), 0); - object->putDirect(vm, JSC::Identifier::fromString(vm, "libarchive"_s), - JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_libarchive))), 0); - object->putDirect(vm, JSC::Identifier::fromString(vm, "mimalloc"_s), - JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_mimalloc))), 0); - object->putDirect(vm, JSC::Identifier::fromString(vm, "picohttpparser"_s), - JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_picohttpparser))), 0); - object->putDirect(vm, JSC::Identifier::fromString(vm, "uwebsockets"_s), - JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_uws))), 0); - object->putDirect(vm, JSC::Identifier::fromString(vm, "webkit"_s), - JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_webkit))), 0); - object->putDirect(vm, JSC::Identifier::fromString(vm, "zig"_s), - JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_zig))), 0); - object->putDirect(vm, JSC::Identifier::fromString(vm, "zlib"_s), - JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_zlib))), 0); - object->putDirect(vm, JSC::Identifier::fromString(vm, "tinycc"_s), - JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_tinycc))), 0); - object->putDirect(vm, JSC::Identifier::fromString(vm, "lolhtml"_s), - JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_lolhtml))), 0); - object->putDirect(vm, JSC::Identifier::fromString(vm, "ares"_s), - JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_c_ares))), 0); - object->putDirect(vm, JSC::Identifier::fromString(vm, "usockets"_s), - JSC::JSValue(JSC::jsString(vm, makeString(Bun__versions_usockets))), 0); + groups->putDirectIndex(globalObject, i, jsNumber(current)); + } - object->putDirect(vm, JSC::Identifier::fromString(vm, "v8"_s), JSValue(JSC::jsString(vm, makeString("10.8.168.20-node.8"_s))), 0); - object->putDirect(vm, JSC::Identifier::fromString(vm, "uv"_s), JSValue(JSC::jsString(vm, makeString("1.44.2"_s))), 0); - object->putDirect(vm, JSC::Identifier::fromString(vm, "napi"_s), JSValue(JSC::jsString(vm, makeString("8"_s))), 0); + if (needsEgid) + groups->push(globalObject, jsNumber(egid)); - object->putDirect(vm, JSC::Identifier::fromString(vm, "modules"_s), - JSC::JSValue(JSC::jsOwnedString(vm, makeAtomString("108")))); + return JSValue::encode(groups); +} - thisObject->putDirect(vm, clientData->builtinNames().versionsPublicName(), object, 0); +JSC_DEFINE_HOST_FUNCTION(Process_functionAssert, (JSGlobalObject * globalObject, CallFrame* callFrame)) +{ + auto& vm = globalObject->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); - RETURN_IF_EXCEPTION(scope, {}); + JSValue arg0 = callFrame->argument(0); + bool condition = arg0.toBoolean(globalObject); + RETURN_IF_EXCEPTION(throwScope, JSValue::encode(jsUndefined())); + if (condition) { + return JSValue::encode(jsUndefined()); + } - return JSValue::encode(object); + JSValue arg1 = callFrame->argument(1); + String message = arg1.isUndefined() ? String() : arg1.toWTFString(globalObject); + RETURN_IF_EXCEPTION(throwScope, JSValue::encode(jsUndefined())); + auto error = createError(globalObject, makeString("Assertion failed: "_s, message)); + error->putDirect(vm, Identifier::fromString(vm, "code"_s), jsString(vm, makeString("ERR_ASSERTION"_s))); + throwException(globalObject, throwScope, error); + return JSValue::encode(jsUndefined()); } -JSC_DEFINE_CUSTOM_SETTER(Process_setVersionsLazy, + +JSC_DEFINE_HOST_FUNCTION(Process_functionReallyExit, (JSGlobalObject * globalObject, CallFrame* callFrame)) +{ + auto& vm = globalObject->vm(); + auto throwScope = DECLARE_THROW_SCOPE(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(extiCode32); + } else if (!arg0.isUndefinedOrNull()) { + throwTypeError(globalObject, throwScope, "The \"code\" argument must be an integer"_s); + return JSC::JSValue::encode(JSC::JSValue {}); + } else { + exitCode = Bun__getExitCode(Bun__getVM()); + } + + auto* zigGlobal = jsDynamicCast(globalObject); + if (UNLIKELY(!zigGlobal)) { + zigGlobal = Bun__getDefaultGlobal(); + } + Bun__Process__exit(zigGlobal, exitCode); + __builtin_unreachable(); +} + +JSC_DEFINE_HOST_FUNCTION(Process_functionOpenStdin, (JSGlobalObject * globalObject, CallFrame* callFrame)) +{ + auto& vm = globalObject->vm(); + Zig::GlobalObject* global = jsDynamicCast(globalObject); + if (UNLIKELY(!global)) { + global = Bun__getDefaultGlobal(); + } + auto throwScope = DECLARE_THROW_SCOPE(vm); + + if (JSValue stdin = global->processObject()->getIfPropertyExists(globalObject, Identifier::fromString(vm, "stdin"_s))) { + RETURN_IF_EXCEPTION(throwScope, JSValue::encode(jsUndefined())); + + if (!stdin.isObject()) { + throwTypeError(globalObject, throwScope, "stdin is not an object"_s); + return JSValue::encode(jsUndefined()); + } + + JSValue resumeValue = stdin.getObject()->getIfPropertyExists(globalObject, Identifier::fromString(vm, "resume"_s)); + RETURN_IF_EXCEPTION(throwScope, JSValue::encode(jsUndefined())); + if (!resumeValue.isUndefinedOrNull()) { + auto resumeFunction = jsDynamicCast(resumeValue); + if (UNLIKELY(!resumeFunction)) { + throwTypeError(globalObject, throwScope, "stdin.resume is not a function"_s); + return JSValue::encode(jsUndefined()); + } + + auto callData = getCallData(resumeFunction); + + MarkedArgumentBuffer args; + JSC::call(globalObject, resumeFunction, callData, stdin, args); + RETURN_IF_EXCEPTION(throwScope, JSValue::encode(jsUndefined())); + } + + RELEASE_AND_RETURN(throwScope, JSValue::encode(stdin)); + } + + RELEASE_AND_RETURN(throwScope, JSValue::encode(jsUndefined())); +} + +JSC_DEFINE_HOST_FUNCTION(Process_stubEmptyFunction, (JSGlobalObject * globalObject, CallFrame* callFrame)) +{ + return JSValue::encode(jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(Process_stubFunctionReturningArray, (JSGlobalObject * globalObject, CallFrame* callFrame)) +{ + return JSValue::encode(JSC::constructEmptyArray(globalObject, nullptr)); +} + +static JSValue Process_stubEmptyObject(VM& vm, JSObject* processObject) +{ + return JSC::constructEmptyObject(processObject->globalObject()); +} + +static JSValue Process_stubEmptyArray(VM& vm, JSObject* processObject) +{ + return JSC::constructEmptyArray(processObject->globalObject(), nullptr); +} + +static JSValue Process_stubEmptySet(VM& vm, JSObject* processObject) +{ + auto* globalObject = processObject->globalObject(); + return JSSet::create(vm, globalObject->setStructure()); +} + +static JSValue constructFeatures(VM& vm, JSObject* processObject) +{ + // { + // inspector: true, + // debug: false, + // uv: true, + // ipv6: true, + // tls_alpn: true, + // tls_sni: true, + // tls_ocsp: true, + // tls: true, + // cached_builtins: [Getter] + // } + auto* globalObject = processObject->globalObject(); + auto* object = constructEmptyObject(globalObject); + + object->putDirect(vm, Identifier::fromString(vm, "inspector"_s), jsBoolean(true)); +#ifdef BUN_DEBUG + object->putDirect(vm, Identifier::fromString(vm, "debug"_s), jsBoolean(true)); +#else + object->putDirect(vm, Identifier::fromString(vm, "debug"_s), jsBoolean(false)); +#endif + // lying + object->putDirect(vm, Identifier::fromString(vm, "uv"_s), jsBoolean(true)); + + object->putDirect(vm, Identifier::fromString(vm, "ipv6"_s), jsBoolean(true)); + object->putDirect(vm, Identifier::fromString(vm, "tls_alpn"_s), jsBoolean(true)); + object->putDirect(vm, Identifier::fromString(vm, "tls_sni"_s), jsBoolean(true)); + object->putDirect(vm, Identifier::fromString(vm, "tls_ocsp"_s), jsBoolean(true)); + object->putDirect(vm, Identifier::fromString(vm, "tls"_s), jsBoolean(true)); + object->putDirect(vm, Identifier::fromString(vm, "cached_builtins"_s), jsBoolean(true)); + + return object; +} + +static int _debugPort; + +JSC_DEFINE_CUSTOM_GETTER(processDebugPort, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName)) +{ + if (_debugPort == 0) { + _debugPort = 9229; + } + + return JSC::JSValue::encode(jsNumber(_debugPort)); +} + +JSC_DEFINE_CUSTOM_SETTER(setProcessDebugPort, + (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, + JSC::EncodedJSValue encodedValue, JSC::PropertyName)) +{ + auto& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + JSValue value = JSValue::decode(encodedValue); + + if (!value.isInt32()) { + throwRangeError(globalObject, scope, "debugPort must be 0 or in range 1024 to 65535"_s); + return false; + } + + int port = value.asInt32(); + + if (port != 0) { + if (port < 1024 || port > 65535) { + throwRangeError(globalObject, scope, "debugPort must be 0 or in range 1024 to 65535"_s); + return false; + } + } + + _debugPort = port; + return true; +} + +JSC_DEFINE_CUSTOM_GETTER(processTitle, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName)) +{ + ZigString str; + Bun__Process__getTitle(globalObject, &str); + return JSValue::encode(Zig::toJSStringValue(str, globalObject)); +} + +JSC_DEFINE_CUSTOM_SETTER(setProcessTitle, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue value, JSC::PropertyName)) { JSC::VM& vm = globalObject->vm(); - auto clientData = WebCore::clientData(vm); - Zig::Process* thisObject = JSC::jsDynamicCast(JSValue::decode(thisValue)); - if (!thisObject) { - return JSValue::encode(JSC::jsUndefined()); + JSC::JSObject* thisObject = JSC::jsDynamicCast(JSValue::decode(thisValue)); + JSC::JSString* jsString = JSC::jsDynamicCast(JSValue::decode(value)); + if (!thisObject || !jsString) { + return false; } - thisObject->putDirect(vm, clientData->builtinNames().versionsPublicName(), - JSC::JSValue::decode(value), 0); + ZigString str = Zig::toZigString(jsString, globalObject); + Bun__Process__setTitle(globalObject, &str); return true; } -static JSC_DEFINE_HOST_FUNCTION(Process_functionCwd, +JSC_DEFINE_HOST_FUNCTION(Process_functionCwd, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) { auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); @@ -1091,4 +1121,78 @@ static JSC_DEFINE_HOST_FUNCTION(Process_functionCwd, return JSC::JSValue::encode(result); } +/* Source for Process.lut.h +@begin processObjectTable + abort Process_functionAbort Function 1 + allowedNodeEnvironmentFlags Process_stubEmptySet PropertyCallback + arch constructArch PropertyCallback + argv constructArgv PropertyCallback + argv0 constructArgv0 PropertyCallback + assert Process_functionAssert Function 1 + binding JSBuiltin Function 1 + browser constructBrowser PropertyCallback + chdir Process_functionChdir Function 1 + config constructProcessConfigObject PropertyCallback + debugPort processDebugPort CustomAccessor + exitCode processExitCode CustomAccessor + title processTitle CustomAccessor + cwd Process_functionCwd Function 1 + dlopen Process_functionDlopen Function 1 + emitWarning Process_emitWarning Function 1 + env constructEnv PropertyCallback + execArgv constructExecArgv PropertyCallback + execPath constructExecPath PropertyCallback + exit Process_functionExit Function 1 + features constructFeatures PropertyCallback + getActiveResourcesInfo Process_stubFunctionReturningArray Function 0 + getegid Process_functiongetegid Function 0 + geteuid Process_functiongeteuid Function 0 + getgid Process_functiongetgid Function 0 + getgroups Process_functiongetgroups Function 0 + getuid Process_functiongetuid Function 0 + hrtime constructProcessHrtimeObject PropertyCallback + isBun constructIsBun PropertyCallback + mainModule JSBuiltin ReadOnly|Builtin|Accessor|Function 0 + moduleLoadList Process_stubEmptyArray PropertyCallback + nextTick Process_functionNextTick Function 1 + openStdin Process_functionOpenStdin Function 0 + pid constructPid PropertyCallback + platform constructPlatform PropertyCallback + ppid constructPpid PropertyCallback + reallyExit Process_functionReallyExit Function 1 + release constructProcessReleaseObject PropertyCallback + revision constructRevision PropertyCallback + setSourceMapsEnabled Process_stubEmptyFunction Function 1 + stderr constructStderr PropertyCallback + stdin constructStdin PropertyCallback + stdout constructStdout PropertyCallback + umask Process_functionUmask Function 1 + uptime Process_functionUptime Function 1 + version constructVersion PropertyCallback + versions constructVersions PropertyCallback + _debugEnd Process_stubEmptyFunction Function 0 + _debugProcess Process_stubEmptyFunction Function 0 + _fatalException Process_stubEmptyFunction Function 1 + _getActiveRequests Process_stubFunctionReturningArray Function 0 + _getActiveHandles Process_stubFunctionReturningArray Function 0 + _linkedBinding Process_stubEmptyFunction Function 0 + _preload_modules Process_stubEmptyObject PropertyCallback + _rawDebug Process_stubEmptyFunction Function 0 + _startProfilerIdleNotifier Process_stubEmptyFunction Function 0 + _stopProfilerIdleNotifier Process_stubEmptyFunction Function 0 + _tickCallback Process_stubEmptyFunction Function 0 +@end +*/ + +#include "Process.lut.h" +const JSC::ClassInfo Process::s_info = { "Process"_s, &Base::s_info, &processObjectTable, nullptr, + CREATE_METHOD_TABLE(Process) }; + +void Process::finishCreation(JSC::VM& vm) +{ + Base::finishCreation(vm); + + this->putDirect(vm, vm.propertyNames->toStringTagSymbol, jsString(vm, String("process"_s)), 0); +} + } // namespace Zig -- cgit v1.2.3 From 2f5e4fffe9554fcc7afa6980b3af6b33bc3a3a5e Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Sun, 9 Jul 2023 21:50:19 -0700 Subject: Implement process.memoryUsage() and process.cpuUsage() (#3586) * Implement process.memoryUsage() and process.cpuUsage() * Avoid mi_process_info * Update bench * Update Process.cpp * fixup * More tests + linux fixup * Skip it for now since it seems less accurate --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> --- bench/snippets/process-info.mjs | 33 ++ src/bun.js/bindings/Process.cpp | 388 ++++++++++++++++++++- src/bun.js/bindings/Process.h | 18 + src/bun.js/bindings/Process.lut.h | 97 +++--- .../bindings/webcore/DOMClientIsoSubspaces.h | 1 + src/bun.js/bindings/webcore/DOMIsoSubspaces.h | 1 + test/js/node/process/process.test.js | 142 +++++++- 7 files changed, 620 insertions(+), 60 deletions(-) create mode 100644 bench/snippets/process-info.mjs (limited to 'src/bun.js/bindings/Process.cpp') diff --git a/bench/snippets/process-info.mjs b/bench/snippets/process-info.mjs new file mode 100644 index 000000000..0366472e5 --- /dev/null +++ b/bench/snippets/process-info.mjs @@ -0,0 +1,33 @@ +import { bench, run } from "./runner.mjs"; +import { performance } from "perf_hooks"; + +bench("process.memoryUsage()", () => { + process.memoryUsage(); +}); + +bench("process.memoryUsage.rss()", () => { + process.memoryUsage.rss(); +}); + +bench("process.cpuUsage()", () => { + process.cpuUsage(); +}); + +const init = process.cpuUsage(); +bench("process.cpuUsage(delta)", () => { + process.cpuUsage(init); +}); + +bench("performance.now()", () => { + performance.now(); +}); + +bench("process.hrtime()", () => { + process.hrtime(); +}); + +bench("process.hrtime.bigint()", () => { + process.hrtime.bigint(); +}); + +await run(); diff --git a/src/bun.js/bindings/Process.cpp b/src/bun.js/bindings/Process.cpp index 8d94594cc..7d7bdd982 100644 --- a/src/bun.js/bindings/Process.cpp +++ b/src/bun.js/bindings/Process.cpp @@ -12,8 +12,24 @@ #include "ZigConsoleClient.h" #include #include +#include +#include +#include + #pragma mark - Node.js Process +#if defined(__APPLE__) +#include +#include +#endif + +#if defined(__linux__) +#include +#include +#include +#include +#endif + #if !defined(_MSC_VER) #include // setuid, getuid #endif @@ -335,9 +351,12 @@ extern "C" uint64_t Bun__readOriginTimer(void*); JSC_DEFINE_HOST_FUNCTION(Process_functionHRTime, (JSC::JSGlobalObject * globalObject_, JSC::CallFrame* callFrame)) { + Zig::GlobalObject* globalObject = reinterpret_cast(globalObject_); auto& vm = globalObject->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + uint64_t time = Bun__readOriginTimer(globalObject->bunVM()); int64_t seconds = static_cast(time / 1000000000); int64_t nanoseconds = time % 1000000000; @@ -346,7 +365,6 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionHRTime, JSC::JSValue arg0 = callFrame->uncheckedArgument(0); if (!arg0.isUndefinedOrNull()) { JSArray* relativeArray = JSC::jsDynamicCast(arg0); - auto throwScope = DECLARE_THROW_SCOPE(vm); if ((!relativeArray && !arg0.isUndefinedOrNull()) || relativeArray->length() < 2) { JSC::throwTypeError(globalObject, throwScope, "hrtime() argument must be an array or undefined"_s); return JSC::JSValue::encode(JSC::JSValue {}); @@ -366,14 +384,28 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionHRTime, seconds--; nanoseconds += 1000000000; } - throwScope.release(); } } - auto* array = JSArray::create(vm, globalObject->originalArrayStructureForIndexingType(ArrayWithContiguous), 2); - array->setIndexQuickly(vm, 0, JSC::jsNumber(seconds)); - array->setIndexQuickly(vm, 1, JSC::jsNumber(nanoseconds)); - return JSC::JSValue::encode(JSC::JSValue(array)); + JSC::JSArray* array = nullptr; + { + JSC::ObjectInitializationScope initializationScope(vm); + if ((array = JSC::JSArray::tryCreateUninitializedRestricted( + initializationScope, nullptr, + globalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous), + 2))) { + + array->initializeIndex(initializationScope, 0, JSC::jsNumber(seconds)); + array->initializeIndex(initializationScope, 1, JSC::jsNumber(nanoseconds)); + } + } + + if (UNLIKELY(!array)) { + JSC::throwOutOfMemoryError(globalObject, throwScope); + return JSC::JSValue::encode(JSC::JSValue {}); + } + + RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(array)); } JSC_DEFINE_HOST_FUNCTION(Process_functionHRTimeBigInt, @@ -680,10 +712,10 @@ static JSValue constructProcessHrtimeObject(VM& vm, JSObject* processObject) { auto* globalObject = processObject->globalObject(); JSC::JSFunction* hrtime = JSC::JSFunction::create(vm, globalObject, 0, - MAKE_STATIC_STRING_IMPL("hrtime"), Process_functionHRTime, ImplementationVisibility::Public); + String("hrtime"_s), Process_functionHRTime, ImplementationVisibility::Public); JSC::JSFunction* hrtimeBigInt = JSC::JSFunction::create(vm, globalObject, 0, - MAKE_STATIC_STRING_IMPL("bigint"), Process_functionHRTimeBigInt, ImplementationVisibility::Public); + String("bigint"_s), Process_functionHRTimeBigInt, ImplementationVisibility::Public); hrtime->putDirect(vm, JSC::Identifier::fromString(vm, "bigint"_s), hrtimeBigInt); @@ -945,6 +977,317 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionReallyExit, (JSGlobalObject * globalObj __builtin_unreachable(); } +template +void Process::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + Process* thisObject = jsCast(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + thisObject->cpuUsageStructure.visit(visitor); + thisObject->memoryUsageStructure.visit(visitor); +} + +DEFINE_VISIT_CHILDREN(Process); + +static Structure* constructCPUUsageStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject) +{ + JSC::Structure* structure = globalObject->structureCache().emptyObjectStructureForPrototype(globalObject, globalObject->objectPrototype(), 2); + PropertyOffset offset; + structure = structure->addPropertyTransition( + vm, + structure, + JSC::Identifier::fromString(vm, "user"_s), + 0, + offset); + structure = structure->addPropertyTransition( + vm, + structure, + JSC::Identifier::fromString(vm, "system"_s), + 0, + offset); + return structure; +} +static Structure* constructMemoryUsageStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject) +{ + JSC::Structure* structure = globalObject->structureCache().emptyObjectStructureForPrototype(globalObject, globalObject->objectPrototype(), 5); + PropertyOffset offset; + structure = structure->addPropertyTransition( + vm, + structure, + JSC::Identifier::fromString(vm, "rss"_s), + 0, + offset); + structure = structure->addPropertyTransition( + vm, + structure, + JSC::Identifier::fromString(vm, "heapTotal"_s), + 0, + offset); + structure = structure->addPropertyTransition( + vm, + structure, + JSC::Identifier::fromString(vm, "heapUsed"_s), + 0, + offset); + structure = structure->addPropertyTransition( + vm, + structure, + JSC::Identifier::fromString(vm, "external"_s), + 0, + offset); + structure = structure->addPropertyTransition( + vm, + structure, + JSC::Identifier::fromString(vm, "arrayBuffers"_s), + 0, + offset); + + return structure; +} + +static Process* getProcessObject(JSC::JSGlobalObject* lexicalGlobalObject, JSValue thisValue) +{ + Process* process = jsDynamicCast(thisValue); + + // Handle "var memoryUsage = process.memoryUsage; memoryUsage()" + if (UNLIKELY(!process)) { + // Handle calling this function from inside a node:vm + Zig::GlobalObject* zigGlobalObject = jsDynamicCast(lexicalGlobalObject); + + if (UNLIKELY(!zigGlobalObject)) { + zigGlobalObject = Bun__getDefaultGlobal(); + } + + return jsCast(zigGlobalObject->processObject()); + } + + return process; +} + +JSC_DEFINE_HOST_FUNCTION(Process_functionCpuUsage, + (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + struct rusage rusage; + if (getrusage(RUSAGE_SELF, &rusage) != 0) { + SystemError error; + error.errno_ = errno; + error.syscall = Bun::toString("getrusage"_s); + error.message = Bun::toString("Failed to get CPU usage"_s); + throwException(globalObject, throwScope, JSValue::decode(SystemError__toErrorInstance(&error, globalObject))); + return JSValue::encode(jsUndefined()); + } + + auto* process = getProcessObject(globalObject, callFrame->thisValue()); + + Structure* cpuUsageStructure = process->cpuUsageStructure.getInitializedOnMainThread(process); + + constexpr double MICROS_PER_SEC = 1000000.0; + + double user = MICROS_PER_SEC * rusage.ru_utime.tv_sec + rusage.ru_utime.tv_usec; + double system = MICROS_PER_SEC * rusage.ru_stime.tv_sec + rusage.ru_stime.tv_usec; + + if (callFrame->argumentCount() > 0) { + JSValue comparatorValue = callFrame->argument(0); + if (!comparatorValue.isUndefined()) { + if (UNLIKELY(!comparatorValue.isObject())) { + throwTypeError(globalObject, throwScope, "Expected an object as the first argument"_s); + return JSC::JSValue::encode(JSC::jsUndefined()); + } + + JSC::JSObject* comparator = comparatorValue.getObject(); + JSValue userValue; + JSValue systemValue; + + if (LIKELY(comparator->structureID() == cpuUsageStructure->id())) { + userValue = comparator->getDirect(0); + systemValue = comparator->getDirect(1); + } else { + userValue = comparator->getIfPropertyExists(globalObject, JSC::Identifier::fromString(vm, "user"_s)); + RETURN_IF_EXCEPTION(throwScope, JSC::JSValue::encode(JSC::jsUndefined())); + + systemValue = comparator->getIfPropertyExists(globalObject, JSC::Identifier::fromString(vm, "system"_s)); + RETURN_IF_EXCEPTION(throwScope, JSC::JSValue::encode(JSC::jsUndefined())); + } + + if (UNLIKELY(!userValue || !userValue.isNumber())) { + throwTypeError(globalObject, throwScope, "Expected a number for the user property"_s); + return JSC::JSValue::encode(JSC::jsUndefined()); + } + + if (UNLIKELY(!systemValue || !systemValue.isNumber())) { + throwTypeError(globalObject, throwScope, "Expected a number for the system property"_s); + return JSC::JSValue::encode(JSC::jsUndefined()); + } + + double userComparator = userValue.asNumber(); + double systemComparator = systemValue.asNumber(); + + user -= userComparator; + system -= systemComparator; + } + } + + JSC::JSObject* result = JSC::constructEmptyObject(vm, cpuUsageStructure); + RETURN_IF_EXCEPTION(throwScope, JSC::JSValue::encode(JSC::jsUndefined())); + + result->putDirectOffset(vm, 0, JSC::jsNumber(user)); + result->putDirectOffset(vm, 1, JSC::jsNumber(system)); + + RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(result)); +} + +static int getRSS(size_t* rss) +{ +#if defined(__APPLE__) + mach_msg_type_number_t count; + task_basic_info_data_t info; + kern_return_t err; + + count = TASK_BASIC_INFO_COUNT; + err = task_info(mach_task_self(), + TASK_BASIC_INFO, + reinterpret_cast(&info), + &count); + + if (err == KERN_SUCCESS) { + *rss = (size_t)info.resident_size; + return 0; + } + + return -1; +#elif defined(__linux__) + // Taken from libuv. + char buf[1024]; + const char* s; + ssize_t n; + long val; + int fd; + int i; + + do + fd = open("/proc/self/stat", O_RDONLY); + while (fd == -1 && errno == EINTR); + + if (fd == -1) + return errno; + + do + n = read(fd, buf, sizeof(buf) - 1); + while (n == -1 && errno == EINTR); + + int closeErrno = 0; + do { + closeErrno = close(fd); + } while (closeErrno == -1 && errno == EINTR); + + if (n == -1) + return errno; + buf[n] = '\0'; + + s = strchr(buf, ' '); + if (s == NULL) + goto err; + + s += 1; + if (*s != '(') + goto err; + + s = strchr(s, ')'); + if (s == NULL) + goto err; + + for (i = 1; i <= 22; i++) { + s = strchr(s + 1, ' '); + if (s == NULL) + goto err; + } + + errno = 0; + val = strtol(s, NULL, 10); + if (errno != 0) + goto err; + if (val < 0) + goto err; + + *rss = val * getpagesize(); + return 0; + +err: + return EINVAL; +#else +#error "Unsupported platform" +#endif +} + +JSC_DEFINE_HOST_FUNCTION(Process_functionMemoryUsage, + (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto* process = getProcessObject(globalObject, callFrame->thisValue()); + + size_t current_rss = 0; + if (getRSS(¤t_rss) != 0) { + SystemError error; + error.errno_ = errno; + error.syscall = Bun::toString("memoryUsage"_s); + error.message = Bun::toString("Failed to get memory usage"_s); + throwException(globalObject, throwScope, JSValue::decode(SystemError__toErrorInstance(&error, globalObject))); + return JSC::JSValue::encode(JSC::JSValue {}); + } + + JSC::JSObject* result = JSC::constructEmptyObject(vm, process->memoryUsageStructure.getInitializedOnMainThread(process)); + if (UNLIKELY(throwScope.exception())) { + return JSC::JSValue::encode(JSC::JSValue {}); + } + + // Node.js: + // { + // rss: 4935680, + // heapTotal: 1826816, + // heapUsed: 650472, + // external: 49879, + // arrayBuffers: 9386 + // } + + result->putDirectOffset(vm, 0, JSC::jsNumber(current_rss)); + result->putDirectOffset(vm, 1, JSC::jsNumber(vm.heap.blockBytesAllocated())); + + // heap.size() loops through every cell... + // TODO: add a binding for heap.sizeAfterLastCollection() + result->putDirectOffset(vm, 2, JSC::jsNumber(vm.heap.sizeAfterLastEdenCollection())); + + result->putDirectOffset(vm, 3, JSC::jsNumber(vm.heap.externalMemorySize())); + + // We report 0 for this because m_arrayBuffers in JSC::Heap is private and we need to add a binding + // If we use objectTypeCounts(), it's hideously slow because it loops through every single object in the heap + // TODO: add a binding for m_arrayBuffers, registerWrapper() in TypedArrayController doesn't work + result->putDirectOffset(vm, 4, JSC::jsNumber(0)); + + RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(result)); +} + +JSC_DEFINE_HOST_FUNCTION(Process_functionMemoryUsageRSS, + (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + + size_t current_rss = 0; + if (getRSS(¤t_rss) != 0) { + SystemError error; + error.errno_ = errno; + error.syscall = Bun::toString("memoryUsage"_s); + error.message = Bun::toString("Failed to get memory usage"_s); + throwException(globalObject, throwScope, JSValue::decode(SystemError__toErrorInstance(&error, globalObject))); + return JSC::JSValue::encode(JSC::JSValue {}); + } + + RELEASE_AND_RETURN(throwScope, JSValue::encode(jsNumber(current_rss))); +} + JSC_DEFINE_HOST_FUNCTION(Process_functionOpenStdin, (JSGlobalObject * globalObject, CallFrame* callFrame)) { auto& vm = globalObject->vm(); @@ -1010,6 +1353,19 @@ static JSValue Process_stubEmptySet(VM& vm, JSObject* processObject) return JSSet::create(vm, globalObject->setStructure()); } +static JSValue constructMemoryUsage(VM& vm, JSObject* processObject) +{ + auto* globalObject = processObject->globalObject(); + JSC::JSFunction* memoryUsage = JSC::JSFunction::create(vm, globalObject, 0, + String("memoryUsage"_s), Process_functionMemoryUsage, ImplementationVisibility::Public); + + JSC::JSFunction* rss = JSC::JSFunction::create(vm, globalObject, 0, + String("rss"_s), Process_functionMemoryUsageRSS, ImplementationVisibility::Public); + + memoryUsage->putDirect(vm, JSC::Identifier::fromString(vm, "rss"_s), rss, JSC::PropertyAttribute::Function | 0); + return memoryUsage; +} + static JSValue constructFeatures(VM& vm, JSObject* processObject) { // { @@ -1133,16 +1489,16 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionCwd, browser constructBrowser PropertyCallback chdir Process_functionChdir Function 1 config constructProcessConfigObject PropertyCallback - debugPort processDebugPort CustomAccessor - exitCode processExitCode CustomAccessor - title processTitle CustomAccessor + cpuUsage Process_functionCpuUsage Function 1 cwd Process_functionCwd Function 1 + debugPort processDebugPort CustomAccessor dlopen Process_functionDlopen Function 1 emitWarning Process_emitWarning Function 1 env constructEnv PropertyCallback execArgv constructExecArgv PropertyCallback execPath constructExecPath PropertyCallback exit Process_functionExit Function 1 + exitCode processExitCode CustomAccessor features constructFeatures PropertyCallback getActiveResourcesInfo Process_stubFunctionReturningArray Function 0 getegid Process_functiongetegid Function 0 @@ -1153,6 +1509,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionCwd, hrtime constructProcessHrtimeObject PropertyCallback isBun constructIsBun PropertyCallback mainModule JSBuiltin ReadOnly|Builtin|Accessor|Function 0 + memoryUsage constructMemoryUsage PropertyCallback moduleLoadList Process_stubEmptyArray PropertyCallback nextTick Process_functionNextTick Function 1 openStdin Process_functionOpenStdin Function 0 @@ -1166,6 +1523,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionCwd, stderr constructStderr PropertyCallback stdin constructStdin PropertyCallback stdout constructStdout PropertyCallback + title processTitle CustomAccessor umask Process_functionUmask Function 1 uptime Process_functionUptime Function 1 version constructVersion PropertyCallback @@ -1192,6 +1550,14 @@ void Process::finishCreation(JSC::VM& vm) { Base::finishCreation(vm); + this->cpuUsageStructure.initLater([](const JSC::LazyProperty::Initializer& init) { + init.set(constructCPUUsageStructure(init.vm, init.owner->globalObject())); + }); + + this->memoryUsageStructure.initLater([](const JSC::LazyProperty::Initializer& init) { + init.set(constructMemoryUsageStructure(init.vm, init.owner->globalObject())); + }); + this->putDirect(vm, vm.propertyNames->toStringTagSymbol, jsString(vm, String("process"_s)), 0); } diff --git a/src/bun.js/bindings/Process.h b/src/bun.js/bindings/Process.h index 8b703b8b1..fbad9b1ff 100644 --- a/src/bun.js/bindings/Process.h +++ b/src/bun.js/bindings/Process.h @@ -45,6 +45,24 @@ public: return accessor; } + LazyProperty cpuUsageStructure; + LazyProperty memoryUsageStructure; + + DECLARE_VISIT_CHILDREN; + + template + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return WebCore::subspaceForImpl( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForProcessObject.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForProcessObject = std::forward(space); }, + [](auto& spaces) { return spaces.m_subspaceForProcessObject.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForProcessObject = std::forward(space); }); + } + void finishCreation(JSC::VM& vm); }; diff --git a/src/bun.js/bindings/Process.lut.h b/src/bun.js/bindings/Process.lut.h index 5c0bd2c5a..81eb1b7b9 100644 --- a/src/bun.js/bindings/Process.lut.h +++ b/src/bun.js/bindings/Process.lut.h @@ -1,6 +1,6 @@ // File generated via `make generate-builtins` -static const struct CompactHashIndex processObjectTableIndex[141] = { - { 42, -1 }, +static const struct CompactHashIndex processObjectTableIndex[142] = { + { 43, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, @@ -11,12 +11,12 @@ static const struct CompactHashIndex processObjectTableIndex[141] = { { -1, -1 }, { -1, -1 }, { -1, -1 }, - { 16, 130 }, + { 15, 129 }, { -1, -1 }, { -1, -1 }, - { 19, 137 }, + { 18, 138 }, { -1, -1 }, - { 43, -1 }, + { 45, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, @@ -33,37 +33,37 @@ static const struct CompactHashIndex processObjectTableIndex[141] = { { -1, -1 }, { -1, -1 }, { -1, -1 }, - { 3, 140 }, + { 3, 141 }, { 1, 128 }, { -1, -1 }, - { 57, -1 }, - { -1, -1 }, + { 59, -1 }, { -1, -1 }, + { 10, -1 }, { -1, -1 }, { -1, -1 }, - { 30, -1 }, + { 31, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, - { 50, -1 }, + { 52, -1 }, { 27, -1 }, - { 10, -1 }, + { 12, -1 }, { -1, -1 }, - { 11, -1 }, + { 19, -1 }, { -1, -1 }, - { 15, 136 }, + { 14, 137 }, { -1, -1 }, - { 35, -1 }, + { 36, -1 }, { -1, -1 }, - { 37, -1 }, - { 53, -1 }, - { 34, -1 }, - { 6, 138 }, + { 38, -1 }, + { 55, -1 }, + { 35, -1 }, + { 6, 139 }, { -1, -1 }, - { 49, -1 }, + { 51, -1 }, { 4, -1 }, - { 45, -1 }, + { 47, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, @@ -75,23 +75,23 @@ static const struct CompactHashIndex processObjectTableIndex[141] = { { -1, -1 }, { -1, -1 }, { -1, -1 }, - { 39, -1 }, + { 40, -1 }, { -1, -1 }, - { 36, -1 }, + { 37, -1 }, { -1, -1 }, { 0, -1 }, - { 12, 129 }, - { 17, 131 }, - { 38, -1 }, + { 26, 135 }, + { 16, 130 }, + { 39, -1 }, { -1, -1 }, { 23, -1 }, - { 13, -1 }, + { 11, -1 }, { -1, -1 }, { -1, -1 }, - { 56, -1 }, + { 58, -1 }, { -1, -1 }, { -1, -1 }, - { 47, -1 }, + { 30, 136 }, { -1, -1 }, { 29, -1 }, { 22, -1 }, @@ -105,45 +105,46 @@ static const struct CompactHashIndex processObjectTableIndex[141] = { { 5, -1 }, { -1, -1 }, { -1, -1 }, - { 46, -1 }, + { 48, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, - { 14, 132 }, + { 13, 131 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { 9, -1 }, - { 25, 134 }, + { 25, 133 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, - { 21, 135 }, + { 21, 134 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, - { 44, 139 }, + { 46, 140 }, { -1, -1 }, - { 18, -1 }, + { 17, -1 }, { 8, -1 }, - { 26, -1 }, { 28, -1 }, - { 31, 133 }, - { 32, -1 }, + { 32, 132 }, { 33, -1 }, - { 40, -1 }, + { 34, -1 }, { 41, -1 }, - { 48, -1 }, - { 51, -1 }, - { 52, -1 }, + { 42, -1 }, + { 44, -1 }, + { 49, -1 }, + { 50, -1 }, + { 53, -1 }, { 54, -1 }, - { 55, -1 }, + { 56, -1 }, + { 57, -1 }, }; -static const struct HashTableValue processObjectTableValues[58] = { +static const struct HashTableValue processObjectTableValues[60] = { { "abort"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionAbort, 1 } }, { "allowedNodeEnvironmentFlags"_s, static_cast(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, Process_stubEmptySet } }, { "arch"_s, static_cast(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructArch } }, @@ -154,16 +155,16 @@ static const struct HashTableValue processObjectTableValues[58] = { { "browser"_s, static_cast(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructBrowser } }, { "chdir"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionChdir, 1 } }, { "config"_s, static_cast(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructProcessConfigObject } }, - { "debugPort"_s, static_cast(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, processDebugPort, setProcessDebugPort } }, - { "exitCode"_s, static_cast(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, processExitCode, setProcessExitCode } }, - { "title"_s, static_cast(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, processTitle, setProcessTitle } }, + { "cpuUsage"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionCpuUsage, 1 } }, { "cwd"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionCwd, 1 } }, + { "debugPort"_s, static_cast(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, processDebugPort, setProcessDebugPort } }, { "dlopen"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionDlopen, 1 } }, { "emitWarning"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_emitWarning, 1 } }, { "env"_s, static_cast(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructEnv } }, { "execArgv"_s, static_cast(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructExecArgv } }, { "execPath"_s, static_cast(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructExecPath } }, { "exit"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionExit, 1 } }, + { "exitCode"_s, static_cast(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, processExitCode, setProcessExitCode } }, { "features"_s, static_cast(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructFeatures } }, { "getActiveResourcesInfo"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_stubFunctionReturningArray, 0 } }, { "getegid"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functiongetegid, 0 } }, @@ -174,6 +175,7 @@ static const struct HashTableValue processObjectTableValues[58] = { { "hrtime"_s, static_cast(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructProcessHrtimeObject } }, { "isBun"_s, static_cast(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructIsBun } }, { "mainModule"_s, ((static_cast(PropertyAttribute::ReadOnly|PropertyAttribute::Builtin|PropertyAttribute::Accessor|PropertyAttribute::Function)) & ~PropertyAttribute::Function) | PropertyAttribute::Builtin, NoIntrinsic, { HashTableValue::BuiltinGeneratorType, processObjectMainModuleCodeGenerator, 0 } }, + { "memoryUsage"_s, static_cast(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructMemoryUsage } }, { "moduleLoadList"_s, static_cast(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, Process_stubEmptyArray } }, { "nextTick"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionNextTick, 1 } }, { "openStdin"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionOpenStdin, 0 } }, @@ -187,6 +189,7 @@ static const struct HashTableValue processObjectTableValues[58] = { { "stderr"_s, static_cast(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructStderr } }, { "stdin"_s, static_cast(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructStdin } }, { "stdout"_s, static_cast(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructStdout } }, + { "title"_s, static_cast(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, processTitle, setProcessTitle } }, { "umask"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionUmask, 1 } }, { "uptime"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionUptime, 1 } }, { "version"_s, static_cast(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructVersion } }, @@ -205,4 +208,4 @@ static const struct HashTableValue processObjectTableValues[58] = { }; static const struct HashTable processObjectTable = - { 58, 127, true, nullptr, processObjectTableValues, processObjectTableIndex }; + { 60, 127, true, nullptr, processObjectTableValues, processObjectTableIndex }; diff --git a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h index 65875d091..82a2c6a24 100644 --- a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h +++ b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h @@ -38,6 +38,7 @@ public: std::unique_ptr m_clientSubspaceForJSMockImplementation; std::unique_ptr m_clientSubspaceForJSMockFunction; std::unique_ptr m_clientSubspaceForMockWithImplementationCleanupData; + std::unique_ptr m_clientSubspaceForProcessObject; #include "ZigGeneratedClasses+DOMClientIsoSubspaces.h" /* --- bun --- */ diff --git a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h index 433832450..f1b290d25 100644 --- a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h +++ b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h @@ -38,6 +38,7 @@ public: std::unique_ptr m_subspaceForJSMockImplementation; std::unique_ptr m_subspaceForJSMockFunction; std::unique_ptr m_subspaceForMockWithImplementationCleanupData; + std::unique_ptr m_subspaceForProcessObject; #include "ZigGeneratedClasses+DOMIsoSubspaces.h" /*-- BUN --*/ diff --git a/test/js/node/process/process.test.js b/test/js/node/process/process.test.js index c4701f664..e038383de 100644 --- a/test/js/node/process/process.test.js +++ b/test/js/node/process/process.test.js @@ -162,7 +162,8 @@ it("process.umask()", () => { expect(process.umask()).toBe(orig); }); -const versions = existsSync(import.meta.dir + "/../../src/generated_versions_list.zig"); +const generated_versions_list = join(import.meta.dir, "../../../../src/generated_versions_list.zig"); +const versions = existsSync(generated_versions_list); (versions ? it : it.skip)("process.versions", () => { // Generate a list of all the versions in the versions object // example: @@ -178,7 +179,7 @@ const versions = existsSync(import.meta.dir + "/../../src/generated_versions_lis // pub const c_ares = "0e7a5dee0fbb04080750cf6eabbe89d8bae87faa"; // pub const usockets = "fafc241e8664243fc0c51d69684d5d02b9805134"; const versions = Object.fromEntries( - readFileSync(import.meta.dir + "/../../src/generated_versions_list.zig", "utf8") + readFileSync(generated_versions_list, "utf8") .split("\n") .filter(line => line.startsWith("pub const") && !line.includes("zig") && line.includes(' = "')) .map(line => line.split(" = ")) @@ -291,3 +292,140 @@ describe("process.onBeforeExit", () => { expect(stdout.toString().trim()).toBe("beforeExit: 0\nbeforeExit: 1\nexit: 2"); }); }); + +it("process.memoryUsage", () => { + expect(process.memoryUsage()).toEqual({ + rss: expect.any(Number), + heapTotal: expect.any(Number), + heapUsed: expect.any(Number), + external: expect.any(Number), + arrayBuffers: expect.any(Number), + }); +}); + +it("process.memoryUsage.rss", () => { + expect(process.memoryUsage.rss()).toEqual(expect.any(Number)); +}); + +describe("process.cpuUsage", () => { + it("works", () => { + expect(process.cpuUsage()).toEqual({ + user: expect.any(Number), + system: expect.any(Number), + }); + }); + + it("works with diff", () => { + const init = process.cpuUsage(); + for (let i = 0; i < 1000; i++) {} + const delta = process.cpuUsage(init); + expect(delta.user).toBeGreaterThan(0); + expect(delta.system).toBeGreaterThan(0); + }); + + it("works with diff of different structure", () => { + const init = { + user: 0, + system: 0, + }; + for (let i = 0; i < 1000; i++) {} + const delta = process.cpuUsage(init); + expect(delta.user).toBeGreaterThan(0); + expect(delta.system).toBeGreaterThan(0); + }); + + it("throws on invalid property", () => { + const fixtures = [ + {}, + { user: null }, + { user: {} }, + { user: "potato" }, + + { user: 123 }, + { user: 123, system: null }, + { user: 123, system: "potato" }, + ]; + for (const fixture of fixtures) { + expect(() => process.cpuUsage(fixture)).toThrow(); + } + }); + + // Skipped on Linux because it seems to not change as often as on macOS + it.skipIf(process.platform === "linux")("increases monotonically", () => { + const init = process.cpuUsage(); + for (let i = 0; i < 10000; i++) {} + const another = process.cpuUsage(); + expect(another.user).toBeGreaterThan(init.user); + expect(another.system).toBeGreaterThan(init.system); + }); +}); + +it("process.getegid", () => { + expect(typeof process.getegid()).toBe("number"); +}); +it("process.geteuid", () => { + expect(typeof process.geteuid()).toBe("number"); +}); +it("process.getgid", () => { + expect(typeof process.getgid()).toBe("number"); +}); +it("process.getgroups", () => { + expect(process.getgroups()).toBeInstanceOf(Array); + expect(process.getgroups().length).toBeGreaterThan(0); +}); +it("process.getuid", () => { + expect(typeof process.getuid()).toBe("number"); +}); + +it("process.getuid", () => { + expect(typeof process.getuid()).toBe("number"); +}); + +const undefinedStubs = [ + "_debugEnd", + "_debugProcess", + "_fatalException", + "_linkedBinding", + "_rawDebug", + "_startProfilerIdleNotifier", + "_stopProfilerIdleNotifier", + "_tickCallback", +]; + +for (const stub of undefinedStubs) { + it(`process.${stub}`, () => { + expect(process[stub]()).toBeUndefined(); + }); +} + +const arrayStubs = ["getActiveResourcesInfo", "_getActiveRequests", "_getActiveHandles"]; + +for (const stub of arrayStubs) { + it(`process.${stub}`, () => { + expect(process[stub]()).toBeInstanceOf(Array); + }); +} + +const emptyObjectStubs = ["_preload_modules"]; +const emptySetStubs = ["allowedNodeEnvironmentFlags"]; +const emptyArrayStubs = ["moduleLoadList"]; + +for (const stub of emptyObjectStubs) { + it(`process.${stub}`, () => { + expect(process[stub]).toEqual({}); + }); +} + +for (const stub of emptySetStubs) { + it(`process.${stub}`, () => { + expect(process[stub]).toBeInstanceOf(Set); + expect(process[stub].size).toBe(0); + }); +} + +for (const stub of emptyArrayStubs) { + it(`process.${stub}`, () => { + expect(process[stub]).toBeInstanceOf(Array); + expect(process[stub]).toHaveLength(0); + }); +} -- cgit v1.2.3