Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 56 additions & 1 deletion src/jsc/modules/NodeModuleModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
#include <JavaScriptCore/VMTrapsInlines.h>
#include <JavaScriptCore/CallData.h>
#include <JavaScriptCore/JSPromise.h>
#include <JavaScriptCore/JSMap.h>
#include <JavaScriptCore/JSMapInlines.h>
#include <JavaScriptCore/JSModuleLoader.h>
#include <JavaScriptCore/CyclicModuleRecord.h>
#include <JavaScriptCore/ModuleRegistryEntry.h>
#include <JavaScriptCore/IteratorOperations.h>
#include "JavaScriptCore/Completion.h"
#include "JavaScriptCore/JSNativeStdFunction.h"
Expand Down Expand Up @@ -800,7 +805,26 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionLoad, (JSGlobalObject * globalObject, JSC::Ca
return JSC::JSValue::encode(JSC::jsUndefined());
}

// Who reports the load Module.runMain() is about to start: the values of `RunMainOwner` in hw_exports.rs.
enum class RunMainOwner : uint8_t {
Caller = 0,
EntryAwaits = 1,
EntryLoads = 2,
};
extern "C" RunMainOwner Bun__VirtualMachine__runMainOwner(void* bunVM, const BunString* key);
Comment thread
robobun marked this conversation as resolved.
Resolved
extern "C" void Bun__VirtualMachine__setOverrideModuleRunMainPromise(void* bunVM, JSPromise* promise);
BUN_DECLARE_HOST_FUNCTION(Bun__onRunMainRejected);

// Being fetched, running, or run: whoever started that load reports it.
static bool isLoadedOrLoading(JSC::AbstractModuleRecord* record)
{
if (!record)
return true;
if (auto* cyclic = dynamicDowncast<JSC::CyclicModuleRecord>(record))
return cyclic->status() >= JSC::CyclicModuleRecord::Status::Evaluating;
return record->moduleEnvironmentMayBeNull();
}

JSC_DEFINE_HOST_FUNCTION(jsFunctionRunMain, (JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
auto& vm = JSC::getVM(globalObject);
Expand All @@ -812,9 +836,40 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionRunMain, (JSGlobalObject * globalObject, JSC:
auto key = globalObject->moduleLoader()->resolve(globalObject, JSC::Identifier::fromString(vm, name), {}, nullptr, /* useImportMap */ true);
RETURN_IF_EXCEPTION(scope, {});

auto* zigGlobalObject = defaultGlobalObject(globalObject);
void* bunVM = zigGlobalObject->bunVM();
BunString keyString = Bun::toString(key.string());
auto owner = Bun__VirtualMachine__runMainOwner(bunVM, &keyString);

if (owner == RunMainOwner::Caller) {
if (auto* entry = globalObject->moduleLoader()->getRegisteredMayBeNull(key, JSC::ScriptFetchParameters::Type::JavaScript)) {
JSValue failure = entry->error(globalObject);
RETURN_IF_EXCEPTION(scope, {});
if (!failure && isLoadedOrLoading(entry->record()))
return JSC::JSValue::encode(JSC::jsUndefined());
} else {
bool required = zigGlobalObject->requireMap()->has(globalObject, jsString(vm, key.string()));
RETURN_IF_EXCEPTION(scope, {});
if (required)
return JSC::JSValue::encode(JSC::jsUndefined());
}
}

auto* promise = JSC::loadAndEvaluateModule(globalObject, key.string(), nullptr, nullptr);
RETURN_IF_EXCEPTION(scope, {});
Bun__VirtualMachine__setOverrideModuleRunMainPromise(defaultGlobalObject(globalObject)->bunVM(), promise);
switch (owner) {
case RunMainOwner::EntryAwaits:
Bun__VirtualMachine__setOverrideModuleRunMainPromise(bunVM, promise);
Comment thread
robobun marked this conversation as resolved.
Resolved
break;
case RunMainOwner::EntryLoads:
break;
case RunMainOwner::Caller: {
// JSC marks this promise as handled, and nobody else looks at it.
auto* onRejected = JSC::JSFunction::create(vm, globalObject, 1, String(), Bun__onRunMainRejected, ImplementationVisibility::Private);
promise->performPromiseThenExported(vm, globalObject, jsUndefined(), onRejected, JSPromise::create(vm, globalObject->promiseStructure()));
Comment thread
robobun marked this conversation as resolved.
Resolved
break;
}
}

return JSC::JSValue::encode(JSC::jsUndefined());
}
Expand Down
46 changes: 44 additions & 2 deletions src/runtime/hw_exports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,51 @@ pub(crate) fn set_override_module_run_main_promise(
vm: &mut VirtualMachine,
promise: *mut JSInternalPromise,
) {
if vm.pending_internal_promise().is_none() {
vm.set_pending_internal_promise(Some(promise));
vm.set_pending_internal_promise(Some(promise));
}

/// Who reports the load `Module.runMain(key)` starts. NodeModuleModule.cpp mirrors the values.
#[repr(u8)]
enum RunMainOwner {
/// `Module.runMain`: nobody else looks at the promise of this load.
Caller = 0,
/// `reload_entry_point`: the first load of the `Module.runMain` override it calls is its own.
EntryAwaits = 1,
/// `reload_entry_point`: it loads the entry point again after the preload that asks for it.
EntryLoads = 2,
}

// HOST_EXPORT(Bun__VirtualMachine__runMainOwner, c)
pub(crate) fn run_main_owner(vm: &VirtualMachine, key: &bun_core::String) -> u8 {
let owner = if vm.has_patched_run_main {
if vm.pending_internal_promise().is_none() {
RunMainOwner::EntryAwaits
} else {
RunMainOwner::Caller
}
} else if vm.is_in_preload
&& (key.eql_utf8(vm.main()) || key.eql_utf8(bun_jsc::virtual_machine::MAIN_FILE_NAME))
Comment thread
robobun marked this conversation as resolved.
Resolved
{
RunMainOwner::EntryLoads
Comment thread
robobun marked this conversation as resolved.
Resolved
} else {
RunMainOwner::Caller
};
owner as u8
}

// HOST_EXPORT(Bun__onRunMainRejected)
pub(crate) fn on_run_main_rejected(
global: &JSGlobalObject,
callframe: &CallFrame,
) -> bun_jsc::JsResult<JSValue> {
let reason = callframe.argument(0);
let vm = global.bun_vm().as_mut();
// Rethrown, it reaches the rejection tracker, which keeps a `--hot` or `--watch` process alive.
if vm.is_watcher_enabled() {
return Err(global.throw_value(reason));
}
let _ = vm.uncaught_exception(global, reason, true);
Comment thread
robobun marked this conversation as resolved.
Resolved
Comment thread
robobun marked this conversation as resolved.
Resolved
Ok(JSValue::UNDEFINED)
}

/// Exported as `Bun__VM__setEntryPointEvalResultESM`.
Expand Down
Loading
Loading