mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
Progress on SourceTextModule evaluation
This commit is contained in:
@@ -362,6 +362,53 @@ std::optional<JSC::EncodedJSValue> 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<NodeVMGlobalObject*>(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 "<name>" 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;
|
||||
|
||||
@@ -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<JSC::Exception> exception, ThrowScope& throwScope);
|
||||
std::optional<JSC::EncodedJSValue> 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.
|
||||
|
||||
@@ -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<NodeVMSourceTextModule*>& 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<NodeVMSourceTextModule*>(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());
|
||||
|
||||
@@ -63,10 +63,11 @@ protected:
|
||||
WTF::String m_identifier;
|
||||
Status m_status = Status::Unlinked;
|
||||
mutable WriteBarrier<JSObject> m_namespace;
|
||||
mutable WriteBarrier<JSObject> m_context;
|
||||
WTF::Vector<NodeVMModuleRequest> m_moduleRequests;
|
||||
mutable WTF::HashMap<WTF::String, WriteBarrier<JSObject>> 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;
|
||||
|
||||
@@ -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 "<name>" 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<NodeVMScript*>(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<JSC::Exception> 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<NodeVMGlobalObject*>(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));
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#include "NodeVMSourceTextModule.h"
|
||||
|
||||
#include <print>
|
||||
|
||||
#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<NodeVMSourceTextModule>(vm)) NodeVMSourceTextModule(vm, zigGlobalObject->NodeVMSourceTextModuleStructure(), identifierValue.toWTFString(globalObject), WTFMove(sourceCode));
|
||||
NodeVMSourceTextModule* ptr = new (NotNull, allocateCell<NodeVMSourceTextModule>(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<JSObject> { 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<int32_t>(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)
|
||||
|
||||
@@ -41,8 +41,8 @@ private:
|
||||
WriteBarrier<JSModuleRecord> 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))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user