[napi] Support import and require of .node modules

This commit is contained in:
Jarred Sumner
2022-05-09 00:24:25 -07:00
parent 3bd83eb134
commit 8014a0b8d8
6 changed files with 80 additions and 53 deletions

View File

@@ -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 {});
}

View File

@@ -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;

View File

@@ -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,

View File

@@ -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 {