mirror of
https://github.com/oven-sh/bun
synced 2026-02-11 03:18:53 +00:00
[napi] Support import and require of .node modules
This commit is contained in:
@@ -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 {});
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
// 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);
|
||||
|
||||
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 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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user