aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Silver <14016168+silversquirl@users.noreply.github.com> 2023-05-19 00:45:18 +0100
committerGravatar GitHub <noreply@github.com> 2023-05-18 16:45:18 -0700
commitac64eb420d6ce29ecca97100b7c64afd63afb26d (patch)
tree96b0d7cd328c037a4d6a9314aafca7bdd64b1466
parentb76974a2a8a794db41b72e174b86d8536793f8e6 (diff)
downloadbun-ac64eb420d6ce29ecca97100b7c64afd63afb26d.tar.gz
bun-ac64eb420d6ce29ecca97100b7c64afd63afb26d.tar.zst
bun-ac64eb420d6ce29ecca97100b7c64afd63afb26d.zip
Implement `node:vm` (#2785)
* feat: begin implementing node:vm Script object * refactor: clean up and address review comments * refactor: rename Script to VMModuleScript * fix: expose VMModuleScript.prototype also oops I forgot to commit the new files last time * feat(vm): Implement contexts and scripts * feat(vm): implement globalThis * feat(vm): expose node:vm module with global helper functions * refactor(vm): rename VMModuleScript to NodeVMScript * feat: implement script options * doc: add TODOs for runIn*Context options
-rw-r--r--src/bun.js/bindings/NodeVMScript.cpp332
-rw-r--r--src/bun.js/bindings/NodeVMScript.h79
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.cpp37
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.h9
-rw-r--r--src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h1
-rw-r--r--src/bun.js/bindings/webcore/DOMIsoSubspaces.h3
-rw-r--r--src/bun.js/vm.exports.js67
7 files changed, 482 insertions, 46 deletions
diff --git a/src/bun.js/bindings/NodeVMScript.cpp b/src/bun.js/bindings/NodeVMScript.cpp
new file mode 100644
index 000000000..0b0b7bcd4
--- /dev/null
+++ b/src/bun.js/bindings/NodeVMScript.cpp
@@ -0,0 +1,332 @@
+#include "root.h"
+
+#include "NodeVMScript.h"
+#include "JavaScriptCore/JSObjectInlines.h"
+#include "wtf/text/ExternalStringImpl.h"
+
+#include "JavaScriptCore/FunctionPrototype.h"
+#include "JavaScriptCore/HeapAnalyzer.h"
+
+#include "JavaScriptCore/JSDestructibleObjectHeapCellType.h"
+#include "JavaScriptCore/SlotVisitorMacros.h"
+#include "JavaScriptCore/ObjectConstructor.h"
+#include "JavaScriptCore/SubspaceInlines.h"
+#include "wtf/GetPtr.h"
+#include "wtf/PointerPreparations.h"
+#include "wtf/URL.h"
+#include "JavaScriptCore/TypedArrayInlines.h"
+#include "JavaScriptCore/PropertyNameArray.h"
+#include "JavaScriptCore/JSWeakMap.h"
+#include "JavaScriptCore/JSWeakMapInlines.h"
+#include "JavaScriptCore/JSWithScope.h"
+#include "Buffer.h"
+#include "GCDefferalContext.h"
+#include "Buffer.h"
+
+#include <JavaScriptCore/DOMJITAbstractHeap.h>
+#include "DOMJITIDLConvert.h"
+#include "DOMJITIDLType.h"
+#include "DOMJITIDLTypeFilter.h"
+#include "DOMJITHelpers.h"
+#include <JavaScriptCore/DFGAbstractHeap.h>
+
+namespace WebCore {
+using namespace JSC;
+
+static EncodedJSValue constructScript(JSGlobalObject* globalObject, CallFrame* callFrame, JSValue newTarget = JSValue())
+{
+ VM& vm = globalObject->vm();
+ JSValue callee = callFrame->jsCallee();
+ ArgList args(callFrame);
+ JSValue sourceArg = args.at(0);
+ String sourceString = sourceArg.isUndefined() ? emptyString() : sourceArg.toWTFString(globalObject);
+
+ JSValue optionsArg = args.at(1);
+ String filename = ""_s;
+ OrdinalNumber lineOffset, columnOffset;
+ if (!optionsArg.isUndefined()) {
+ if (!optionsArg.isObject()) {
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ return throwVMTypeError(globalObject, scope, "options must be an object"_s);
+ }
+ JSObject* options = asObject(optionsArg);
+
+ JSValue filenameOpt = options->get(globalObject, Identifier::fromString(vm, "filename"_s));
+ if (filenameOpt.isString()) {
+ filename = filenameOpt.toWTFString(globalObject);
+ }
+
+ JSValue lineOffsetOpt = options->get(globalObject, Identifier::fromString(vm, "lineOffset"_s));
+ if (lineOffsetOpt.isAnyInt()) {
+ lineOffset = OrdinalNumber::fromZeroBasedInt(lineOffsetOpt.asAnyInt());
+ }
+ JSValue columnOffsetOpt = options->get(globalObject, Identifier::fromString(vm, "columnOffset"_s));
+ if (columnOffsetOpt.isAnyInt()) {
+ columnOffset = OrdinalNumber::fromZeroBasedInt(columnOffsetOpt.asAnyInt());
+ }
+
+ // TODO: cachedData
+ // TODO: importModuleDynamically
+ }
+
+ auto* zigGlobalObject = reinterpret_cast<Zig::GlobalObject*>(globalObject);
+ Structure* structure = zigGlobalObject->NodeVMScriptStructure();
+ if (zigGlobalObject->NodeVMScript() != newTarget) {
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ JSObject* targetObj = asObject(newTarget);
+ auto* functionGlobalObject = reinterpret_cast<Zig::GlobalObject*>(getFunctionRealm(globalObject, targetObj));
+ RETURN_IF_EXCEPTION(scope, {});
+ structure = InternalFunction::createSubclassStructure(
+ globalObject, targetObj, functionGlobalObject->NodeVMScriptStructure());
+ scope.release();
+ }
+
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ SourceCode source(
+ JSC::StringSourceProvider::create(sourceString, JSC::SourceOrigin(), filename, TextPosition(lineOffset, columnOffset)),
+ lineOffset.zeroBasedInt(), columnOffset.zeroBasedInt());
+ RETURN_IF_EXCEPTION(scope, {});
+ NodeVMScript* script = NodeVMScript::create(vm, globalObject, structure, source);
+ return JSValue::encode(JSValue(script));
+}
+
+static EncodedJSValue runInContext(JSGlobalObject* globalObject, NodeVMScript* script, JSObject* globalThis, JSScope* scope, JSValue optionsArg)
+{
+ auto& vm = globalObject->vm();
+
+ if (!optionsArg.isUndefined()) {
+ if (!optionsArg.isObject()) {
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ return throwVMTypeError(globalObject, scope, "options must be an object"_s);
+ }
+ JSObject* options = asObject(optionsArg);
+
+ // TODO: displayErrors - Not really sure what this option even does or why it's useful
+ // TODO: timeout - I can't figure out how to make Watchdog work so leaving this for now
+ // TODO: breakOnSigint - Bun doesn't support signal handlers at all yet I believe
+ }
+
+ auto err_scope = DECLARE_THROW_SCOPE(vm);
+ auto* eval = DirectEvalExecutable::create(
+ globalObject, script->source(), DerivedContextType::None, NeedsClassFieldInitializer::No, PrivateBrandRequirement::None,
+ false, false, EvalContextType::None, nullptr, nullptr, ECMAMode::sloppy());
+ RETURN_IF_EXCEPTION(err_scope, {});
+
+ return JSValue::encode(vm.interpreter.executeEval(eval, globalThis, scope));
+}
+
+JSC_DEFINE_HOST_FUNCTION(scriptConstructorCall, (JSGlobalObject* globalObject, CallFrame* callFrame))
+{
+ return constructScript(globalObject, callFrame);
+}
+
+JSC_DEFINE_HOST_FUNCTION(scriptConstructorConstruct, (JSGlobalObject* globalObject, CallFrame* callFrame))
+{
+ return constructScript(globalObject, callFrame, callFrame->newTarget());
+}
+
+JSC_DEFINE_CUSTOM_GETTER(scriptGetCachedDataRejected, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
+{
+ auto& vm = globalObject->vm();
+ return JSValue::encode(jsBoolean(true)); // TODO
+}
+JSC_DEFINE_HOST_FUNCTION(scriptCreateCachedData, (JSGlobalObject* globalObject, CallFrame* callFrame))
+{
+ auto& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ return throwVMError(globalObject, scope, "TODO: Script.createCachedData"_s);
+}
+
+JSC_DEFINE_HOST_FUNCTION(scriptRunInContext, (JSGlobalObject* globalObject, CallFrame* callFrame))
+{
+ auto& vm = globalObject->vm();
+
+ JSValue thisValue = callFrame->thisValue();
+ auto* script = jsDynamicCast<NodeVMScript*>(thisValue);
+ if (UNLIKELY(!script)) {
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ return throwVMTypeError(globalObject, scope, "Script.prototype.runInContext can only be called on a Script object"_s);
+ }
+
+ ArgList args(callFrame);
+
+ JSValue contextArg = args.at(0);
+ if (!UNLIKELY(contextArg.isObject())) {
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ return throwVMTypeError(globalObject, scope, "context parameter must be a contextified object"_s);
+ }
+ JSObject* context = asObject(contextArg);
+
+ auto* zigGlobalObject = reinterpret_cast<Zig::GlobalObject*>(globalObject);
+ JSValue scopeVal = zigGlobalObject->vmModuleContextMap()->get(context);
+ if (UNLIKELY(scopeVal.isUndefined())) {
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ return throwVMTypeError(globalObject, scope, "context parameter must be a contextified object"_s);
+ }
+ JSScope* scope = jsDynamicCast<JSScope*>(scopeVal);
+ ASSERT(scope);
+
+ return runInContext(globalObject, script, context, scope, args.at(1));
+}
+JSC_DEFINE_HOST_FUNCTION(scriptRunInThisContext, (JSGlobalObject* globalObject, CallFrame* callFrame))
+{
+ auto& vm = globalObject->vm();
+ JSValue thisValue = callFrame->thisValue();
+ auto* script = jsDynamicCast<NodeVMScript*>(thisValue);
+ if (UNLIKELY(!script)) {
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ return throwVMTypeError(globalObject, scope, "Script.prototype.runInThisContext can only be called on a Script object"_s);
+ }
+
+ ArgList args(callFrame);
+ return runInContext(globalObject, script, globalObject->globalThis(), globalObject->globalScope(), args.at(0));
+}
+
+JSC_DEFINE_CUSTOM_GETTER(scriptGetSourceMapURL, (JSGlobalObject* globalObject, EncodedJSValue thisValueEncoded, PropertyName))
+{
+ auto& vm = globalObject->vm();
+ JSValue thisValue = JSValue::decode(thisValueEncoded);
+ auto* script = jsDynamicCast<NodeVMScript*>(thisValue);
+ if (UNLIKELY(!script)) {
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ return throwVMTypeError(globalObject, scope, "Script.prototype.sourceMapURL getter can only be called on a Script object"_s);
+ }
+
+ // FIXME: doesn't seem to work? Just returns undefined
+ const auto& url = script->source().provider()->sourceMappingURLDirective();
+ return JSValue::encode(jsString(vm, url));
+}
+
+JSC_DEFINE_HOST_FUNCTION(vmModule_createContext, (JSGlobalObject* globalObject, CallFrame* callFrame))
+{
+ auto& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ ArgList args(callFrame);
+ JSValue contextArg = args.at(0);
+ if (!contextArg.isObject()) {
+ return throwVMTypeError(globalObject, scope, "parameter to createContext must be an object"_s);
+ }
+ JSObject* context = asObject(contextArg);
+
+ PropertyDescriptor descriptor;
+ descriptor.setWritable(false);
+ descriptor.setEnumerable(false);
+ descriptor.setValue(context);
+ JSObject::defineOwnProperty(context, globalObject, Identifier::fromString(vm, "globalThis"_s), descriptor, true);
+ JSScope* contextScope = JSWithScope::create(vm, globalObject, globalObject->globalScope(), context);
+
+ auto* zigGlobalObject = reinterpret_cast<Zig::GlobalObject*>(globalObject);
+ zigGlobalObject->vmModuleContextMap()->set(vm, context, contextScope);
+
+ return JSValue::encode(context);
+}
+
+JSC_DEFINE_HOST_FUNCTION(vmModule_isContext, (JSGlobalObject* globalObject, CallFrame* callFrame))
+{
+ ArgList args(callFrame);
+ JSValue contextArg = args.at(0);
+ bool isContext;
+ if (!contextArg.isObject()) {
+ isContext = false;
+ } else {
+ auto* zigGlobalObject = reinterpret_cast<Zig::GlobalObject*>(globalObject);
+ isContext = zigGlobalObject->vmModuleContextMap()->has(asObject(contextArg));
+ }
+ return JSValue::encode(jsBoolean(isContext));
+}
+
+class NodeVMScriptPrototype final : public JSNonFinalObject {
+public:
+ using Base = JSNonFinalObject;
+
+ static NodeVMScriptPrototype* create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
+ {
+ NodeVMScriptPrototype* ptr = new (NotNull, allocateCell<NodeVMScriptPrototype>(vm)) NodeVMScriptPrototype(vm, structure);
+ ptr->finishCreation(vm);
+ return ptr;
+ }
+
+ DECLARE_INFO;
+ template<typename CellType, SubspaceAccess>
+ static GCClient::IsoSubspace* subspaceFor(VM& vm)
+ {
+ return &vm.plainObjectSpace();
+ }
+ static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
+ {
+ return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
+ }
+
+private:
+ NodeVMScriptPrototype(VM& vm, Structure* structure)
+ : Base(vm, structure)
+ {
+ }
+
+ void finishCreation(VM&);
+};
+STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(NodeVMScriptPrototype, NodeVMScriptPrototype::Base);
+
+static const struct HashTableValue scriptPrototypeTableValues[] = {
+ { "cachedDataRejected"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, scriptGetCachedDataRejected, nullptr } },
+ { "createCachedData"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, scriptCreateCachedData, 0 } },
+ { "runInContext"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, scriptRunInContext, 0 } },
+ { "runInThisContext"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, scriptRunInThisContext, 0 } },
+ { "sourceMapURL"_s, static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, scriptGetSourceMapURL, nullptr } },
+};
+
+const ClassInfo NodeVMScriptPrototype::s_info = { "Script"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(NodeVMScriptPrototype) };
+const ClassInfo NodeVMScript::s_info = { "Script"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(NodeVMScript) };
+const ClassInfo NodeVMScriptConstructor::s_info = { "Script"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(NodeVMScriptConstructor) };
+
+NodeVMScriptConstructor::NodeVMScriptConstructor(VM& vm, Structure* structure)
+ : NodeVMScriptConstructor::Base(vm, structure, scriptConstructorCall, scriptConstructorConstruct)
+{
+}
+
+NodeVMScriptConstructor* NodeVMScriptConstructor::create(VM& vm, JSGlobalObject* globalObject, Structure* structure, JSObject* prototype)
+{
+ NodeVMScriptConstructor* ptr = new (NotNull, allocateCell<NodeVMScriptConstructor>(vm)) NodeVMScriptConstructor(vm, structure);
+ ptr->finishCreation(vm, prototype);
+ return ptr;
+}
+
+void NodeVMScriptConstructor::finishCreation(VM& vm, JSObject* prototype)
+{
+ Base::finishCreation(vm, 1, "Script"_s, PropertyAdditionMode::WithStructureTransition);
+ putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
+ ASSERT(inherits(info()));
+}
+
+void NodeVMScriptPrototype::finishCreation(VM& vm)
+{
+ Base::finishCreation(vm);
+ reifyStaticProperties(vm, NodeVMScript::info(), scriptPrototypeTableValues, *this);
+ JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
+}
+
+JSObject* NodeVMScript::createPrototype(VM& vm, JSGlobalObject* globalObject)
+{
+ return NodeVMScriptPrototype::create(vm, globalObject, NodeVMScriptPrototype::createStructure(vm, globalObject, globalObject->objectPrototype()));
+}
+
+NodeVMScript* NodeVMScript::create(VM& vm, JSGlobalObject* globalObject, Structure* structure, SourceCode source)
+{
+ NodeVMScript* ptr = new (NotNull, allocateCell<NodeVMScript>(vm)) NodeVMScript(vm, structure, source);
+ ptr->finishCreation(vm);
+ return ptr;
+}
+
+void NodeVMScript::finishCreation(VM& vm)
+{
+ Base::finishCreation(vm);
+ ASSERT(inherits(info()));
+}
+
+void NodeVMScript::destroy(JSCell* cell)
+{
+ static_cast<NodeVMScript*>(cell)->NodeVMScript::~NodeVMScript();
+}
+
+}
diff --git a/src/bun.js/bindings/NodeVMScript.h b/src/bun.js/bindings/NodeVMScript.h
new file mode 100644
index 000000000..09878e533
--- /dev/null
+++ b/src/bun.js/bindings/NodeVMScript.h
@@ -0,0 +1,79 @@
+#pragma once
+
+#include "root.h"
+#include "ZigGlobalObject.h"
+
+#include "JavaScriptCore/JSFunction.h"
+#include "JavaScriptCore/VM.h"
+
+#include "headers-handwritten.h"
+#include "BunClientData.h"
+#include "JavaScriptCore/CallFrame.h"
+
+namespace WebCore {
+
+class NodeVMScriptConstructor final : public JSC::InternalFunction {
+public:
+ using Base = JSC::InternalFunction;
+
+ static NodeVMScriptConstructor* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSC::JSObject* prototype);
+
+ DECLARE_EXPORT_INFO;
+
+ static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
+ {
+ return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, Base::StructureFlags), info());
+ }
+
+private:
+ NodeVMScriptConstructor(JSC::VM& vm, JSC::Structure* structure);
+
+ void finishCreation(JSC::VM&, JSC::JSObject* prototype);
+};
+STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(NodeVMScriptConstructor, InternalFunction);
+
+class NodeVMScript final : public JSC::JSDestructibleObject {
+public:
+ using Base = JSC::JSDestructibleObject;
+
+ static NodeVMScript* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSC::SourceCode source);
+
+ DECLARE_EXPORT_INFO;
+ template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
+ {
+ if constexpr (mode == JSC::SubspaceAccess::Concurrently)
+ return nullptr;
+ return WebCore::subspaceForImpl<NodeVMScript, WebCore::UseCustomHeapCellType::No>(
+ vm,
+ [](auto& spaces) { return spaces.m_clientSubspaceForNodeVMScript.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForNodeVMScript = std::forward<decltype(space)>(space); },
+ [](auto& spaces) { return spaces.m_subspaceForNodeVMScript.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_subspaceForNodeVMScript = std::forward<decltype(space)>(space); });
+ }
+
+ static void destroy(JSC::JSCell*);
+ static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
+ {
+ return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
+ }
+
+ static JSObject* createPrototype(VM& vm, JSGlobalObject* globalObject);
+
+ const JSC::SourceCode& source() const { return m_source; }
+
+private:
+ JSC::SourceCode m_source;
+
+ NodeVMScript(JSC::VM& vm, JSC::Structure* structure, JSC::SourceCode source)
+ : Base(vm, structure)
+ , m_source(source)
+ {
+ }
+
+ void finishCreation(JSC::VM&);
+};
+
+JSC_DECLARE_HOST_FUNCTION(vmModule_createContext);
+JSC_DECLARE_HOST_FUNCTION(vmModule_isContext);
+
+}
diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp
index eeccb6650..84cef9452 100644
--- a/src/bun.js/bindings/ZigGlobalObject.cpp
+++ b/src/bun.js/bindings/ZigGlobalObject.cpp
@@ -46,6 +46,7 @@
#include "JavaScriptCore/JSString.h"
#include "JavaScriptCore/JSValueInternal.h"
#include "JavaScriptCore/JSVirtualMachineInternal.h"
+#include "JavaScriptCore/JSWeakMap.h"
#include "JavaScriptCore/ObjectConstructor.h"
#include "JavaScriptCore/OptionsList.h"
#include "JavaScriptCore/ParserError.h"
@@ -105,6 +106,7 @@
#include "ReadableStreamBuiltins.h"
#include "BunJSCModule.h"
#include "ModuleLoader.h"
+#include "NodeVMScript.h"
#include "ZigGeneratedClasses.h"
#include "JavaScriptCore/DateInstance.h"
@@ -1209,6 +1211,7 @@ JSC:
static NeverDestroyed<const String> noopString(MAKE_STATIC_STRING_IMPL("noop"));
static NeverDestroyed<const String> createImportMeta(MAKE_STATIC_STRING_IMPL("createImportMeta"));
static NeverDestroyed<const String> masqueradesAsUndefined(MAKE_STATIC_STRING_IMPL("masqueradesAsUndefined"));
+ static NeverDestroyed<const String> vmString(MAKE_STATIC_STRING_IMPL("vm"));
JSC::JSValue moduleName = callFrame->argument(0);
if (moduleName.isNumber()) {
@@ -1292,6 +1295,20 @@ JSC:
return JSValue::encode(InternalFunction::createFunctionThatMasqueradesAsUndefined(vm, globalObject, 0, String(), functionCallNotImplemented));
}
+ if (string == vmString) {
+ auto* obj = constructEmptyObject(globalObject);
+ obj->putDirect(
+ vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "Script"_s)),
+ reinterpret_cast<Zig::GlobalObject*>(globalObject)->NodeVMScript(), 0);
+ obj->putDirect(
+ vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "createContext"_s)),
+ JSC::JSFunction::create(vm, globalObject, 0, "createContext"_s, vmModule_createContext, ImplementationVisibility::Public), 0);
+ obj->putDirect(
+ vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "isContext"_s)),
+ JSC::JSFunction::create(vm, globalObject, 0, "isContext"_s, vmModule_isContext, ImplementationVisibility::Public), 0);
+ return JSValue::encode(obj);
+ }
+
if (UNLIKELY(string == noopString)) {
auto* obj = constructEmptyObject(globalObject);
obj->putDirectCustomAccessor(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "getterSetter"_s)), JSC::CustomGetterSetter::create(vm, noop_getter, noop_setter), 0);
@@ -2579,6 +2596,11 @@ void GlobalObject::finishCreation(VM& vm)
init.set(dnsObject);
});
+ m_vmModuleContextMap.initLater(
+ [](const Initializer<JSWeakMap>& init) {
+ init.set(JSWeakMap::create(init.vm, init.owner->weakMapStructure()));
+ });
+
m_JSBufferSubclassStructure.initLater(
[](const Initializer<Structure>& init) {
auto* globalObject = reinterpret_cast<Zig::GlobalObject*>(init.owner);
@@ -2849,6 +2871,19 @@ void GlobalObject::finishCreation(VM& vm)
init.setStructure(Zig::JSFFIFunction::createStructure(init.vm, init.global, init.global->functionPrototype()));
});
+ m_NodeVMScriptClassStructure.initLater(
+ [](LazyClassStructure::Initializer& init) {
+ auto prototype = NodeVMScript::createPrototype(init.vm, init.global);
+ auto* structure = NodeVMScript::createStructure(init.vm, init.global, prototype);
+ auto* constructorStructure = NodeVMScriptConstructor::createStructure(
+ init.vm, init.global, init.global->m_functionPrototype.get());
+ auto* constructor = NodeVMScriptConstructor::create(
+ init.vm, init.global, constructorStructure, prototype);
+ init.setPrototype(prototype);
+ init.setStructure(structure);
+ init.setConstructor(constructor);
+ });
+
addBuiltinGlobals(vm);
#if ENABLE(REMOTE_INSPECTOR)
@@ -3672,6 +3707,7 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor)
thisObject->m_JSStringDecoderClassStructure.visit(visitor);
thisObject->m_NapiClassStructure.visit(visitor);
thisObject->m_JSBufferClassStructure.visit(visitor);
+ thisObject->m_NodeVMScriptClassStructure.visit(visitor);
thisObject->m_pendingVirtualModuleResultStructure.visit(visitor);
thisObject->m_performMicrotaskFunction.visit(visitor);
@@ -3696,6 +3732,7 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor)
thisObject->m_requireResolveFunctionStructure.visit(visitor);
thisObject->m_resolveFunctionPrototype.visit(visitor);
thisObject->m_dnsObject.visit(visitor);
+ thisObject->m_vmModuleContextMap.visit(visitor);
thisObject->m_bunSleepThenCallback.visit(visitor);
for (auto& barrier : thisObject->m_thenables) {
diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h
index 879644963..3aa1defa8 100644
--- a/src/bun.js/bindings/ZigGlobalObject.h
+++ b/src/bun.js/bindings/ZigGlobalObject.h
@@ -229,6 +229,10 @@ public:
JSC::JSObject* JSReadableState() { return m_JSReadableStateClassStructure.constructorInitializedOnMainThread(this); }
JSC::JSValue JSReadableStatePrototype() { return m_JSReadableStateClassStructure.prototypeInitializedOnMainThread(this); }
+ JSC::Structure* NodeVMScriptStructure() { return m_NodeVMScriptClassStructure.getInitializedOnMainThread(this); }
+ JSC::JSObject* NodeVMScript() { return m_NodeVMScriptClassStructure.constructorInitializedOnMainThread(this); }
+ JSC::JSValue NodeVMScriptPrototype() { return m_NodeVMScriptClassStructure.prototypeInitializedOnMainThread(this); }
+
JSC::JSMap* readableStreamNativeMap() { return m_lazyReadableStreamPrototypeMap.getInitializedOnMainThread(this); }
JSC::JSMap* requireMap() { return m_requireMap.getInitializedOnMainThread(this); }
JSC::Structure* encodeIntoObjectStructure() { return m_encodeIntoObjectStructure.getInitializedOnMainThread(this); }
@@ -250,6 +254,8 @@ public:
JSObject* dnsObject() { return m_dnsObject.getInitializedOnMainThread(this); }
+ JSWeakMap* vmModuleContextMap() { return m_vmModuleContextMap.getInitializedOnMainThread(this); }
+
JSC::JSObject* processObject()
{
return m_processObject.getInitializedOnMainThread(this);
@@ -423,6 +429,7 @@ private:
LazyClassStructure m_NapiClassStructure;
LazyClassStructure m_callSiteStructure;
LazyClassStructure m_JSBufferClassStructure;
+ LazyClassStructure m_NodeVMScriptClassStructure;
/**
* WARNING: You must update visitChildrenImpl() if you add a new field.
@@ -455,6 +462,8 @@ private:
LazyProperty<JSGlobalObject, JSC::Structure> m_requireResolveFunctionStructure;
LazyProperty<JSGlobalObject, JSObject> m_resolveFunctionPrototype;
LazyProperty<JSGlobalObject, JSObject> m_dnsObject;
+ LazyProperty<JSGlobalObject, JSWeakMap> m_vmModuleContextMap;
+
LazyProperty<JSGlobalObject, JSFunction> m_bunSleepThenCallback;
DOMGuardedObjectSet m_guardedObjects WTF_GUARDED_BY_LOCK(m_gcLock);
diff --git a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h
index 1f7419c88..75fff16a0 100644
--- a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h
+++ b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h
@@ -32,6 +32,7 @@ public:
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForNapiExternal;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForRequireResolveFunction;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForBundlerPlugin;
+ std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForNodeVMScript;
#include "ZigGeneratedClasses+DOMClientIsoSubspaces.h"
/* --- bun --- */
diff --git a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h
index d55810fcb..2b392a49d 100644
--- a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h
+++ b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h
@@ -32,6 +32,7 @@ public:
std::unique_ptr<IsoSubspace> m_subspaceForNapiExternal;
std::unique_ptr<IsoSubspace> m_subspaceForRequireResolveFunction;
std::unique_ptr<IsoSubspace> m_subspaceForBundlerPlugin;
+ std::unique_ptr<IsoSubspace> m_subspaceForNodeVMScript;
#include "ZigGeneratedClasses+DOMIsoSubspaces.h"
/*-- BUN --*/
@@ -898,4 +899,4 @@ public:
namespace WebCore {
using DOMIsoSubspaces = WebCore::DOMIsoSubspaces;
-} \ No newline at end of file
+}
diff --git a/src/bun.js/vm.exports.js b/src/bun.js/vm.exports.js
index 77fa31b01..f01a61581 100644
--- a/src/bun.js/vm.exports.js
+++ b/src/bun.js/vm.exports.js
@@ -1,12 +1,8 @@
-// TODO: Implement vm module
-
-function hideFromStack(fns) {
- for (const fn of fns) {
- Object.defineProperty(fn, "name", {
- value: "::bunternal::",
- });
- }
+const lazy = globalThis[Symbol.for("Bun.lazy")];
+if (!lazy || typeof lazy !== "function") {
+ throw new Error("Something went wrong while loading Bun. Expected 'Bun.lazy' to be defined.");
}
+const vm = lazy("vm");
class TODO extends Error {
constructor(messageName) {
@@ -22,23 +18,26 @@ function notimpl(message) {
throw new TODO(message);
}
-function createContext() {
- notimpl("createContext");
-}
-function createScript() {
- notimpl("createScript");
-}
-function runInContext() {
- notimpl("runInContext");
-}
-function runInNewContext() {
- notimpl("runInNewContext");
+const createContext = vm.createContext;
+const isContext = vm.isContext;
+const Script = vm.Script;
+
+Script.prototype.runInNewContext = function (contextObject, options) {
+ if (contextObject === undefined) {
+ contextObject = {};
+ }
+ const context = createContext(contextObject);
+ this.runInContext(context, options);
+};
+
+function runInContext(code, context, options) {
+ new Script(code).runInContext(context, options);
}
-function runInThisContext() {
- notimpl("runInThisContext");
+function runInNewContext(code, contextObject, options) {
+ new Script(code).runInNewContext(contextObject, options);
}
-function isContext() {
- notimpl("isContext");
+function runInThisContext(code, options) {
+ new Script(code).runInNewContext(options);
}
function compileFunction() {
notimpl("compileFunction");
@@ -47,15 +46,8 @@ function measureMemory() {
notimpl("measureMemory");
}
-class Script {
- constructor() {
- notimpl("Script");
- }
-}
-
const defaultObject = {
createContext,
- createScript,
runInContext,
runInNewContext,
runInThisContext,
@@ -69,7 +61,6 @@ const defaultObject = {
export {
defaultObject as default,
createContext,
- createScript,
runInContext,
runInNewContext,
runInThisContext,
@@ -78,17 +69,3 @@ export {
measureMemory,
Script,
};
-
-hideFromStack([
- TODO.prototype.constructor,
- notimpl,
- createContext,
- createScript,
- runInContext,
- runInNewContext,
- runInThisContext,
- isContext,
- compileFunction,
- measureMemory,
- Script.prototype.constructor,
-]);