From a2444f3e7eb920f1860605ca0a2c98d33a186d9a Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Fri, 27 Sep 2024 23:35:49 -0700 Subject: [PATCH 1/7] Module module --- cmake/targets/BuildBun.cmake | 3 + src/bun.js/bindings/CommonJSModuleRecord.h | 4 +- src/bun.js/bindings/ImportMetaObject.cpp | 38 +- src/bun.js/bindings/ZigGlobalObject.cpp | 6 +- src/bun.js/bindings/ZigGlobalObject.h | 7 +- src/bun.js/modules/NodeModuleModule.cpp | 759 +++++++++++++++++++++ src/bun.js/modules/NodeModuleModule.h | 743 +++++++------------- 7 files changed, 1030 insertions(+), 530 deletions(-) create mode 100644 src/bun.js/modules/NodeModuleModule.cpp diff --git a/cmake/targets/BuildBun.cmake b/cmake/targets/BuildBun.cmake index 01d4e9fedb9a50..79a93cdcb608ac 100644 --- a/cmake/targets/BuildBun.cmake +++ b/cmake/targets/BuildBun.cmake @@ -398,6 +398,7 @@ set(BUN_OBJECT_LUT_SOURCES ${CWD}/src/bun.js/bindings/BunProcess.cpp ${CWD}/src/bun.js/bindings/ProcessBindingConstants.cpp ${CWD}/src/bun.js/bindings/ProcessBindingNatives.cpp + ${CWD}/src/bun.js/modules/NodeModuleModule.cpp ) set(BUN_OBJECT_LUT_OUTPUTS @@ -407,8 +408,10 @@ set(BUN_OBJECT_LUT_OUTPUTS ${CODEGEN_PATH}/BunProcess.lut.h ${CODEGEN_PATH}/ProcessBindingConstants.lut.h ${CODEGEN_PATH}/ProcessBindingNatives.lut.h + ${CODEGEN_PATH}/NodeModuleModule.lut.h ) + macro(WEBKIT_ADD_SOURCE_DEPENDENCIES _source _deps) set(_tmp) get_source_file_property(_tmp ${_source} OBJECT_DEPENDS) diff --git a/src/bun.js/bindings/CommonJSModuleRecord.h b/src/bun.js/bindings/CommonJSModuleRecord.h index d3f0fde00a1dae..917209de851c1e 100644 --- a/src/bun.js/bindings/CommonJSModuleRecord.h +++ b/src/bun.js/bindings/CommonJSModuleRecord.h @@ -154,7 +154,7 @@ class RequireResolveFunctionPrototype final : public JSC::JSNonFinalObject { using Base = JSC::JSNonFinalObject; static RequireResolveFunctionPrototype* create(JSC::JSGlobalObject* globalObject); - static Structure* createStructure(VM& vm, JSC::JSGlobalObject* globalObject); + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject); DECLARE_INFO; @@ -180,7 +180,7 @@ class RequireFunctionPrototype final : public JSC::JSNonFinalObject { using Base = JSC::JSNonFinalObject; static RequireFunctionPrototype* create(JSC::JSGlobalObject* globalObject); - static Structure* createStructure(VM& vm, JSC::JSGlobalObject* globalObject); + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject); DECLARE_INFO; diff --git a/src/bun.js/bindings/ImportMetaObject.cpp b/src/bun.js/bindings/ImportMetaObject.cpp index 74473f7bf1cc89..686925c7817fa3 100644 --- a/src/bun.js/bindings/ImportMetaObject.cpp +++ b/src/bun.js/bindings/ImportMetaObject.cpp @@ -303,26 +303,28 @@ extern "C" JSC::EncodedJSValue functionImportMeta__resolveSyncPrivate(JSC::JSGlo if (!isESM) { if (LIKELY(globalObject)) { - auto overrideHandler = globalObject->m_nodeModuleOverriddenResolveFilename.get(); - if (UNLIKELY(overrideHandler)) { - ASSERT(overrideHandler->isCallable()); - JSValue parentModuleObject = globalObject->requireMap()->get(globalObject, from); - - JSValue parentID = jsUndefined(); - if (auto* parent = jsDynamicCast(parentModuleObject)) { - parentID = parent->id(); - } else { - parentID = from; - } + if (UNLIKELY(globalObject->hasOverridenModuleResolveFilenameFunction)) { + auto overrideHandler = jsCast(globalObject->m_moduleResolveFilenameFunction.getInitializedOnMainThread(globalObject)); + if (UNLIKELY(overrideHandler)) { + ASSERT(overrideHandler->isCallable()); + JSValue parentModuleObject = globalObject->requireMap()->get(globalObject, from); + + JSValue parentID = jsUndefined(); + if (auto* parent = jsDynamicCast(parentModuleObject)) { + parentID = parent->id(); + } else { + parentID = from; + } - MarkedArgumentBuffer args; - args.append(moduleName); - args.append(parentModuleObject); - auto parentIdStr = parentID.toWTFString(globalObject); - auto bunStr = Bun::toString(parentIdStr); - args.append(jsBoolean(Bun__isBunMain(lexicalGlobalObject, &bunStr))); + MarkedArgumentBuffer args; + args.append(moduleName); + args.append(parentModuleObject); + auto parentIdStr = parentID.toWTFString(globalObject); + auto bunStr = Bun::toString(parentIdStr); + args.append(jsBoolean(Bun__isBunMain(lexicalGlobalObject, &bunStr))); - return JSValue::encode(JSC::call(lexicalGlobalObject, overrideHandler, JSC::getCallData(overrideHandler), parentModuleObject, args)); + return JSValue::encode(JSC::call(lexicalGlobalObject, overrideHandler, JSC::getCallData(overrideHandler), parentModuleObject, args)); + } } } } diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index f5169df19651cf..b99b37aa689fb6 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -149,6 +149,7 @@ #include "ErrorCode.h" #include "v8/shim/GlobalInternals.h" #include "EventLoopTask.h" +#include "NodeModuleModule.h" #if ENABLE(REMOTE_INSPECTOR) #include "JavaScriptCore/RemoteInspectorServer.h" @@ -2662,6 +2663,8 @@ void GlobalObject::finishCreation(VM& vm) m_commonStrings.initialize(); + Bun::addNodeModuleConstructorProperties(vm, this); + m_JSDOMFileConstructor.initLater( [](const Initializer& init) { JSObject* fileConstructor = Bun::createJSDOMFileConstructor(init.vm, init.owner); @@ -3579,7 +3582,6 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor) visitor.append(thisObject->m_readableStreamToJSON); visitor.append(thisObject->m_readableStreamToText); visitor.append(thisObject->m_readableStreamToFormData); - visitor.append(thisObject->m_nodeModuleOverriddenResolveFilename); visitor.append(thisObject->m_nextTickQueue); visitor.append(thisObject->m_errorConstructorPrepareStackTraceValue); @@ -3589,6 +3591,8 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor) visitor.append(thisObject->m_currentNapiHandleScopeImpl); + thisObject->m_moduleResolveFilenameFunction.visit(visitor); + thisObject->m_nodeModuleConstructor.visit(visitor); thisObject->m_asyncBoundFunctionStructure.visit(visitor); thisObject->m_bunObject.visit(visitor); thisObject->m_cachedNodeVMGlobalObjectStructure.visit(visitor); diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h index 225dea67fff08b..52226012dea720 100644 --- a/src/bun.js/bindings/ZigGlobalObject.h +++ b/src/bun.js/bindings/ZigGlobalObject.h @@ -395,9 +395,8 @@ class GlobalObject : public Bun::GlobalScope { mutable WriteBarrier m_readableStreamToText; mutable WriteBarrier m_readableStreamToFormData; - // This is set when doing `require('module')._resolveFilename = ...` - // a hack used by Next.js to inject their versions of webpack and react - mutable WriteBarrier m_nodeModuleOverriddenResolveFilename; + LazyProperty m_moduleResolveFilenameFunction; + LazyProperty m_nodeModuleConstructor; mutable WriteBarrier m_nextTickQueue; @@ -586,6 +585,8 @@ class GlobalObject : public Bun::GlobalScope { LazyProperty m_performanceObject; LazyProperty m_processObject; + bool hasOverridenModuleResolveFilenameFunction = false; + private: DOMGuardedObjectSet m_guardedObjects WTF_GUARDED_BY_LOCK(m_gcLock); WebCore::SubtleCrypto* m_subtleCrypto = nullptr; diff --git a/src/bun.js/modules/NodeModuleModule.cpp b/src/bun.js/modules/NodeModuleModule.cpp new file mode 100644 index 00000000000000..b5026930ecf786 --- /dev/null +++ b/src/bun.js/modules/NodeModuleModule.cpp @@ -0,0 +1,759 @@ +#include "root.h" + +#include + +#include + +#include + +#include "headers-handwritten.h" + +#include "PathInlines.h" +#include "ZigGlobalObject.h" +#include "headers.h" +#include + +#include "NodeModuleModule.h" + +#include + +namespace Bun { + +using namespace JSC; + +BUN_DECLARE_HOST_FUNCTION(Resolver__nodeModulePathsForJS); +JSC_DECLARE_HOST_FUNCTION(jsFunctionDebugNoop); +JSC_DECLARE_HOST_FUNCTION(jsFunctionFindPath); +JSC_DECLARE_HOST_FUNCTION(jsFunctionFindSourceMap); +JSC_DECLARE_HOST_FUNCTION(jsFunctionIsBuiltinModule); +JSC_DECLARE_HOST_FUNCTION(jsFunctionNodeModuleCreateRequire); +JSC_DECLARE_HOST_FUNCTION(jsFunctionNodeModuleModuleConstructor); +JSC_DECLARE_HOST_FUNCTION(jsFunctionResolveFileName); +JSC_DECLARE_HOST_FUNCTION(jsFunctionResolveLookupPaths); +JSC_DECLARE_HOST_FUNCTION(jsFunctionSourceMap); +JSC_DECLARE_HOST_FUNCTION(jsFunctionSyncBuiltinExports); +JSC_DECLARE_HOST_FUNCTION(jsFunctionWrap); + +JSC_DECLARE_CUSTOM_GETTER(get_resolveFilename); +JSC_DECLARE_CUSTOM_SETTER(set_resolveFilename); +JSC_DECLARE_CUSTOM_GETTER(getterRequireFunction); +JSC_DECLARE_CUSTOM_SETTER(setterRequireFunction); + +// This is a mix of bun's builtin module names and also the ones reported by +// node v20.4.0 +static constexpr ASCIILiteral builtinModuleNames[] = { + "_http_agent"_s, + "_http_client"_s, + "_http_common"_s, + "_http_incoming"_s, + "_http_outgoing"_s, + "_http_server"_s, + "_stream_duplex"_s, + "_stream_passthrough"_s, + "_stream_readable"_s, + "_stream_transform"_s, + "_stream_wrap"_s, + "_stream_writable"_s, + "_tls_common"_s, + "_tls_wrap"_s, + "assert"_s, + "assert/strict"_s, + "async_hooks"_s, + "buffer"_s, + "bun"_s, + "bun:ffi"_s, + "bun:jsc"_s, + "bun:sqlite"_s, + "bun:test"_s, + "bun:wrap"_s, + "child_process"_s, + "cluster"_s, + "console"_s, + "constants"_s, + "crypto"_s, + "detect-libc"_s, + "dgram"_s, + "diagnostics_channel"_s, + "dns"_s, + "dns/promises"_s, + "domain"_s, + "events"_s, + "fs"_s, + "fs/promises"_s, + "http"_s, + "http2"_s, + "https"_s, + "inspector"_s, + "inspector/promises"_s, + "module"_s, + "net"_s, + "os"_s, + "path"_s, + "path/posix"_s, + "path/win32"_s, + "perf_hooks"_s, + "process"_s, + "punycode"_s, + "querystring"_s, + "readline"_s, + "readline/promises"_s, + "repl"_s, + "stream"_s, + "stream/consumers"_s, + "stream/promises"_s, + "stream/web"_s, + "string_decoder"_s, + "sys"_s, + "timers"_s, + "timers/promises"_s, + "tls"_s, + "trace_events"_s, + "tty"_s, + "undici"_s, + "url"_s, + "util"_s, + "util/types"_s, + "v8"_s, + "vm"_s, + "wasi"_s, + "worker_threads"_s, + "ws"_s, + "zlib"_s, +}; + +template consteval std::size_t countof(T (&)[N]) { + return N; +} + +JSC_DEFINE_HOST_FUNCTION(jsFunctionDebugNoop, + (JSC::JSGlobalObject * globalObject, + JSC::CallFrame *callFrame)) { + return JSValue::encode(jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunctionNodeModuleModuleCall, + (JSC::JSGlobalObject * globalObject, + JSC::CallFrame *callFrame)) { + return JSValue::encode(jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunctionNodeModuleModuleConstructor, + (JSC::JSGlobalObject * globalObject, + JSC::CallFrame *callFrame)) { + + // In node, this is supposed to be the actual CommonJSModule constructor. + // We are cutting a huge corner by not doing all that work. + // This code is only to support babel. + JSC::VM &vm = globalObject->vm(); + JSString *idString = JSC::jsString(vm, WTF::String("."_s)); + + JSString *dirname = jsEmptyString(vm); + + // TODO: handle when JSGlobalObject !== Zig::GlobalObject, such as in node:vm + Structure *structure = static_cast(globalObject) + ->CommonJSModuleObjectStructure(); + + // TODO: handle ShadowRealm, node:vm, new.target, subclasses + JSValue idValue = callFrame->argument(0); + JSValue parentValue = callFrame->argument(1); + + auto scope = DECLARE_THROW_SCOPE(vm); + if (idValue.isString()) { + idString = idValue.toString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + auto index = idString->tryGetValue()->reverseFind('/', idString->length()); + + if (index != WTF::notFound) { + dirname = JSC::jsSubstring(globalObject, idString, 0, index); + } + } + + auto *out = Bun::JSCommonJSModule::create(vm, structure, idString, jsNull(), + dirname, SourceCode()); + + if (!parentValue.isUndefined()) { + out->putDirect(vm, JSC::Identifier::fromString(vm, "parent"_s), parentValue, + 0); + } + + out->putDirect(vm, JSC::Identifier::fromString(vm, "exports"_s), + JSC::constructEmptyObject(globalObject, + globalObject->objectPrototype(), 0), + 0); + + return JSValue::encode(out); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsBuiltinModule, + (JSC::JSGlobalObject * globalObject, + JSC::CallFrame *callFrame)) { + JSC::VM &vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + JSValue moduleName = callFrame->argument(0); + if (!moduleName.isString()) { + return JSValue::encode(jsBoolean(false)); + } + + auto moduleStr = moduleName.toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, JSValue::encode(jsBoolean(false))); + + return JSValue::encode(jsBoolean(Bun::isBuiltinModule(moduleStr))); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunctionWrap, (JSC::JSGlobalObject * globalObject, + JSC::CallFrame *callFrame)) { + auto &vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + JSString *code = callFrame->argument(0).toStringOrNull(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + if (!code) { + return JSC::JSValue::encode(JSC::jsUndefined()); + } + + JSString *prefix = jsString( + vm, + String( + "(function (exports, require, module, __filename, __dirname) { "_s)); + JSString *suffix = jsString(vm, String("\n});"_s)); + + return JSValue::encode(jsString(globalObject, prefix, code, suffix)); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunctionNodeModuleCreateRequire, + (JSC::JSGlobalObject * globalObject, + JSC::CallFrame *callFrame)) { + JSC::VM &vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + if (callFrame->argumentCount() < 1) { + throwTypeError(globalObject, scope, + "createRequire() requires at least one argument"_s); + RELEASE_AND_RETURN(scope, JSC::JSValue::encode({})); + } + + auto val = callFrame->uncheckedArgument(0).toWTFString(globalObject); + + if (val.startsWith("file://"_s)) { + WTF::URL url(val); + if (!url.isValid()) { + throwTypeError(globalObject, scope, + makeString("createRequire() was given an invalid URL '"_s, + url.string(), "'"_s)); + RELEASE_AND_RETURN(scope, JSValue::encode({})); + } + if (!url.protocolIsFile()) { + throwTypeError(globalObject, scope, + "createRequire() does not support non-file URLs"_s); + RELEASE_AND_RETURN(scope, JSValue::encode({})); + } + val = url.fileSystemPath(); + } + + RETURN_IF_EXCEPTION(scope, {}); + RELEASE_AND_RETURN( + scope, JSValue::encode(Bun::JSCommonJSModule::createBoundRequireFunction( + vm, globalObject, val))); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunctionFindSourceMap, + (JSGlobalObject * globalObject, + CallFrame *callFrame)) { + auto &vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + throwException( + globalObject, scope, + createError(globalObject, + "module.SourceMap is not yet implemented in Bun"_s)); + return {}; +} + +JSC_DEFINE_HOST_FUNCTION(jsFunctionSyncBuiltinExports, + (JSGlobalObject * globalObject, + CallFrame *callFrame)) { + return JSValue::encode(jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunctionSourceMap, (JSGlobalObject * globalObject, + CallFrame *callFrame)) { + auto &vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + throwException(globalObject, scope, + createError(globalObject, "Not implemented"_s)); + return {}; +} + +JSC_DEFINE_HOST_FUNCTION(jsFunctionResolveFileName, + (JSC::JSGlobalObject * globalObject, + JSC::CallFrame *callFrame)) { + JSC::VM &vm = globalObject->vm(); + + switch (callFrame->argumentCount()) { + case 0: { + auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); + // not "requires" because "require" could be confusing + JSC::throwTypeError( + globalObject, scope, + "Module._resolveFilename needs 2+ arguments (a string)"_s); + scope.release(); + return JSC::JSValue::encode(JSC::JSValue{}); + } + default: { + JSC::JSValue moduleName = callFrame->argument(0); + JSC::JSValue fromValue = callFrame->argument(1); + + if (moduleName.isUndefinedOrNull()) { + auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); + JSC::throwTypeError(globalObject, scope, + "Module._resolveFilename expects a string"_s); + scope.release(); + return JSC::JSValue::encode(JSC::JSValue{}); + } + + if ( + // fast path: it's a real CommonJS module object. + auto *cjs = jsDynamicCast(fromValue)) { + fromValue = cjs->id(); + } else if + // slow path: userland code did something weird. lets let them do that + // weird thing. + (fromValue.isObject()) { + + if (auto idValue = fromValue.getObject()->getIfPropertyExists( + globalObject, builtinNames(vm).filenamePublicName())) { + if (idValue.isString()) { + fromValue = idValue; + } + } + } + + auto result = + Bun__resolveSync(globalObject, JSC::JSValue::encode(moduleName), + JSValue::encode(fromValue), false); + auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); + + if (!JSC::JSValue::decode(result).isString()) { + JSC::throwException(globalObject, scope, JSC::JSValue::decode(result)); + return JSC::JSValue::encode(JSC::JSValue{}); + } + + scope.release(); + return result; + } + } +} + +JSC_DEFINE_CUSTOM_GETTER(get_resolveFilename, + (JSGlobalObject * lexicalGlobalObject, + EncodedJSValue thisValue, + PropertyName propertyName)) { + + auto *globalObject = defaultGlobalObject(lexicalGlobalObject); + return JSValue::encode( + globalObject->m_moduleResolveFilenameFunction.getInitializedOnMainThread( + globalObject)); +} + +JSC_DEFINE_CUSTOM_SETTER(set_resolveFilename, + (JSGlobalObject * lexicalGlobalObject, + EncodedJSValue thisValue, EncodedJSValue encodedValue, + PropertyName propertyName)) { + auto *globalObject = defaultGlobalObject(lexicalGlobalObject); + auto value = JSValue::decode(encodedValue); + if (value.isCell()) { + bool isOriginal = false; + if (value.isCallable()) { + JSC::CallData callData = JSC::getCallData(value); + + if (callData.type == JSC::CallData::Type::Native) { + if (callData.native.function.untaggedPtr() == + &jsFunctionResolveFileName) { + isOriginal = true; + } + } + } + globalObject->hasOverridenModuleResolveFilenameFunction = !isOriginal; + globalObject->m_moduleResolveFilenameFunction.set( + lexicalGlobalObject->vm(), globalObject, value.asCell()); + } + + return true; +} + +extern "C" bool ModuleLoader__isBuiltin(const char *data, size_t len); + +struct Parent { + JSArray *paths; + JSString *filename; +}; + +Parent getParent(VM &vm, JSGlobalObject *global, JSValue maybe_parent) { + Parent value{nullptr, nullptr}; + + if (!maybe_parent) { + return value; + } + + auto parent = maybe_parent.getObject(); + if (!parent) { + return value; + } + + auto scope = DECLARE_THROW_SCOPE(vm); + const auto &builtinNames = Bun::builtinNames(vm); + JSValue paths = parent->get(global, builtinNames.pathsPublicName()); + RETURN_IF_EXCEPTION(scope, value); + if (paths.isCell()) { + value.paths = jsDynamicCast(paths); + } + + JSValue filename = parent->get(global, builtinNames.filenamePublicName()); + RETURN_IF_EXCEPTION(scope, value); + if (filename.isString()) { + value.filename = filename.toString(global); + } + RELEASE_AND_RETURN(scope, value); +} + +// https://github.com/nodejs/node/blob/40ef9d541ed79470977f90eb445c291b95ab75a0/lib/internal/modules/cjs/loader.js#L895 +JSC_DEFINE_HOST_FUNCTION(jsFunctionResolveLookupPaths, + (JSC::JSGlobalObject * globalObject, + JSC::CallFrame *callFrame)) { + auto &vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + String request = callFrame->argument(0).toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + auto utf8 = request.utf8(); + if (ModuleLoader__isBuiltin(utf8.data(), utf8.length())) { + return JSC::JSValue::encode(JSC::jsNull()); + } + + auto parent = getParent(vm, globalObject, callFrame->argument(1)); + RETURN_IF_EXCEPTION(scope, {}); + + // Check for node modules paths. + if (request.characterAt(0) != '.' || + (request.length() > 1 && request.characterAt(1) != '.' && + request.characterAt(1) != '/' && +#if OS(WINDOWS) + request.characterAt(1) != '\\' +#else + true +#endif + )) { + auto array = JSC::constructArray( + globalObject, (ArrayAllocationProfile *)nullptr, nullptr, 0); + if (parent.paths) { + auto len = parent.paths->length(); + for (size_t i = 0; i < len; i++) { + auto path = parent.paths->getIndex(globalObject, i); + array->push(globalObject, path); + } + } + return JSValue::encode(array); + } + + JSValue dirname; + if (parent.filename) { + EncodedJSValue encodedFilename = JSValue::encode(parent.filename); +#if OS(WINDOWS) + dirname = JSValue::decode( + Bun__Path__dirname(globalObject, true, &encodedFilename, 1)); +#else + dirname = JSValue::decode( + Bun__Path__dirname(globalObject, false, &encodedFilename, 1)); +#endif + } else { + dirname = jsString(vm, String("."_s)); + } + + JSValue values[] = {dirname}; + auto array = JSC::constructArray( + globalObject, (ArrayAllocationProfile *)nullptr, values, 1); + RELEASE_AND_RETURN(scope, JSValue::encode(array)); +} + +extern "C" JSC::EncodedJSValue NodeModuleModule__findPath(JSGlobalObject *, + BunString, JSArray *); + +JSC_DEFINE_HOST_FUNCTION(jsFunctionFindPath, (JSGlobalObject * globalObject, + JSC::CallFrame *callFrame)) { + JSC::VM &vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSValue request_value = callFrame->argument(0); + JSValue paths_value = callFrame->argument(1); + + String request = request_value.toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + BunString request_bun_str = Bun::toString(request); + + JSArray *paths = + paths_value.isCell() ? jsDynamicCast(paths_value) : nullptr; + + return NodeModuleModule__findPath(globalObject, request_bun_str, paths); +} + +// These two setters are only used if you directly hit +// `Module.prototype.require` or `module.require`. When accessing the cjs +// require argument, this is a bound version of `require`, which calls into the +// overridden one. +// +// This require function also intentionally does not have .resolve on it, nor +// does it have any of the other properties. +// +// Note: allowing require to be overridable at all is only needed for Next.js to +// work (they do Module.prototype.require = ...) + +JSC_DEFINE_CUSTOM_GETTER(getterRequireFunction, + (JSC::JSGlobalObject * globalObject, + JSC::EncodedJSValue thisValue, JSC::PropertyName)) { + return JSValue::encode(globalObject->getDirect( + globalObject->vm(), WebCore::clientData(globalObject->vm()) + ->builtinNames() + .overridableRequirePrivateName())); +} + +JSC_DEFINE_CUSTOM_SETTER(setterRequireFunction, + (JSC::JSGlobalObject * globalObject, + JSC::EncodedJSValue thisValue, + JSC::EncodedJSValue value, + JSC::PropertyName propertyName)) { + globalObject->putDirect(globalObject->vm(), + WebCore::clientData(globalObject->vm()) + ->builtinNames() + .overridableRequirePrivateName(), + JSValue::decode(value), 0); + return true; +} + +static JSValue getModuleCacheObject(VM &vm, JSObject *moduleObject) { + return jsCast(moduleObject->globalObject()) + ->lazyRequireCacheObject(); +} + +static JSValue getModuleDebugObject(VM &vm, JSObject *moduleObject) { + return JSC::constructEmptyObject(moduleObject->globalObject()); +} + +static JSValue getPathCacheObject(VM &vm, JSObject *moduleObject) { + auto *globalObject = defaultGlobalObject(moduleObject->globalObject()); + return JSC::constructEmptyObject( + vm, globalObject->nullPrototypeObjectStructure()); +} + +static JSValue getModuleExtensionsObject(VM &vm, JSObject *moduleObject) { + auto *globalObject = defaultGlobalObject(moduleObject->globalObject()); + return globalObject->requireFunctionUnbound()->getIfPropertyExists( + globalObject, Identifier::fromString(vm, "extensions"_s)); +} + +static JSValue getSourceMapFunction(VM &vm, JSObject *moduleObject) { + auto *globalObject = defaultGlobalObject(moduleObject->globalObject()); + JSFunction *sourceMapFunction = JSFunction::create( + vm, globalObject, 1, "SourceMap"_s, jsFunctionSourceMap, + ImplementationVisibility::Public, NoIntrinsic, jsFunctionSourceMap); + return sourceMapFunction; +} + +static JSValue getBuiltinModulesObject(VM &vm, JSObject *moduleObject) { + MarkedArgumentBuffer args; + args.ensureCapacity(countof(builtinModuleNames)); + + for (unsigned i = 0; i < countof(builtinModuleNames); ++i) { + args.append(JSC::jsOwnedString(vm, String(builtinModuleNames[i]))); + } + + auto *globalObject = defaultGlobalObject(moduleObject->globalObject()); + return JSC::constructArray( + globalObject, static_cast(nullptr), + JSC::ArgList(args)); +} + +static JSValue getConstantsObject(VM &vm, JSObject *moduleObject) { + auto *globalObject = defaultGlobalObject(moduleObject->globalObject()); + auto *compileCacheStatus = JSC::constructEmptyObject( + vm, globalObject->nullPrototypeObjectStructure()); + compileCacheStatus->putDirect(vm, JSC::Identifier::fromString(vm, "FAILED"_s), + JSC::jsNumber(0)); + compileCacheStatus->putDirect( + vm, JSC::Identifier::fromString(vm, "ENABLED"_s), JSC::jsNumber(1)); + compileCacheStatus->putDirect( + vm, JSC::Identifier::fromString(vm, "ALREADY_ENABLED"_s), + JSC::jsNumber(2)); + compileCacheStatus->putDirect( + vm, JSC::Identifier::fromString(vm, "DISABLED"_s), JSC::jsNumber(3)); + + auto *constantsObject = JSC::constructEmptyObject( + vm, globalObject->nullPrototypeObjectStructure()); + constantsObject->putDirect( + vm, JSC::Identifier::fromString(vm, "compileCacheStatus"_s), + compileCacheStatus); + return constantsObject; +} + +static JSValue getGlobalPathsObject(VM &vm, JSObject *moduleObject) { + return JSC::constructEmptyArray( + moduleObject->globalObject(), + static_cast(nullptr), 0); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunctionInitPaths, (JSGlobalObject * globalObject, + JSC::CallFrame *callFrame)) { + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +static JSValue getModulePrototypeObject(VM &vm, JSObject *moduleObject) { + auto *globalObject = defaultGlobalObject(moduleObject->globalObject()); + auto prototype = + constructEmptyObject(globalObject, globalObject->objectPrototype(), 1); + prototype->putDirectCustomAccessor( + vm, WebCore::clientData(vm)->builtinNames().requirePublicName(), + JSC::CustomGetterSetter::create(vm, getterRequireFunction, + setterRequireFunction), + 0); + + return prototype; +} + +JSC_DEFINE_HOST_FUNCTION(jsFunctionLoad, (JSGlobalObject * globalObject, + JSC::CallFrame *callFrame)) { + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunctionRunMain, (JSGlobalObject * globalObject, + JSC::CallFrame *callFrame)) { + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunctionPreloadModules, + (JSGlobalObject * globalObject, + JSC::CallFrame *callFrame)) { + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunctionSyncBuiltinESMExports, + (JSGlobalObject * globalObject, + JSC::CallFrame *callFrame)) { + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunctionRegister, (JSGlobalObject * globalObject, + JSC::CallFrame *callFrame)) { + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunctionEnableCompileCache, + (JSGlobalObject * globalObject, + JSC::CallFrame *callFrame)) { + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunctionGetCompileCacheDir, + (JSGlobalObject * globalObject, + JSC::CallFrame *callFrame)) { + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +static JSValue getModuleObject(VM &vm, JSObject *moduleObject) { + return moduleObject; +} + +/* Source for NodeModuleModule.lut.h +@begin nodeModuleObjectTable +_cache getModuleCacheObject PropertyCallback +_debug getModuleDebugObject PropertyCallback +_extensions getModuleExtensionsObject PropertyCallback +_findPath jsFunctionFindPath Function 3 +_initPaths jsFunctionInitPaths Function 0 +_load jsFunctionLoad Function 1 +_nodeModulePaths Resolver__nodeModulePathsForJS Function 1 +_pathCache getPathCacheObject PropertyCallback +_preloadModules jsFunctionPreloadModules Function 0 +_resolveFilename jsFunctionResolveFileName Function 2 +_resolveLookupPaths jsFunctionResolveLookupPaths Function 2 +builtinModules getBuiltinModulesObject PropertyCallback +constants getConstantsObject PropertyCallback +createRequire jsFunctionNodeModuleCreateRequire Function 1 +enableCompileCache jsFunctionEnableCompileCache Function 0 +findSourceMap jsFunctionFindSourceMap Function 0 +getCompileCacheDir jsFunctionGetCompileCacheDir Function 0 +globalPaths getGlobalPathsObject PropertyCallback +isBuiltin jsFunctionIsBuiltinModule Function 1 +prototype getModulePrototypeObject PropertyCallback +register jsFunctionRegister Function 1 +runMain jsFunctionRunMain Function 0 +SourceMap getSourceMapFunction PropertyCallback +syncBuiltinESMExports jsFunctionSyncBuiltinESMExports Function 0 +wrap jsFunctionWrap Function 1 +Module getModuleObject PropertyCallback +@end +*/ +#include "NodeModuleModule.lut.h" + +class JSModuleConstructor : public JSC::InternalFunction { + using Base = JSC::InternalFunction; + +public: + DECLARE_EXPORT_INFO; + static constexpr bool needsDestruction = false; + static constexpr unsigned StructureFlags = + Base::StructureFlags | HasStaticPropertyTable; + + static JSC::Structure *createStructure(JSC::VM &vm, + JSC::JSGlobalObject *globalObject, + JSC::JSValue prototype) { + ASSERT(globalObject); + return JSC::Structure::create( + vm, globalObject, prototype, + JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info()); + } + + static JSModuleConstructor *create(JSC::VM &vm, + Zig::GlobalObject *globalObject) { + auto *structure = + createStructure(vm, globalObject, globalObject->functionPrototype()); + + auto *moduleConstructor = + new (NotNull, JSC::allocateCell(vm)) + JSModuleConstructor(vm, structure); + moduleConstructor->finishCreation(vm); + return moduleConstructor; + } + +private: + JSModuleConstructor(JSC::VM &vm, JSC::Structure *structure) + : Base(vm, structure, jsFunctionNodeModuleModuleCall, + jsFunctionNodeModuleModuleConstructor) {} + + void finishCreation(JSC::VM &vm) { + Base::finishCreation(vm, 1, "Module"_s, + PropertyAdditionMode::WithStructureTransition); + } +}; + +const JSC::ClassInfo JSModuleConstructor::s_info = { + "Module"_s, &Base::s_info, &nodeModuleObjectTable, nullptr, + CREATE_METHOD_TABLE(JSModuleConstructor)}; + +void addNodeModuleConstructorProperties(JSC::VM &vm, + Zig::GlobalObject *globalObject) { + globalObject->m_nodeModuleConstructor.initLater( + [](const Zig::GlobalObject::Initializer &init) { + JSObject *moduleConstructor = JSModuleConstructor::create( + init.vm, static_cast(init.owner)); + init.set(moduleConstructor); + }); + + globalObject->m_moduleResolveFilenameFunction.initLater( + [](const Zig::GlobalObject::Initializer &init) { + JSFunction *resolveFilenameFunction = JSFunction::create( + init.vm, init.owner, 2, "_resolveFilename"_s, + jsFunctionResolveFileName, JSC::ImplementationVisibility::Public, + JSC::NoIntrinsic, jsFunctionResolveFileName); + init.set(resolveFilenameFunction); + }); +} + +} // namespace Bun \ No newline at end of file diff --git a/src/bun.js/modules/NodeModuleModule.h b/src/bun.js/modules/NodeModuleModule.h index e8251708482500..e11b295b6282fe 100644 --- a/src/bun.js/modules/NodeModuleModule.h +++ b/src/bun.js/modules/NodeModuleModule.h @@ -1,6 +1,8 @@ // clang-format off #pragma once +#include "root.h" + #include "CommonJSModuleRecord.h" #include "ImportMetaObject.h" #include "JavaScriptCore/ArgList.h" @@ -9,551 +11,280 @@ #include "isBuiltinModule.h" #include #include -#include "PathInlines.h" + using namespace Zig; using namespace JSC; -// This is a mix of bun's builtin module names and also the ones reported by -// node v20.4.0 -static constexpr ASCIILiteral builtinModuleNames[] = { - "_http_agent"_s, - "_http_client"_s, - "_http_common"_s, - "_http_incoming"_s, - "_http_outgoing"_s, - "_http_server"_s, - "_stream_duplex"_s, - "_stream_passthrough"_s, - "_stream_readable"_s, - "_stream_transform"_s, - "_stream_wrap"_s, - "_stream_writable"_s, - "_tls_common"_s, - "_tls_wrap"_s, - "assert"_s, - "assert/strict"_s, - "async_hooks"_s, - "buffer"_s, - "bun"_s, - "bun:ffi"_s, - "bun:jsc"_s, - "bun:sqlite"_s, - "bun:test"_s, - "bun:wrap"_s, - "child_process"_s, - "cluster"_s, - "console"_s, - "constants"_s, - "crypto"_s, - "detect-libc"_s, - "dgram"_s, - "diagnostics_channel"_s, - "dns"_s, - "dns/promises"_s, - "domain"_s, - "events"_s, - "fs"_s, - "fs/promises"_s, - "http"_s, - "http2"_s, - "https"_s, - "inspector"_s, - "inspector/promises"_s, - "module"_s, - "net"_s, - "os"_s, - "path"_s, - "path/posix"_s, - "path/win32"_s, - "perf_hooks"_s, - "process"_s, - "punycode"_s, - "querystring"_s, - "readline"_s, - "readline/promises"_s, - "repl"_s, - "stream"_s, - "stream/consumers"_s, - "stream/promises"_s, - "stream/web"_s, - "string_decoder"_s, - "sys"_s, - "timers"_s, - "timers/promises"_s, - "tls"_s, - "trace_events"_s, - "tty"_s, - "undici"_s, - "url"_s, - "util"_s, - "util/types"_s, - "v8"_s, - "vm"_s, - "wasi"_s, - "worker_threads"_s, - "ws"_s, - "zlib"_s, -}; -JSC_DEFINE_HOST_FUNCTION(jsFunctionDebugNoop, (JSC::JSGlobalObject * globalObject, JSC::CallFrame *callFrame)) { - return JSValue::encode(jsUndefined()); +namespace Bun { + void addNodeModuleConstructorProperties(JSC::VM &vm, Zig::GlobalObject *globalObject); } -JSC_DEFINE_HOST_FUNCTION(jsFunctionNodeModuleModuleConstructor, (JSC::JSGlobalObject * globalObject, JSC::CallFrame *callFrame)) { - // In node, this is supposed to be the actual CommonJSModule constructor. - // We are cutting a huge corner by not doing all that work. - // This code is only to support babel. - JSC::VM &vm = globalObject->vm(); - JSString *idString = JSC::jsString(vm, WTF::String("."_s)); - - JSString *dirname = jsEmptyString(vm); - // TODO: handle when JSGlobalObject !== Zig::GlobalObject, such as in node:vm - Structure *structure = static_cast(globalObject) - ->CommonJSModuleObjectStructure(); - // TODO: handle ShadowRealm, node:vm, new.target, subclasses - JSValue idValue = callFrame->argument(0); - JSValue parentValue = callFrame->argument(1); - auto scope = DECLARE_THROW_SCOPE(vm); - if (idValue.isString()) { - idString = idValue.toString(globalObject); - RETURN_IF_EXCEPTION(scope, {}); - auto index = idString->tryGetValue()->reverseFind('/', idString->length()); - if (index != WTF::notFound) { - dirname = JSC::jsSubstring(globalObject, idString, 0, index); - } - } +namespace Zig { - auto *out = Bun::JSCommonJSModule::create(vm, structure, idString, jsNull(), - dirname, SourceCode()); +DEFINE_NATIVE_MODULE(NodeModule) { + // the default object here is a function, so we cant use the + // INIT_NATIVE_MODULE helper + Zig::GlobalObject *globalObject = reinterpret_cast(lexicalGlobalObject); + JSC::VM &vm = globalObject->vm(); + auto scope = DECLARE_CATCH_SCOPE(vm); + auto *object = globalObject->m_nodeModuleConstructor.getInitializedOnMainThread(globalObject); + - if (!parentValue.isUndefined()) { - out->putDirect(vm, JSC::Identifier::fromString(vm, "parent"_s), parentValue, 0); +const auto _cacheIdentifier = Identifier::fromString(vm, "_cache"_s); + auto _cacheValue = + object->getIfPropertyExists(globalObject, _cacheIdentifier); + if (scope.exception()) { + scope.clearException(); + _cacheValue = jsUndefined(); } - - out->putDirect( - vm, - JSC::Identifier::fromString(vm, "exports"_s), - JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), 0), - 0 - ); - - return JSValue::encode(out); -} - -JSC_DEFINE_HOST_FUNCTION(jsFunctionIsBuiltinModule, (JSC::JSGlobalObject * globalObject, JSC::CallFrame *callFrame)) { - JSC::VM &vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - JSValue moduleName = callFrame->argument(0); - if (!moduleName.isString()) { - return JSValue::encode(jsBoolean(false)); + const auto _debugIdentifier = Identifier::fromString(vm, "_debug"_s); + auto _debugValue = + object->getIfPropertyExists(globalObject, _debugIdentifier); + if (scope.exception()) { + scope.clearException(); + _debugValue = jsUndefined(); } - - auto moduleStr = moduleName.toWTFString(globalObject); - RETURN_IF_EXCEPTION(scope, JSValue::encode(jsBoolean(false))); - - return JSValue::encode(jsBoolean(Bun::isBuiltinModule(moduleStr))); -} - -JSC_DEFINE_HOST_FUNCTION(jsFunctionWrap, (JSC::JSGlobalObject * globalObject, - JSC::CallFrame *callFrame)) { - auto &vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - JSString *code = callFrame->argument(0).toStringOrNull(globalObject); - RETURN_IF_EXCEPTION(scope, {}); - if (!code) { - return JSC::JSValue::encode(JSC::jsUndefined()); + const auto _extensionsIdentifier = + Identifier::fromString(vm, "_extensions"_s); + auto _extensionsValue = + object->getIfPropertyExists(globalObject, _extensionsIdentifier); + if (scope.exception()) { + scope.clearException(); + _extensionsValue = jsUndefined(); } - - JSString *prefix = jsString(vm, String( "(function (exports, require, module, __filename, __dirname) { "_s)); - JSString *suffix = jsString(vm, String("\n});"_s)); - - return JSValue::encode(jsString(globalObject, prefix, code, suffix)); -} - -JSC_DEFINE_HOST_FUNCTION(jsFunctionNodeModuleCreateRequire, - (JSC::JSGlobalObject * globalObject, - JSC::CallFrame *callFrame)) { - JSC::VM &vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - if (callFrame->argumentCount() < 1) { - throwTypeError(globalObject, scope, - "createRequire() requires at least one argument"_s); - RELEASE_AND_RETURN(scope, JSC::JSValue::encode({})); + const auto _findPathIdentifier = Identifier::fromString(vm, "_findPath"_s); + auto _findPathValue = + object->getIfPropertyExists(globalObject, _findPathIdentifier); + if (scope.exception()) { + scope.clearException(); + _findPathValue = jsUndefined(); } - - auto val = callFrame->uncheckedArgument(0).toWTFString(globalObject); - - if (val.startsWith("file://"_s)) { - WTF::URL url(val); - if (!url.isValid()) { - throwTypeError(globalObject, scope, - makeString("createRequire() was given an invalid URL '"_s, - url.string(), "'"_s)); - RELEASE_AND_RETURN(scope, JSValue::encode({})); - } - if (!url.protocolIsFile()) { - throwTypeError(globalObject, scope, - "createRequire() does not support non-file URLs"_s); - RELEASE_AND_RETURN(scope, JSValue::encode({})); - } - val = url.fileSystemPath(); + const auto _initPathsIdentifier = Identifier::fromString(vm, "_initPaths"_s); + auto _initPathsValue = + object->getIfPropertyExists(globalObject, _initPathsIdentifier); + if (scope.exception()) { + scope.clearException(); + _initPathsValue = jsUndefined(); } - - RETURN_IF_EXCEPTION(scope, {}); - RELEASE_AND_RETURN( - scope, JSValue::encode(Bun::JSCommonJSModule::createBoundRequireFunction( - vm, globalObject, val))); -} -BUN_DECLARE_HOST_FUNCTION(Resolver__nodeModulePathsForJS); - -JSC_DEFINE_HOST_FUNCTION(jsFunctionFindSourceMap, (JSGlobalObject * globalObject, CallFrame *callFrame)) { - auto &vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - throwException(globalObject, scope, createError(globalObject, "module.SourceMap is not yet implemented in Bun"_s)); - return {}; -} - -JSC_DEFINE_HOST_FUNCTION(jsFunctionSyncBuiltinExports, - (JSGlobalObject * globalObject, - CallFrame *callFrame)) { - return JSValue::encode(jsUndefined()); -} - -JSC_DEFINE_HOST_FUNCTION(jsFunctionSourceMap, (JSGlobalObject * globalObject, CallFrame *callFrame)) { - auto &vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - throwException(globalObject, scope, - createError(globalObject, "Not implemented"_s)); - return {}; -} - -JSC_DEFINE_HOST_FUNCTION(jsFunctionResolveFileName, (JSC::JSGlobalObject * globalObject, JSC::CallFrame *callFrame)) { - JSC::VM &vm = globalObject->vm(); - - switch (callFrame->argumentCount()) { - case 0: { - auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); - // not "requires" because "require" could be confusing - JSC::throwTypeError( - globalObject, scope, - "Module._resolveFilename needs 2+ arguments (a string)"_s); - scope.release(); - return JSC::JSValue::encode(JSC::JSValue{}); + const auto _loadIdentifier = Identifier::fromString(vm, "_load"_s); + auto _loadValue = object->getIfPropertyExists(globalObject, _loadIdentifier); + if (scope.exception()) { + scope.clearException(); + _loadValue = jsUndefined(); } - default: { - JSC::JSValue moduleName = callFrame->argument(0); - JSC::JSValue fromValue = callFrame->argument(1); - - if (moduleName.isUndefinedOrNull()) { - auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); - JSC::throwTypeError(globalObject, scope, - "Module._resolveFilename expects a string"_s); - scope.release(); - return JSC::JSValue::encode(JSC::JSValue{}); - } - - if ( - // fast path: it's a real CommonJS module object. - auto *cjs = jsDynamicCast(fromValue)) { - fromValue = cjs->id(); - } else if - // slow path: userland code did something weird. lets let them do that - // weird thing. - (fromValue.isObject()) { - - if (auto idValue = fromValue.getObject()->getIfPropertyExists(globalObject, builtinNames(vm).filenamePublicName())) { - if (idValue.isString()) { - fromValue = idValue; - } - } - } - - auto result = Bun__resolveSync(globalObject, JSC::JSValue::encode(moduleName), JSValue::encode(fromValue), false); - auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); - - if (!JSC::JSValue::decode(result).isString()) { - JSC::throwException(globalObject, scope, JSC::JSValue::decode(result)); - return JSC::JSValue::encode(JSC::JSValue{}); - } - - scope.release(); - return result; + + const auto _nodeModulePathsIdentifier = + Identifier::fromString(vm, "_nodeModulePaths"_s); + auto _nodeModulePathsValue = + object->getIfPropertyExists(globalObject, _nodeModulePathsIdentifier); + if (scope.exception()) { + scope.clearException(); + _nodeModulePathsValue = jsUndefined(); } + const auto _pathCacheIdentifier = Identifier::fromString(vm, "_pathCache"_s); + auto _pathCacheValue = + object->getIfPropertyExists(globalObject, _pathCacheIdentifier); + if (scope.exception()) { + scope.clearException(); + _pathCacheValue = jsUndefined(); } -} -template consteval std::size_t countof(T (&)[N]) { - return N; -} - -JSC_DEFINE_CUSTOM_GETTER(get_resolveFilename, (JSGlobalObject * globalObject, - EncodedJSValue thisValue, - PropertyName propertyName)) { - auto override = static_cast(globalObject) - ->m_nodeModuleOverriddenResolveFilename.get(); - if (override) { - return JSValue::encode(override); + const auto _preloadModulesIdentifier = + Identifier::fromString(vm, "_preloadModules"_s); + auto _preloadModulesValue = + object->getIfPropertyExists(globalObject, _preloadModulesIdentifier); + if (scope.exception()) { + scope.clearException(); + _preloadModulesValue = jsUndefined(); } - // Instead of storing the original function on the global object and have - // those extra bytes, just have it be a property alias. - JSObject *thisObject = JSValue::decode(thisValue).getObject(); - if (!thisObject) - return JSValue::encode(jsUndefined()); - auto &vm = globalObject->vm(); - return JSValue::encode(thisObject->getDirect( - vm, Identifier::fromString(vm, "__resolveFilename"_s))); -} - -JSC_DEFINE_CUSTOM_SETTER(set_resolveFilename, - (JSGlobalObject * globalObject, - EncodedJSValue thisValue, EncodedJSValue value, - PropertyName propertyName)) { - auto valueJS = JSValue::decode(value); - if (valueJS.isCell()) { - if (auto fn = jsDynamicCast(valueJS.asCell())) { - static_cast(globalObject) - ->m_nodeModuleOverriddenResolveFilename.set(globalObject->vm(), - globalObject, fn); - return true; - } + const auto _resolveFilenameIdentifier = + Identifier::fromString(vm, "_resolveFilename"_s); + auto _resolveFilenameValue = + object->getIfPropertyExists(globalObject, _resolveFilenameIdentifier); + if (scope.exception()) { + scope.clearException(); + _resolveFilenameValue = jsUndefined(); } - return false; -} - -extern "C" bool ModuleLoader__isBuiltin(const char* data, size_t len); - -struct Parent { - JSArray* paths; - JSString* filename; -}; - -Parent getParent(VM&vm, JSGlobalObject* global, JSValue maybe_parent) { - Parent value { nullptr, nullptr }; - - if (!maybe_parent) { - return value; + const auto _resolveLookupPathsIdentifier = + Identifier::fromString(vm, "_resolveLookupPaths"_s); + auto _resolveLookupPathsValue = + object->getIfPropertyExists(globalObject, _resolveLookupPathsIdentifier); + if (scope.exception()) { + scope.clearException(); + _resolveLookupPathsValue = jsUndefined(); } - - auto parent = maybe_parent.getObject(); - if (!parent) { - return value; + const auto builtinModulesIdentifier = + Identifier::fromString(vm, "builtinModules"_s); + auto builtinModulesValue = + object->getIfPropertyExists(globalObject, builtinModulesIdentifier); + if (scope.exception()) { + scope.clearException(); + builtinModulesValue = jsUndefined(); } - - auto scope = DECLARE_THROW_SCOPE(vm); - const auto& builtinNames = Bun::builtinNames(vm); - JSValue paths = parent->get(global, builtinNames.pathsPublicName()); - RETURN_IF_EXCEPTION(scope, value); - if (paths.isCell()) { - value.paths = jsDynamicCast(paths); + const auto constantsIdentifier = Identifier::fromString(vm, "constants"_s); + auto constantsValue = + object->getIfPropertyExists(globalObject, constantsIdentifier); + if (scope.exception()) { + scope.clearException(); + constantsValue = jsUndefined(); } - - JSValue filename = parent->get(global, builtinNames.filenamePublicName()); - RETURN_IF_EXCEPTION(scope, value); - if (filename.isString()) { - value.filename = filename.toString(global); + const auto createRequireIdentifier = + Identifier::fromString(vm, "createRequire"_s); + auto createRequireValue = + object->getIfPropertyExists(globalObject, createRequireIdentifier); + if (scope.exception()) { + scope.clearException(); + createRequireValue = jsUndefined(); } - RELEASE_AND_RETURN(scope, value); -} - -// https://github.com/nodejs/node/blob/40ef9d541ed79470977f90eb445c291b95ab75a0/lib/internal/modules/cjs/loader.js#L895 -JSC_DEFINE_HOST_FUNCTION(jsFunctionResolveLookupPaths, (JSC::JSGlobalObject * globalObject, JSC::CallFrame *callFrame)) { - auto &vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - String request = callFrame->argument(0).toWTFString(globalObject); - RETURN_IF_EXCEPTION(scope, {}); - - auto utf8 = request.utf8(); - if(ModuleLoader__isBuiltin(utf8.data(), utf8.length())) { - return JSC::JSValue::encode(JSC::jsNull()); + const auto enableCompileCacheIdentifier = + Identifier::fromString(vm, "enableCompileCache"_s); + auto enableCompileCacheValue = + object->getIfPropertyExists(globalObject, enableCompileCacheIdentifier); + if (scope.exception()) { + scope.clearException(); + enableCompileCacheValue = jsUndefined(); } - - auto parent = getParent(vm, globalObject, callFrame->argument(1)); - RETURN_IF_EXCEPTION(scope, {}); - - // Check for node modules paths. - if ( - request.characterAt(0) != '.' || - (request.length() > 1 && - request.characterAt(1) != '.' && - request.characterAt(1) != '/' && -#if OS(WINDOWS) - request.characterAt(1) != '\\' -#else - true -#endif - ) - ) { - auto array = JSC::constructArray(globalObject, (ArrayAllocationProfile*)nullptr, nullptr, 0); - if (parent.paths) { - auto len = parent.paths->length(); - for (size_t i = 0; i < len; i++) { - auto path = parent.paths->getIndex(globalObject, i); - array->push(globalObject, path); - } - } - return JSValue::encode(array); + const auto findSourceMapIdentifier = + Identifier::fromString(vm, "findSourceMap"_s); + auto findSourceMapValue = + object->getIfPropertyExists(globalObject, findSourceMapIdentifier); + if (scope.exception()) { + scope.clearException(); + findSourceMapValue = jsUndefined(); } - - JSValue dirname; - if (parent.filename) { - EncodedJSValue encodedFilename = JSValue::encode(parent.filename); -#if OS(WINDOWS) - dirname = JSValue::decode(Bun__Path__dirname(globalObject, true, &encodedFilename, 1)); -#else - dirname = JSValue::decode(Bun__Path__dirname(globalObject, false, &encodedFilename, 1)); -#endif - } else { - dirname = jsString(vm, String("."_s)); + const auto getCompileCacheDirIdentifier = + Identifier::fromString(vm, "getCompileCacheDir"_s); + auto getCompileCacheDirValue = + object->getIfPropertyExists(globalObject, getCompileCacheDirIdentifier); + if (scope.exception()) { + scope.clearException(); + getCompileCacheDirValue = jsUndefined(); } - - JSValue values[] = { dirname }; - auto array = JSC::constructArray( - globalObject, - (ArrayAllocationProfile*)nullptr, - values, - 1 - ); - RELEASE_AND_RETURN(scope, JSValue::encode(array)); -} - -extern "C" JSC::EncodedJSValue NodeModuleModule__findPath(JSGlobalObject*, BunString, JSArray*); - -JSC_DEFINE_HOST_FUNCTION(jsFunctionFindPath, (JSGlobalObject * globalObject, JSC::CallFrame *callFrame)) { - JSC::VM &vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - JSValue request_value = callFrame->argument(0); - JSValue paths_value = callFrame->argument(1); - - String request = request_value.toWTFString(globalObject); - RETURN_IF_EXCEPTION(scope, {}); - BunString request_bun_str = Bun::toString(request); - - JSArray* paths = paths_value.isCell() ? jsDynamicCast(paths_value) : nullptr; - - return NodeModuleModule__findPath(globalObject, request_bun_str, paths); + const auto globalPathsIdentifier = + Identifier::fromString(vm, "globalPaths"_s); + auto globalPathsValue = + object->getIfPropertyExists(globalObject, globalPathsIdentifier); + if (scope.exception()) { + scope.clearException(); + globalPathsValue = jsUndefined(); + } + const auto isBuiltinIdentifier = Identifier::fromString(vm, "isBuiltin"_s); + auto isBuiltinValue = + object->getIfPropertyExists(globalObject, isBuiltinIdentifier); + if (scope.exception()) { + scope.clearException(); + isBuiltinValue = jsUndefined(); + } + const auto prototypeIdentifier = Identifier::fromString(vm, "prototype"_s); + auto prototypeValue = + object->getIfPropertyExists(globalObject, prototypeIdentifier); + if (scope.exception()) { + scope.clearException(); + prototypeValue = jsUndefined(); + } + const auto registerIdentifier = Identifier::fromString(vm, "register"_s); + auto registerValue = + object->getIfPropertyExists(globalObject, registerIdentifier); + if (scope.exception()) { + scope.clearException(); + registerValue = jsUndefined(); + } + const auto runMainIdentifier = Identifier::fromString(vm, "runMain"_s); + auto runMainValue = + object->getIfPropertyExists(globalObject, runMainIdentifier); + if (scope.exception()) { + scope.clearException(); + runMainValue = jsUndefined(); + } + const auto SourceMapIdentifier = Identifier::fromString(vm, "SourceMap"_s); + auto SourceMapValue = + object->getIfPropertyExists(globalObject, SourceMapIdentifier); + if (scope.exception()) { + scope.clearException(); + SourceMapValue = jsUndefined(); + } + const auto syncBuiltinESMExportsIdentifier = + Identifier::fromString(vm, "syncBuiltinESMExports"_s); + auto syncBuiltinESMExportsValue = object->getIfPropertyExists( + globalObject, syncBuiltinESMExportsIdentifier); +if (scope.exception()) { + scope.clearException(); + syncBuiltinESMExportsValue = jsUndefined(); } -// These two setters are only used if you directly hit -// `Module.prototype.require` or `module.require`. When accessing the cjs -// require argument, this is a bound version of `require`, which calls into the -// overridden one. -// -// This require function also intentionally does not have .resolve on it, nor -// does it have any of the other properties. -// -// Note: allowing require to be overridable at all is only needed for Next.js to -// work (they do Module.prototype.require = ...) + const auto wrapIdentifier = Identifier::fromString(vm, "wrap"_s); + auto wrapValue = object->getIfPropertyExists(globalObject, wrapIdentifier); + if (scope.exception()) { + scope.clearException(); + wrapValue = jsUndefined(); + } -JSC_DEFINE_CUSTOM_GETTER(getterRequireFunction, - (JSC::JSGlobalObject * globalObject, - JSC::EncodedJSValue thisValue, JSC::PropertyName)) { - return JSValue::encode(globalObject->getDirect( - globalObject->vm(), WebCore::clientData(globalObject->vm()) - ->builtinNames() - .overridableRequirePrivateName())); -} -JSC_DEFINE_CUSTOM_SETTER(setterRequireFunction, - (JSC::JSGlobalObject * globalObject, - JSC::EncodedJSValue thisValue, - JSC::EncodedJSValue value, - JSC::PropertyName propertyName)) { - globalObject->putDirect(globalObject->vm(), - WebCore::clientData(globalObject->vm()) - ->builtinNames() - .overridableRequirePrivateName(), - JSValue::decode(value), 0); - return true; -} -namespace Zig { - -DEFINE_NATIVE_MODULE(NodeModule) { - // the default object here is a function, so we cant use the - // INIT_NATIVE_MODULE helper - Zig::GlobalObject *globalObject = reinterpret_cast(lexicalGlobalObject); - JSC::VM &vm = globalObject->vm(); - JSC::JSObject *defaultObject = JSC::JSFunction::create( - vm, globalObject, 0, "Module"_s, jsFunctionNodeModuleModuleConstructor, - JSC::ImplementationVisibility::Public, JSC::NoIntrinsic, - jsFunctionNodeModuleModuleConstructor); - auto put = [&](JSC::Identifier name, JSC::JSValue value) { - defaultObject->putDirect(vm, name, value); - exportNames.append(name); - exportValues.append(value); - }; - auto putNativeFn = [&](JSC::Identifier name, JSC::NativeFunction ptr) { - JSC::JSFunction *value = JSC::JSFunction::create( - vm, globalObject, 1, name.string(), ptr, - JSC::ImplementationVisibility::Public, JSC::NoIntrinsic, ptr); - defaultObject->putDirect(vm, name, value); - exportNames.append(name); - exportValues.append(value); - }; - exportNames.reserveCapacity(16); - exportValues.ensureCapacity(16); + exportNames.reserveCapacity(24+1); + exportValues.ensureCapacity(24+1); exportNames.append(vm.propertyNames->defaultKeyword); - exportValues.append(defaultObject); + exportValues.append(object); + + exportNames.append(_cacheIdentifier); + exportValues.append(_cacheValue); + exportNames.append(_debugIdentifier); + exportValues.append(_debugValue); + exportNames.append(_extensionsIdentifier); + exportValues.append(_extensionsValue); + exportNames.append(_findPathIdentifier); + exportValues.append(_findPathValue); + exportNames.append(_initPathsIdentifier); + exportValues.append(_initPathsValue); + exportNames.append(_loadIdentifier); + exportValues.append(_loadValue); + exportNames.append(_nodeModulePathsIdentifier); + exportValues.append(_nodeModulePathsValue); + exportNames.append(_pathCacheIdentifier); + exportValues.append(_pathCacheValue); + exportNames.append(_preloadModulesIdentifier); + exportValues.append(_preloadModulesValue); + exportNames.append(_resolveFilenameIdentifier); + exportValues.append(_resolveFilenameValue); + exportNames.append(_resolveLookupPathsIdentifier); + exportValues.append(_resolveLookupPathsValue); + exportNames.append(builtinModulesIdentifier); + exportValues.append(builtinModulesValue); + exportNames.append(constantsIdentifier); + exportValues.append(constantsValue); + exportNames.append(createRequireIdentifier); + exportValues.append(createRequireValue); + exportNames.append(enableCompileCacheIdentifier); + exportValues.append(enableCompileCacheValue); + exportNames.append(findSourceMapIdentifier); + exportValues.append(findSourceMapValue); + exportNames.append(getCompileCacheDirIdentifier); + exportValues.append(getCompileCacheDirValue); + exportNames.append(globalPathsIdentifier); + exportValues.append(globalPathsValue); + exportNames.append(isBuiltinIdentifier); + exportValues.append(isBuiltinValue); + exportNames.append(prototypeIdentifier); + exportValues.append(prototypeValue); + exportNames.append(registerIdentifier); + exportValues.append(registerValue); + exportNames.append(runMainIdentifier); + exportValues.append(runMainValue); + exportNames.append(SourceMapIdentifier); + exportValues.append(SourceMapValue); + exportNames.append(syncBuiltinESMExportsIdentifier); + exportValues.append(syncBuiltinESMExportsValue); + exportNames.append(wrapIdentifier); + exportValues.append(wrapValue); - put(Identifier::fromString(vm, "Module"_s), defaultObject); - // Module._extensions === require.extensions - put( - Identifier::fromString(vm, "_extensions"_s), - globalObject->requireFunctionUnbound()->get( globalObject, Identifier::fromString(vm, "extensions"_s)) - ); - - defaultObject->putDirectCustomAccessor( - vm, - JSC::Identifier::fromString(vm, "_resolveFilename"_s), - JSC::CustomGetterSetter::create(vm, get_resolveFilename, set_resolveFilename), - JSC::PropertyAttribute::CustomAccessor | 0 - ); - - putNativeFn(Identifier::fromString(vm, "__resolveFilename"_s), jsFunctionResolveFileName); - putNativeFn(Identifier::fromString(vm, "_resolveLookupPaths"_s), jsFunctionResolveLookupPaths); - - putNativeFn(Identifier::fromString(vm, "createRequire"_s), jsFunctionNodeModuleCreateRequire); - putNativeFn(builtinNames(vm).pathsPublicName(), Resolver__nodeModulePathsForJS); - putNativeFn(Identifier::fromString(vm, "findSourceMap"_s), jsFunctionFindSourceMap); - putNativeFn(Identifier::fromString(vm, "syncBuiltinExports"_s), jsFunctionSyncBuiltinExports); - putNativeFn(Identifier::fromString(vm, "SourceMap"_s), jsFunctionSourceMap); - putNativeFn(Identifier::fromString(vm, "isBuiltin"_s), jsFunctionIsBuiltinModule); - putNativeFn(Identifier::fromString(vm, "_nodeModulePaths"_s), Resolver__nodeModulePathsForJS); - putNativeFn(Identifier::fromString(vm, "wrap"_s), jsFunctionWrap); - - put(Identifier::fromString(vm, "_cache"_s), jsCast(globalObject)->lazyRequireCacheObject()); - put(Identifier::fromString(vm, "globalPaths"_s), constructEmptyArray(globalObject, nullptr, 0)); - - auto prototype = constructEmptyObject(globalObject, globalObject->objectPrototype(), 1); - prototype->putDirectCustomAccessor( - vm, WebCore::clientData(vm)->builtinNames().requirePublicName(), - JSC::CustomGetterSetter::create( - vm, - getterRequireFunction, - setterRequireFunction - ), - 0 - ); - - defaultObject->putDirect(vm, vm.propertyNames->prototype, prototype); - - MarkedArgumentBuffer args; - args.ensureCapacity(countof(builtinModuleNames)); - - for (unsigned i = 0; i < countof(builtinModuleNames); ++i) { - args.append(JSC::jsOwnedString(vm, String(builtinModuleNames[i]))); - } - - - put(JSC::Identifier::fromString(vm, "builtinModules"_s), JSC::constructArray(globalObject, static_cast(nullptr), JSC::ArgList(args))); + } } // namespace Zig From 13ab3eb91bcbb103027e919e34518068e2291af3 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Tue, 1 Oct 2024 01:14:48 -0700 Subject: [PATCH 2/7] Fix --- src/bun.js/modules/NodeModuleModule.cpp | 10 ++++++++-- src/bun.js/modules/NodeModuleModule.h | 8 +++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/bun.js/modules/NodeModuleModule.cpp b/src/bun.js/modules/NodeModuleModule.cpp index b5026930ecf786..22574eaefdfc58 100644 --- a/src/bun.js/modules/NodeModuleModule.cpp +++ b/src/bun.js/modules/NodeModuleModule.cpp @@ -607,7 +607,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionInitPaths, (JSGlobalObject * globalObject, static JSValue getModulePrototypeObject(VM &vm, JSObject *moduleObject) { auto *globalObject = defaultGlobalObject(moduleObject->globalObject()); auto prototype = - constructEmptyObject(globalObject, globalObject->objectPrototype(), 1); + constructEmptyObject(globalObject, globalObject->objectPrototype(), 2); prototype->putDirectCustomAccessor( vm, WebCore::clientData(vm)->builtinNames().requirePublicName(), JSC::CustomGetterSetter::create(vm, getterRequireFunction, @@ -710,6 +710,12 @@ class JSModuleConstructor : public JSC::InternalFunction { JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info()); } + template + static JSC::GCClient::IsoSubspace *subspaceFor(JSC::VM &vm) { + STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSModuleConstructor, Base); + return &vm.internalFunctionSpace(); + } + static JSModuleConstructor *create(JSC::VM &vm, Zig::GlobalObject *globalObject) { auto *structure = @@ -729,7 +735,7 @@ class JSModuleConstructor : public JSC::InternalFunction { void finishCreation(JSC::VM &vm) { Base::finishCreation(vm, 1, "Module"_s, - PropertyAdditionMode::WithStructureTransition); + PropertyAdditionMode::WithoutStructureTransition); } }; diff --git a/src/bun.js/modules/NodeModuleModule.h b/src/bun.js/modules/NodeModuleModule.h index e11b295b6282fe..ee7d63c4a0dd63 100644 --- a/src/bun.js/modules/NodeModuleModule.h +++ b/src/bun.js/modules/NodeModuleModule.h @@ -35,7 +35,13 @@ DEFINE_NATIVE_MODULE(NodeModule) { JSC::VM &vm = globalObject->vm(); auto scope = DECLARE_CATCH_SCOPE(vm); auto *object = globalObject->m_nodeModuleConstructor.getInitializedOnMainThread(globalObject); - + +if (object->hasNonReifiedStaticProperties()) { + object->reifyAllStaticProperties(globalObject); + if (scope.exception()) { + scope.clearException(); + } +} const auto _cacheIdentifier = Identifier::fromString(vm, "_cache"_s); auto _cacheValue = From f7487c48ab126698c745c2468ae69438e4f7ff06 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Tue, 1 Oct 2024 21:29:02 -0700 Subject: [PATCH 3/7] Update NodeModuleModule.cpp --- src/bun.js/modules/NodeModuleModule.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bun.js/modules/NodeModuleModule.cpp b/src/bun.js/modules/NodeModuleModule.cpp index 22574eaefdfc58..40f00c0e03feda 100644 --- a/src/bun.js/modules/NodeModuleModule.cpp +++ b/src/bun.js/modules/NodeModuleModule.cpp @@ -16,7 +16,7 @@ #include "NodeModuleModule.h" #include - +#include namespace Bun { using namespace JSC; From 884174b478d634963429ee0f664834f6c19dcbfc Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Tue, 1 Oct 2024 23:04:17 -0700 Subject: [PATCH 4/7] fix test failure --- src/bun.js/bindings/CommonJSModuleRecord.cpp | 11 +- src/bun.js/bindings/CommonJSModuleRecord.h | 1 + src/bun.js/bindings/ModuleLoader.cpp | 14 +- src/bun.js/bindings/ZigGlobalObject.cpp | 3 +- src/bun.js/modules/NodeModuleModule.cpp | 66 ++++- src/bun.js/modules/NodeModuleModule.h | 268 +------------------ src/bun.js/modules/_NativeModule.h | 15 +- src/js/internal-for-testing.ts | 6 + test/js/bun/resolve/import-meta.test.js | 10 +- 9 files changed, 110 insertions(+), 284 deletions(-) diff --git a/src/bun.js/bindings/CommonJSModuleRecord.cpp b/src/bun.js/bindings/CommonJSModuleRecord.cpp index b0e84311f23699..baf862139fc407 100644 --- a/src/bun.js/bindings/CommonJSModuleRecord.cpp +++ b/src/bun.js/bindings/CommonJSModuleRecord.cpp @@ -942,6 +942,11 @@ void JSCommonJSModule::toSyntheticSource(JSC::JSGlobalObject* globalObject, populateESMExports(globalObject, result, exportNames, exportValues, this->ignoreESModuleAnnotation); } +void JSCommonJSModule::setExportsObject(JSC::JSValue exportsObject) +{ + this->putDirect(vm(), JSC::PropertyName(clientData(vm())->builtinNames().exportsPublicName()), exportsObject, 0); +} + JSValue JSCommonJSModule::exportsObject() { return this->get(globalObject(), JSC::PropertyName(clientData(vm())->builtinNames().exportsPublicName())); @@ -1004,12 +1009,6 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionRequireCommonJS, (JSGlobalObject * lexicalGlo WTF::String specifier = specifierValue.toWTFString(globalObject); RETURN_IF_EXCEPTION(throwScope, {}); - // Special-case for "process" to just return the process object directly. - if (UNLIKELY(specifier == "process"_s || specifier == "node:process"_s)) { - jsCast(callframe->argument(1))->putDirect(vm, builtinNames(vm).exportsPublicName(), globalObject->processObject(), 0); - return JSValue::encode(globalObject->processObject()); - } - WTF::String referrer = thisObject->id().toWTFString(globalObject); RETURN_IF_EXCEPTION(throwScope, {}); diff --git a/src/bun.js/bindings/CommonJSModuleRecord.h b/src/bun.js/bindings/CommonJSModuleRecord.h index 917209de851c1e..fc5092eb79b15a 100644 --- a/src/bun.js/bindings/CommonJSModuleRecord.h +++ b/src/bun.js/bindings/CommonJSModuleRecord.h @@ -102,6 +102,7 @@ class JSCommonJSModule final : public JSC::JSDestructibleObject { JSC::MarkedArgumentBuffer& exportValues); JSValue exportsObject(); + void setExportsObject(JSC::JSValue exportsObject); JSValue id(); bool load(JSC::VM& vm, Zig::GlobalObject* globalObject, WTF::NakedPtr&); diff --git a/src/bun.js/bindings/ModuleLoader.cpp b/src/bun.js/bindings/ModuleLoader.cpp index 9c5714a1d65a56..7f732aea1af3b0 100644 --- a/src/bun.js/bindings/ModuleLoader.cpp +++ b/src/bun.js/bindings/ModuleLoader.cpp @@ -517,6 +517,16 @@ JSValue fetchCommonJSModule( auto tag = res->result.value.tag; switch (tag) { + case SyntheticModuleType::NodeModule: { + target->setExportsObject(globalObject->m_nodeModuleConstructor.getInitializedOnMainThread(globalObject)); + target->hasEvaluated = true; + RELEASE_AND_RETURN(scope, target); + } + case SyntheticModuleType::NodeProcess: { + target->setExportsObject(globalObject->processObject()); + target->hasEvaluated = true; + RELEASE_AND_RETURN(scope, target); + } // Generated native module cases #define CASE(str, name) \ case SyntheticModuleType::name: { \ @@ -524,7 +534,7 @@ JSValue fetchCommonJSModule( RETURN_IF_EXCEPTION(scope, {}); \ RELEASE_AND_RETURN(scope, target); \ } - BUN_FOREACH_NATIVE_MODULE(CASE) + BUN_FOREACH_CJS_NATIVE_MODULE(CASE) #undef CASE case SyntheticModuleType::ESM: { @@ -746,7 +756,7 @@ static JSValue fetchESMSourceCode( auto source = JSC::SourceCode(JSC::SyntheticSourceProvider::create(generateNativeModule_##name, JSC::SourceOrigin(), WTFMove(moduleKey))); \ return rejectOrResolve(JSSourceCode::create(vm, WTFMove(source))); \ } - BUN_FOREACH_NATIVE_MODULE(CASE) + BUN_FOREACH_ESM_NATIVE_MODULE(CASE) #undef CASE // CommonJS modules from src/js/* diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index 960fcd4f76da4d..7da7d3e3badac9 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -3969,7 +3969,8 @@ JSC::JSInternalPromise* GlobalObject::moduleLoaderImportModule(JSGlobalObject* j ASSERT(startRefCount == moduleName.impl()->refCount()); if (!resolved.success) { throwException(scope, resolved.result.err, globalObject); - return JSC::JSInternalPromise::rejectedPromiseWithCaughtException(globalObject, scope); + auto* promise = JSC::JSInternalPromise::create(vm, globalObject->internalPromiseStructure()); + return promise->rejectWithCaughtException(globalObject, scope); } JSC::Identifier resolvedIdentifier; diff --git a/src/bun.js/modules/NodeModuleModule.cpp b/src/bun.js/modules/NodeModuleModule.cpp index 40f00c0e03feda..b4b17855f57656 100644 --- a/src/bun.js/modules/NodeModuleModule.cpp +++ b/src/bun.js/modules/NodeModuleModule.cpp @@ -34,8 +34,6 @@ JSC_DECLARE_HOST_FUNCTION(jsFunctionSourceMap); JSC_DECLARE_HOST_FUNCTION(jsFunctionSyncBuiltinExports); JSC_DECLARE_HOST_FUNCTION(jsFunctionWrap); -JSC_DECLARE_CUSTOM_GETTER(get_resolveFilename); -JSC_DECLARE_CUSTOM_SETTER(set_resolveFilename); JSC_DECLARE_CUSTOM_GETTER(getterRequireFunction); JSC_DECLARE_CUSTOM_SETTER(setterRequireFunction); @@ -342,7 +340,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionResolveFileName, } } -JSC_DEFINE_CUSTOM_GETTER(get_resolveFilename, +JSC_DEFINE_CUSTOM_GETTER(nodeModuleResolveFilename, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName propertyName)) { @@ -353,7 +351,7 @@ JSC_DEFINE_CUSTOM_GETTER(get_resolveFilename, globalObject)); } -JSC_DEFINE_CUSTOM_SETTER(set_resolveFilename, +JSC_DEFINE_CUSTOM_SETTER(setNodeModuleResolveFilename, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, EncodedJSValue encodedValue, PropertyName propertyName)) { @@ -608,6 +606,7 @@ static JSValue getModulePrototypeObject(VM &vm, JSObject *moduleObject) { auto *globalObject = defaultGlobalObject(moduleObject->globalObject()); auto prototype = constructEmptyObject(globalObject, globalObject->objectPrototype(), 2); + prototype->putDirectCustomAccessor( vm, WebCore::clientData(vm)->builtinNames().requirePublicName(), JSC::CustomGetterSetter::create(vm, getterRequireFunction, @@ -671,7 +670,7 @@ _load jsFunctionLoad Function 1 _nodeModulePaths Resolver__nodeModulePathsForJS Function 1 _pathCache getPathCacheObject PropertyCallback _preloadModules jsFunctionPreloadModules Function 0 -_resolveFilename jsFunctionResolveFileName Function 2 +_resolveFilename nodeModuleResolveFilename CustomAccessor _resolveLookupPaths jsFunctionResolveLookupPaths Function 2 builtinModules getBuiltinModulesObject PropertyCallback constants getConstantsObject PropertyCallback @@ -762,4 +761,59 @@ void addNodeModuleConstructorProperties(JSC::VM &vm, }); } -} // namespace Bun \ No newline at end of file +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsModuleResolveFilenameSlowPathEnabled, + (JSGlobalObject * globalObject, + CallFrame *callframe)) { + return JSValue::encode( + jsBoolean(defaultGlobalObject(globalObject) + ->hasOverridenModuleResolveFilenameFunction)); +} + +} // namespace Bun + +namespace Zig { +void generateNativeModule_NodeModule(JSC::JSGlobalObject *lexicalGlobalObject, + JSC::Identifier moduleKey, + Vector &exportNames, + JSC::MarkedArgumentBuffer &exportValues) { + Zig::GlobalObject *globalObject = defaultGlobalObject(lexicalGlobalObject); + auto &vm = globalObject->vm(); + auto catchScope = DECLARE_CATCH_SCOPE(vm); + auto *constructor = + globalObject->m_nodeModuleConstructor.getInitializedOnMainThread( + globalObject); + if (constructor->hasNonReifiedStaticProperties()) { + constructor->reifyAllStaticProperties(globalObject); + if (catchScope.exception()) { + catchScope.clearException(); + } + } + + exportNames.reserveCapacity(Bun::countof(Bun::nodeModuleObjectTableValues) + + 1); + exportValues.ensureCapacity(Bun::countof(Bun::nodeModuleObjectTableValues) + + 1); + + for (unsigned i = 0; i < Bun::countof(Bun::nodeModuleObjectTableValues); + ++i) { + const auto &entry = Bun::nodeModuleObjectTableValues[i]; + const auto &property = Identifier::fromString(vm, entry.m_key); + JSValue value = constructor->getIfPropertyExists(globalObject, property); + + if (UNLIKELY(catchScope.exception())) { + value = {}; + catchScope.clearException(); + } + if (UNLIKELY(value.isEmpty())) { + value = JSC::jsUndefined(); + } + + exportNames.append(property); + exportValues.append(value); + } + + exportNames.append(vm.propertyNames->defaultKeyword); + exportValues.append(constructor); +} + +} // namespace Zig diff --git a/src/bun.js/modules/NodeModuleModule.h b/src/bun.js/modules/NodeModuleModule.h index ee7d63c4a0dd63..bc25556136d652 100644 --- a/src/bun.js/modules/NodeModuleModule.h +++ b/src/bun.js/modules/NodeModuleModule.h @@ -18,6 +18,7 @@ using namespace JSC; namespace Bun { + JSC_DECLARE_HOST_FUNCTION(jsFunctionIsModuleResolveFilenameSlowPathEnabled); void addNodeModuleConstructorProperties(JSC::VM &vm, Zig::GlobalObject *globalObject); } @@ -28,269 +29,10 @@ namespace Bun { namespace Zig { -DEFINE_NATIVE_MODULE(NodeModule) { - // the default object here is a function, so we cant use the - // INIT_NATIVE_MODULE helper - Zig::GlobalObject *globalObject = reinterpret_cast(lexicalGlobalObject); - JSC::VM &vm = globalObject->vm(); - auto scope = DECLARE_CATCH_SCOPE(vm); - auto *object = globalObject->m_nodeModuleConstructor.getInitializedOnMainThread(globalObject); +void generateNativeModule_NodeModule( + JSC::JSGlobalObject *lexicalGlobalObject, JSC::Identifier moduleKey, + Vector &exportNames, + JSC::MarkedArgumentBuffer &exportValues); -if (object->hasNonReifiedStaticProperties()) { - object->reifyAllStaticProperties(globalObject); - if (scope.exception()) { - scope.clearException(); - } -} - -const auto _cacheIdentifier = Identifier::fromString(vm, "_cache"_s); - auto _cacheValue = - object->getIfPropertyExists(globalObject, _cacheIdentifier); - if (scope.exception()) { - scope.clearException(); - _cacheValue = jsUndefined(); - } - const auto _debugIdentifier = Identifier::fromString(vm, "_debug"_s); - auto _debugValue = - object->getIfPropertyExists(globalObject, _debugIdentifier); - if (scope.exception()) { - scope.clearException(); - _debugValue = jsUndefined(); - } - const auto _extensionsIdentifier = - Identifier::fromString(vm, "_extensions"_s); - auto _extensionsValue = - object->getIfPropertyExists(globalObject, _extensionsIdentifier); - if (scope.exception()) { - scope.clearException(); - _extensionsValue = jsUndefined(); - } - const auto _findPathIdentifier = Identifier::fromString(vm, "_findPath"_s); - auto _findPathValue = - object->getIfPropertyExists(globalObject, _findPathIdentifier); - if (scope.exception()) { - scope.clearException(); - _findPathValue = jsUndefined(); - } - const auto _initPathsIdentifier = Identifier::fromString(vm, "_initPaths"_s); - auto _initPathsValue = - object->getIfPropertyExists(globalObject, _initPathsIdentifier); - if (scope.exception()) { - scope.clearException(); - _initPathsValue = jsUndefined(); - } - const auto _loadIdentifier = Identifier::fromString(vm, "_load"_s); - auto _loadValue = object->getIfPropertyExists(globalObject, _loadIdentifier); - if (scope.exception()) { - scope.clearException(); - _loadValue = jsUndefined(); - } - - const auto _nodeModulePathsIdentifier = - Identifier::fromString(vm, "_nodeModulePaths"_s); - auto _nodeModulePathsValue = - object->getIfPropertyExists(globalObject, _nodeModulePathsIdentifier); - if (scope.exception()) { - scope.clearException(); - _nodeModulePathsValue = jsUndefined(); - } - const auto _pathCacheIdentifier = Identifier::fromString(vm, "_pathCache"_s); - auto _pathCacheValue = - object->getIfPropertyExists(globalObject, _pathCacheIdentifier); - if (scope.exception()) { - scope.clearException(); - _pathCacheValue = jsUndefined(); - } - const auto _preloadModulesIdentifier = - Identifier::fromString(vm, "_preloadModules"_s); - auto _preloadModulesValue = - object->getIfPropertyExists(globalObject, _preloadModulesIdentifier); - if (scope.exception()) { - scope.clearException(); - _preloadModulesValue = jsUndefined(); - } - const auto _resolveFilenameIdentifier = - Identifier::fromString(vm, "_resolveFilename"_s); - auto _resolveFilenameValue = - object->getIfPropertyExists(globalObject, _resolveFilenameIdentifier); - if (scope.exception()) { - scope.clearException(); - _resolveFilenameValue = jsUndefined(); - } - const auto _resolveLookupPathsIdentifier = - Identifier::fromString(vm, "_resolveLookupPaths"_s); - auto _resolveLookupPathsValue = - object->getIfPropertyExists(globalObject, _resolveLookupPathsIdentifier); - if (scope.exception()) { - scope.clearException(); - _resolveLookupPathsValue = jsUndefined(); - } - const auto builtinModulesIdentifier = - Identifier::fromString(vm, "builtinModules"_s); - auto builtinModulesValue = - object->getIfPropertyExists(globalObject, builtinModulesIdentifier); - if (scope.exception()) { - scope.clearException(); - builtinModulesValue = jsUndefined(); - } - const auto constantsIdentifier = Identifier::fromString(vm, "constants"_s); - auto constantsValue = - object->getIfPropertyExists(globalObject, constantsIdentifier); - if (scope.exception()) { - scope.clearException(); - constantsValue = jsUndefined(); - } - const auto createRequireIdentifier = - Identifier::fromString(vm, "createRequire"_s); - auto createRequireValue = - object->getIfPropertyExists(globalObject, createRequireIdentifier); - if (scope.exception()) { - scope.clearException(); - createRequireValue = jsUndefined(); - } - const auto enableCompileCacheIdentifier = - Identifier::fromString(vm, "enableCompileCache"_s); - auto enableCompileCacheValue = - object->getIfPropertyExists(globalObject, enableCompileCacheIdentifier); - if (scope.exception()) { - scope.clearException(); - enableCompileCacheValue = jsUndefined(); - } - const auto findSourceMapIdentifier = - Identifier::fromString(vm, "findSourceMap"_s); - auto findSourceMapValue = - object->getIfPropertyExists(globalObject, findSourceMapIdentifier); - if (scope.exception()) { - scope.clearException(); - findSourceMapValue = jsUndefined(); - } - const auto getCompileCacheDirIdentifier = - Identifier::fromString(vm, "getCompileCacheDir"_s); - auto getCompileCacheDirValue = - object->getIfPropertyExists(globalObject, getCompileCacheDirIdentifier); - if (scope.exception()) { - scope.clearException(); - getCompileCacheDirValue = jsUndefined(); - } - const auto globalPathsIdentifier = - Identifier::fromString(vm, "globalPaths"_s); - auto globalPathsValue = - object->getIfPropertyExists(globalObject, globalPathsIdentifier); - if (scope.exception()) { - scope.clearException(); - globalPathsValue = jsUndefined(); - } - const auto isBuiltinIdentifier = Identifier::fromString(vm, "isBuiltin"_s); - auto isBuiltinValue = - object->getIfPropertyExists(globalObject, isBuiltinIdentifier); - if (scope.exception()) { - scope.clearException(); - isBuiltinValue = jsUndefined(); - } - const auto prototypeIdentifier = Identifier::fromString(vm, "prototype"_s); - auto prototypeValue = - object->getIfPropertyExists(globalObject, prototypeIdentifier); - if (scope.exception()) { - scope.clearException(); - prototypeValue = jsUndefined(); - } - const auto registerIdentifier = Identifier::fromString(vm, "register"_s); - auto registerValue = - object->getIfPropertyExists(globalObject, registerIdentifier); - if (scope.exception()) { - scope.clearException(); - registerValue = jsUndefined(); - } - const auto runMainIdentifier = Identifier::fromString(vm, "runMain"_s); - auto runMainValue = - object->getIfPropertyExists(globalObject, runMainIdentifier); - if (scope.exception()) { - scope.clearException(); - runMainValue = jsUndefined(); - } - const auto SourceMapIdentifier = Identifier::fromString(vm, "SourceMap"_s); - auto SourceMapValue = - object->getIfPropertyExists(globalObject, SourceMapIdentifier); - if (scope.exception()) { - scope.clearException(); - SourceMapValue = jsUndefined(); - } - const auto syncBuiltinESMExportsIdentifier = - Identifier::fromString(vm, "syncBuiltinESMExports"_s); - auto syncBuiltinESMExportsValue = object->getIfPropertyExists( - globalObject, syncBuiltinESMExportsIdentifier); -if (scope.exception()) { - scope.clearException(); - syncBuiltinESMExportsValue = jsUndefined(); -} - - const auto wrapIdentifier = Identifier::fromString(vm, "wrap"_s); - auto wrapValue = object->getIfPropertyExists(globalObject, wrapIdentifier); - if (scope.exception()) { - scope.clearException(); - wrapValue = jsUndefined(); - } - - - - exportNames.reserveCapacity(24+1); - exportValues.ensureCapacity(24+1); - exportNames.append(vm.propertyNames->defaultKeyword); - exportValues.append(object); - - exportNames.append(_cacheIdentifier); - exportValues.append(_cacheValue); - exportNames.append(_debugIdentifier); - exportValues.append(_debugValue); - exportNames.append(_extensionsIdentifier); - exportValues.append(_extensionsValue); - exportNames.append(_findPathIdentifier); - exportValues.append(_findPathValue); - exportNames.append(_initPathsIdentifier); - exportValues.append(_initPathsValue); - exportNames.append(_loadIdentifier); - exportValues.append(_loadValue); - exportNames.append(_nodeModulePathsIdentifier); - exportValues.append(_nodeModulePathsValue); - exportNames.append(_pathCacheIdentifier); - exportValues.append(_pathCacheValue); - exportNames.append(_preloadModulesIdentifier); - exportValues.append(_preloadModulesValue); - exportNames.append(_resolveFilenameIdentifier); - exportValues.append(_resolveFilenameValue); - exportNames.append(_resolveLookupPathsIdentifier); - exportValues.append(_resolveLookupPathsValue); - exportNames.append(builtinModulesIdentifier); - exportValues.append(builtinModulesValue); - exportNames.append(constantsIdentifier); - exportValues.append(constantsValue); - exportNames.append(createRequireIdentifier); - exportValues.append(createRequireValue); - exportNames.append(enableCompileCacheIdentifier); - exportValues.append(enableCompileCacheValue); - exportNames.append(findSourceMapIdentifier); - exportValues.append(findSourceMapValue); - exportNames.append(getCompileCacheDirIdentifier); - exportValues.append(getCompileCacheDirValue); - exportNames.append(globalPathsIdentifier); - exportValues.append(globalPathsValue); - exportNames.append(isBuiltinIdentifier); - exportValues.append(isBuiltinValue); - exportNames.append(prototypeIdentifier); - exportValues.append(prototypeValue); - exportNames.append(registerIdentifier); - exportValues.append(registerValue); - exportNames.append(runMainIdentifier); - exportValues.append(runMainValue); - exportNames.append(SourceMapIdentifier); - exportValues.append(SourceMapValue); - exportNames.append(syncBuiltinESMExportsIdentifier); - exportValues.append(syncBuiltinESMExportsValue); - exportNames.append(wrapIdentifier); - exportValues.append(wrapValue); - - - -} } // namespace Zig diff --git a/src/bun.js/modules/_NativeModule.h b/src/bun.js/modules/_NativeModule.h index 82151175ad1508..cd77b528d2acd3 100644 --- a/src/bun.js/modules/_NativeModule.h +++ b/src/bun.js/modules/_NativeModule.h @@ -24,18 +24,25 @@ // If you decide to not use INIT_NATIVE_MODULE. make sure the first property // given is the default export -#define BUN_FOREACH_NATIVE_MODULE(macro) \ +#define BUN_FOREACH_ESM_AND_CJS_NATIVE_MODULE(macro) \ macro("bun"_s, BunObject) \ macro("bun:test"_s, BunTest) \ macro("bun:jsc"_s, BunJSC) \ macro("node:buffer"_s, NodeBuffer) \ macro("node:constants"_s, NodeConstants) \ - macro("node:module"_s, NodeModule) \ - macro("node:process"_s, NodeProcess) \ macro("node:string_decoder"_s, NodeStringDecoder) \ macro("node:util/types"_s, NodeUtilTypes) \ macro("utf-8-validate"_s, UTF8Validate) \ - macro("abort-controller"_s, AbortControllerModule) \ + macro("abort-controller"_s, AbortControllerModule) + +#define BUN_FOREACH_ESM_NATIVE_MODULE(macro) \ + BUN_FOREACH_ESM_AND_CJS_NATIVE_MODULE(macro) \ + macro("node:module"_s, NodeModule) \ + macro("node:process"_s, NodeProcess) + +#define BUN_FOREACH_CJS_NATIVE_MODULE(macro) \ + BUN_FOREACH_ESM_AND_CJS_NATIVE_MODULE(macro) + #if ASSERT_ENABLED diff --git a/src/js/internal-for-testing.ts b/src/js/internal-for-testing.ts index 800fa5f543fb94..31b0ca73fd64c6 100644 --- a/src/js/internal-for-testing.ts +++ b/src/js/internal-for-testing.ts @@ -135,3 +135,9 @@ export const isOperatingSystemMatch: (operatingSystem: string[]) => boolean = $n ); export const createSocketPair: () => [number, number] = $newZigFunction("socket.zig", "jsCreateSocketPair", 0); + +export const isModuleResolveFilenameSlowPathEnabled: () => boolean = $newCppFunction( + "NodeModuleModule.cpp", + "jsFunctionIsModuleResolveFilenameSlowPathEnabled", + 0, +); diff --git a/test/js/bun/resolve/import-meta.test.js b/test/js/bun/resolve/import-meta.test.js index 668e3ca8e96870..10b58c627e1df8 100644 --- a/test/js/bun/resolve/import-meta.test.js +++ b/test/js/bun/resolve/import-meta.test.js @@ -2,10 +2,11 @@ import { spawnSync } from "bun"; import { expect, it } from "bun:test"; import { bunEnv, bunExe, ospath } from "harness"; import { mkdirSync, rmSync, writeFileSync } from "node:fs"; -import * as Module from "node:module"; +import Module from "node:module"; import { tmpdir } from "node:os"; import { join, sep } from "node:path"; import sync from "./require-json.json"; +import { isModuleResolveFilenameSlowPathEnabled } from "bun:internal-for-testing"; const { path, dir, dirname, filename } = import.meta; @@ -127,7 +128,12 @@ it("Module._cache", () => { }); it("Module._resolveFilename()", () => { - expect(Module._resolveFilename).toBeUndefined(); + expect(isModuleResolveFilenameSlowPathEnabled()).toBe(false); + const original = Module._resolveFilename; + Module._resolveFilename = () => {}; + expect(isModuleResolveFilenameSlowPathEnabled()).toBe(true); + Module._resolveFilename = original; + expect(isModuleResolveFilenameSlowPathEnabled()).toBe(false); }); it("Module.createRequire(file://url).resolve(file://url)", () => { From e74dcc1261c361d3ffa20d6b9dba1957c561b9d9 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Wed, 2 Oct 2024 22:38:04 -0700 Subject: [PATCH 5/7] Update internal-module-registry-scanner.ts --- src/codegen/internal-module-registry-scanner.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/codegen/internal-module-registry-scanner.ts b/src/codegen/internal-module-registry-scanner.ts index 5f1dee46216131..74c1abc669b637 100644 --- a/src/codegen/internal-module-registry-scanner.ts +++ b/src/codegen/internal-module-registry-scanner.ts @@ -31,10 +31,10 @@ export function createInternalModuleRegistry(basedir: string) { // Native Module registry const nativeModuleH = fs.readFileSync(path.join(basedir, "../bun.js/modules/_NativeModule.h"), "utf8"); - const nativeModuleDefine = nativeModuleH.match(/BUN_FOREACH_NATIVE_MODULE\(macro\)\s*\\\n((.*\\\n)*\n)/); + const nativeModuleDefine = nativeModuleH.match(/BUN_FOREACH_ESM_AND_CJS_NATIVE_MODULE\(macro\)\s*\\\n((.*\\\n)*\n)/); if (!nativeModuleDefine) { throw new Error( - "Could not find BUN_FOREACH_NATIVE_MODULE in _NativeModule.h. Knowing native module IDs is a part of the codegen process.", + "Could not find BUN_FOREACH_ESM_AND_CJS_NATIVE_MODULE in _NativeModule.h. Knowing native module IDs is a part of the codegen process.", ); } let nextNativeModuleId = 0; From 4b8f3bae3714932fb46cc14a4817c39f49fcea38 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Wed, 2 Oct 2024 23:48:50 -0700 Subject: [PATCH 6/7] Update internal-module-registry-scanner.ts --- .../internal-module-registry-scanner.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/codegen/internal-module-registry-scanner.ts b/src/codegen/internal-module-registry-scanner.ts index 74c1abc669b637..71458fd2e27c11 100644 --- a/src/codegen/internal-module-registry-scanner.ts +++ b/src/codegen/internal-module-registry-scanner.ts @@ -29,19 +29,14 @@ export function createInternalModuleRegistry(basedir: string) { moduleList.push("internal-for-testing.ts"); internalRegistry.set("bun:internal-for-testing", moduleList.length - 1); - // Native Module registry - const nativeModuleH = fs.readFileSync(path.join(basedir, "../bun.js/modules/_NativeModule.h"), "utf8"); - const nativeModuleDefine = nativeModuleH.match(/BUN_FOREACH_ESM_AND_CJS_NATIVE_MODULE\(macro\)\s*\\\n((.*\\\n)*\n)/); - if (!nativeModuleDefine) { - throw new Error( - "Could not find BUN_FOREACH_ESM_AND_CJS_NATIVE_MODULE in _NativeModule.h. Knowing native module IDs is a part of the codegen process.", - ); - } let nextNativeModuleId = 0; const nativeModuleIds: Record = {}; const nativeModuleEnums: Record = {}; const nativeModuleEnumToId: Record = {}; - for (const [_, idString, enumValue] of nativeModuleDefine[0].matchAll(/macro\((.*?),(.*?)\)/g)) { + + // Native Module registry + const nativeModuleH = fs.readFileSync(path.join(basedir, "../bun.js/modules/_NativeModule.h"), "utf8"); + for (const [_, idString, enumValue] of nativeModuleH.matchAll(/macro\((.*?),(.*?)\)/g)) { const processedIdString = JSON.parse(idString.trim().replace(/_s$/, "")); const processedEnumValue = enumValue.trim(); const processedNumericId = nextNativeModuleId++; @@ -50,6 +45,12 @@ export function createInternalModuleRegistry(basedir: string) { nativeModuleEnumToId[processedEnumValue] = processedNumericId; } + if (nextNativeModuleId === 0) { + throw new Error( + "Could not find BUN_FOREACH_ESM_AND_CJS_NATIVE_MODULE in _NativeModule.h. Knowing native module IDs is a part of the codegen process.", + ); + } + function codegenRequireId(id: string) { return `(__intrinsic__getInternalField(__intrinsic__internalModuleRegistry, ${id}) || __intrinsic__createInternalModuleById(${id}))`; } From bcab8f1bc2aa17834364830b76cacd3713ae01bc Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Thu, 3 Oct 2024 00:16:50 -0700 Subject: [PATCH 7/7] Fix bug in createRequire --- src/bun.js/modules/NodeModuleModule.cpp | 30 ++++++++++++++++--- src/bun.js/node/path.zig | 15 ++++++++++ .../js/node/module/node-module-module.test.js | 16 +++++++++- 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/bun.js/modules/NodeModuleModule.cpp b/src/bun.js/modules/NodeModuleModule.cpp index b4b17855f57656..46d0cf36fb3d4a 100644 --- a/src/bun.js/modules/NodeModuleModule.cpp +++ b/src/bun.js/modules/NodeModuleModule.cpp @@ -15,6 +15,7 @@ #include "NodeModuleModule.h" +#include "ErrorCode.h" #include #include namespace Bun { @@ -217,19 +218,21 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionWrap, (JSC::JSGlobalObject * globalObject, return JSValue::encode(jsString(globalObject, prefix, code, suffix)); } - +extern "C" void Bun__Node__Path_joinWTF(BunString *lhs, const char *rhs, + size_t len, BunString *result); JSC_DEFINE_HOST_FUNCTION(jsFunctionNodeModuleCreateRequire, (JSC::JSGlobalObject * globalObject, JSC::CallFrame *callFrame)) { JSC::VM &vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); if (callFrame->argumentCount() < 1) { - throwTypeError(globalObject, scope, - "createRequire() requires at least one argument"_s); - RELEASE_AND_RETURN(scope, JSC::JSValue::encode({})); + return Bun::throwError(globalObject, scope, + Bun::ErrorCode::ERR_MISSING_ARGS, + "createRequire() requires at least one argument"_s); } auto val = callFrame->uncheckedArgument(0).toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); if (val.startsWith("file://"_s)) { WTF::URL url(val); @@ -247,6 +250,25 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionNodeModuleCreateRequire, val = url.fileSystemPath(); } + bool trailingSlash = val.endsWith('/'); +#if OS(WINDOWS) + if (val.endsWith('\\')) { + trailingSlash = true; + } +#endif + + // https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/lib/internal/modules/cjs/loader.js#L1603-L1620 + if (trailingSlash) { + BunString lhs = Bun::toString(val); + BunString result; + Bun__Node__Path_joinWTF(&lhs, "noop.js", sizeof("noop.js") - 1, &result); + val = result.toWTFString(); + if (!val.isNull()) { + ASSERT(val.impl()->refCount() == 2); + val.impl()->deref(); + } + } + RETURN_IF_EXCEPTION(scope, {}); RELEASE_AND_RETURN( scope, JSValue::encode(Bun::JSCommonJSModule::createBoundRequireFunction( diff --git a/src/bun.js/node/path.zig b/src/bun.js/node/path.zig index dcabe3aae5f6d3..d271f495c49ec6 100644 --- a/src/bun.js/node/path.zig +++ b/src/bun.js/node/path.zig @@ -1104,6 +1104,21 @@ pub inline fn joinPosixT(comptime T: type, paths: []const []const T, buf: []T, b return normalizePosixT(T, joined, buf); } +export fn Bun__Node__Path_joinWTF(lhs: *bun.String, rhs_ptr: [*]const u8, rhs_len: usize, result: *bun.String) void { + const rhs = rhs_ptr[0..rhs_len]; + var buf: [PATH_SIZE(u8)]u8 = undefined; + var buf2: [PATH_SIZE(u8)]u8 = undefined; + var slice = lhs.toUTF8(bun.default_allocator); + defer slice.deinit(); + if (Environment.isWindows) { + const win = joinWindowsT(u8, &.{ slice.slice(), rhs }, &buf, &buf2); + result.* = bun.String.createUTF8(win); + } else { + const posix = joinPosixT(u8, &.{ slice.slice(), rhs }, &buf, &buf2); + result.* = bun.String.createUTF8(posix); + } +} + /// Based on Node v21.6.1 path.win32.join: /// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L425 pub fn joinWindowsT(comptime T: type, paths: []const []const T, buf: []T, buf2: []T) []const T { diff --git a/test/js/node/module/node-module-module.test.js b/test/js/node/module/node-module-module.test.js index ee5d6c303ea287..9c44c9656e461a 100644 --- a/test/js/node/module/node-module-module.test.js +++ b/test/js/node/module/node-module-module.test.js @@ -1,6 +1,6 @@ import { expect, test } from "bun:test"; import { bunEnv, bunExe, ospath } from "harness"; -import Module, { _nodeModulePaths, builtinModules, isBuiltin, wrap } from "module"; +import Module, { _nodeModulePaths, builtinModules, isBuiltin, wrap, createRequire } from "module"; import path from "path"; test("builtinModules exists", () => { @@ -23,6 +23,20 @@ test("module.globalPaths exists", () => { expect(Array.isArray(require("module").globalPaths)).toBe(true); }); +test("createRequire trailing slash", () => { + const req = createRequire(import.meta.dir + "/"); + expect(req.resolve("./node-module-module.test.js")).toBe( + ospath(path.resolve(import.meta.dir, "./node-module-module.test.js")), + ); +}); + +test("createRequire trailing slash file url", () => { + const req = createRequire(Bun.pathToFileURL(import.meta.dir + "/")); + expect(req.resolve("./node-module-module.test.js")).toBe( + ospath(path.resolve(import.meta.dir, "./node-module-module.test.js")), + ); +}); + test("Module exists", () => { expect(Module).toBeDefined(); });