diff --git a/src/bun.js/bindings/NodeVM.cpp b/src/bun.js/bindings/NodeVM.cpp index cf357c79d6..dfbef183e0 100644 --- a/src/bun.js/bindings/NodeVM.cpp +++ b/src/bun.js/bindings/NodeVM.cpp @@ -362,6 +362,53 @@ std::optional getNodeVMContextOptions(JSGlobalObject* globa return std::nullopt; } +NodeVMGlobalObject* getGlobalObjectFromContext(JSGlobalObject* globalObject, JSValue contextValue, bool canThrow) +{ + auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); + + if (contextValue.isUndefinedOrNull()) { + if (canThrow) { + ERR::INVALID_ARG_TYPE(scope, globalObject, "context"_s, "object"_s, contextValue); + } + return nullptr; + } + + if (!contextValue.isObject()) { + if (canThrow) { + ERR::INVALID_ARG_TYPE(scope, globalObject, "context"_s, "object"_s, contextValue); + } + return nullptr; + } + + JSObject* context = asObject(contextValue); + auto* zigGlobalObject = defaultGlobalObject(globalObject); + JSValue scopeValue = zigGlobalObject->vmModuleContextMap()->get(context); + if (scopeValue.isUndefined()) { + if (canThrow) { + INVALID_ARG_VALUE_VM_VARIATION(scope, globalObject, "contextifiedObject"_s, context); + } + return nullptr; + } + + NodeVMGlobalObject* nodeVmGlobalObject = jsDynamicCast(scopeValue); + if (!nodeVmGlobalObject) { + if (canThrow) { + INVALID_ARG_VALUE_VM_VARIATION(scope, globalObject, "contextifiedObject"_s, context); + } + return nullptr; + } + + return nodeVmGlobalObject; +} + +/// For some reason Node has this error message with a grammatical error and we have to match it so the tests pass: +/// `The "" argument must be an vm.Context` +JSC::EncodedJSValue INVALID_ARG_VALUE_VM_VARIATION(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, WTF::ASCIILiteral name, JSC::JSValue value) +{ + throwScope.throwException(globalObject, createError(globalObject, ErrorCode::ERR_INVALID_ARG_TYPE, makeString("The \""_s, name, "\" argument must be an vm.Context"_s))); + return {}; +} + } // namespace NodeVM using namespace NodeVM; diff --git a/src/bun.js/bindings/NodeVM.h b/src/bun.js/bindings/NodeVM.h index d4eec2b79f..869ba031b6 100644 --- a/src/bun.js/bindings/NodeVM.h +++ b/src/bun.js/bindings/NodeVM.h @@ -26,6 +26,8 @@ JSC::EncodedJSValue createCachedData(JSGlobalObject* globalObject, const JSC::So NodeVMGlobalObject* createContextImpl(JSC::VM& vm, JSGlobalObject* globalObject, JSObject* sandbox); bool handleException(JSGlobalObject* globalObject, VM& vm, NakedPtr exception, ThrowScope& throwScope); std::optional getNodeVMContextOptions(JSGlobalObject* globalObject, JSC::VM& vm, JSC::ThrowScope& scope, JSValue optionsArg, NodeVMContextOptions& outOptions, ASCIILiteral codeGenerationKey); +NodeVMGlobalObject* getGlobalObjectFromContext(JSGlobalObject* globalObject, JSValue contextValue, bool canThrow); +JSC::EncodedJSValue INVALID_ARG_VALUE_VM_VARIATION(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, WTF::ASCIILiteral name, JSC::JSValue value); /// For vm.compileFunction we need to return an anonymous function expression /// /// This code is adapted/inspired from JSC::constructFunction, which is used for function declarations. diff --git a/src/bun.js/bindings/NodeVMModule.cpp b/src/bun.js/bindings/NodeVMModule.cpp index 02712ec958..6c016c1492 100644 --- a/src/bun.js/bindings/NodeVMModule.cpp +++ b/src/bun.js/bindings/NodeVMModule.cpp @@ -31,10 +31,13 @@ JSArray* NodeVMModuleRequest::toJS(JSGlobalObject* globalObject) const return array; } -NodeVMModule::NodeVMModule(JSC::VM& vm, JSC::Structure* structure, WTF::String identifier) +NodeVMModule::NodeVMModule(JSC::VM& vm, JSC::Structure* structure, WTF::String identifier, JSValue context) : Base(vm, structure) , m_identifier(WTFMove(identifier)) { + if (context.isObject()) { + m_context.set(vm, this, asObject(context)); + } } bool NodeVMModule::finishInstantiate(JSC::JSGlobalObject* globalObject, WTF::Deque& stack, unsigned* dfsIndex) @@ -195,18 +198,17 @@ JSC_DEFINE_HOST_FUNCTION(jsNodeVmModuleEvaluate, (JSC::JSGlobalObject * globalOb auto scope = DECLARE_THROW_SCOPE(vm); JSValue timeoutValue = callFrame->argument(0); - if (!timeoutValue.isUInt32() || timeoutValue.asUInt32() == 0) { - return throwArgumentTypeError(*globalObject, scope, 0, "timeout"_s, "Module"_s, "Module"_s, "positive integer"_s); + uint32_t timeout = 0; + if (timeoutValue.isUInt32()) { + timeout = timeoutValue.asUInt32(); } JSValue breakOnSigintValue = callFrame->argument(1); - if (!breakOnSigintValue.isBoolean()) { - return throwArgumentTypeError(*globalObject, scope, 1, "breakOnSigint"_s, "Module"_s, "Module"_s, "boolean"_s); + bool breakOnSigint = false; + if (breakOnSigintValue.isBoolean()) { + breakOnSigint = breakOnSigintValue.asBoolean(); } - uint32_t timeout = timeoutValue.asUInt32(); - bool breakOnSigint = breakOnSigintValue.asBoolean(); - if (auto* thisObject = jsDynamicCast(callFrame->thisValue())) { return JSValue::encode(thisObject->evaluate(globalObject, timeout, breakOnSigint)); // return thisObject->link(globalObject, linker); @@ -283,6 +285,7 @@ void NodeVMModule::visitChildrenImpl(JSCell* cell, Visitor& visitor) Base::visitChildren(vmModule, visitor); visitor.append(vmModule->m_namespace); + visitor.append(vmModule->m_context); auto moduleNatives = vmModule->m_resolveCache.values(); visitor.append(moduleNatives.begin(), moduleNatives.end()); diff --git a/src/bun.js/bindings/NodeVMModule.h b/src/bun.js/bindings/NodeVMModule.h index 665aa0baf4..454b684683 100644 --- a/src/bun.js/bindings/NodeVMModule.h +++ b/src/bun.js/bindings/NodeVMModule.h @@ -63,10 +63,11 @@ protected: WTF::String m_identifier; Status m_status = Status::Unlinked; mutable WriteBarrier m_namespace; + mutable WriteBarrier m_context; WTF::Vector m_moduleRequests; mutable WTF::HashMap> m_resolveCache; - NodeVMModule(JSC::VM& vm, JSC::Structure* structure, WTF::String identifier); + NodeVMModule(JSC::VM& vm, JSC::Structure* structure, WTF::String identifier, JSValue context); DECLARE_EXPORT_INFO; DECLARE_VISIT_CHILDREN; diff --git a/src/bun.js/bindings/NodeVMScript.cpp b/src/bun.js/bindings/NodeVMScript.cpp index c22e8b5b1c..a866ed0b55 100644 --- a/src/bun.js/bindings/NodeVMScript.cpp +++ b/src/bun.js/bindings/NodeVMScript.cpp @@ -12,19 +12,6 @@ namespace Bun { using namespace NodeVM; -/// For some reason Node has this error message with a grammar error and we have to match it so the tests pass: -/// `The "" argument must be an vm.Context` -static JSC::EncodedJSValue INVALID_ARG_VALUE_VM_VARIATION(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, WTF::ASCIILiteral name, JSC::JSValue value) -{ - WTF::StringBuilder builder; - builder.append("The \""_s); - builder.append(name); - builder.append("\" argument must be an vm.Context"_s); - - throwScope.throwException(globalObject, createError(globalObject, ErrorCode::ERR_INVALID_ARG_TYPE, builder.toString())); - return {}; -} - bool ScriptOptions::fromJS(JSC::JSGlobalObject* globalObject, JSC::VM& vm, JSC::ThrowScope& scope, JSC::JSValue optionsArg) { bool any = BaseVMOptions::fromJS(globalObject, vm, scope, optionsArg); @@ -254,34 +241,6 @@ void NodeVMScript::destroy(JSCell* cell) static_cast(cell)->NodeVMScript::~NodeVMScript(); } -extern "C" void Bun__ensureSignalHandler(); - -static void ensureSigintHandler() -{ -#if !OS(WINDOWS) - Bun__ensureSignalHandler(); - - struct sigaction action; - memset(&action, 0, sizeof(struct sigaction)); - - // Set the handler in the action struct - action.sa_handler = [](int signalNumber) { - SigintWatcher::get().signalReceived(); - }; - - // Clear the sa_mask - sigemptyset(&action.sa_mask); - sigaddset(&action.sa_mask, SIGINT); - action.sa_flags = SA_RESTART; - - sigaction(SIGINT, &action, nullptr); - - SigintWatcher::get().install(); -#else - static_assert(false, "TODO(@heimskr): implement sigint handler on Windows"); -#endif -} - static JSC::EncodedJSValue runInContext(NodeVMGlobalObject* globalObject, NodeVMScript* script, JSObject* contextifiedObject, JSValue optionsArg, bool allowStringInPlaceOfOptions = false) { @@ -301,7 +260,7 @@ static JSC::EncodedJSValue runInContext(NodeVMGlobalObject* globalObject, NodeVM globalObject->setContextifiedObject(contextifiedObject); if (options.breakOnSigint) { - ensureSigintHandler(); + SigintWatcher::ensureSigintHandler(); SigintWatcher::get().registerGlobalObject(globalObject); } @@ -346,7 +305,8 @@ JSC_DEFINE_HOST_FUNCTION(scriptRunInThisContext, (JSGlobalObject * globalObject, } if (options.breakOnSigint) { - ensureSigintHandler(); + // TODO(@heimskr): register global object as appropriate + SigintWatcher::ensureSigintHandler(); } NakedPtr exception; @@ -456,25 +416,10 @@ JSC_DEFINE_HOST_FUNCTION(scriptRunInContext, (JSGlobalObject * globalObject, Cal ArgList args(callFrame); JSValue contextArg = args.at(0); - if (contextArg.isUndefinedOrNull()) { - return ERR::INVALID_ARG_TYPE(scope, globalObject, "context"_s, "object"_s, contextArg); - } - - if (!contextArg.isObject()) { - return ERR::INVALID_ARG_TYPE(scope, globalObject, "context"_s, "object"_s, contextArg); - } - + NodeVMGlobalObject* nodeVmGlobalObject = getGlobalObjectFromContext(globalObject, contextArg, true); + RETURN_IF_EXCEPTION(scope, {}); JSObject* context = asObject(contextArg); - auto* zigGlobalObject = defaultGlobalObject(globalObject); - JSValue scopeValue = zigGlobalObject->vmModuleContextMap()->get(context); - if (scopeValue.isUndefined()) { - return INVALID_ARG_VALUE_VM_VARIATION(scope, globalObject, "contextifiedObject"_s, context); - } - - NodeVMGlobalObject* nodeVmGlobalObject = jsDynamicCast(scopeValue); - if (!nodeVmGlobalObject) { - return INVALID_ARG_VALUE_VM_VARIATION(scope, globalObject, "contextifiedObject"_s, context); - } + ASSERT(nodeVmGlobalObject != nullptr); return runInContext(nodeVmGlobalObject, script, context, args.at(1)); } diff --git a/src/bun.js/bindings/NodeVMSourceTextModule.cpp b/src/bun.js/bindings/NodeVMSourceTextModule.cpp index 1b51527c0d..efea4cbde5 100644 --- a/src/bun.js/bindings/NodeVMSourceTextModule.cpp +++ b/src/bun.js/bindings/NodeVMSourceTextModule.cpp @@ -1,13 +1,13 @@ #include "NodeVMSourceTextModule.h" -#include - #include "ErrorCode.h" #include "JSDOMExceptionHandling.h" #include "JSModuleRecord.h" #include "ModuleAnalyzer.h" #include "Parser.h" +#include "../vm/SigintWatcher.h" + namespace Bun { using namespace NodeVM; @@ -63,7 +63,7 @@ NodeVMSourceTextModule* NodeVMSourceTextModule::create(VM& vm, JSGlobalObject* g SourceCode sourceCode(WTFMove(sourceProvider), lineOffset, columnOffset); auto* zigGlobalObject = defaultGlobalObject(globalObject); - NodeVMSourceTextModule* ptr = new (NotNull, allocateCell(vm)) NodeVMSourceTextModule(vm, zigGlobalObject->NodeVMSourceTextModuleStructure(), identifierValue.toWTFString(globalObject), WTFMove(sourceCode)); + NodeVMSourceTextModule* ptr = new (NotNull, allocateCell(vm)) NodeVMSourceTextModule(vm, zigGlobalObject->NodeVMSourceTextModuleStructure(), identifierValue.toWTFString(globalObject), contextValue, WTFMove(sourceCode)); ptr->finishCreation(vm); return ptr; } @@ -116,11 +116,6 @@ JSValue NodeVMSourceTextModule::createModuleRecord(JSGlobalObject* globalObject) m_moduleRecord.set(vm, this, moduleRecord); m_moduleRequests.clear(); - std::println("import entries count: {}", moduleRecord->importEntries().size()); - for (const auto& [key, value] : moduleRecord->importEntries()) { - std::println("import entry({}): name={{{}}}, type={{{}}}", key->utf8().data(), value.importName.utf8().data(), int(value.type)); - } - const auto& requests = moduleRecord->requestedModules(); if (requests.isEmpty()) { @@ -203,8 +198,6 @@ JSValue NodeVMSourceTextModule::link(JSGlobalObject* globalObject, JSArray* spec return JSC::jsUndefined(); } - // JSModuleRecord* record = m_moduleRecord.get(); - VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); @@ -219,12 +212,11 @@ JSValue NodeVMSourceTextModule::link(JSGlobalObject* globalObject, JSArray* spec JSObject* moduleNative = moduleNativeValue.getObject(); m_resolveCache.set(WTFMove(specifier), WriteBarrier { vm, this, moduleNative }); - - // AbstractModuleRecord::ImportEntry entry; - - // record->addImportEntry(entry); } + // JSModuleRecord* record = m_moduleRecord.get(); + // record->link(globalObject, jsUndefined()); + status(Status::Linked); return JSC::jsUndefined(); } @@ -239,7 +231,33 @@ JSValue NodeVMSourceTextModule::evaluate(JSGlobalObject* globalObject, uint32_t return {}; } - return JSC::jsUndefined(); + JSModuleRecord* record = m_moduleRecord.get(); + JSValue result {}; + + NodeVMGlobalObject* nodeVmGlobalObject = getGlobalObjectFromContext(globalObject, m_context.get(), false); + + auto run = [&] { + // TODO(@heimskr): top-level await support + result = record->evaluate(globalObject, jsUndefined(), jsNumber(static_cast(JSGenerator::ResumeMode::NormalMode))); + }; + + if (timeout != 0 && breakOnSigint) { + // TODO(@heimskr): timeout support + auto holder = SigintWatcher::hold(nodeVmGlobalObject); + run(); + } else if (timeout != 0) { + // TODO(@heimskr): timeout support + run(); + } else if (breakOnSigint) { + auto holder = SigintWatcher::hold(nodeVmGlobalObject); + run(); + } else { + run(); + } + + RETURN_IF_EXCEPTION(scope, (status(Status::Errored), JSValue {})); + status(Status::Evaluated); + return result; } JSObject* NodeVMSourceTextModule::createPrototype(VM& vm, JSGlobalObject* globalObject) diff --git a/src/bun.js/bindings/NodeVMSourceTextModule.h b/src/bun.js/bindings/NodeVMSourceTextModule.h index 748332262f..b1059346b9 100644 --- a/src/bun.js/bindings/NodeVMSourceTextModule.h +++ b/src/bun.js/bindings/NodeVMSourceTextModule.h @@ -41,8 +41,8 @@ private: WriteBarrier m_moduleRecord; SourceCode m_sourceCode; - NodeVMSourceTextModule(JSC::VM& vm, JSC::Structure* structure, WTF::String identifier, SourceCode sourceCode) - : Base(vm, structure, WTFMove(identifier)) + NodeVMSourceTextModule(JSC::VM& vm, JSC::Structure* structure, WTF::String identifier, JSValue context, SourceCode sourceCode) + : Base(vm, structure, WTFMove(identifier), context) , m_sourceCode(WTFMove(sourceCode)) { } diff --git a/src/vm/SigintWatcher.cpp b/src/vm/SigintWatcher.cpp index 2b3bd97e50..910bebf9bb 100644 --- a/src/vm/SigintWatcher.cpp +++ b/src/vm/SigintWatcher.cpp @@ -57,6 +57,10 @@ void SigintWatcher::signalReceived() void SigintWatcher::registerGlobalObject(NodeVMGlobalObject* globalObject) { + if (globalObject == nullptr) { + return; + } + std::unique_lock lock(m_globalObjectsMutex); m_globalObjects.push_back(globalObject); @@ -64,6 +68,10 @@ void SigintWatcher::registerGlobalObject(NodeVMGlobalObject* globalObject) void SigintWatcher::unregisterGlobalObject(NodeVMGlobalObject* globalObject) { + if (globalObject == nullptr) { + return; + } + std::unique_lock lock(m_globalObjectsMutex); auto iter = std::find(m_globalObjects.begin(), m_globalObjects.end(), globalObject); @@ -96,4 +104,32 @@ bool SigintWatcher::signalAll() return true; } +extern "C" void Bun__ensureSignalHandler(); + +void SigintWatcher::ensureSigintHandler() +{ +#if !OS(WINDOWS) + Bun__ensureSignalHandler(); + + struct sigaction action; + memset(&action, 0, sizeof(struct sigaction)); + + // Set the handler in the action struct + action.sa_handler = [](int signalNumber) { + get().signalReceived(); + }; + + // Clear the sa_mask + sigemptyset(&action.sa_mask); + sigaddset(&action.sa_mask, SIGINT); + action.sa_flags = SA_RESTART; + + sigaction(SIGINT, &action, nullptr); + + get().install(); +#else + static_assert(false, "TODO(@heimskr): implement sigint handler on Windows"); +#endif +} + } // namespace Bun diff --git a/src/vm/SigintWatcher.h b/src/vm/SigintWatcher.h index 7f934933d7..ebd15f93a2 100644 --- a/src/vm/SigintWatcher.h +++ b/src/vm/SigintWatcher.h @@ -25,6 +25,44 @@ public: static SigintWatcher& get(); + class GlobalObjectHolder { + public: + GlobalObjectHolder(NodeVMGlobalObject* globalObject) + : m_globalObject(globalObject) + { + ensureSigintHandler(); + get().registerGlobalObject(globalObject); + } + + ~GlobalObjectHolder() + { + get().unregisterGlobalObject(m_globalObject); + } + + GlobalObjectHolder(const GlobalObjectHolder&) = delete; + GlobalObjectHolder(GlobalObjectHolder&& other) + : m_globalObject(std::exchange(other.m_globalObject, nullptr)) + { + } + + GlobalObjectHolder& operator=(const GlobalObjectHolder&) = delete; + GlobalObjectHolder& operator=(GlobalObjectHolder&& other) + { + m_globalObject = std::exchange(other.m_globalObject, nullptr); + return *this; + } + + private: + NodeVMGlobalObject* m_globalObject; + }; + + static GlobalObjectHolder hold(NodeVMGlobalObject* globalObject) + { + return { globalObject }; + } + + static void ensureSigintHandler(); + private: std::thread m_thread; std::atomic_bool m_installed = false;