diff options
author | 2022-05-09 00:24:25 -0700 | |
---|---|---|
committer | 2022-05-09 00:24:25 -0700 | |
commit | 8014a0b8d8f5d9f7c20ad25f1ee7045bd2ca384a (patch) | |
tree | 8b5b2c4a67e64c6f04da79e8a0577a0ad78e6359 | |
parent | 3bd83eb134f60044a231b6357e597be47831bdd4 (diff) | |
download | bun-8014a0b8d8f5d9f7c20ad25f1ee7045bd2ca384a.tar.gz bun-8014a0b8d8f5d9f7c20ad25f1ee7045bd2ca384a.tar.zst bun-8014a0b8d8f5d9f7c20ad25f1ee7045bd2ca384a.zip |
[napi] Support `import` and `require` of `.node` modules
-rw-r--r-- | src/base64/neonbase64.cc | 2 | ||||
-rw-r--r-- | src/defines.zig | 23 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/Process.cpp | 2 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/ZigGlobalObject.cpp | 35 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/napi.cpp | 66 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/napi.h | 5 |
6 files changed, 80 insertions, 53 deletions
diff --git a/src/base64/neonbase64.cc b/src/base64/neonbase64.cc index a1249d21b..deaf7ac38 100644 --- a/src/base64/neonbase64.cc +++ b/src/base64/neonbase64.cc @@ -6,7 +6,7 @@ #include "chromiumbase64.h" #define MODP_B64_ERROR ((size_t)-1) -#include <iostream> +// #include <iostream> extern "C" int neon_base64_decode(char *out, const char *src, size_t srclen, size_t *outlen); diff --git a/src/defines.zig b/src/defines.zig index 507ad8851..a0c885f34 100644 --- a/src/defines.zig +++ b/src/defines.zig @@ -196,11 +196,6 @@ pub const DotDefine = struct { var nan_val = js_ast.E.Number{ .value = std.math.nan_f64 }; var inf_val = js_ast.E.Number{ .value = std.math.inf_f64 }; -const __dirname_str: string = std.fs.path.sep_str ++ "__dirname_is_not_implemented"; -const __filename_str: string = "__filename_is_not_implemented.js"; -var __dirname = js_ast.E.String.init(__dirname_str); -var __filename = js_ast.E.String.init(__filename_str); - pub const Define = struct { identifiers: std.StringHashMap(IdentifierDefine), dots: std.StringHashMap([]DotDefine), @@ -301,24 +296,6 @@ pub const Define = struct { } } - // Node.js backwards compatibility hack - define.identifiers.putAssumeCapacity( - "__dirname", - DefineData{ - .value = js_ast.Expr.Data{ - .e_string = &__dirname, - }, - }, - ); - define.identifiers.putAssumeCapacity( - "__filename", - DefineData{ - .value = js_ast.Expr.Data{ - .e_string = &__filename, - }, - }, - ); - // Step 2. Swap in certain literal values because those can be constant folded define.identifiers.putAssumeCapacity("undefined", .{ .value = val, diff --git a/src/javascript/jsc/bindings/Process.cpp b/src/javascript/jsc/bindings/Process.cpp index e732b0d54..5fc15db4d 100644 --- a/src/javascript/jsc/bindings/Process.cpp +++ b/src/javascript/jsc/bindings/Process.cpp @@ -146,7 +146,7 @@ static JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, if (!napi_register_module_v1) { dlclose(handle); - JSC::throwTypeError(globalObject, scope, "dlopen failed to napi_register_module_v1"_s); + JSC::throwTypeError(globalObject, scope, "symbol 'napi_register_module_v1' not found in native module. Is this a Node API (napi) module?"_s); return JSC::JSValue::encode(JSC::JSValue {}); } diff --git a/src/javascript/jsc/bindings/ZigGlobalObject.cpp b/src/javascript/jsc/bindings/ZigGlobalObject.cpp index 72ea28c59..3820dcef2 100644 --- a/src/javascript/jsc/bindings/ZigGlobalObject.cpp +++ b/src/javascript/jsc/bindings/ZigGlobalObject.cpp @@ -108,6 +108,9 @@ using JSObject = JSC::JSObject; using JSNonFinalObject = JSC::JSNonFinalObject; namespace JSCastingHelpers = JSC::JSCastingHelpers; using JSBuffer = WebCore::JSBuffer; +#include <dlfcn.h> + +// #include <iostream> static bool has_loaded_jsc = false; @@ -1041,6 +1044,38 @@ JSC::JSInternalPromise* GlobalObject::moduleLoaderFetch(JSGlobalObject* globalOb auto moduleKey = key.toWTFString(globalObject); RETURN_IF_EXCEPTION(scope, promise->rejectWithCaughtException(globalObject, scope)); + if (moduleKey.endsWith(".node")) { + CString utf8 = moduleKey.utf8(); + void* handle = dlopen(utf8.data(), RTLD_LAZY); + + if (!handle) { + WTF::String msg = WTF::String::fromUTF8(dlerror()); + return rejectWithError(JSC::createTypeError(globalObject, msg)); + } + + JSC::EncodedJSValue (*napi_register_module_v1)(JSC::JSGlobalObject * globalObject, + JSC::EncodedJSValue exports); + + napi_register_module_v1 = reinterpret_cast<JSC::EncodedJSValue (*)(JSC::JSGlobalObject*, + JSC::EncodedJSValue)>( + dlsym(handle, "napi_register_module_v1")); + + if (!napi_register_module_v1) { + dlclose(handle); + return rejectWithError(JSC::createTypeError(globalObject, "symbol 'napi_register_module_v1' not found in native module. Is this a Node API (napi) module?"_s)); + } + JSC::JSValue exports = JSC::constructEmptyObject(globalObject); + + JSC::JSValue returnedExports = JSC::JSValue::decode(napi_register_module_v1(globalObject, JSC::JSValue::encode(exports))); + + auto sourceCode = Napi::generateSourceCode(moduleKey, vm, returnedExports.getObject(), globalObject); + + scope.releaseAssertNoExceptionExceptTermination(); + auto jsSourceCode = JSC::JSSourceCode::create(vm, WTFMove(sourceCode)); + promise->resolve(globalObject, jsSourceCode); + return promise; + } + auto moduleKeyZig = toZigString(moduleKey); auto source = Zig::toZigString(value1, globalObject); ErrorableResolvedSource res; diff --git a/src/javascript/jsc/bindings/napi.cpp b/src/javascript/jsc/bindings/napi.cpp index 5ec8bb6b2..94c18ebac 100644 --- a/src/javascript/jsc/bindings/napi.cpp +++ b/src/javascript/jsc/bindings/napi.cpp @@ -44,11 +44,44 @@ #include "JavaScriptCore/JSWeakValue.h" #include "napi.h" #include "JavaScriptCore/GetterSetter.h" +#include "JavaScriptCore/JSSourceCode.h" -#include <iostream> +// #include <iostream> using namespace JSC; using namespace Zig; +namespace Napi { + +JSC::SourceCode generateSourceCode(WTF::String keyString, JSC::VM& vm, JSC::JSObject* object, JSC::JSGlobalObject* globalObject) +{ + + JSC::JSArray* exportKeys = ownPropertyKeys(globalObject, object, PropertyNameMode::StringsAndSymbols, DontEnumPropertiesMode::Include, std::nullopt); + auto symbol = vm.symbolRegistry().symbolForKey("__BunTemporaryGlobal"_s); + JSC::Identifier ident = JSC::Identifier::fromUid(symbol); + WTF::StringBuilder sourceCodeBuilder = WTF::StringBuilder(); + // TODO: handle symbol collision + sourceCodeBuilder.append("var $$TempSymbol = Symbol.for('__BunTemporaryGlobal'), $$NativeModule = globalThis[$$TempSymbol]; globalThis[$$TempSymbol] = null;\n if (!$$NativeModule) { throw new Error('Assertion failure: Native module not found'); }\n\n"_s); + + for (unsigned i = 0; i < exportKeys->length(); i++) { + auto key = exportKeys->getIndexQuickly(i); + if (key.isSymbol()) { + continue; + } + auto named = key.toWTFString(globalObject); + sourceCodeBuilder.append(""_s); + // TODO: handle invalid identifiers + sourceCodeBuilder.append("export var "_s); + sourceCodeBuilder.append(named); + sourceCodeBuilder.append(" = $$NativeModule."_s); + sourceCodeBuilder.append(named); + sourceCodeBuilder.append(";\n"_s); + } + globalObject->putDirect(vm, ident, object, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::DontEnum); + return JSC::makeSource(sourceCodeBuilder.toString(), JSC::SourceOrigin(), keyString, WTF::TextPosition(), JSC::SourceProviderSourceType::Module); +} + +} + // #include <csignal> #define NAPI_OBJECT_EXPECTED napi_object_expected @@ -233,43 +266,20 @@ static void defineNapiProperty(Zig::GlobalObject* globalObject, JSC::JSObject* t extern "C" void napi_module_register(napi_module* mod) { - auto* globalObject = Bun__getDefaultGlobal(); JSC::VM& vm = globalObject->vm(); JSC::JSObject* object = JSC::constructEmptyObject(globalObject); auto result = reinterpret_cast<JSC::EncodedJSValue>( mod->nm_register_func(reinterpret_cast<napi_env>(globalObject), reinterpret_cast<napi_value>(JSC::JSValue::encode(JSC::JSValue(object))))); - auto keyString = WTF::String::fromUTF8(mod->nm_modname); - JSC::JSString* key = JSC::jsString(vm, keyString); - - JSC::JSArray* exportKeys = ownPropertyKeys(globalObject, object, PropertyNameMode::StringsAndSymbols, DontEnumPropertiesMode::Include, std::nullopt); - auto symbol = vm.symbolRegistry().symbolForKey("__BunTemporaryGlobal"_s); - JSC::Identifier ident = JSC::Identifier::fromUid(symbol); - WTF::StringBuilder sourceCodeBuilder = WTF::StringBuilder(); - // TODO: handle symbol collision - sourceCodeBuilder.append("var $$TempSymbol = Symbol.for('__BunTemporaryGlobal'), $$NativeModule = globalThis[$$TempSymbol]; globalThis[$$TempSymbol] = null;\n if (!$$NativeModule) { throw new Error('Assertion failure: Native module not found'); }\n\n"_s); + // std::cout << "loaded " << mod->nm_modname << std::endl; + auto keyStr = WTF::String::fromUTF8(mod->nm_modname); + auto key = JSC::jsString(vm, keyStr); + auto sourceCode = Napi::generateSourceCode(keyStr, vm, object, globalObject); - for (unsigned i = 0; i < exportKeys->length(); i++) { - auto key = exportKeys->getIndexQuickly(i); - if (key.isSymbol()) { - continue; - } - auto keyString = key.toWTFString(globalObject); - sourceCodeBuilder.append(""_s); - // TODO: handle invalid identifiers - sourceCodeBuilder.append("export var "_s); - sourceCodeBuilder.append(keyString); - sourceCodeBuilder.append(" = $$NativeModule."_s); - sourceCodeBuilder.append(keyString); - sourceCodeBuilder.append(";\n"_s); - } - auto sourceCode = JSC::makeSource(sourceCodeBuilder.toString(), JSC::SourceOrigin(), keyString, WTF::TextPosition(), JSC::SourceProviderSourceType::Module); - globalObject->putDirect(vm, ident, object, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::DontEnum); globalObject->moduleLoader()->provideFetch(globalObject, key, WTFMove(sourceCode)); auto promise = globalObject->moduleLoader()->loadAndEvaluateModule(globalObject, key, jsUndefined(), jsUndefined()); vm.drainMicrotasks(); - promise->result(vm); } extern "C" napi_status napi_wrap(napi_env env, diff --git a/src/javascript/jsc/bindings/napi.h b/src/javascript/jsc/bindings/napi.h index 5bc11e9a5..4a9743d6e 100644 --- a/src/javascript/jsc/bindings/napi.h +++ b/src/javascript/jsc/bindings/napi.h @@ -18,6 +18,11 @@ class GlobalObject; namespace JSC { class JSGlobalObject; +class JSSourceCode; +} + +namespace Napi { +JSC::SourceCode generateSourceCode(WTF::String keyString, JSC::VM& vm, JSC::JSObject* object, JSC::JSGlobalObject* globalObject); } namespace Zig { |