Compare commits

...

1 Commits

Author SHA1 Message Date
Jarred Sumner
4787487b31 not-yet-working code for --experimental-vm-modules 2025-02-07 05:04:29 -08:00
7 changed files with 795 additions and 1 deletions

View File

@@ -89,6 +89,14 @@ const errors: ErrorCodeMapping = [
["ERR_STREAM_WRAP", Error],
["ERR_BORINGSSL", Error],
// VM Module errors
["ERR_VM_MODULE_NOT_MODULE", TypeError],
["ERR_VM_MODULE_STATUS", Error],
["ERR_VM_MODULE_ALREADY_LINKED", Error],
["ERR_VM_MODULE_DIFFERENT_CONTEXT", Error],
["ERR_VM_MODULE_CANNOT_CREATE_CACHED_DATA", Error],
["ERR_VM_MODULE_LINK_FAILURE", Error],
// Console
["ERR_CONSOLE_WRITABLE_STREAM", TypeError, "TypeError"],

View File

@@ -0,0 +1,764 @@
#include "root.h"
#include "JavaScriptCore/SlotVisitorMacros.h"
#include "NodeVMModule.h"
#include "ZigGlobalObject.h"
#include "DOMIsoSubspaces.h"
#include "DOMClientIsoSubspaces.h"
#include <JavaScriptCore/FunctionPrototype.h>
#include <JavaScriptCore/JSString.h>
#include "JavaScriptCore/JSModuleNamespaceObject.h"
#include "JavaScriptCore/JSModuleRecord.h"
#include "JavaScriptCore/JSModuleEnvironment.h"
#include "JavaScriptCore/JSInternalPromise.h"
#include "JavaScriptCore/JSPromise.h"
#include "JavaScriptCore/JSCInlines.h"
#include "JavaScriptCore/JSGlobalObjectInlines.h"
#include "JavaScriptCore/JSCJSValueInlines.h"
#include "JavaScriptCore/CallData.h"
#include "JavaScriptCore/JSWeakMapInlines.h"
#include "ErrorCode.h"
#include <JavaScriptCore/TypedArrayInlines.h>
#include <JavaScriptCore/PropertyNameArray.h>
#include "GCDefferalContext.h"
#include "JavaScriptCore/JSWeakMap.h"
#include "JavaScriptCore/Uint8Array.h"
#include "JavaScriptCore/SourceCode.h"
#include "JavaScriptCore/SourceProvider.h"
#include "JavaScriptCore/TypedArrayInlines.h"
#include "JavaScriptCore/JSArray.h"
#include "JavaScriptCore/ArrayBuffer.h"
#include "JavaScriptCore/JSArrayBuffer.h"
#include "JavaScriptCore/JSTypedArrays.h"
#include "JavaScriptCore/SourceOrigin.h"
#include "wtf/URL.h"
#include "JavaScriptCore/ModuleAnalyzer.h"
#include "JavaScriptCore/JSObject.h"
#include "JavaScriptCore/JSFunction.h"
#include "JavaScriptCore/JSCBuiltins.h"
#include "JavaScriptCore/JSGlobalObject.h"
#include "JavaScriptCore/JSCast.h"
#include "JavaScriptCore/JSObjectInlines.h"
#include "JavaScriptCore/JSModuleLoader.h"
#include "JavaScriptCore/JSWeakMap.h"
namespace Bun {
using namespace JSC;
// Forward declarations
static JSC_DECLARE_HOST_FUNCTION(callNodeVMModule);
static JSC_DECLARE_HOST_FUNCTION(constructNodeVMModule);
static JSC_DECLARE_CUSTOM_GETTER(jsNodeVMModuleGetter_identifier);
static JSC_DECLARE_CUSTOM_GETTER(jsNodeVMModuleGetter_context);
static JSC_DECLARE_CUSTOM_GETTER(jsNodeVMModuleGetter_namespace);
static JSC_DECLARE_CUSTOM_GETTER(jsNodeVMModuleGetter_status);
static JSC_DECLARE_CUSTOM_GETTER(jsNodeVMModuleGetter_error);
static JSC_DECLARE_HOST_FUNCTION(jsNodeVMModuleProtoFuncLink);
static JSC_DECLARE_HOST_FUNCTION(jsNodeVMModuleProtoFuncEvaluate);
static JSC_DECLARE_HOST_FUNCTION(jsNodeVMModuleProtoFuncInspectCustom);
// Property table for prototype
static const HashTableValue NodeVMModulePrototypeTableValues[] = {
{ "identifier"_s, static_cast<unsigned>(PropertyAttribute::CustomAccessor | PropertyAttribute::ReadOnly), NoIntrinsic,
{ HashTableValue::GetterSetterType, jsNodeVMModuleGetter_identifier, 0 } },
{ "context"_s, static_cast<unsigned>(PropertyAttribute::CustomAccessor | PropertyAttribute::ReadOnly), NoIntrinsic,
{ HashTableValue::GetterSetterType, jsNodeVMModuleGetter_context, 0 } },
{ "namespace"_s, static_cast<unsigned>(PropertyAttribute::CustomAccessor | PropertyAttribute::ReadOnly), NoIntrinsic,
{ HashTableValue::GetterSetterType, jsNodeVMModuleGetter_namespace, 0 } },
{ "status"_s, static_cast<unsigned>(PropertyAttribute::CustomAccessor | PropertyAttribute::ReadOnly), NoIntrinsic,
{ HashTableValue::GetterSetterType, jsNodeVMModuleGetter_status, 0 } },
{ "error"_s, static_cast<unsigned>(PropertyAttribute::CustomAccessor | PropertyAttribute::ReadOnly), NoIntrinsic,
{ HashTableValue::GetterSetterType, jsNodeVMModuleGetter_error, 0 } },
{ "link"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic,
{ HashTableValue::NativeFunctionType, jsNodeVMModuleProtoFuncLink, 1 } },
{ "evaluate"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic,
{ HashTableValue::NativeFunctionType, jsNodeVMModuleProtoFuncEvaluate, 0 } },
{ "Symbol(nodejs.util.inspect.custom)"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic,
{ HashTableValue::NativeFunctionType, jsNodeVMModuleProtoFuncInspectCustom, 0 } },
};
// Add at the top of the file after the includes
static uint32_t globalModuleId = 0;
// Main Module class implementation
class NodeVMModule : public JSDestructibleObject {
public:
using Base = JSDestructibleObject;
static constexpr unsigned StructureFlags = Base::StructureFlags;
// For source text based modules
static NodeVMModule* create(VM& vm, Structure* structure, JSGlobalObject* globalObject, JSObject* context,
const String& identifier, RefPtr<SourceProvider>&& sourceProvider, JSUint8Array* cachedData, JSObject* importModuleDynamically, JSObject* initializeImportMeta)
{
NodeVMModule* module = new (NotNull, allocateCell<NodeVMModule>(vm)) NodeVMModule(vm, structure);
module->finishCreation(vm, globalObject, context, identifier, WTFMove(sourceProvider), cachedData, importModuleDynamically, initializeImportMeta);
return module;
}
// For synthetic modules
static NodeVMModule* create(VM& vm, Structure* structure, JSGlobalObject* globalObject, JSObject* context,
const String& identifier, JSArray* syntheticExportNames, JSObject* syntheticEvaluationSteps)
{
NodeVMModule* module = new (NotNull, allocateCell<NodeVMModule>(vm)) NodeVMModule(vm, structure);
module->finishCreation(vm, globalObject, context, identifier, syntheticExportNames, syntheticEvaluationSteps);
return module;
}
DECLARE_INFO;
DECLARE_VISIT_CHILDREN;
DECLARE_VISIT_OUTPUT_CONSTRAINTS;
template<typename Visitor> void visitAdditionalChildren(Visitor&);
static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&);
template<typename CellType, SubspaceAccess mode>
static GCClient::IsoSubspace* subspaceFor(VM& vm)
{
if constexpr (mode == SubspaceAccess::Concurrently)
return nullptr;
return WebCore::subspaceForImpl<CellType, WebCore::UseCustomHeapCellType::No>(
vm,
[](auto& spaces) { return spaces.m_clientSubspaceForNodeVMModule.get(); },
[](auto& spaces, auto&& space) { spaces.m_clientSubspaceForNodeVMModule = std::forward<decltype(space)>(space); },
[](auto& spaces) { return spaces.m_subspaceForNodeVMModule.get(); },
[](auto& spaces, auto&& space) { spaces.m_subspaceForNodeVMModule = std::forward<decltype(space)>(space); });
}
static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
{
return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
}
const String& identifier() const { return m_identifier; }
JSObject* context() const { return m_context.get(); }
JSObject* syntheticEvaluationSteps() const { return m_syntheticEvaluationSteps.get(); }
JSArray* syntheticExportNames() const { return m_syntheticExportNames.get(); }
RefPtr<SourceProvider> sourceProvider() const { return m_sourceProvider; }
JSModuleEnvironment* moduleEnvironment() const { return m_moduleEnvironment.get(); }
void setModuleEnvironment(VM& vm, JSModuleEnvironment* environment) { m_moduleEnvironment.set(vm, this, environment); }
JSObject* importModuleDynamically() const { return m_importModuleDynamically.get(); }
JSObject* initializeImportMeta() const { return m_initializeImportMeta.get(); }
// Add link method declaration
Synchronousness link(JSGlobalObject*, JSValue scriptFetcher);
Structure* moduleStructure(JSGlobalObject* globalObject) const
{
return static_cast<ZigGlobalObject*>(globalObject)->NodeVMModuleStructure();
}
JSValue evaluateModule(JSGlobalObject* globalObject, JSValue key, JSValue moduleRecordValue, JSValue scriptFetcher, JSValue sentValue, JSValue resumeMode);
JSValue importModuleDynamically(JSGlobalObject* globalObject, JSValue key, JSValue moduleRecordValue, JSValue scriptFetcher);
protected:
mutable WriteBarrier<JSC::AbstractModuleRecord> m_moduleRecord;
mutable WriteBarrier<JSC::JSObject> m_context;
mutable WriteBarrier<JSC::JSArray> m_syntheticExportNames;
mutable WriteBarrier<JSC::JSObject> m_syntheticEvaluationSteps;
RefPtr<JSC::SourceProvider> m_sourceProvider;
WriteBarrier<JSC::JSObject> m_importModuleDynamically;
WriteBarrier<JSC::JSObject> m_initializeImportMeta;
private:
NodeVMModule(VM& vm, Structure* structure)
: Base(vm, structure)
{
}
void finishCreation(VM& vm, JSGlobalObject* globalObject, JSObject* context, const String& identifier,
RefPtr<SourceProvider>&& sourceProvider, JSUint8Array* cachedData, JSObject* importModuleDynamically, JSObject* initializeImportMeta)
{
Base::finishCreation(vm);
m_context.setEarlyValue(vm, this, context);
m_sourceProvider = WTFMove(sourceProvider);
m_importModuleDynamically.setEarlyValue(vm, this, importModuleDynamically);
m_initializeImportMeta.setEarlyValue(vm, this, initializeImportMeta);
m_moduleRecord.clear();
}
void finishCreation(VM& vm, JSGlobalObject* globalObject, JSObject* context, const String& identifier,
JSArray* syntheticExportNames, JSObject* syntheticEvaluationSteps)
{
Base::finishCreation(vm);
m_context.setEarlyValue(vm, this, context);
m_syntheticExportNames.setEarlyValue(vm, this, syntheticExportNames);
m_syntheticEvaluationSteps.setEarlyValue(vm, this, syntheticEvaluationSteps);
m_moduleRecord.clear();
// Create a synthetic source provider for this module
auto generator = [syntheticExportNames = m_syntheticExportNames, syntheticEvaluationSteps = m_syntheticEvaluationSteps](JSGlobalObject* globalObject, Identifier moduleKey, Vector<Identifier, 4>& exportNames, MarkedArgumentBuffer& exportValues) {
if (syntheticExportNames && syntheticEvaluationSteps) {
// Get the export names from the synthetic module
unsigned length = syntheticExportNames->length();
for (unsigned i = 0; i < length; ++i) {
JSValue exportName = syntheticExportNames->get(globalObject, i);
if (exportName.isString()) {
exportNames.append(Identifier::fromString(globalObject->vm(), exportName.getString(globalObject)));
}
}
// Call the evaluation steps to get the export values
MarkedArgumentBuffer args;
args.append(syntheticExportNames.get());
JSValue result = call(globalObject, syntheticEvaluationSteps.get(), jsUndefined(), args);
if (!result.isUndefined()) {
exportValues.append(result);
}
}
};
URL moduleURL = URL::fileURLWithFileSystemPath(identifier);
m_sourceProvider = SyntheticSourceProvider::create(WTFMove(generator), SourceOrigin(moduleURL), identifier);
}
};
// Prototype class implementation
class NodeVMModulePrototype final : public JSNonFinalObject {
public:
using Base = JSNonFinalObject;
static constexpr unsigned StructureFlags = Base::StructureFlags;
static NodeVMModulePrototype* create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
{
NodeVMModulePrototype* prototype = new (NotNull, allocateCell<NodeVMModulePrototype>(vm)) NodeVMModulePrototype(vm, structure);
prototype->finishCreation(vm);
return prototype;
}
DECLARE_INFO;
template<typename CellType, SubspaceAccess>
static GCClient::IsoSubspace* subspaceFor(VM& vm)
{
return &vm.plainObjectSpace();
}
static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
{
auto* structure = Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
structure->setMayBePrototype(true);
return structure;
}
private:
NodeVMModulePrototype(VM& vm, Structure* structure)
: Base(vm, structure)
{
}
void finishCreation(VM& vm);
};
// Constructor class implementation
class NodeVMModuleConstructor final : public InternalFunction {
public:
using Base = InternalFunction;
static constexpr unsigned StructureFlags = Base::StructureFlags;
static NodeVMModuleConstructor* create(VM& vm, Structure* structure, JSObject* prototype)
{
NodeVMModuleConstructor* constructor = new (NotNull, allocateCell<NodeVMModuleConstructor>(vm)) NodeVMModuleConstructor(vm, structure);
constructor->finishCreation(vm, prototype);
return constructor;
}
DECLARE_INFO;
template<typename CellType, SubspaceAccess>
static GCClient::IsoSubspace* subspaceFor(VM& vm)
{
return &vm.internalFunctionSpace();
}
static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
{
return Structure::create(vm, globalObject, prototype, TypeInfo(InternalFunctionType, StructureFlags), info());
}
private:
NodeVMModuleConstructor(VM& vm, Structure* structure)
: Base(vm, structure, callNodeVMModule, constructNodeVMModule)
{
}
void finishCreation(VM& vm, JSObject* prototype)
{
Base::finishCreation(vm, 1, "Module"_s);
putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
}
};
// Class info definitions
const ClassInfo NodeVMModule::s_info = { "Module"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(NodeVMModule) };
const ClassInfo NodeVMModulePrototype::s_info = { "Module"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(NodeVMModulePrototype) };
const ClassInfo NodeVMModuleConstructor::s_info = { "Module"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(NodeVMModuleConstructor) };
void NodeVMModulePrototype::finishCreation(VM& vm)
{
Base::finishCreation(vm);
reifyStaticProperties(vm, NodeVMModule::info(), NodeVMModulePrototypeTableValues, *this);
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
}
// Getter implementations
JSC_DEFINE_CUSTOM_GETTER(jsNodeVMModuleGetter_identifier, (JSGlobalObject * globalObject, EncodedJSValue thisValue, PropertyName))
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
NodeVMModule* thisObject = jsDynamicCast<NodeVMModule*>(JSValue::decode(thisValue));
if (UNLIKELY(!thisObject)) {
throwVMError(globalObject, scope, "Receiver must be a Module instance"_s);
return {};
}
return JSValue::encode(jsString(vm, thisObject->identifier()));
}
JSC_DEFINE_CUSTOM_GETTER(jsNodeVMModuleGetter_context, (JSGlobalObject * globalObject, EncodedJSValue thisValue, PropertyName))
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
NodeVMModule* thisObject = jsDynamicCast<NodeVMModule*>(JSValue::decode(thisValue));
if (UNLIKELY(!thisObject)) {
throwVMError(globalObject, scope, "Receiver must be a Module instance"_s);
return {};
}
return JSValue::encode(thisObject->context());
}
JSC_DEFINE_CUSTOM_GETTER(jsNodeVMModuleGetter_namespace, (JSGlobalObject * globalObject, EncodedJSValue thisValue, PropertyName))
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
NodeVMModule* thisObject = jsDynamicCast<NodeVMModule*>(JSValue::decode(thisValue));
if (UNLIKELY(!thisObject)) {
throwVMError(globalObject, scope, "Receiver must be a Module instance"_s);
return {};
}
// TODO: Return actual namespace
return JSValue::encode(jsNull());
}
JSC_DEFINE_CUSTOM_GETTER(jsNodeVMModuleGetter_status, (JSGlobalObject * globalObject, EncodedJSValue thisValue, PropertyName))
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
NodeVMModule* thisObject = jsDynamicCast<NodeVMModule*>(JSValue::decode(thisValue));
if (UNLIKELY(!thisObject)) {
throwVMError(globalObject, scope, "Receiver must be a Module instance"_s);
return {};
}
// TODO: Return actual status
return JSValue::encode(jsString(vm, String("unlinked"_s)));
}
JSC_DEFINE_CUSTOM_GETTER(jsNodeVMModuleGetter_error, (JSGlobalObject * globalObject, EncodedJSValue thisValue, PropertyName))
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
NodeVMModule* thisObject = jsDynamicCast<NodeVMModule*>(JSValue::decode(thisValue));
if (UNLIKELY(!thisObject)) {
throwVMError(globalObject, scope, "Receiver must be a Module instance"_s);
return {};
}
// TODO: Return actual error
return JSValue::encode(jsUndefined());
}
// Function implementations
JSC_DEFINE_HOST_FUNCTION(jsNodeVMModuleProtoFuncLink, (JSGlobalObject * globalObject, CallFrame* callFrame))
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
NodeVMModule* thisObject = jsDynamicCast<NodeVMModule*>(callFrame->thisValue());
if (UNLIKELY(!thisObject)) {
throwVMError(globalObject, scope, "Receiver must be a Module instance"_s);
return {};
}
// TODO: Implement link functionality
return JSValue::encode(jsUndefined());
}
JSC_DEFINE_HOST_FUNCTION(jsNodeVMModuleProtoFuncEvaluate, (JSGlobalObject * globalObject, CallFrame* callFrame))
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
NodeVMModule* thisObject = jsDynamicCast<NodeVMModule*>(callFrame->thisValue());
if (UNLIKELY(!thisObject)) {
throwVMError(globalObject, scope, "Receiver must be a Module instance"_s);
return {};
}
// TODO: Implement evaluate functionality
return JSValue::encode(jsUndefined());
}
JSC_DEFINE_HOST_FUNCTION(jsNodeVMModuleProtoFuncInspectCustom, (JSGlobalObject * globalObject, CallFrame* callFrame))
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
NodeVMModule* thisObject = jsDynamicCast<NodeVMModule*>(callFrame->thisValue());
if (UNLIKELY(!thisObject)) {
throwVMError(globalObject, scope, "Receiver must be a Module instance"_s);
return {};
}
// TODO: Implement custom inspect
return JSValue::encode(jsUndefined());
}
// Constructor function implementations
JSC_DEFINE_HOST_FUNCTION(callNodeVMModule, (JSGlobalObject * globalObject, CallFrame*))
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
return throwVMError(globalObject, scope, "Module constructor cannot be called without 'new'"_s);
}
JSC_DEFINE_HOST_FUNCTION(constructNodeVMModule, (JSGlobalObject * globalObject, CallFrame* callFrame))
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
// Must be called with new
JSValue newTarget = callFrame->newTarget();
if (!newTarget) {
throwTypeError(globalObject, scope, "Module is not a constructor"_s);
return {};
}
// Check if we have at least one argument (options object)
if (callFrame->argumentCount() < 1) {
return ERR::INVALID_ARG_TYPE(scope, globalObject, "options"_s, "object"_s, callFrame->uncheckedArgument(0));
}
JSValue optionsValue = callFrame->uncheckedArgument(0);
if (!optionsValue.isObject()) {
return ERR::INVALID_ARG_TYPE(scope, globalObject, "options"_s, "object"_s, optionsValue);
}
auto* zigGlobalObject = defaultGlobalObject(globalObject);
JSObject* options = optionsValue.getObject();
// Get context option
JSValue contextValue = options->get(globalObject, JSC::Identifier::fromString(vm, "context"_s));
RETURN_IF_EXCEPTION(scope, {});
JSObject* context = nullptr;
if (!contextValue.isUndefined()) {
if (!contextValue.isObject()) {
return ERR::INVALID_ARG_TYPE(scope, globalObject, "options.context"_s, "vm.Context"_s, contextValue);
}
context = contextValue.getObject();
if (!zigGlobalObject->vmModuleContextMap()->has(context)) {
return ERR::INVALID_ARG_TYPE(scope, globalObject, "options.context"_s, "vm.Context"_s, contextValue);
}
}
// Get identifier option
JSValue identifierValue = options->get(globalObject, JSC::Identifier::fromString(vm, "identifier"_s));
RETURN_IF_EXCEPTION(scope, {});
String identifier;
if (!identifierValue.isUndefined()) {
if (!identifierValue.isString()) {
return ERR::INVALID_ARG_TYPE(scope, globalObject, "options.identifier"_s, "string"_s, identifierValue);
}
identifier = identifierValue.getString(globalObject);
RETURN_IF_EXCEPTION(scope, {});
}
Structure* structure = zigGlobalObject->NodeVMModuleStructure();
if (UNLIKELY(zigGlobalObject->NodeVMModuleConstructor() != newTarget)) {
if (!newTarget) {
throwTypeError(globalObject, scope, "Class constructor Module cannot be invoked without 'new'"_s);
return {};
}
auto* functionGlobalObject = defaultGlobalObject(getFunctionRealm(globalObject, newTarget.getObject()));
RETURN_IF_EXCEPTION(scope, {});
structure = InternalFunction::createSubclassStructure(
globalObject, newTarget.getObject(), functionGlobalObject->NodeVMModuleStructure());
RETURN_IF_EXCEPTION(scope, {});
}
// Get sourceText and synthetic options
JSValue sourceTextValue = options->get(globalObject, JSC::Identifier::fromString(vm, "sourceText"_s));
RETURN_IF_EXCEPTION(scope, {});
JSValue syntheticExportNamesValue = options->get(globalObject, JSC::Identifier::fromString(vm, "syntheticExportNames"_s));
RETURN_IF_EXCEPTION(scope, {});
JSValue syntheticEvaluationStepsValue = options->get(globalObject, JSC::Identifier::fromString(vm, "syntheticEvaluationSteps"_s));
RETURN_IF_EXCEPTION(scope, {});
// Handle source text case
if (!sourceTextValue.isUndefined()) {
if (!sourceTextValue.isString()) {
return ERR::INVALID_ARG_TYPE(scope, globalObject, "options.sourceText"_s, "string"_s, sourceTextValue);
}
String sourceText = sourceTextValue.getString(globalObject);
RETURN_IF_EXCEPTION(scope, {});
// Get line and column offsets
JSValue lineOffsetValue = options->get(globalObject, JSC::Identifier::fromString(vm, "lineOffset"_s));
RETURN_IF_EXCEPTION(scope, {});
JSValue columnOffsetValue = options->get(globalObject, JSC::Identifier::fromString(vm, "columnOffset"_s));
RETURN_IF_EXCEPTION(scope, {});
int32_t lineOffset = 0;
int32_t columnOffset = 0;
if (!lineOffsetValue.isUndefined()) {
lineOffset = lineOffsetValue.asInt32();
RETURN_IF_EXCEPTION(scope, {});
}
if (!columnOffsetValue.isUndefined()) {
if (!columnOffsetValue.isNumber()) {
return ERR::INVALID_ARG_TYPE(scope, globalObject, "options.columnOffset"_s, "number"_s, columnOffsetValue);
}
columnOffset = columnOffsetValue.asInt32();
RETURN_IF_EXCEPTION(scope, {});
}
// Get optional cachedData
JSValue cachedDataValue = options->get(globalObject, JSC::Identifier::fromString(vm, "cachedData"_s));
RETURN_IF_EXCEPTION(scope, {});
UNUSED_PARAM(cachedDataValue);
// RefPtr<Uint8Array> cachedData;
// if (!cachedDataValue.isUndefined()) {
// if (!cachedDataValue.isObject()) {
// return ERR::INVALID_ARG_TYPE(scope, globalObject, "options.cachedData"_s, "Uint8Array"_s, cachedDataValue);
// }
// JSObject* obj = cachedDataValue.getObject();
// auto* uint8Array = jsDynamicCast<JSUint8Array*>(obj);
// if (!uint8Array) {
// return ERR::INVALID_ARG_TYPE(scope, globalObject, "options.cachedData"_s, "Uint8Array"_s, cachedDataValue);
// }
// cachedData = jsDynamicCast<JSUint8Array*>(obj);
// }
// Get optional importModuleDynamically and initializeImportMeta callbacks
JSValue importModuleDynamicallyValue = options->get(globalObject, JSC::Identifier::fromString(vm, "importModuleDynamically"_s));
RETURN_IF_EXCEPTION(scope, {});
JSValue initializeImportMetaValue = options->get(globalObject, JSC::Identifier::fromString(vm, "initializeImportMeta"_s));
RETURN_IF_EXCEPTION(scope, {});
JSObject* importModuleDynamically = nullptr;
if (!importModuleDynamicallyValue.isUndefined()) {
if (!importModuleDynamicallyValue.isObject() || !importModuleDynamicallyValue.getObject()->isCallable()) {
return ERR::INVALID_ARG_TYPE(scope, globalObject, "options.importModuleDynamically"_s, "function"_s, importModuleDynamicallyValue);
}
importModuleDynamically = importModuleDynamicallyValue.getObject();
}
JSObject* initializeImportMeta = nullptr;
if (!initializeImportMetaValue.isUndefined()) {
if (!initializeImportMetaValue.isObject() || !initializeImportMetaValue.getObject()->isCallable()) {
return ERR::INVALID_ARG_TYPE(scope, globalObject, "options.initializeImportMeta"_s, "function"_s, initializeImportMetaValue);
}
initializeImportMeta = initializeImportMetaValue.getObject();
}
String moduleId = identifier.isEmpty() ? makeString("vm:module("_s, String::number(globalModuleId++), ")"_s) : identifier;
// Create source provider
TextPosition startPosition(OrdinalNumber::fromOneBasedInt(lineOffset + 1), OrdinalNumber::fromZeroBasedInt(columnOffset));
URL moduleURL = URL::fileURLWithFileSystemPath(moduleId);
auto sourceProvider = StringSourceProvider::create(sourceText, SourceOrigin(moduleURL), moduleId, SourceTaintedOrigin::Untainted, startPosition, SourceProviderSourceType::Module);
return JSValue::encode(NodeVMModule::create(vm, structure, globalObject, context,
moduleId, WTFMove(sourceProvider), nullptr, importModuleDynamically, initializeImportMeta));
}
// Handle synthetic module case
if (!syntheticEvaluationStepsValue.isUndefined()) {
if (!syntheticExportNamesValue.isObject() || !isJSArray(syntheticExportNamesValue.getObject())) {
return ERR::INVALID_ARG_TYPE(scope, globalObject, "options.syntheticExportNames"_s, "Array"_s, syntheticExportNamesValue);
}
if (!syntheticEvaluationStepsValue.isObject() || !syntheticEvaluationStepsValue.getObject()->isCallable()) {
return ERR::INVALID_ARG_TYPE(scope, globalObject, "options.syntheticEvaluationSteps"_s, "function"_s, syntheticEvaluationStepsValue);
}
JSArray* syntheticExportNames = jsCast<JSArray*>(syntheticExportNamesValue);
JSObject* syntheticEvaluationSteps = syntheticEvaluationStepsValue.getObject();
String moduleId = identifier.isEmpty() ? makeString("vm:module("_s, String::number(globalModuleId++), ")"_s) : identifier;
return JSValue::encode(NodeVMModule::create(vm, structure, globalObject, context,
moduleId, syntheticExportNames, syntheticEvaluationSteps));
}
// If we get here, neither sourceText nor synthetic options were provided
String moduleId;
if (identifier.isEmpty()) {
if (context) {
JSValue perContextModuleId = context->get(globalObject, JSC::Identifier::fromString(vm, "kPerContextModuleId"_s));
RETURN_IF_EXCEPTION(scope, {});
uint32_t id = 0;
if (perContextModuleId.isUndefined()) {
context->putDirect(vm, JSC::Identifier::fromString(vm, "kPerContextModuleId"_s), jsNumber(1));
} else {
id = static_cast<uint32_t>(perContextModuleId.asNumber());
RETURN_IF_EXCEPTION(scope, {});
context->putDirect(vm, JSC::Identifier::fromString(vm, "kPerContextModuleId"_s), jsNumber(id + 1));
}
moduleId = makeString("vm:module("_s, String::number(id), ")"_s);
} else {
moduleId = makeString("vm:module("_s, String::number(globalModuleId++), ")"_s);
}
} else {
moduleId = identifier;
}
// Create empty module with an empty source provider
URL emptyModuleURL = URL::fileURLWithFileSystemPath(moduleId);
auto emptySourceProvider = StringSourceProvider::create(""_s, SourceOrigin(emptyModuleURL), moduleId, SourceTaintedOrigin::Untainted, TextPosition(), SourceProviderSourceType::Module);
return JSValue::encode(NodeVMModule::create(vm, structure, globalObject, context,
moduleId, WTFMove(emptySourceProvider), nullptr, nullptr, nullptr));
}
template<typename Visitor>
void NodeVMModule::visitAdditionalChildren(Visitor& visitor)
{
visitor.append(m_context);
visitor.append(m_syntheticExportNames);
visitor.append(m_syntheticEvaluationSteps);
visitor.append(m_moduleRecord);
}
template<typename Visitor>
void NodeVMModule::visitChildrenImpl(JSCell* cell, Visitor& visitor)
{
auto* thisObject = jsCast<NodeVMModule*>(cell);
ASSERT_GC_OBJECT_INHERITS(thisObject, info());
Base::visitChildren(thisObject, visitor);
thisObject->visitAdditionalChildren(visitor);
}
DEFINE_VISIT_CHILDREN(NodeVMModule);
template<typename Visitor>
void NodeVMModule::visitOutputConstraintsImpl(JSCell* cell, Visitor& visitor)
{
auto* thisObject = jsCast<NodeVMModule*>(cell);
ASSERT_GC_OBJECT_INHERITS(thisObject, info());
Base::visitOutputConstraints(thisObject, visitor);
thisObject->visitAdditionalChildren(visitor);
}
DEFINE_VISIT_OUTPUT_CONSTRAINTS(NodeVMModule);
// Setup function for class structure
void setupNodeVMModuleClassStructure(LazyClassStructure::Initializer& init)
{
auto* prototypeStructure = NodeVMModulePrototype::createStructure(
init.vm, init.global, init.global->objectPrototype());
auto* prototype = NodeVMModulePrototype::create(init.vm, init.global, prototypeStructure);
auto* constructorStructure = NodeVMModuleConstructor::createStructure(
init.vm, init.global, init.global->functionPrototype());
auto* constructor = NodeVMModuleConstructor::create(
init.vm, constructorStructure, prototype);
auto* structure = NodeVMModule::createStructure(init.vm, init.global, prototype);
init.setPrototype(prototype);
init.setStructure(structure);
init.setConstructor(constructor);
}
// Module evaluation implementation
JSValue NodeVMModule::evaluateModule(JSGlobalObject* globalObject, JSValue key, JSValue moduleRecordValue, JSValue scriptFetcher, JSValue sentValue, JSValue resumeMode)
{
VM& vm = getVM(globalObject);
auto scope = DECLARE_THROW_SCOPE(vm);
// Handle synthetic modules
if (m_syntheticEvaluationSteps) {
JSObject* function = m_syntheticEvaluationSteps.get();
JSC::CallData callData = JSC::getCallData(function);
if (callData.type == JSC::CallData::Type::None) {
throwTypeError(globalObject, scope, "Synthetic evaluation steps must be callable"_s);
return jsUndefined();
}
MarkedArgumentBuffer args;
args.append(m_syntheticExportNames.get());
ASSERT(!args.hasOverflowed());
JSValue result = JSC::profiledCall(globalObject, JSC::ProfilingReason::API, function, callData, this, args);
RETURN_IF_EXCEPTION(scope, {});
return result;
}
// Handle source text modules
if (auto* moduleRecord = jsDynamicCast<AbstractModuleRecord*>(moduleRecordValue)) {
auto* moduleLoader = globalObject->moduleLoader();
if (!moduleLoader) {
throwTypeError(globalObject, scope, "Module loader not found"_s);
return jsUndefined();
}
moduleLoader->evaluateNonVirtual(globalObject, key, moduleRecordValue, scriptFetcher, sentValue, resumeMode);
RETURN_IF_EXCEPTION(scope, {});
JSModuleNamespaceObject* namespaceObject = moduleLoader->getModuleNamespaceObject(globalObject, moduleRecord);
RETURN_IF_EXCEPTION(scope, {});
return namespaceObject;
}
return jsUndefined();
}
// Dynamic import implementation
JSValue NodeVMModule::importModuleDynamically(JSGlobalObject* globalObject, JSValue key, JSValue moduleRecordValue, JSValue scriptFetcher)
{
VM& vm = getVM(globalObject);
auto scope = DECLARE_THROW_SCOPE(vm);
auto* promise = JSInternalPromise::create(vm, globalObject->internalPromiseStructure());
if (auto* moduleRecord = jsDynamicCast<AbstractModuleRecord*>(moduleRecordValue)) {
auto* moduleLoader = globalObject->moduleLoader();
if (!moduleLoader) {
throwTypeError(globalObject, scope, "Module loader not found"_s);
return promise;
}
// auto* importPromise = moduleLoader->requestImportModule(globalObject, moduleRecord, key, scriptFetcher, jsUndefined());
RETURN_IF_EXCEPTION(scope, promise);
return importPromise;
}
return promise;
}
} // namespace Bun

View File

@@ -0,0 +1,10 @@
#pragma once
#include "root.h"
#include "ZigGlobalObject.h"
namespace Bun {
void setupNodeVMModuleClassStructure(JSC::LazyClassStructure::Initializer&);
} // namespace Bun

View File

@@ -182,6 +182,8 @@
#include <unistd.h>
#endif
#include "NodeVMModule.h"
using namespace Bun;
BUN_DECLARE_HOST_FUNCTION(Bun__NodeUtil__jsParseArgs);
@@ -3376,6 +3378,11 @@ void GlobalObject::finishCreation(VM& vm)
addBuiltinGlobals(vm);
ASSERT(classInfo());
m_NodeVMModuleStructure.initLater(
[](LazyClassStructure::Initializer& init) {
Bun::setupNodeVMModuleClassStructure(init);
});
}
JSC_DEFINE_CUSTOM_GETTER(JSDOMFileConstructor_getter, (JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, PropertyName))

View File

@@ -497,6 +497,9 @@ public:
void finishCreation(JSC::VM&);
JSC::Structure* NodeVMModuleStructure() { return m_NodeVMModuleStructure.get(this); }
JSC::JSObject* NodeVMModuleConstructor() { return m_NodeVMModuleStructure.constructor(this); }
private:
void addBuiltinGlobals(JSC::VM&);
@@ -622,6 +625,7 @@ private:
WTF::Vector<JSC::Strong<JSC::JSPromise>> m_aboutToBeNotifiedRejectedPromises;
WTF::Vector<JSC::Strong<JSC::JSFunction>> m_ffiFunctions;
JSC::LazyClassStructure m_NodeVMModuleStructure;
};
class EvalGlobalObject : public GlobalObject {

View File

@@ -61,6 +61,7 @@ public:
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSS3Bucket;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSS3File;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSX509Certificate;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForNodeVMModule;
#include "ZigGeneratedClasses+DOMClientIsoSubspaces.h"
/* --- bun --- */

View File

@@ -1,4 +1,3 @@
#pragma once
#include "root.h"
@@ -61,6 +60,7 @@ public:
std::unique_ptr<IsoSubspace> m_subspaceForJSS3Bucket;
std::unique_ptr<IsoSubspace> m_subspaceForJSS3File;
std::unique_ptr<IsoSubspace> m_subspaceForJSX509Certificate;
std::unique_ptr<IsoSubspace> m_subspaceForNodeVMModule;
#include "ZigGeneratedClasses+DOMIsoSubspaces.h"
/*-- BUN --*/