mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
Add support for vm.constants.DONT_CONTEXTIFY (#20088)
This commit is contained in:
@@ -2499,6 +2499,8 @@ JSC_DEFINE_HOST_FUNCTION(Bun::jsFunctionMakeErrorWithCode, (JSC::JSGlobalObject
|
||||
return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_VM_MODULE_NOT_MODULE, "Provided module is not an instance of Module"_s));
|
||||
case ErrorCode::ERR_VM_MODULE_DIFFERENT_CONTEXT:
|
||||
return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_VM_MODULE_DIFFERENT_CONTEXT, "Linked modules must use the same context"_s));
|
||||
case ErrorCode::ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING:
|
||||
return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING, "A dynamic import callback was not specified."_s));
|
||||
|
||||
default: {
|
||||
break;
|
||||
|
||||
@@ -296,6 +296,7 @@ const errors: ErrorCodeMapping = [
|
||||
["ERR_VM_MODULE_DIFFERENT_CONTEXT", Error],
|
||||
["ERR_VM_MODULE_LINK_FAILURE", Error],
|
||||
["ERR_VM_MODULE_CACHED_DATA_REJECTED", Error],
|
||||
["ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING", TypeError],
|
||||
["HPE_INVALID_HEADER_TOKEN", Error],
|
||||
["HPE_HEADER_OVERFLOW", Error],
|
||||
];
|
||||
|
||||
@@ -55,16 +55,23 @@
|
||||
#include "JavaScriptCore/FunctionCodeBlock.h"
|
||||
#include "JavaScriptCore/JIT.h"
|
||||
#include "JavaScriptCore/ProgramCodeBlock.h"
|
||||
#include "JavaScriptCore/GlobalObjectMethodTable.h"
|
||||
#include "NodeVMScriptFetcher.h"
|
||||
#include "wtf/FileHandle.h"
|
||||
|
||||
#include "../vm/SigintWatcher.h"
|
||||
|
||||
#include "JavaScriptCore/GetterSetter.h"
|
||||
|
||||
namespace Bun {
|
||||
using namespace WebCore;
|
||||
|
||||
static JSInternalPromise* moduleLoaderImportModuleInner(NodeVMGlobalObject* globalObject, JSC::JSModuleLoader* moduleLoader, JSC::JSString* moduleName, JSC::JSValue parameters, const JSC::SourceOrigin& sourceOrigin);
|
||||
|
||||
namespace NodeVM {
|
||||
|
||||
static JSInternalPromise* importModuleInner(JSGlobalObject* globalObject, JSString* moduleName, JSValue parameters, const SourceOrigin& sourceOrigin, JSValue dynamicImportCallback, JSValue owner);
|
||||
|
||||
bool extractCachedData(JSValue cachedDataValue, WTF::Vector<uint8_t>& outCachedData)
|
||||
{
|
||||
if (!cachedDataValue.isCell()) {
|
||||
@@ -126,6 +133,8 @@ JSC::JSFunction* constructAnonymousFunction(JSC::JSGlobalObject* globalObject, c
|
||||
|
||||
if (actuallyValid) {
|
||||
auto exception = error.toErrorObject(globalObject, sourceCode, -1);
|
||||
RETURN_IF_EXCEPTION(throwScope, nullptr);
|
||||
|
||||
throwException(globalObject, throwScope, exception);
|
||||
return nullptr;
|
||||
}
|
||||
@@ -174,6 +183,7 @@ JSC::JSFunction* constructAnonymousFunction(JSC::JSGlobalObject* globalObject, c
|
||||
{
|
||||
DeferGC deferGC(vm);
|
||||
programCodeBlock = ProgramCodeBlock::create(vm, programExecutable, unlinkedProgramCodeBlock, scope);
|
||||
RETURN_IF_EXCEPTION(throwScope, nullptr);
|
||||
}
|
||||
|
||||
if (!programCodeBlock || programCodeBlock->numberOfFunctionExprs() == 0) {
|
||||
@@ -193,6 +203,7 @@ JSC::JSFunction* constructAnonymousFunction(JSC::JSGlobalObject* globalObject, c
|
||||
RefPtr<JSC::CachedBytecode> producedBytecode = getBytecode(globalObject, programExecutable, sourceCode);
|
||||
if (producedBytecode) {
|
||||
JSC::JSUint8Array* buffer = WebCore::createBuffer(globalObject, producedBytecode->span());
|
||||
RETURN_IF_EXCEPTION(throwScope, nullptr);
|
||||
function->putDirect(vm, JSC::Identifier::fromString(vm, "cachedData"_s), buffer);
|
||||
function->putDirect(vm, JSC::Identifier::fromString(vm, "cachedDataProduced"_s), jsBoolean(true));
|
||||
} else {
|
||||
@@ -201,39 +212,84 @@ JSC::JSFunction* constructAnonymousFunction(JSC::JSGlobalObject* globalObject, c
|
||||
}
|
||||
} else {
|
||||
function->putDirect(vm, JSC::Identifier::fromString(vm, "cachedDataRejected"_s), jsBoolean(bytecodeAccepted == TriState::False));
|
||||
RETURN_IF_EXCEPTION(throwScope, nullptr);
|
||||
}
|
||||
|
||||
return function;
|
||||
}
|
||||
|
||||
JSInternalPromise* importModule(JSGlobalObject* globalObject, JSString* moduleNameValue, JSValue parameters, const SourceOrigin& sourceOrigin)
|
||||
JSInternalPromise* importModule(JSGlobalObject* globalObject, JSString* moduleName, JSValue parameters, const SourceOrigin& sourceOrigin)
|
||||
{
|
||||
if (auto* fetcher = sourceOrigin.fetcher(); !fetcher || fetcher->fetcherType() != ScriptFetcher::Type::NodeVM) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* fetcher = static_cast<NodeVMScriptFetcher*>(sourceOrigin.fetcher());
|
||||
|
||||
JSValue dynamicImportCallback = fetcher->dynamicImportCallback();
|
||||
|
||||
if (!dynamicImportCallback || !dynamicImportCallback.isCallable()) {
|
||||
if (auto* fetcher = sourceOrigin.fetcher(); !fetcher || fetcher->fetcherType() != ScriptFetcher::Type::NodeVM) {
|
||||
if (!sourceOrigin.url().isEmpty()) {
|
||||
if (auto* nodeVmGlobalObject = jsDynamicCast<NodeVMGlobalObject*>(globalObject)) {
|
||||
if (nodeVmGlobalObject->dynamicImportCallback()) {
|
||||
RELEASE_AND_RETURN(scope, moduleLoaderImportModuleInner(nodeVmGlobalObject, globalObject->moduleLoader(), moduleName, parameters, sourceOrigin));
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSFunction* owner = fetcher->owner();
|
||||
auto* fetcher = static_cast<NodeVMScriptFetcher*>(sourceOrigin.fetcher());
|
||||
|
||||
if (fetcher->isUsingDefaultLoader()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSValue dynamicImportCallback = fetcher->dynamicImportCallback();
|
||||
|
||||
if (isUseMainContextDefaultLoaderConstant(globalObject, dynamicImportCallback)) {
|
||||
auto defer = fetcher->temporarilyUseDefaultLoader();
|
||||
Zig::GlobalObject* zigGlobalObject = defaultGlobalObject(globalObject);
|
||||
RELEASE_AND_RETURN(scope, zigGlobalObject->moduleLoaderImportModule(zigGlobalObject, zigGlobalObject->moduleLoader(), moduleName, parameters, sourceOrigin));
|
||||
} else if (!dynamicImportCallback || !dynamicImportCallback.isCallable()) {
|
||||
throwException(globalObject, scope, createError(globalObject, ErrorCode::ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING, "A dynamic import callback was not specified."_s));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RELEASE_AND_RETURN(scope, importModuleInner(globalObject, moduleName, parameters, sourceOrigin, dynamicImportCallback, fetcher->owner()));
|
||||
}
|
||||
|
||||
static JSInternalPromise* importModuleInner(JSGlobalObject* globalObject, JSString* moduleName, JSValue parameters, const SourceOrigin& sourceOrigin, JSValue dynamicImportCallback, JSValue owner)
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
if (parameters.isObject()) {
|
||||
if (JSValue with = asObject(parameters)->getIfPropertyExists(globalObject, vm.propertyNames->with)) {
|
||||
parameters = with;
|
||||
}
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
}
|
||||
|
||||
MarkedArgumentBuffer args;
|
||||
args.append(moduleNameValue);
|
||||
args.append(owner ? owner : jsUndefined());
|
||||
args.append(moduleName);
|
||||
if (owner) {
|
||||
args.append(owner);
|
||||
} else if (auto* nodeVmGlobalObject = jsDynamicCast<NodeVMGlobalObject*>(globalObject)) {
|
||||
if (nodeVmGlobalObject->isNotContextified()) {
|
||||
args.append(nodeVmGlobalObject->specialSandbox());
|
||||
} else {
|
||||
args.append(nodeVmGlobalObject->contextifiedObject());
|
||||
}
|
||||
} else {
|
||||
args.append(jsUndefined());
|
||||
}
|
||||
args.append(parameters);
|
||||
|
||||
JSValue result = AsyncContextFrame::call(globalObject, dynamicImportCallback, jsUndefined(), args);
|
||||
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
|
||||
if (result.isUndefinedOrNull()) {
|
||||
throwException(globalObject, scope, createError(globalObject, ErrorCode::ERR_VM_MODULE_NOT_MODULE, "Provided module is not an instance of Module"_s));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (auto* promise = jsDynamicCast<JSInternalPromise*>(result)) {
|
||||
return promise;
|
||||
}
|
||||
@@ -267,7 +323,7 @@ JSInternalPromise* importModule(JSGlobalObject* globalObject, JSString* moduleNa
|
||||
promise = promise->then(globalObject, transformer, nullptr);
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
|
||||
return promise;
|
||||
RELEASE_AND_RETURN(scope, promise);
|
||||
}
|
||||
|
||||
// Helper function to create an anonymous function expression with parameters
|
||||
@@ -368,9 +424,11 @@ JSC::EncodedJSValue createCachedData(JSGlobalObject* globalObject, const JSC::So
|
||||
|
||||
std::span<const uint8_t> bytes = bytecode->span();
|
||||
JSC::JSUint8Array* buffer = WebCore::createBuffer(globalObject, bytes);
|
||||
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
ASSERT(buffer);
|
||||
|
||||
if (!buffer) {
|
||||
return throwVMError(globalObject, scope, "Failed to create buffer"_s);
|
||||
}
|
||||
|
||||
return JSValue::encode(buffer);
|
||||
}
|
||||
@@ -386,6 +444,7 @@ bool handleException(JSGlobalObject* globalObject, VM& vm, NakedPtr<JSC::Excepti
|
||||
return false;
|
||||
}
|
||||
String stack = stack_jsval.toWTFString(globalObject);
|
||||
RETURN_IF_EXCEPTION(throwScope, {});
|
||||
|
||||
auto& e_stack = exception->stack();
|
||||
size_t stack_size = e_stack.size();
|
||||
@@ -411,8 +470,12 @@ bool handleException(JSGlobalObject* globalObject, VM& vm, NakedPtr<JSC::Excepti
|
||||
|
||||
// Returns an encoded exception if the options are invalid.
|
||||
// Otherwise, returns an empty optional.
|
||||
std::optional<JSC::EncodedJSValue> getNodeVMContextOptions(JSGlobalObject* globalObject, JSC::VM& vm, JSC::ThrowScope& scope, JSValue optionsArg, NodeVMContextOptions& outOptions, ASCIILiteral codeGenerationKey)
|
||||
std::optional<JSC::EncodedJSValue> getNodeVMContextOptions(JSGlobalObject* globalObject, JSC::VM& vm, JSC::ThrowScope& scope, JSValue optionsArg, NodeVMContextOptions& outOptions, ASCIILiteral codeGenerationKey, JSValue* importer)
|
||||
{
|
||||
if (importer) {
|
||||
*importer = jsUndefined();
|
||||
}
|
||||
|
||||
outOptions = {};
|
||||
|
||||
// If options is provided, validate name and origin properties
|
||||
@@ -440,10 +503,19 @@ std::optional<JSC::EncodedJSValue> getNodeVMContextOptions(JSGlobalObject* globa
|
||||
}
|
||||
}
|
||||
|
||||
auto codeGenerationValue = options->getIfPropertyExists(globalObject, Identifier::fromString(vm, codeGenerationKey));
|
||||
JSValue importModuleDynamicallyValue = options->getIfPropertyExists(globalObject, Identifier::fromString(vm, "importModuleDynamically"_s));
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
if (codeGenerationValue) {
|
||||
|
||||
if (importModuleDynamicallyValue) {
|
||||
if (importer && importModuleDynamicallyValue && (importModuleDynamicallyValue.isCallable() || isUseMainContextDefaultLoaderConstant(globalObject, importModuleDynamicallyValue))) {
|
||||
*importer = importModuleDynamicallyValue;
|
||||
}
|
||||
}
|
||||
|
||||
JSValue codeGenerationValue = options->getIfPropertyExists(globalObject, Identifier::fromString(vm, codeGenerationKey));
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
if (codeGenerationValue) {
|
||||
if (codeGenerationValue.isUndefined()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -462,6 +534,7 @@ std::optional<JSC::EncodedJSValue> getNodeVMContextOptions(JSGlobalObject* globa
|
||||
}
|
||||
|
||||
outOptions.allowStrings = allowStringsValue.toBoolean(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
}
|
||||
|
||||
auto allowWasmValue = codeGenerationObject->getIfPropertyExists(globalObject, Identifier::fromString(vm, "wasm"_s));
|
||||
@@ -472,6 +545,7 @@ std::optional<JSC::EncodedJSValue> getNodeVMContextOptions(JSGlobalObject* globa
|
||||
}
|
||||
|
||||
outOptions.allowWasm = allowWasmValue.toBoolean(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -500,13 +574,28 @@ NodeVMGlobalObject* getGlobalObjectFromContext(JSGlobalObject* globalObject, JSV
|
||||
auto* zigGlobalObject = defaultGlobalObject(globalObject);
|
||||
JSValue scopeValue = zigGlobalObject->vmModuleContextMap()->get(context);
|
||||
if (scopeValue.isUndefined()) {
|
||||
if (auto* specialSandbox = jsDynamicCast<NodeVMSpecialSandbox*>(context)) {
|
||||
return specialSandbox->parentGlobal();
|
||||
}
|
||||
|
||||
if (auto* proxy = jsDynamicCast<JSGlobalProxy*>(context)) {
|
||||
if (auto* nodeVmGlobalObject = jsDynamicCast<NodeVMGlobalObject*>(proxy->target())) {
|
||||
return nodeVmGlobalObject;
|
||||
}
|
||||
}
|
||||
|
||||
if (canThrow) {
|
||||
INVALID_ARG_VALUE_VM_VARIATION(scope, globalObject, "contextifiedObject"_s, context);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NodeVMGlobalObject* nodeVmGlobalObject = jsDynamicCast<NodeVMGlobalObject*>(scopeValue);
|
||||
auto* nodeVmGlobalObject = jsDynamicCast<NodeVMGlobalObject*>(scopeValue);
|
||||
|
||||
if (!nodeVmGlobalObject) {
|
||||
nodeVmGlobalObject = jsDynamicCast<NodeVMGlobalObject*>(context);
|
||||
}
|
||||
|
||||
if (!nodeVmGlobalObject) {
|
||||
if (canThrow) {
|
||||
INVALID_ARG_VALUE_VM_VARIATION(scope, globalObject, "contextifiedObject"_s, context);
|
||||
@@ -525,12 +614,98 @@ JSC::EncodedJSValue INVALID_ARG_VALUE_VM_VARIATION(JSC::ThrowScope& throwScope,
|
||||
return {};
|
||||
}
|
||||
|
||||
bool isContext(JSGlobalObject* globalObject, JSValue value)
|
||||
{
|
||||
auto* zigGlobalObject = defaultGlobalObject(globalObject);
|
||||
|
||||
if (zigGlobalObject->vmModuleContextMap()->has(asObject(value))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value.inherits(NodeVMSpecialSandbox::info())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (auto* proxy = jsDynamicCast<JSGlobalProxy*>(value); proxy && proxy->target()) {
|
||||
return proxy->target()->inherits(NodeVMGlobalObject::info());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getContextArg(JSGlobalObject* globalObject, JSValue& contextArg)
|
||||
{
|
||||
if (contextArg.isUndefined()) {
|
||||
contextArg = JSC::constructEmptyObject(globalObject);
|
||||
} else if (contextArg.isSymbol()) {
|
||||
Zig::GlobalObject* zigGlobalObject = defaultGlobalObject(globalObject);
|
||||
if (contextArg == zigGlobalObject->m_nodeVMDontContextify.get(zigGlobalObject)) {
|
||||
contextArg = JSC::constructEmptyObject(globalObject);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isUseMainContextDefaultLoaderConstant(JSGlobalObject* globalObject, JSValue value)
|
||||
{
|
||||
if (value.isSymbol()) {
|
||||
Zig::GlobalObject* zigGlobalObject = defaultGlobalObject(globalObject);
|
||||
if (value == zigGlobalObject->m_nodeVMUseMainContextDefaultLoader.get(zigGlobalObject)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace NodeVM
|
||||
|
||||
using namespace NodeVM;
|
||||
|
||||
NodeVMGlobalObject::NodeVMGlobalObject(JSC::VM& vm, JSC::Structure* structure)
|
||||
template<typename, JSC::SubspaceAccess mode> JSC::GCClient::IsoSubspace* NodeVMSpecialSandbox::subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
if constexpr (mode == JSC::SubspaceAccess::Concurrently)
|
||||
return nullptr;
|
||||
return WebCore::subspaceForImpl<NodeVMSpecialSandbox, WebCore::UseCustomHeapCellType::No>(
|
||||
vm,
|
||||
[](auto& spaces) { return spaces.m_clientSubspaceForNodeVMSpecialSandbox.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_clientSubspaceForNodeVMSpecialSandbox = std::forward<decltype(space)>(space); },
|
||||
[](auto& spaces) { return spaces.m_subspaceForNodeVMSpecialSandbox.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_subspaceForNodeVMSpecialSandbox = std::forward<decltype(space)>(space); });
|
||||
}
|
||||
|
||||
NodeVMSpecialSandbox* NodeVMSpecialSandbox::create(VM& vm, Structure* structure, NodeVMGlobalObject* globalObject)
|
||||
{
|
||||
NodeVMSpecialSandbox* ptr = new (NotNull, allocateCell<NodeVMSpecialSandbox>(vm)) NodeVMSpecialSandbox(vm, structure, globalObject);
|
||||
ptr->finishCreation(vm);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
JSC::Structure* NodeVMSpecialSandbox::createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
|
||||
}
|
||||
|
||||
NodeVMSpecialSandbox::NodeVMSpecialSandbox(VM& vm, Structure* structure, NodeVMGlobalObject* globalObject)
|
||||
: Base(vm, structure)
|
||||
{
|
||||
m_parentGlobal.set(vm, this, globalObject);
|
||||
}
|
||||
|
||||
void NodeVMSpecialSandbox::finishCreation(VM& vm)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
ASSERT(inherits(info()));
|
||||
}
|
||||
|
||||
const JSC::ClassInfo NodeVMSpecialSandbox::s_info = { "NodeVMSpecialSandbox"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(NodeVMSpecialSandbox) };
|
||||
|
||||
NodeVMGlobalObject::NodeVMGlobalObject(JSC::VM& vm, JSC::Structure* structure, NodeVMContextOptions contextOptions, JSValue importer)
|
||||
: Base(vm, structure, &globalObjectMethodTable())
|
||||
, m_dynamicImportCallback(vm, this, importer)
|
||||
, m_contextOptions(contextOptions)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -547,10 +722,10 @@ template<typename, JSC::SubspaceAccess mode> JSC::GCClient::IsoSubspace* NodeVMG
|
||||
[](auto& server) -> JSC::HeapCellType& { return server.m_heapCellTypeForNodeVMGlobalObject; });
|
||||
}
|
||||
|
||||
NodeVMGlobalObject* NodeVMGlobalObject::create(JSC::VM& vm, JSC::Structure* structure, NodeVMContextOptions options)
|
||||
NodeVMGlobalObject* NodeVMGlobalObject::create(JSC::VM& vm, JSC::Structure* structure, NodeVMContextOptions options, JSValue importer)
|
||||
{
|
||||
auto* cell = new (NotNull, JSC::allocateCell<NodeVMGlobalObject>(vm)) NodeVMGlobalObject(vm, structure);
|
||||
cell->finishCreation(vm, options);
|
||||
auto* cell = new (NotNull, JSC::allocateCell<NodeVMGlobalObject>(vm)) NodeVMGlobalObject(vm, structure, options, importer);
|
||||
cell->finishCreation(vm);
|
||||
return cell;
|
||||
}
|
||||
|
||||
@@ -560,11 +735,40 @@ Structure* NodeVMGlobalObject::createStructure(JSC::VM& vm, JSC::JSValue prototy
|
||||
return JSC::Structure::create(vm, nullptr, prototype, JSC::TypeInfo(JSC::GlobalObjectType, StructureFlags & ~IsImmutablePrototypeExoticObject), info());
|
||||
}
|
||||
|
||||
void NodeVMGlobalObject::finishCreation(JSC::VM& vm, NodeVMContextOptions options)
|
||||
const JSC::GlobalObjectMethodTable& NodeVMGlobalObject::globalObjectMethodTable()
|
||||
{
|
||||
static const JSC::GlobalObjectMethodTable table {
|
||||
&supportsRichSourceInfo,
|
||||
&shouldInterruptScript,
|
||||
&javaScriptRuntimeFlags,
|
||||
nullptr, // queueTaskToEventLoop
|
||||
nullptr, // shouldInterruptScriptBeforeTimeout,
|
||||
&moduleLoaderImportModule,
|
||||
nullptr, // moduleLoaderResolve
|
||||
nullptr, // moduleLoaderFetch
|
||||
nullptr, // moduleLoaderCreateImportMetaProperties
|
||||
nullptr, // moduleLoaderEvaluate
|
||||
nullptr, // promiseRejectionTracker
|
||||
&reportUncaughtExceptionAtEventLoop,
|
||||
¤tScriptExecutionOwner,
|
||||
&scriptExecutionStatus,
|
||||
nullptr, // reportViolationForUnsafeEval
|
||||
nullptr, // defaultLanguage
|
||||
nullptr, // compileStreaming
|
||||
nullptr, // instantiateStreaming
|
||||
nullptr,
|
||||
&codeForEval,
|
||||
&canCompileStrings,
|
||||
&trustedScriptStructure,
|
||||
};
|
||||
return table;
|
||||
}
|
||||
|
||||
void NodeVMGlobalObject::finishCreation(JSC::VM& vm)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
setEvalEnabled(options.allowStrings, "Code generation from strings disallowed for this context"_s);
|
||||
setWebAssemblyEnabled(options.allowWasm, "Wasm code generation disallowed by embedder"_s);
|
||||
setEvalEnabled(m_contextOptions.allowStrings, "Code generation from strings disallowed for this context"_s);
|
||||
setWebAssemblyEnabled(m_contextOptions.allowWasm, "Wasm code generation disallowed by embedder"_s);
|
||||
vm.ensureTerminationException();
|
||||
}
|
||||
|
||||
@@ -619,18 +823,27 @@ bool NodeVMGlobalObject::put(JSCell* cell, JSGlobalObject* globalObject, Propert
|
||||
bool isFunction = value.isCallable();
|
||||
|
||||
if (slot.isStrictMode() && !isDeclared && isContextualStore && !isFunction) {
|
||||
return Base::put(cell, globalObject, propertyName, value, slot);
|
||||
RELEASE_AND_RETURN(scope, Base::put(cell, globalObject, propertyName, value, slot));
|
||||
}
|
||||
|
||||
if (!isDeclared && value.isSymbol()) {
|
||||
return Base::put(cell, globalObject, propertyName, value, slot);
|
||||
RELEASE_AND_RETURN(scope, Base::put(cell, globalObject, propertyName, value, slot));
|
||||
}
|
||||
|
||||
if (thisObject->m_contextOptions.notContextified) {
|
||||
JSObject* specialSandbox = thisObject->specialSandbox();
|
||||
slot.setThisValue(specialSandbox);
|
||||
RELEASE_AND_RETURN(scope, specialSandbox->putInline(globalObject, propertyName, value, slot));
|
||||
}
|
||||
|
||||
slot.setThisValue(sandbox);
|
||||
bool result = sandbox->methodTable()->put(sandbox, globalObject, propertyName, value, slot);
|
||||
RETURN_IF_EXCEPTION(scope, false);
|
||||
|
||||
if (!sandbox->methodTable()->put(sandbox, globalObject, propertyName, value, slot)) {
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RETURN_IF_EXCEPTION(scope, false);
|
||||
|
||||
if (isDeclaredOnSandbox && getter.isAccessor() and (getter.attributes() & PropertyAttribute::DontEnum) == 0) {
|
||||
@@ -638,21 +851,54 @@ bool NodeVMGlobalObject::put(JSCell* cell, JSGlobalObject* globalObject, Propert
|
||||
}
|
||||
|
||||
slot.setThisValue(thisValue);
|
||||
|
||||
return Base::put(cell, globalObject, propertyName, value, slot);
|
||||
RELEASE_AND_RETURN(scope, Base::put(cell, globalObject, propertyName, value, slot));
|
||||
}
|
||||
|
||||
// This is copy-pasted from JSC's ProxyObject.cpp
|
||||
static const ASCIILiteral s_proxyAlreadyRevokedErrorMessage { "Proxy has already been revoked. No more operations are allowed to be performed on it"_s };
|
||||
|
||||
bool NodeVMSpecialSandbox::getOwnPropertySlot(JSObject* cell, JSGlobalObject* globalObject, PropertyName propertyName, PropertySlot& slot)
|
||||
{
|
||||
VM& vm = JSC::getVM(globalObject);
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsCast<NodeVMSpecialSandbox*>(cell);
|
||||
NodeVMGlobalObject* parentGlobal = thisObject->parentGlobal();
|
||||
|
||||
if (propertyName.uid()->utf8() == "globalThis") [[unlikely]] {
|
||||
slot.disableCaching();
|
||||
slot.setThisValue(thisObject);
|
||||
slot.setValue(thisObject, slot.attributes(), thisObject);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool result = parentGlobal->getOwnPropertySlot(parentGlobal, globalObject, propertyName, slot);
|
||||
RETURN_IF_EXCEPTION(scope, false);
|
||||
|
||||
if (result) {
|
||||
return true;
|
||||
}
|
||||
|
||||
RELEASE_AND_RETURN(scope, Base::getOwnPropertySlot(cell, globalObject, propertyName, slot));
|
||||
}
|
||||
|
||||
bool NodeVMGlobalObject::getOwnPropertySlot(JSObject* cell, JSGlobalObject* globalObject, PropertyName propertyName, PropertySlot& slot)
|
||||
{
|
||||
VM& vm = JSC::getVM(globalObject);
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsCast<NodeVMGlobalObject*>(cell);
|
||||
if (thisObject->m_sandbox) {
|
||||
auto* contextifiedObject = thisObject->m_sandbox.get();
|
||||
|
||||
bool notContextified = thisObject->isNotContextified();
|
||||
|
||||
if (notContextified && propertyName.uid()->utf8() == "globalThis") [[unlikely]] {
|
||||
slot.disableCaching();
|
||||
slot.setThisValue(thisObject);
|
||||
slot.setValue(thisObject, slot.attributes(), thisObject->specialSandbox());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (JSObject* contextifiedObject = thisObject->contextifiedObject()) {
|
||||
slot.setThisValue(contextifiedObject);
|
||||
// Unfortunately we must special case ProxyObjects. Why?
|
||||
//
|
||||
@@ -719,8 +965,12 @@ bool NodeVMGlobalObject::getOwnPropertySlot(JSObject* cell, JSGlobalObject* glob
|
||||
goto try_from_global;
|
||||
}
|
||||
|
||||
if (contextifiedObject->getPropertySlot(globalObject, propertyName, slot)) {
|
||||
return true;
|
||||
if (!notContextified) {
|
||||
bool result = contextifiedObject->getPropertySlot(globalObject, propertyName, slot);
|
||||
RETURN_IF_EXCEPTION(scope, false);
|
||||
if (result) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
try_from_global:
|
||||
@@ -729,47 +979,61 @@ bool NodeVMGlobalObject::getOwnPropertySlot(JSObject* cell, JSGlobalObject* glob
|
||||
RETURN_IF_EXCEPTION(scope, false);
|
||||
}
|
||||
|
||||
return Base::getOwnPropertySlot(cell, globalObject, propertyName, slot);
|
||||
bool result = Base::getOwnPropertySlot(cell, globalObject, propertyName, slot);
|
||||
RETURN_IF_EXCEPTION(scope, false);
|
||||
|
||||
if (result) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (thisObject->m_contextOptions.notContextified) {
|
||||
JSObject* specialSandbox = thisObject->specialSandbox();
|
||||
RELEASE_AND_RETURN(scope, JSObject::getOwnPropertySlot(specialSandbox, globalObject, propertyName, slot));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NodeVMGlobalObject::defineOwnProperty(JSObject* cell, JSGlobalObject* globalObject, PropertyName propertyName, const PropertyDescriptor& descriptor, bool shouldThrow)
|
||||
{
|
||||
// if (!propertyName.isSymbol())
|
||||
// printf("defineOwnProperty called for %s\n", propertyName.publicName()->utf8().data());
|
||||
auto* thisObject = jsCast<NodeVMGlobalObject*>(cell);
|
||||
if (!thisObject->m_sandbox) {
|
||||
return Base::defineOwnProperty(cell, globalObject, propertyName, descriptor, shouldThrow);
|
||||
}
|
||||
|
||||
auto* contextifiedObject = thisObject->m_sandbox.get();
|
||||
VM& vm = JSC::getVM(globalObject);
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsCast<NodeVMGlobalObject*>(cell);
|
||||
if (!thisObject->m_sandbox) {
|
||||
RELEASE_AND_RETURN(scope, Base::defineOwnProperty(cell, globalObject, propertyName, descriptor, shouldThrow));
|
||||
}
|
||||
|
||||
auto* contextifiedObject = thisObject->isNotContextified() ? thisObject->specialSandbox() : thisObject->m_sandbox.get();
|
||||
|
||||
PropertySlot slot(globalObject, PropertySlot::InternalMethodType::GetOwnProperty, nullptr);
|
||||
bool isDeclaredOnGlobalProxy = globalObject->JSC::JSGlobalObject::getOwnPropertySlot(globalObject, globalObject, propertyName, slot);
|
||||
|
||||
// If the property is set on the global as neither writable nor
|
||||
// configurable, don't change it on the global or sandbox.
|
||||
if (isDeclaredOnGlobalProxy && (slot.attributes() & PropertyAttribute::ReadOnly) != 0 && (slot.attributes() & PropertyAttribute::DontDelete) != 0) {
|
||||
return Base::defineOwnProperty(cell, globalObject, propertyName, descriptor, shouldThrow);
|
||||
RELEASE_AND_RETURN(scope, Base::defineOwnProperty(cell, globalObject, propertyName, descriptor, shouldThrow));
|
||||
}
|
||||
|
||||
if (descriptor.isAccessorDescriptor()) {
|
||||
return contextifiedObject->defineOwnProperty(contextifiedObject, contextifiedObject->globalObject(), propertyName, descriptor, shouldThrow);
|
||||
RELEASE_AND_RETURN(scope, JSObject::defineOwnProperty(contextifiedObject, contextifiedObject->globalObject(), propertyName, descriptor, shouldThrow));
|
||||
}
|
||||
|
||||
bool isDeclaredOnSandbox = contextifiedObject->getPropertySlot(globalObject, propertyName, slot);
|
||||
RETURN_IF_EXCEPTION(scope, false);
|
||||
|
||||
if (isDeclaredOnSandbox && !isDeclaredOnGlobalProxy) {
|
||||
return contextifiedObject->defineOwnProperty(contextifiedObject, contextifiedObject->globalObject(), propertyName, descriptor, shouldThrow);
|
||||
RELEASE_AND_RETURN(scope, JSObject::defineOwnProperty(contextifiedObject, contextifiedObject->globalObject(), propertyName, descriptor, shouldThrow));
|
||||
}
|
||||
|
||||
if (!contextifiedObject->defineOwnProperty(contextifiedObject, contextifiedObject->globalObject(), propertyName, descriptor, shouldThrow)) {
|
||||
bool result = JSObject::defineOwnProperty(contextifiedObject, contextifiedObject->globalObject(), propertyName, descriptor, shouldThrow);
|
||||
RETURN_IF_EXCEPTION(scope, false);
|
||||
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Base::defineOwnProperty(cell, globalObject, propertyName, descriptor, shouldThrow);
|
||||
RELEASE_AND_RETURN(scope, Base::defineOwnProperty(cell, globalObject, propertyName, descriptor, shouldThrow));
|
||||
}
|
||||
|
||||
DEFINE_VISIT_CHILDREN(NodeVMGlobalObject);
|
||||
@@ -780,6 +1044,8 @@ void NodeVMGlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor)
|
||||
Base::visitChildren(cell, visitor);
|
||||
auto* thisObject = jsCast<NodeVMGlobalObject*>(cell);
|
||||
visitor.append(thisObject->m_sandbox);
|
||||
visitor.append(thisObject->m_specialSandbox);
|
||||
visitor.append(thisObject->m_dynamicImportCallback);
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(vmModuleRunInNewContext, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||||
@@ -792,41 +1058,44 @@ JSC_DEFINE_HOST_FUNCTION(vmModuleRunInNewContext, (JSGlobalObject * globalObject
|
||||
return ERR::INVALID_ARG_TYPE(scope, globalObject, "code"_s, "string"_s, code);
|
||||
|
||||
JSValue contextArg = callFrame->argument(1);
|
||||
if (contextArg.isUndefined()) {
|
||||
contextArg = JSC::constructEmptyObject(globalObject);
|
||||
}
|
||||
bool notContextified = getContextArg(globalObject, contextArg);
|
||||
|
||||
if (!contextArg.isObject())
|
||||
if (!contextArg.isObject()) {
|
||||
return ERR::INVALID_ARG_TYPE(scope, globalObject, "context"_s, "object"_s, contextArg);
|
||||
}
|
||||
|
||||
JSObject* sandbox = asObject(contextArg);
|
||||
|
||||
JSValue contextOptionsArg = callFrame->argument(2);
|
||||
|
||||
NodeVMContextOptions contextOptions {};
|
||||
|
||||
if (auto encodedException = getNodeVMContextOptions(globalObject, vm, scope, contextOptionsArg, contextOptions, "contextCodeGeneration")) {
|
||||
JSValue globalObjectDynamicImportCallback;
|
||||
|
||||
if (auto encodedException = getNodeVMContextOptions(globalObject, vm, scope, contextOptionsArg, contextOptions, "contextCodeGeneration", &globalObjectDynamicImportCallback)) {
|
||||
return *encodedException;
|
||||
}
|
||||
|
||||
contextOptions.notContextified = notContextified;
|
||||
|
||||
// Create context and run code
|
||||
auto* context = NodeVMGlobalObject::create(vm,
|
||||
defaultGlobalObject(globalObject)->NodeVMGlobalObjectStructure(),
|
||||
contextOptions);
|
||||
contextOptions, globalObjectDynamicImportCallback);
|
||||
|
||||
context->setContextifiedObject(sandbox);
|
||||
|
||||
JSValue optionsArg = callFrame->argument(2);
|
||||
JSValue scriptDynamicImportCallback;
|
||||
|
||||
ScriptOptions options(optionsArg.toWTFString(globalObject), OrdinalNumber::fromZeroBasedInt(0), OrdinalNumber::fromZeroBasedInt(0));
|
||||
if (optionsArg.isString()) {
|
||||
options.filename = optionsArg.toWTFString(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
} else if (!options.fromJS(globalObject, vm, scope, optionsArg)) {
|
||||
} else if (!options.fromJS(globalObject, vm, scope, optionsArg, &scriptDynamicImportCallback)) {
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
}
|
||||
|
||||
RefPtr fetcher(NodeVMScriptFetcher::create(vm, options.importer));
|
||||
RefPtr fetcher(NodeVMScriptFetcher::create(vm, scriptDynamicImportCallback, jsUndefined()));
|
||||
|
||||
SourceCode sourceCode(
|
||||
JSC::StringSourceProvider::create(
|
||||
@@ -862,19 +1131,21 @@ JSC_DEFINE_HOST_FUNCTION(vmModuleRunInThisContext, (JSGlobalObject * globalObjec
|
||||
return ERR::INVALID_ARG_TYPE(throwScope, globalObject, "code"_s, "string"_s, sourceStringValue);
|
||||
}
|
||||
|
||||
auto sourceString = sourceStringValue.toWTFString(globalObject);
|
||||
String sourceString = sourceStringValue.toWTFString(globalObject);
|
||||
RETURN_IF_EXCEPTION(throwScope, encodedJSUndefined());
|
||||
|
||||
JSValue importer;
|
||||
|
||||
JSValue optionsArg = callFrame->argument(1);
|
||||
ScriptOptions options(optionsArg.toWTFString(globalObject), OrdinalNumber::fromZeroBasedInt(0), OrdinalNumber::fromZeroBasedInt(0));
|
||||
if (optionsArg.isString()) {
|
||||
options.filename = optionsArg.toWTFString(globalObject);
|
||||
RETURN_IF_EXCEPTION(throwScope, {});
|
||||
} else if (!options.fromJS(globalObject, vm, throwScope, optionsArg)) {
|
||||
} else if (!options.fromJS(globalObject, vm, throwScope, optionsArg, &importer)) {
|
||||
RETURN_IF_EXCEPTION(throwScope, encodedJSUndefined());
|
||||
}
|
||||
|
||||
RefPtr fetcher(NodeVMScriptFetcher::create(vm, options.importer));
|
||||
RefPtr fetcher(NodeVMScriptFetcher::create(vm, importer, jsUndefined()));
|
||||
|
||||
SourceCode source(
|
||||
JSC::StringSourceProvider::create(sourceString, JSC::SourceOrigin(WTF::URL::fileURLWithFileSystemPath(options.filename), *fetcher), options.filename, JSC::SourceTaintedOrigin::Untainted, TextPosition(options.lineOffset, options.columnOffset)),
|
||||
@@ -927,7 +1198,9 @@ JSC_DEFINE_HOST_FUNCTION(vmModuleCompileFunction, (JSGlobalObject * globalObject
|
||||
// Get options argument
|
||||
JSValue optionsArg = callFrame->argument(2);
|
||||
CompileFunctionOptions options;
|
||||
if (!options.fromJS(globalObject, vm, scope, optionsArg)) {
|
||||
JSValue importer;
|
||||
|
||||
if (!options.fromJS(globalObject, vm, scope, optionsArg, &importer)) {
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
options = {};
|
||||
options.parsingContext = globalObject;
|
||||
@@ -949,7 +1222,7 @@ JSC_DEFINE_HOST_FUNCTION(vmModuleCompileFunction, (JSGlobalObject * globalObject
|
||||
// Add the function body
|
||||
constructFunctionArgs.append(jsString(vm, sourceString));
|
||||
|
||||
RefPtr fetcher(NodeVMScriptFetcher::create(vm, options.importer));
|
||||
RefPtr fetcher(NodeVMScriptFetcher::create(vm, importer, jsUndefined()));
|
||||
|
||||
// Create the source origin
|
||||
SourceOrigin sourceOrigin { WTF::URL::fileURLWithFileSystemPath(options.filename), *fetcher };
|
||||
@@ -983,14 +1256,18 @@ JSC_DEFINE_HOST_FUNCTION(vmModuleCompileFunction, (JSGlobalObject * globalObject
|
||||
|
||||
// Create the function using constructAnonymousFunction with the appropriate scope chain
|
||||
JSFunction* function = constructAnonymousFunction(globalObject, ArgList(constructFunctionArgs), sourceOrigin, WTFMove(options), JSC::SourceTaintedOrigin::Untainted, functionScope);
|
||||
fetcher->owner(vm, function);
|
||||
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
if (!function) {
|
||||
return throwVMError(globalObject, scope, "Failed to compile function"_s);
|
||||
}
|
||||
|
||||
fetcher->owner(vm, function);
|
||||
|
||||
if (!function) {
|
||||
return throwVMError(globalObject, scope, "Failed to compile function"_s);
|
||||
}
|
||||
|
||||
return JSValue::encode(function);
|
||||
}
|
||||
|
||||
@@ -999,31 +1276,16 @@ Structure* createNodeVMGlobalObjectStructure(JSC::VM& vm)
|
||||
return NodeVMGlobalObject::createStructure(vm, jsNull());
|
||||
}
|
||||
|
||||
NodeVMGlobalObject* createContextImpl(JSC::VM& vm, JSGlobalObject* globalObject, JSObject* sandbox)
|
||||
{
|
||||
auto* targetContext = NodeVMGlobalObject::create(vm,
|
||||
defaultGlobalObject(globalObject)->NodeVMGlobalObjectStructure(),
|
||||
NodeVMContextOptions {});
|
||||
|
||||
// Set sandbox as contextified object
|
||||
targetContext->setContextifiedObject(sandbox);
|
||||
|
||||
// Store context in WeakMap for isContext checks
|
||||
auto* zigGlobalObject = defaultGlobalObject(globalObject);
|
||||
zigGlobalObject->vmModuleContextMap()->set(vm, sandbox, targetContext);
|
||||
|
||||
return targetContext;
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(vmModule_createContext, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
NodeVMContextOptions contextOptions {};
|
||||
|
||||
JSValue contextArg = callFrame->argument(0);
|
||||
if (contextArg.isUndefined()) {
|
||||
contextArg = JSC::constructEmptyObject(globalObject);
|
||||
}
|
||||
bool notContextified = getContextArg(globalObject, contextArg);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
if (!contextArg.isObject()) {
|
||||
return ERR::INVALID_ARG_TYPE(scope, globalObject, "context"_s, "object"_s, contextArg);
|
||||
@@ -1036,25 +1298,48 @@ JSC_DEFINE_HOST_FUNCTION(vmModule_createContext, (JSGlobalObject * globalObject,
|
||||
return ERR::INVALID_ARG_TYPE(scope, globalObject, "options"_s, "object"_s, optionsArg);
|
||||
}
|
||||
|
||||
NodeVMContextOptions contextOptions {};
|
||||
JSValue importer;
|
||||
|
||||
if (auto encodedException = getNodeVMContextOptions(globalObject, vm, scope, optionsArg, contextOptions, "codeGeneration")) {
|
||||
if (auto encodedException = getNodeVMContextOptions(globalObject, vm, scope, optionsArg, contextOptions, "codeGeneration", &importer)) {
|
||||
return *encodedException;
|
||||
}
|
||||
|
||||
contextOptions.notContextified = notContextified;
|
||||
|
||||
JSObject* sandbox = asObject(contextArg);
|
||||
|
||||
if (isContext(globalObject, sandbox)) {
|
||||
if (auto* proxy = jsDynamicCast<JSC::JSGlobalProxy*>(sandbox)) {
|
||||
if (auto* targetContext = jsDynamicCast<NodeVMGlobalObject*>(proxy->target())) {
|
||||
if (targetContext->isNotContextified()) {
|
||||
return JSValue::encode(targetContext->specialSandbox());
|
||||
}
|
||||
}
|
||||
}
|
||||
return JSValue::encode(sandbox);
|
||||
}
|
||||
|
||||
auto* zigGlobalObject = defaultGlobalObject(globalObject);
|
||||
|
||||
auto* targetContext = NodeVMGlobalObject::create(vm,
|
||||
defaultGlobalObject(globalObject)->NodeVMGlobalObjectStructure(),
|
||||
contextOptions);
|
||||
zigGlobalObject->NodeVMGlobalObjectStructure(),
|
||||
contextOptions, importer);
|
||||
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
// Set sandbox as contextified object
|
||||
targetContext->setContextifiedObject(sandbox);
|
||||
|
||||
// Store context in WeakMap for isContext checks
|
||||
auto* zigGlobalObject = defaultGlobalObject(globalObject);
|
||||
zigGlobalObject->vmModuleContextMap()->set(vm, sandbox, targetContext);
|
||||
|
||||
if (notContextified) {
|
||||
auto* specialSandbox = NodeVMSpecialSandbox::create(vm, zigGlobalObject->NodeVMSpecialSandboxStructure(), targetContext);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
targetContext->setSpecialSandbox(specialSandbox);
|
||||
return JSValue::encode(targetContext->specialSandbox());
|
||||
}
|
||||
|
||||
return JSValue::encode(sandbox);
|
||||
}
|
||||
|
||||
@@ -1064,39 +1349,12 @@ JSC_DEFINE_HOST_FUNCTION(vmModule_isContext, (JSGlobalObject * globalObject, Cal
|
||||
JSValue contextArg = callFrame->argument(0);
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
bool isContext;
|
||||
if (!contextArg || !contextArg.isObject()) {
|
||||
isContext = false;
|
||||
return ERR::INVALID_ARG_TYPE(scope, globalObject, "object"_s, "object"_s, contextArg);
|
||||
} else {
|
||||
auto* zigGlobalObject = defaultGlobalObject(globalObject);
|
||||
isContext = zigGlobalObject->vmModuleContextMap()->has(asObject(contextArg));
|
||||
}
|
||||
return JSValue::encode(jsBoolean(isContext));
|
||||
return JSValue::encode(jsBoolean(isContext(globalObject, contextArg)));
|
||||
}
|
||||
|
||||
// NodeVMGlobalObject* NodeVMGlobalObject::create(JSC::VM& vm, JSC::Structure* structure)
|
||||
// {
|
||||
// auto* obj = new (NotNull, allocateCell<NodeVMGlobalObject>(vm)) NodeVMGlobalObject(vm, structure);
|
||||
// obj->finishCreation(vm);
|
||||
// return obj;
|
||||
// }
|
||||
|
||||
// void NodeVMGlobalObject::finishCreation(VM& vm, JSObject* context)
|
||||
// {
|
||||
// Base::finishCreation(vm);
|
||||
// // We don't need to store the context anymore since we use proxies
|
||||
// }
|
||||
|
||||
// DEFINE_VISIT_CHILDREN(NodeVMGlobalObject);
|
||||
|
||||
// template<typename Visitor>
|
||||
// void NodeVMGlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor)
|
||||
// {
|
||||
// Base::visitChildren(cell, visitor);
|
||||
// // auto* thisObject = jsCast<NodeVMGlobalObject*>(cell);
|
||||
// // visitor.append(thisObject->m_proxyTarget);
|
||||
// }
|
||||
const ClassInfo NodeVMGlobalObject::s_info = { "NodeVMGlobalObject"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(NodeVMGlobalObject) };
|
||||
|
||||
bool NodeVMGlobalObject::deleteProperty(JSCell* cell, JSGlobalObject* globalObject, PropertyName propertyName, JSC::DeletePropertySlot& slot)
|
||||
@@ -1118,19 +1376,55 @@ bool NodeVMGlobalObject::deleteProperty(JSCell* cell, JSGlobalObject* globalObje
|
||||
return Base::deleteProperty(cell, globalObject, propertyName, slot);
|
||||
}
|
||||
|
||||
static JSInternalPromise* moduleLoaderImportModuleInner(NodeVMGlobalObject* globalObject, JSC::JSModuleLoader* moduleLoader, JSC::JSString* moduleName, JSC::JSValue parameters, const JSC::SourceOrigin& sourceOrigin)
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* promise = JSInternalPromise::create(vm, globalObject->internalPromiseStructure());
|
||||
|
||||
if (sourceOrigin.fetcher() == nullptr && sourceOrigin.url().isEmpty()) {
|
||||
if (globalObject->dynamicImportCallback().isCallable()) {
|
||||
return NodeVM::importModuleInner(globalObject, moduleName, parameters, sourceOrigin, globalObject->dynamicImportCallback(), JSValue {});
|
||||
}
|
||||
|
||||
promise->reject(globalObject, createError(globalObject, ErrorCode::ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING, "A dynamic import callback was not specified."_s));
|
||||
return promise;
|
||||
}
|
||||
|
||||
// Default behavior copied from JSModuleLoader::importModule
|
||||
auto moduleNameString = moduleName->value(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, promise->rejectWithCaughtException(globalObject, scope));
|
||||
|
||||
scope.release();
|
||||
promise->reject(globalObject, createError(globalObject, makeString("Could not import the module '"_s, moduleNameString.data, "'."_s)));
|
||||
return promise;
|
||||
}
|
||||
|
||||
JSInternalPromise* NodeVMGlobalObject::moduleLoaderImportModule(JSGlobalObject* globalObject, JSC::JSModuleLoader* moduleLoader, JSC::JSString* moduleName, JSC::JSValue parameters, const JSC::SourceOrigin& sourceOrigin)
|
||||
{
|
||||
auto* nodeVmGlobalObject = static_cast<NodeVMGlobalObject*>(globalObject);
|
||||
|
||||
if (JSInternalPromise* result = NodeVM::importModule(nodeVmGlobalObject, moduleName, parameters, sourceOrigin)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return moduleLoaderImportModuleInner(nodeVmGlobalObject, moduleLoader, moduleName, parameters, sourceOrigin);
|
||||
}
|
||||
|
||||
void NodeVMGlobalObject::getOwnPropertyNames(JSObject* cell, JSGlobalObject* globalObject, JSC::PropertyNameArray& propertyNames, JSC::DontEnumPropertiesMode mode)
|
||||
{
|
||||
auto* thisObject = jsCast<NodeVMGlobalObject*>(cell);
|
||||
|
||||
if (thisObject->m_sandbox) {
|
||||
thisObject->m_sandbox->getOwnPropertyNames(
|
||||
thisObject->m_sandbox.get(),
|
||||
globalObject,
|
||||
propertyNames,
|
||||
mode);
|
||||
VM& vm = JSC::getVM(globalObject);
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
if (thisObject->m_sandbox) [[likely]] {
|
||||
thisObject->m_sandbox->getOwnPropertyNames(thisObject->m_sandbox.get(), globalObject, propertyNames, mode);
|
||||
RETURN_IF_EXCEPTION(scope, );
|
||||
}
|
||||
|
||||
Base::getOwnPropertyNames(cell, globalObject, propertyNames, mode);
|
||||
RELEASE_AND_RETURN(scope, Base::getOwnPropertyNames(cell, globalObject, propertyNames, mode));
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(vmIsModuleNamespaceObject, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||||
@@ -1150,7 +1444,7 @@ JSC::JSValue createNodeVMBinding(Zig::GlobalObject* globalObject)
|
||||
defaultGlobalObject(globalObject)->NodeVMSourceTextModule(), 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);
|
||||
JSC::JSFunction::create(vm, globalObject, 0, "createContext"_s, vmModule_createContext, ImplementationVisibility::Public, Intrinsic::NoIntrinsic, vmModule_createContext), 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);
|
||||
@@ -1190,11 +1484,24 @@ JSC::JSValue createNodeVMBinding(Zig::GlobalObject* globalObject)
|
||||
obj->putDirect(
|
||||
vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "kSynthetic"_s)),
|
||||
JSC::jsNumber(static_cast<unsigned>(NodeVMModule::Type::Synthetic)), 0);
|
||||
obj->putDirect(
|
||||
vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "DONT_CONTEXTIFY"_s)),
|
||||
globalObject->m_nodeVMDontContextify.get(globalObject), 0);
|
||||
obj->putDirect(
|
||||
vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "USE_MAIN_CONTEXT_DEFAULT_LOADER"_s)),
|
||||
globalObject->m_nodeVMUseMainContextDefaultLoader.get(globalObject), 0);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void configureNodeVM(JSC::VM& vm, Zig::GlobalObject* globalObject)
|
||||
{
|
||||
globalObject->m_nodeVMDontContextify.initLater([](const LazyProperty<JSC::JSGlobalObject, Symbol>::Initializer& init) {
|
||||
init.set(JSC::Symbol::createWithDescription(init.vm, "vm_dont_contextify"_s));
|
||||
});
|
||||
globalObject->m_nodeVMUseMainContextDefaultLoader.initLater([](const LazyProperty<JSC::JSGlobalObject, Symbol>::Initializer& init) {
|
||||
init.set(JSC::Symbol::createWithDescription(init.vm, "vm_use_main_context_default_loader"_s));
|
||||
});
|
||||
|
||||
globalObject->m_NodeVMScriptClassStructure.initLater(
|
||||
[](LazyClassStructure::Initializer& init) {
|
||||
auto prototype = NodeVMScript::createPrototype(init.vm, init.global);
|
||||
@@ -1238,6 +1545,11 @@ void configureNodeVM(JSC::VM& vm, Zig::GlobalObject* globalObject)
|
||||
[](const JSC::LazyProperty<JSC::JSGlobalObject, Structure>::Initializer& init) {
|
||||
init.set(createNodeVMGlobalObjectStructure(init.vm));
|
||||
});
|
||||
|
||||
globalObject->m_cachedNodeVMSpecialSandboxStructure.initLater(
|
||||
[](const JSC::LazyProperty<JSC::JSGlobalObject, Structure>::Initializer& init) {
|
||||
init.set(NodeVMSpecialSandbox::createStructure(init.vm, init.owner, init.owner->objectPrototype())); // TODO(@heimskr): or maybe jsNull() for the prototype?
|
||||
});
|
||||
}
|
||||
|
||||
BaseVMOptions::BaseVMOptions(String filename)
|
||||
@@ -1376,8 +1688,12 @@ bool BaseVMOptions::validateTimeout(JSC::JSGlobalObject* globalObject, JSC::VM&
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CompileFunctionOptions::fromJS(JSC::JSGlobalObject* globalObject, JSC::VM& vm, JSC::ThrowScope& scope, JSC::JSValue optionsArg)
|
||||
bool CompileFunctionOptions::fromJS(JSC::JSGlobalObject* globalObject, JSC::VM& vm, JSC::ThrowScope& scope, JSC::JSValue optionsArg, JSValue* importer)
|
||||
{
|
||||
if (importer) {
|
||||
*importer = jsUndefined();
|
||||
}
|
||||
|
||||
this->parsingContext = globalObject;
|
||||
bool any = BaseVMOptions::fromJS(globalObject, vm, scope, optionsArg);
|
||||
RETURN_IF_EXCEPTION(scope, false);
|
||||
@@ -1448,8 +1764,10 @@ bool CompileFunctionOptions::fromJS(JSC::JSGlobalObject* globalObject, JSC::VM&
|
||||
JSValue importModuleDynamicallyValue = options->getIfPropertyExists(globalObject, Identifier::fromString(vm, "importModuleDynamically"_s));
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
if (importModuleDynamicallyValue && importModuleDynamicallyValue.isCallable()) {
|
||||
this->importer = importModuleDynamicallyValue;
|
||||
if (importModuleDynamicallyValue && (importModuleDynamicallyValue.isCallable() || isUseMainContextDefaultLoaderConstant(globalObject, importModuleDynamicallyValue))) {
|
||||
if (importer) {
|
||||
*importer = importModuleDynamicallyValue;
|
||||
}
|
||||
any = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,65 +25,19 @@ RefPtr<JSC::CachedBytecode> getBytecode(JSGlobalObject* globalObject, JSC::Modul
|
||||
bool extractCachedData(JSValue cachedDataValue, WTF::Vector<uint8_t>& outCachedData);
|
||||
String stringifyAnonymousFunction(JSGlobalObject* globalObject, const ArgList& args, ThrowScope& scope, int* outOffset);
|
||||
JSC::EncodedJSValue createCachedData(JSGlobalObject* globalObject, const JSC::SourceCode& source);
|
||||
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);
|
||||
std::optional<JSC::EncodedJSValue> getNodeVMContextOptions(JSGlobalObject* globalObject, JSC::VM& vm, JSC::ThrowScope& scope, JSValue optionsArg, NodeVMContextOptions& outOptions, ASCIILiteral codeGenerationKey, JSValue* importer);
|
||||
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 from/inspired by JSC::constructFunction, which is used for function declarations.
|
||||
JSC::JSFunction* constructAnonymousFunction(JSC::JSGlobalObject* globalObject, const ArgList& args, const SourceOrigin& sourceOrigin, CompileFunctionOptions&& options, JSC::SourceTaintedOrigin sourceTaintOrigin, JSC::JSScope* scope);
|
||||
JSInternalPromise* importModule(JSGlobalObject* globalObject, JSString* moduleNameValue, JSValue parameters, const SourceOrigin& sourceOrigin);
|
||||
bool isContext(JSC::JSGlobalObject* globalObject, JSValue);
|
||||
bool getContextArg(JSC::JSGlobalObject* globalObject, JSValue& contextArg);
|
||||
bool isUseMainContextDefaultLoaderConstant(JSC::JSGlobalObject* globalObject, JSValue value);
|
||||
|
||||
} // namespace NodeVM
|
||||
|
||||
// This class represents a sandboxed global object for vm contexts
|
||||
class NodeVMGlobalObject final : public Bun::GlobalScope {
|
||||
using Base = Bun::GlobalScope;
|
||||
|
||||
public:
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags | JSC::OverridesGetOwnPropertySlot | JSC::OverridesPut | JSC::OverridesGetOwnPropertyNames | JSC::GetOwnPropertySlotMayBeWrongAboutDontEnum | JSC::ProhibitsPropertyCaching;
|
||||
static constexpr JSC::DestructionMode needsDestruction = NeedsDestruction;
|
||||
|
||||
template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm);
|
||||
static NodeVMGlobalObject* create(JSC::VM& vm, JSC::Structure* structure, NodeVMContextOptions options);
|
||||
static Structure* createStructure(JSC::VM& vm, JSC::JSValue prototype);
|
||||
|
||||
DECLARE_INFO;
|
||||
DECLARE_VISIT_CHILDREN;
|
||||
|
||||
void finishCreation(JSC::VM&, NodeVMContextOptions options);
|
||||
static void destroy(JSCell* cell);
|
||||
void setContextifiedObject(JSC::JSObject* contextifiedObject);
|
||||
JSC::JSObject* contextifiedObject() const { return m_sandbox.get(); }
|
||||
void clearContextifiedObject();
|
||||
void sigintReceived();
|
||||
|
||||
// Override property access to delegate to contextified object
|
||||
static bool getOwnPropertySlot(JSObject*, JSGlobalObject*, JSC::PropertyName, JSC::PropertySlot&);
|
||||
static bool put(JSCell*, JSGlobalObject*, JSC::PropertyName, JSC::JSValue, JSC::PutPropertySlot&);
|
||||
static void getOwnPropertyNames(JSObject*, JSGlobalObject*, JSC::PropertyNameArray&, JSC::DontEnumPropertiesMode);
|
||||
static bool defineOwnProperty(JSObject* object, JSGlobalObject* globalObject, PropertyName propertyName, const PropertyDescriptor& descriptor, bool shouldThrow);
|
||||
static bool deleteProperty(JSCell* cell, JSGlobalObject* globalObject, PropertyName propertyName, JSC::DeletePropertySlot& slot);
|
||||
|
||||
private:
|
||||
NodeVMGlobalObject(JSC::VM& vm, JSC::Structure* structure);
|
||||
~NodeVMGlobalObject();
|
||||
|
||||
// The contextified object that acts as the global proxy
|
||||
mutable JSC::WriteBarrier<JSC::JSObject> m_sandbox;
|
||||
};
|
||||
|
||||
// Helper functions to create vm contexts and run code
|
||||
JSC::JSValue createNodeVMBinding(Zig::GlobalObject*);
|
||||
Structure* createNodeVMGlobalObjectStructure(JSC::VM&);
|
||||
void configureNodeVM(JSC::VM&, Zig::GlobalObject*);
|
||||
|
||||
// VM module functions
|
||||
JSC_DECLARE_HOST_FUNCTION(vmModule_createContext);
|
||||
JSC_DECLARE_HOST_FUNCTION(vmModule_isContext);
|
||||
JSC_DECLARE_HOST_FUNCTION(vmModuleRunInNewContext);
|
||||
JSC_DECLARE_HOST_FUNCTION(vmModuleRunInThisContext);
|
||||
|
||||
class BaseVMOptions {
|
||||
public:
|
||||
String filename;
|
||||
@@ -106,18 +60,103 @@ public:
|
||||
WTF::Vector<uint8_t> cachedData;
|
||||
JSGlobalObject* parsingContext = nullptr;
|
||||
JSValue contextExtensions {};
|
||||
JSValue importer {};
|
||||
bool produceCachedData = false;
|
||||
|
||||
using BaseVMOptions::BaseVMOptions;
|
||||
|
||||
bool fromJS(JSC::JSGlobalObject* globalObject, JSC::VM& vm, JSC::ThrowScope& scope, JSC::JSValue optionsArg);
|
||||
bool fromJS(JSC::JSGlobalObject* globalObject, JSC::VM& vm, JSC::ThrowScope& scope, JSC::JSValue optionsArg, JSValue* importer);
|
||||
};
|
||||
|
||||
class NodeVMContextOptions final {
|
||||
public:
|
||||
bool allowStrings = true;
|
||||
bool allowWasm = true;
|
||||
bool notContextified = false;
|
||||
};
|
||||
|
||||
class NodeVMGlobalObject;
|
||||
|
||||
class NodeVMSpecialSandbox final : public JSC::JSNonFinalObject {
|
||||
public:
|
||||
using Base = JSC::JSNonFinalObject;
|
||||
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags | JSC::OverridesGetOwnPropertySlot;
|
||||
|
||||
static NodeVMSpecialSandbox* create(VM& vm, Structure* structure, NodeVMGlobalObject* globalObject);
|
||||
|
||||
DECLARE_INFO;
|
||||
template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm);
|
||||
static Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype);
|
||||
|
||||
static bool getOwnPropertySlot(JSObject*, JSGlobalObject*, JSC::PropertyName, JSC::PropertySlot&);
|
||||
|
||||
NodeVMGlobalObject* parentGlobal() const { return m_parentGlobal.get(); }
|
||||
|
||||
private:
|
||||
WriteBarrier<NodeVMGlobalObject> m_parentGlobal;
|
||||
|
||||
NodeVMSpecialSandbox(VM& vm, Structure* structure, NodeVMGlobalObject* globalObject);
|
||||
|
||||
void finishCreation(VM&);
|
||||
};
|
||||
|
||||
// This class represents a sandboxed global object for vm contexts
|
||||
class NodeVMGlobalObject final : public Bun::GlobalScope {
|
||||
public:
|
||||
using Base = Bun::GlobalScope;
|
||||
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags | JSC::OverridesGetOwnPropertySlot | JSC::OverridesPut | JSC::OverridesGetOwnPropertyNames | JSC::GetOwnPropertySlotMayBeWrongAboutDontEnum | JSC::ProhibitsPropertyCaching;
|
||||
static constexpr JSC::DestructionMode needsDestruction = NeedsDestruction;
|
||||
|
||||
template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm);
|
||||
static NodeVMGlobalObject* create(JSC::VM& vm, JSC::Structure* structure, NodeVMContextOptions options, JSValue importer);
|
||||
static Structure* createStructure(JSC::VM& vm, JSC::JSValue prototype);
|
||||
static const JSC::GlobalObjectMethodTable& globalObjectMethodTable();
|
||||
|
||||
DECLARE_INFO;
|
||||
DECLARE_VISIT_CHILDREN;
|
||||
|
||||
~NodeVMGlobalObject();
|
||||
|
||||
void finishCreation(JSC::VM&);
|
||||
static void destroy(JSCell* cell);
|
||||
void setContextifiedObject(JSC::JSObject* contextifiedObject);
|
||||
JSObject* contextifiedObject() const { return m_sandbox.get(); }
|
||||
void clearContextifiedObject();
|
||||
void sigintReceived();
|
||||
bool isNotContextified() const { return m_contextOptions.notContextified; }
|
||||
NodeVMSpecialSandbox* specialSandbox() const { return m_specialSandbox.get(); }
|
||||
void setSpecialSandbox(NodeVMSpecialSandbox* sandbox) { m_specialSandbox.set(vm(), this, sandbox); }
|
||||
JSValue dynamicImportCallback() const { return m_dynamicImportCallback.get(); }
|
||||
|
||||
// Override property access to delegate to contextified object
|
||||
static bool getOwnPropertySlot(JSObject*, JSGlobalObject*, JSC::PropertyName, JSC::PropertySlot&);
|
||||
static bool put(JSCell*, JSGlobalObject*, JSC::PropertyName, JSC::JSValue, JSC::PutPropertySlot&);
|
||||
static void getOwnPropertyNames(JSObject*, JSGlobalObject*, JSC::PropertyNameArray&, JSC::DontEnumPropertiesMode);
|
||||
static bool defineOwnProperty(JSObject* object, JSGlobalObject* globalObject, PropertyName propertyName, const PropertyDescriptor& descriptor, bool shouldThrow);
|
||||
static bool deleteProperty(JSCell* cell, JSGlobalObject* globalObject, PropertyName propertyName, JSC::DeletePropertySlot& slot);
|
||||
static JSC::JSInternalPromise* moduleLoaderImportModule(JSGlobalObject*, JSC::JSModuleLoader*, JSC::JSString* moduleNameValue, JSC::JSValue parameters, const JSC::SourceOrigin&);
|
||||
|
||||
private:
|
||||
// The contextified object that acts as the global proxy
|
||||
WriteBarrier<JSObject> m_sandbox;
|
||||
// A special object used when the context is not contextified.
|
||||
WriteBarrier<NodeVMSpecialSandbox> m_specialSandbox;
|
||||
WriteBarrier<Unknown> m_dynamicImportCallback;
|
||||
NodeVMContextOptions m_contextOptions {};
|
||||
|
||||
NodeVMGlobalObject(VM& vm, Structure* structure, NodeVMContextOptions contextOptions, JSValue importer);
|
||||
};
|
||||
|
||||
// Helper functions to create vm contexts and run code
|
||||
JSC::JSValue createNodeVMBinding(Zig::GlobalObject*);
|
||||
Structure* createNodeVMGlobalObjectStructure(JSC::VM&);
|
||||
void configureNodeVM(JSC::VM&, Zig::GlobalObject*);
|
||||
|
||||
// VM module functions
|
||||
JSC_DECLARE_HOST_FUNCTION(vmModule_createContext);
|
||||
JSC_DECLARE_HOST_FUNCTION(vmModule_isContext);
|
||||
JSC_DECLARE_HOST_FUNCTION(vmModuleRunInNewContext);
|
||||
JSC_DECLARE_HOST_FUNCTION(vmModuleRunInThisContext);
|
||||
|
||||
} // namespace Bun
|
||||
|
||||
@@ -29,15 +29,20 @@ JSArray* NodeVMModuleRequest::toJS(JSGlobalObject* globalObject) const
|
||||
|
||||
JSArray* array = JSC::constructEmptyArray(globalObject, nullptr, 2);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
array->putDirectIndex(globalObject, 0, JSC::jsString(globalObject->vm(), m_specifier));
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
JSObject* attributes = JSC::constructEmptyObject(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
for (const auto& [key, value] : m_importAttributes) {
|
||||
attributes->putDirect(globalObject->vm(), JSC::Identifier::fromString(globalObject->vm(), key), JSC::jsString(globalObject->vm(), value),
|
||||
PropertyAttribute::ReadOnly | PropertyAttribute::DontDelete);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
}
|
||||
array->putDirectIndex(globalObject, 1, attributes);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
return array;
|
||||
}
|
||||
@@ -73,6 +78,7 @@ JSValue NodeVMModule::evaluate(JSGlobalObject* globalObject, uint32_t timeout, b
|
||||
JSValue result {};
|
||||
|
||||
NodeVMGlobalObject* nodeVmGlobalObject = NodeVM::getGlobalObjectFromContext(globalObject, m_context.get(), false);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
if (nodeVmGlobalObject) {
|
||||
globalObject = nodeVmGlobalObject;
|
||||
@@ -82,13 +88,12 @@ JSValue NodeVMModule::evaluate(JSGlobalObject* globalObject, uint32_t timeout, b
|
||||
if (sourceTextThis) {
|
||||
status(Status::Evaluating);
|
||||
evaluateDependencies(globalObject, record, timeout, breakOnSigint);
|
||||
RETURN_IF_EXCEPTION(scope, );
|
||||
sourceTextThis->initializeImportMeta(globalObject);
|
||||
} else if (syntheticThis) {
|
||||
syntheticThis->evaluate(globalObject);
|
||||
}
|
||||
if (scope.exception()) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
RETURN_IF_EXCEPTION(scope, );
|
||||
result = record->evaluate(globalObject, jsUndefined(), jsNumber(static_cast<int32_t>(JSGenerator::ResumeMode::NormalMode)));
|
||||
};
|
||||
|
||||
@@ -206,11 +211,11 @@ NodeVMModule* NodeVMModule::create(JSC::VM& vm, JSC::JSGlobalObject* globalObjec
|
||||
JSValue disambiguator = args.at(2);
|
||||
|
||||
if (disambiguator.isString()) {
|
||||
return NodeVMSourceTextModule::create(vm, globalObject, args);
|
||||
RELEASE_AND_RETURN(scope, NodeVMSourceTextModule::create(vm, globalObject, args));
|
||||
}
|
||||
|
||||
if (disambiguator.inherits(JSArray::info())) {
|
||||
return NodeVMSyntheticModule::create(vm, globalObject, args);
|
||||
RELEASE_AND_RETURN(scope, NodeVMSyntheticModule::create(vm, globalObject, args));
|
||||
}
|
||||
|
||||
throwArgumentTypeError(*globalObject, scope, 2, "sourceText or syntheticExportNames"_s, "Module"_s, "Module"_s, "string or array"_s);
|
||||
@@ -227,11 +232,14 @@ JSModuleNamespaceObject* NodeVMModule::namespaceObject(JSC::JSGlobalObject* glob
|
||||
if (auto* thisObject = jsDynamicCast<NodeVMModule*>(this)) {
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
object = thisObject->moduleRecord(globalObject)->getModuleNamespace(globalObject);
|
||||
AbstractModuleRecord* record = thisObject->moduleRecord(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
object = record->getModuleNamespace(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
if (object) {
|
||||
namespaceObject(vm, object);
|
||||
}
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
} else {
|
||||
RELEASE_ASSERT_NOT_REACHED_WITH_MESSAGE("NodeVMModule::namespaceObject called on an unsupported module type (%s)", classInfo()->className.characters());
|
||||
}
|
||||
@@ -333,7 +341,7 @@ JSC_DEFINE_HOST_FUNCTION(jsNodeVmModuleGetNamespace, (JSC::JSGlobalObject * glob
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
if (auto* thisObject = jsDynamicCast<NodeVMModule*>(callFrame->thisValue())) {
|
||||
return JSValue::encode(thisObject->namespaceObject(globalObject));
|
||||
RELEASE_AND_RETURN(scope, JSValue::encode(thisObject->namespaceObject(globalObject)));
|
||||
}
|
||||
|
||||
throwTypeError(globalObject, scope, "This function must be called on a SourceTextModule or SyntheticModule"_s);
|
||||
@@ -366,6 +374,7 @@ JSC_DEFINE_HOST_FUNCTION(jsNodeVmModuleGetModuleRequests, (JSC::JSGlobalObject *
|
||||
|
||||
if (auto* sourceTextModule = jsDynamicCast<NodeVMSourceTextModule*>(callFrame->thisValue())) {
|
||||
sourceTextModule->ensureModuleRecord(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
}
|
||||
|
||||
const WTF::Vector<NodeVMModuleRequest>& requests = thisObject->moduleRequests();
|
||||
@@ -399,11 +408,11 @@ JSC_DEFINE_HOST_FUNCTION(jsNodeVmModuleEvaluate, (JSC::JSGlobalObject * globalOb
|
||||
}
|
||||
|
||||
if (auto* thisObject = jsDynamicCast<NodeVMModule*>(callFrame->thisValue())) {
|
||||
return JSValue::encode(thisObject->evaluate(globalObject, timeout, breakOnSigint));
|
||||
} else {
|
||||
throwTypeError(globalObject, scope, "This function must be called on a SourceTextModule or SyntheticModule"_s);
|
||||
return {};
|
||||
RELEASE_AND_RETURN(scope, JSValue::encode(thisObject->evaluate(globalObject, timeout, breakOnSigint)));
|
||||
}
|
||||
|
||||
throwTypeError(globalObject, scope, "This function must be called on a SourceTextModule or SyntheticModule"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsNodeVmModuleLink, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
@@ -423,14 +432,11 @@ JSC_DEFINE_HOST_FUNCTION(jsNodeVmModuleLink, (JSC::JSGlobalObject * globalObject
|
||||
}
|
||||
|
||||
if (auto* thisObject = jsDynamicCast<NodeVMSourceTextModule*>(callFrame->thisValue())) {
|
||||
return JSValue::encode(thisObject->link(globalObject, specifiers, moduleNatives, callFrame->argument(2)));
|
||||
// return thisObject->link(globalObject, linker);
|
||||
// } else if (auto* thisObject = jsDynamicCast<NodeVMSyntheticModule*>(callFrame->thisValue())) {
|
||||
// return thisObject->link(globalObject, specifiers, moduleNatives);
|
||||
} else {
|
||||
throwTypeError(globalObject, scope, "This function must be called on a SourceTextModule or SyntheticModule"_s);
|
||||
return {};
|
||||
RELEASE_AND_RETURN(scope, JSValue::encode(thisObject->link(globalObject, specifiers, moduleNatives, callFrame->argument(2))));
|
||||
}
|
||||
|
||||
throwTypeError(globalObject, scope, "This function must be called on a SourceTextModule"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsNodeVmModuleInstantiate, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
@@ -439,11 +445,11 @@ JSC_DEFINE_HOST_FUNCTION(jsNodeVmModuleInstantiate, (JSC::JSGlobalObject * globa
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
if (auto* thisObject = jsDynamicCast<NodeVMSourceTextModule*>(callFrame->thisValue())) {
|
||||
return JSValue::encode(thisObject->instantiate(globalObject));
|
||||
RELEASE_AND_RETURN(scope, JSValue::encode(thisObject->instantiate(globalObject)));
|
||||
}
|
||||
|
||||
if (auto* thisObject = jsDynamicCast<NodeVMSyntheticModule*>(callFrame->thisValue())) {
|
||||
return JSValue::encode(thisObject->instantiate(globalObject));
|
||||
RELEASE_AND_RETURN(scope, JSValue::encode(thisObject->instantiate(globalObject)));
|
||||
}
|
||||
|
||||
throwTypeError(globalObject, scope, "This function must be called on a SourceTextModule or SyntheticModule"_s);
|
||||
@@ -455,7 +461,7 @@ JSC_DEFINE_HOST_FUNCTION(jsNodeVmModuleSetExport, (JSC::JSGlobalObject * globalO
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
if (auto* thisObject = jsCast<NodeVMSyntheticModule*>(callFrame->thisValue())) {
|
||||
if (auto* thisObject = jsDynamicCast<NodeVMSyntheticModule*>(callFrame->thisValue())) {
|
||||
JSValue nameValue = callFrame->argument(0);
|
||||
if (!nameValue.isString()) {
|
||||
Bun::ERR::INVALID_ARG_TYPE(scope, globalObject, "name"_str, "string"_s, nameValue);
|
||||
@@ -478,7 +484,7 @@ JSC_DEFINE_HOST_FUNCTION(jsNodeVmModuleCreateCachedData, (JSC::JSGlobalObject *
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
if (auto* thisObject = jsDynamicCast<NodeVMSourceTextModule*>(callFrame->thisValue())) {
|
||||
return JSValue::encode(thisObject->cachedData(globalObject));
|
||||
RELEASE_AND_RETURN(scope, JSValue::encode(thisObject->cachedData(globalObject)));
|
||||
}
|
||||
|
||||
throwTypeError(globalObject, scope, "This function must be called on a SourceTextModule"_s);
|
||||
@@ -517,6 +523,7 @@ constructModule(JSGlobalObject* globalObject, CallFrame* callFrame, JSValue newT
|
||||
ArgList args(callFrame);
|
||||
|
||||
NodeVMModule* module = NodeVMModule::create(vm, globalObject, args);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
return JSValue::encode(module);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "JavaScriptCore/ProgramCodeBlock.h"
|
||||
#include "JavaScriptCore/SourceCodeKey.h"
|
||||
|
||||
#include "NodeVMScriptFetcher.h"
|
||||
#include "../vm/SigintWatcher.h"
|
||||
|
||||
#include <bit>
|
||||
@@ -16,8 +17,12 @@
|
||||
namespace Bun {
|
||||
using namespace NodeVM;
|
||||
|
||||
bool ScriptOptions::fromJS(JSC::JSGlobalObject* globalObject, JSC::VM& vm, JSC::ThrowScope& scope, JSC::JSValue optionsArg)
|
||||
bool ScriptOptions::fromJS(JSC::JSGlobalObject* globalObject, JSC::VM& vm, JSC::ThrowScope& scope, JSC::JSValue optionsArg, JSValue* importer)
|
||||
{
|
||||
if (importer) {
|
||||
*importer = jsUndefined();
|
||||
}
|
||||
|
||||
bool any = BaseVMOptions::fromJS(globalObject, vm, scope, optionsArg);
|
||||
RETURN_IF_EXCEPTION(scope, false);
|
||||
|
||||
@@ -64,9 +69,16 @@ bool ScriptOptions::fromJS(JSC::JSGlobalObject* globalObject, JSC::VM& vm, JSC::
|
||||
JSValue importModuleDynamicallyValue = options->getIfPropertyExists(globalObject, Identifier::fromString(vm, "importModuleDynamically"_s));
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
if (importModuleDynamicallyValue && importModuleDynamicallyValue.isCallable()) {
|
||||
this->importer = importModuleDynamicallyValue;
|
||||
any = true;
|
||||
if (importModuleDynamicallyValue) {
|
||||
if ((importModuleDynamicallyValue.isCallable() || isUseMainContextDefaultLoaderConstant(globalObject, importModuleDynamicallyValue))) {
|
||||
if (importer) {
|
||||
*importer = importModuleDynamicallyValue;
|
||||
}
|
||||
any = true;
|
||||
} else if (!importModuleDynamicallyValue.isUndefined()) {
|
||||
ERR::INVALID_ARG_TYPE(scope, globalObject, "options.importModuleDynamically"_s, "function"_s, importModuleDynamicallyValue);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,15 +92,22 @@ constructScript(JSGlobalObject* globalObject, CallFrame* callFrame, JSValue newT
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
ArgList args(callFrame);
|
||||
JSValue sourceArg = args.at(0);
|
||||
String sourceString = sourceArg.isUndefined() ? emptyString() : sourceArg.toWTFString(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, encodedJSUndefined());
|
||||
String sourceString;
|
||||
if (sourceArg.isUndefined()) {
|
||||
sourceString = emptyString();
|
||||
} else {
|
||||
sourceString = sourceArg.toWTFString(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, encodedJSUndefined());
|
||||
}
|
||||
|
||||
JSValue optionsArg = args.at(1);
|
||||
ScriptOptions options(""_s);
|
||||
JSValue importer;
|
||||
|
||||
if (optionsArg.isString()) {
|
||||
options.filename = optionsArg.toWTFString(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
} else if (!options.fromJS(globalObject, vm, scope, optionsArg)) {
|
||||
} else if (!options.fromJS(globalObject, vm, scope, optionsArg, &importer)) {
|
||||
RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined()));
|
||||
}
|
||||
|
||||
@@ -108,13 +127,18 @@ constructScript(JSGlobalObject* globalObject, CallFrame* callFrame, JSValue newT
|
||||
scope.release();
|
||||
}
|
||||
|
||||
SourceCode source = makeSource(sourceString, JSC::SourceOrigin(WTF::URL::fileURLWithFileSystemPath(options.filename)), JSC::SourceTaintedOrigin::Untainted, options.filename, TextPosition(options.lineOffset, options.columnOffset));
|
||||
RefPtr fetcher(NodeVMScriptFetcher::create(vm, importer, jsUndefined()));
|
||||
|
||||
SourceCode source = makeSource(sourceString, JSC::SourceOrigin(WTF::URL::fileURLWithFileSystemPath(options.filename), *fetcher), JSC::SourceTaintedOrigin::Untainted, options.filename, TextPosition(options.lineOffset, options.columnOffset));
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
const bool produceCachedData = options.produceCachedData;
|
||||
auto filename = options.filename;
|
||||
|
||||
NodeVMScript* script = NodeVMScript::create(vm, globalObject, structure, WTFMove(source), WTFMove(options));
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
fetcher->owner(vm, script);
|
||||
|
||||
WTF::Vector<uint8_t>& cachedData = script->cachedData();
|
||||
|
||||
@@ -139,6 +163,7 @@ constructScript(JSGlobalObject* globalObject, CallFrame* callFrame, JSValue newT
|
||||
// JSC::ProgramCodeBlock::create() requires GC to be deferred.
|
||||
DeferGC deferGC(vm);
|
||||
codeBlock = JSC::ProgramCodeBlock::create(vm, executable, unlinkedBlock, jsScope);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
}
|
||||
JSC::CompilationResult compilationResult = JIT::compileSync(vm, codeBlock, JITCompilationEffort::JITCompilationCanFail);
|
||||
if (compilationResult != JSC::CompilationResult::CompilationFailed) {
|
||||
@@ -199,6 +224,9 @@ JSC::JSUint8Array* NodeVMScript::getBytecodeBuffer()
|
||||
|
||||
std::span<const uint8_t> bytes = m_cachedBytecode->span();
|
||||
m_cachedBytecodeBuffer.set(vm(), this, WebCore::createBuffer(globalObject(), bytes));
|
||||
if (!m_cachedBytecodeBuffer) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(m_cachedBytecodeBuffer);
|
||||
@@ -333,6 +361,8 @@ static JSC::EncodedJSValue runInContext(NodeVMGlobalObject* globalObject, NodeVM
|
||||
run();
|
||||
}
|
||||
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
if (options.timeout) {
|
||||
vm.watchdog()->setTimeLimit(WTF::Seconds::fromMilliseconds(*oldLimit));
|
||||
}
|
||||
@@ -351,7 +381,8 @@ static JSC::EncodedJSValue runInContext(NodeVMGlobalObject* globalObject, NodeVM
|
||||
return {};
|
||||
}
|
||||
|
||||
return JSValue::encode(result);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
RELEASE_AND_RETURN(scope, JSValue::encode(result));
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(scriptRunInThisContext, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||||
@@ -414,7 +445,7 @@ JSC_DEFINE_HOST_FUNCTION(scriptRunInThisContext, (JSGlobalObject * globalObject,
|
||||
}
|
||||
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
return JSValue::encode(result);
|
||||
RELEASE_AND_RETURN(scope, JSValue::encode(result));
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(scriptGetSourceMapURL, (JSGlobalObject * globalObject, JSC::EncodedJSValue thisValueEncoded, PropertyName))
|
||||
@@ -433,7 +464,7 @@ JSC_DEFINE_CUSTOM_GETTER(scriptGetSourceMapURL, (JSGlobalObject * globalObject,
|
||||
return encodedJSUndefined();
|
||||
}
|
||||
|
||||
return JSValue::encode(jsString(vm, url));
|
||||
RELEASE_AND_RETURN(scope, JSValue::encode(jsString(vm, url)));
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(scriptGetCachedData, (JSGlobalObject * globalObject, JSC::EncodedJSValue thisValueEncoded, PropertyName))
|
||||
@@ -447,10 +478,10 @@ JSC_DEFINE_CUSTOM_GETTER(scriptGetCachedData, (JSGlobalObject * globalObject, JS
|
||||
}
|
||||
|
||||
if (auto* buffer = script->getBytecodeBuffer()) {
|
||||
return JSValue::encode(buffer);
|
||||
RELEASE_AND_RETURN(scope, JSValue::encode(buffer));
|
||||
}
|
||||
|
||||
return JSValue::encode(jsUndefined());
|
||||
RELEASE_AND_RETURN(scope, JSValue::encode(jsUndefined()));
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(scriptGetCachedDataProduced, (JSGlobalObject * globalObject, JSC::EncodedJSValue thisValueEncoded, PropertyName))
|
||||
@@ -463,7 +494,7 @@ JSC_DEFINE_CUSTOM_GETTER(scriptGetCachedDataProduced, (JSGlobalObject * globalOb
|
||||
return ERR::INVALID_ARG_VALUE(scope, globalObject, "this"_s, thisValue, "must be a Script"_s);
|
||||
}
|
||||
|
||||
return JSValue::encode(jsBoolean(script->cachedDataProduced()));
|
||||
RELEASE_AND_RETURN(scope, JSValue::encode(jsBoolean(script->cachedDataProduced())));
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(scriptGetCachedDataRejected, (JSGlobalObject * globalObject, JSC::EncodedJSValue thisValueEncoded, PropertyName))
|
||||
@@ -478,11 +509,11 @@ JSC_DEFINE_CUSTOM_GETTER(scriptGetCachedDataRejected, (JSGlobalObject * globalOb
|
||||
|
||||
switch (script->cachedDataRejected()) {
|
||||
case TriState::True:
|
||||
return JSValue::encode(jsBoolean(true));
|
||||
RELEASE_AND_RETURN(scope, JSValue::encode(jsBoolean(true)));
|
||||
case TriState::False:
|
||||
return JSValue::encode(jsBoolean(false));
|
||||
RELEASE_AND_RETURN(scope, JSValue::encode(jsBoolean(false)));
|
||||
default:
|
||||
return JSValue::encode(jsUndefined());
|
||||
RELEASE_AND_RETURN(scope, encodedJSUndefined());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -498,7 +529,7 @@ JSC_DEFINE_HOST_FUNCTION(scriptCreateCachedData, (JSGlobalObject * globalObject,
|
||||
}
|
||||
|
||||
const JSC::SourceCode& source = script->source();
|
||||
return createCachedData(globalObject, source);
|
||||
RELEASE_AND_RETURN(scope, createCachedData(globalObject, source));
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(scriptRunInContext, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||||
@@ -519,7 +550,7 @@ JSC_DEFINE_HOST_FUNCTION(scriptRunInContext, (JSGlobalObject * globalObject, Cal
|
||||
JSObject* context = asObject(contextArg);
|
||||
ASSERT(nodeVmGlobalObject != nullptr);
|
||||
|
||||
return runInContext(nodeVmGlobalObject, script, context, args.at(1));
|
||||
RELEASE_AND_RETURN(scope, runInContext(nodeVmGlobalObject, script, context, args.at(1)));
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(scriptRunInNewContext, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||||
@@ -527,8 +558,6 @@ JSC_DEFINE_HOST_FUNCTION(scriptRunInNewContext, (JSGlobalObject * globalObject,
|
||||
VM& vm = JSC::getVM(globalObject);
|
||||
NodeVMScript* script = jsDynamicCast<NodeVMScript*>(callFrame->thisValue());
|
||||
JSValue contextObjectValue = callFrame->argument(0);
|
||||
// TODO: options
|
||||
// JSValue optionsObjectValue = callFrame->argument(1);
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
if (!script) {
|
||||
@@ -536,24 +565,36 @@ JSC_DEFINE_HOST_FUNCTION(scriptRunInNewContext, (JSGlobalObject * globalObject,
|
||||
return {};
|
||||
}
|
||||
|
||||
if (contextObjectValue.isUndefined()) {
|
||||
contextObjectValue = JSC::constructEmptyObject(globalObject);
|
||||
}
|
||||
bool notContextified = NodeVM::getContextArg(globalObject, contextObjectValue);
|
||||
|
||||
if (!contextObjectValue || !contextObjectValue.isObject()) [[unlikely]] {
|
||||
throwTypeError(globalObject, scope, "Context must be an object"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
// we don't care about options for now
|
||||
// TODO: options
|
||||
// bool didThrow = false;
|
||||
JSValue contextOptionsArg = callFrame->argument(1);
|
||||
NodeVMContextOptions contextOptions {};
|
||||
JSValue importer;
|
||||
|
||||
auto* zigGlobal = defaultGlobalObject(globalObject);
|
||||
if (auto encodedException = getNodeVMContextOptions(globalObject, vm, scope, contextOptionsArg, contextOptions, "contextCodeGeneration", &importer)) {
|
||||
return *encodedException;
|
||||
}
|
||||
|
||||
contextOptions.notContextified = notContextified;
|
||||
|
||||
auto* zigGlobalObject = defaultGlobalObject(globalObject);
|
||||
JSObject* context = asObject(contextObjectValue);
|
||||
auto* targetContext = NodeVMGlobalObject::create(vm,
|
||||
zigGlobal->NodeVMGlobalObjectStructure(),
|
||||
{});
|
||||
zigGlobalObject->NodeVMGlobalObjectStructure(),
|
||||
contextOptions, importer);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
if (notContextified) {
|
||||
auto* specialSandbox = NodeVMSpecialSandbox::create(vm, zigGlobalObject->NodeVMSpecialSandboxStructure(), targetContext);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
targetContext->setSpecialSandbox(specialSandbox);
|
||||
RELEASE_AND_RETURN(scope, runInContext(targetContext, script, targetContext->specialSandbox(), callFrame->argument(1)));
|
||||
}
|
||||
|
||||
RELEASE_AND_RETURN(scope, runInContext(targetContext, script, context, callFrame->argument(1)));
|
||||
}
|
||||
|
||||
@@ -10,12 +10,11 @@ class ScriptOptions : public BaseVMOptions {
|
||||
public:
|
||||
WTF::Vector<uint8_t> cachedData;
|
||||
std::optional<int64_t> timeout = std::nullopt;
|
||||
JSValue importer {};
|
||||
bool produceCachedData = false;
|
||||
|
||||
using BaseVMOptions::BaseVMOptions;
|
||||
|
||||
bool fromJS(JSC::JSGlobalObject* globalObject, JSC::VM& vm, JSC::ThrowScope& scope, JSC::JSValue optionsArg);
|
||||
bool fromJS(JSC::JSGlobalObject* globalObject, JSC::VM& vm, JSC::ThrowScope& scope, JSC::JSValue optionsArg, JSValue* importer);
|
||||
};
|
||||
|
||||
class NodeVMScriptConstructor final : public JSC::InternalFunction {
|
||||
|
||||
@@ -3,27 +3,39 @@
|
||||
#include "root.h"
|
||||
|
||||
#include <JavaScriptCore/ScriptFetcher.h>
|
||||
#include <wtf/Scope.h>
|
||||
|
||||
namespace Bun {
|
||||
|
||||
// The presence of this class in a JSFunction's sourceOrigin indicates that the function was compiled by Bun's node:vm implementation.
|
||||
class NodeVMScriptFetcher : public JSC::ScriptFetcher {
|
||||
public:
|
||||
static Ref<NodeVMScriptFetcher> create(JSC::VM& vm, JSC::JSValue dynamicImportCallback) { return adoptRef(*new NodeVMScriptFetcher(vm, dynamicImportCallback)); }
|
||||
static Ref<NodeVMScriptFetcher> create(JSC::VM& vm, JSC::JSValue dynamicImportCallback, JSC::JSValue owner) { return adoptRef(*new NodeVMScriptFetcher(vm, dynamicImportCallback, owner)); }
|
||||
|
||||
Type fetcherType() const final { return Type::NodeVM; }
|
||||
|
||||
JSC::JSValue dynamicImportCallback() const { return m_dynamicImportCallback.get(); }
|
||||
|
||||
JSC::JSFunction* owner() const { return m_owner.get(); }
|
||||
void owner(JSC::VM& vm, JSC::JSFunction* value) { m_owner.set(vm, value); }
|
||||
JSC::JSValue owner() const { return m_owner.get(); }
|
||||
void owner(JSC::VM& vm, JSC::JSValue value) { m_owner.set(vm, value); }
|
||||
|
||||
bool isUsingDefaultLoader() const { return m_isUsingDefaultLoader; }
|
||||
auto temporarilyUseDefaultLoader()
|
||||
{
|
||||
m_isUsingDefaultLoader = true;
|
||||
return makeScopeExit([this] {
|
||||
m_isUsingDefaultLoader = false;
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
JSC::Strong<JSC::Unknown> m_dynamicImportCallback;
|
||||
JSC::Strong<JSC::JSFunction> m_owner;
|
||||
JSC::Strong<JSC::Unknown> m_owner;
|
||||
bool m_isUsingDefaultLoader = false;
|
||||
|
||||
NodeVMScriptFetcher(JSC::VM& vm, JSC::JSValue dynamicImportCallback)
|
||||
NodeVMScriptFetcher(JSC::VM& vm, JSC::JSValue dynamicImportCallback, JSC::JSValue owner)
|
||||
: m_dynamicImportCallback(vm, dynamicImportCallback)
|
||||
, m_owner(vm, owner)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "NodeVMScriptFetcher.h"
|
||||
#include "NodeVMSourceTextModule.h"
|
||||
#include "NodeVMSyntheticModule.h"
|
||||
|
||||
@@ -77,16 +78,35 @@ NodeVMSourceTextModule* NodeVMSourceTextModule::create(VM& vm, JSGlobalObject* g
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t lineOffset = lineOffsetValue.toUInt32(globalObject);
|
||||
uint32_t columnOffset = columnOffsetValue.toUInt32(globalObject);
|
||||
JSValue dynamicImportCallback = args.at(8);
|
||||
if (!dynamicImportCallback.isUndefined() && !dynamicImportCallback.isCallable()) {
|
||||
throwArgumentTypeError(*globalObject, scope, 8, "dynamicImportCallback"_s, "Module"_s, "Module"_s, "function"_s);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Ref<StringSourceProvider> sourceProvider = StringSourceProvider::create(sourceTextValue.toWTFString(globalObject), SourceOrigin {}, String {}, SourceTaintedOrigin::Untainted,
|
||||
uint32_t lineOffset = lineOffsetValue.toUInt32(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
uint32_t columnOffset = columnOffsetValue.toUInt32(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
|
||||
RefPtr fetcher(NodeVMScriptFetcher::create(vm, dynamicImportCallback, moduleWrapper));
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
|
||||
SourceOrigin sourceOrigin { {}, *fetcher };
|
||||
|
||||
WTF::String sourceText = sourceTextValue.toWTFString(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
|
||||
Ref<StringSourceProvider> sourceProvider = StringSourceProvider::create(WTFMove(sourceText), sourceOrigin, String {}, SourceTaintedOrigin::Untainted,
|
||||
TextPosition { OrdinalNumber::fromZeroBasedInt(lineOffset), OrdinalNumber::fromZeroBasedInt(columnOffset) }, SourceProviderSourceType::Module);
|
||||
|
||||
SourceCode sourceCode(WTFMove(sourceProvider), lineOffset, columnOffset);
|
||||
|
||||
auto* zigGlobalObject = defaultGlobalObject(globalObject);
|
||||
NodeVMSourceTextModule* ptr = new (NotNull, allocateCell<NodeVMSourceTextModule>(vm)) NodeVMSourceTextModule(vm, zigGlobalObject->NodeVMSourceTextModuleStructure(), identifierValue.toWTFString(globalObject), contextValue, WTFMove(sourceCode), moduleWrapper);
|
||||
WTF::String identifier = identifierValue.toWTFString(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
NodeVMSourceTextModule* ptr = new (NotNull, allocateCell<NodeVMSourceTextModule>(vm)) NodeVMSourceTextModule(vm, zigGlobalObject->NodeVMSourceTextModuleStructure(), WTFMove(identifier), contextValue, WTFMove(sourceCode), moduleWrapper);
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
ptr->finishCreation(vm);
|
||||
|
||||
if (!initializeImportMeta.isUndefined()) {
|
||||
@@ -111,7 +131,9 @@ NodeVMSourceTextModule* NodeVMSourceTextModule::create(VM& vm, JSGlobalObject* g
|
||||
LexicallyScopedFeatures lexicallyScopedFeatures = globalObject->globalScopeExtension() ? TaintedByWithScopeLexicallyScopedFeature : NoLexicallyScopedFeatures;
|
||||
SourceCodeKey key(ptr->sourceCode(), {}, SourceCodeType::ProgramType, lexicallyScopedFeatures, JSParserScriptMode::Classic, DerivedContextType::None, EvalContextType::None, false, {}, std::nullopt);
|
||||
Ref<CachedBytecode> cachedBytecode = CachedBytecode::create(std::span(cachedData), nullptr, {});
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
UnlinkedModuleProgramCodeBlock* unlinkedBlock = decodeCodeBlock<UnlinkedModuleProgramCodeBlock>(vm, key, WTFMove(cachedBytecode));
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
|
||||
if (unlinkedBlock) {
|
||||
JSScope* jsScope = globalObject->globalScope();
|
||||
@@ -120,9 +142,11 @@ NodeVMSourceTextModule* NodeVMSourceTextModule::create(VM& vm, JSGlobalObject* g
|
||||
// JSC::ProgramCodeBlock::create() requires GC to be deferred.
|
||||
DeferGC deferGC(vm);
|
||||
codeBlock = ModuleProgramCodeBlock::create(vm, executable, unlinkedBlock, jsScope);
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
}
|
||||
if (codeBlock) {
|
||||
CompilationResult compilationResult = JIT::compileSync(vm, codeBlock, JITCompilationEffort::JITCompilationCanFail);
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
if (compilationResult != CompilationResult::CompilationFailed) {
|
||||
executable->installCode(codeBlock);
|
||||
return ptr;
|
||||
@@ -184,7 +208,9 @@ JSValue NodeVMSourceTextModule::createModuleRecord(JSGlobalObject* globalObject)
|
||||
const auto& requests = moduleRecord->requestedModules();
|
||||
|
||||
if (requests.isEmpty()) {
|
||||
return constructEmptyArray(globalObject, nullptr, 0);
|
||||
JSArray* requestsArray = constructEmptyArray(globalObject, nullptr, 0);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
return requestsArray;
|
||||
}
|
||||
|
||||
JSArray* requestsArray = constructEmptyArray(globalObject, nullptr, requests.size());
|
||||
@@ -312,26 +338,35 @@ JSValue NodeVMSourceTextModule::link(JSGlobalObject* globalObject, JSArray* spec
|
||||
if (length != 0) {
|
||||
for (unsigned i = 0; i < length; i++) {
|
||||
JSValue specifierValue = specifiers->getDirectIndex(globalObject, i);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
JSValue moduleNativeValue = moduleNatives->getDirectIndex(globalObject, i);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
ASSERT(specifierValue.isString());
|
||||
ASSERT(moduleNativeValue.isObject());
|
||||
|
||||
WTF::String specifier = specifierValue.toWTFString(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
JSObject* moduleNative = moduleNativeValue.getObject();
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
AbstractModuleRecord* resolvedRecord = jsCast<NodeVMModule*>(moduleNative)->moduleRecord(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
record->setImportedModule(globalObject, Identifier::fromString(vm, specifier), resolvedRecord);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
m_resolveCache.set(WTFMove(specifier), WriteBarrier<JSObject> { vm, this, moduleNative });
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
}
|
||||
}
|
||||
|
||||
if (NodeVMGlobalObject* nodeVmGlobalObject = getGlobalObjectFromContext(globalObject, m_context.get(), false)) {
|
||||
NodeVMGlobalObject* nodeVmGlobalObject = getGlobalObjectFromContext(globalObject, m_context.get(), false);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
if (nodeVmGlobalObject) {
|
||||
globalObject = nodeVmGlobalObject;
|
||||
}
|
||||
|
||||
Synchronousness sync = record->link(globalObject, scriptFetcher);
|
||||
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
if (sync == Synchronousness::Async) {
|
||||
@@ -355,6 +390,7 @@ RefPtr<CachedBytecode> NodeVMSourceTextModule::bytecode(JSGlobalObject* globalOb
|
||||
if (!m_bytecode) {
|
||||
if (!m_cachedExecutable) {
|
||||
ModuleProgramExecutable* executable = ModuleProgramExecutable::tryCreate(globalObject, m_sourceCode);
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
if (!executable) {
|
||||
if (!scope.exception()) {
|
||||
throwSyntaxError(globalObject, scope, "Failed to create cached executable"_s);
|
||||
@@ -364,6 +400,7 @@ RefPtr<CachedBytecode> NodeVMSourceTextModule::bytecode(JSGlobalObject* globalOb
|
||||
m_cachedExecutable.set(vm, this, executable);
|
||||
}
|
||||
m_bytecode = getBytecode(globalObject, m_cachedExecutable.get(), m_sourceCode);
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
}
|
||||
|
||||
return m_bytecode;
|
||||
@@ -371,10 +408,16 @@ RefPtr<CachedBytecode> NodeVMSourceTextModule::bytecode(JSGlobalObject* globalOb
|
||||
|
||||
JSUint8Array* NodeVMSourceTextModule::cachedData(JSGlobalObject* globalObject)
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
if (!m_cachedBytecodeBuffer) {
|
||||
RefPtr<CachedBytecode> cachedBytecode = bytecode(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
std::span<const uint8_t> bytes = cachedBytecode->span();
|
||||
m_cachedBytecodeBuffer.set(vm(), this, WebCore::createBuffer(globalObject, bytes));
|
||||
JSUint8Array* buffer = WebCore::createBuffer(globalObject, bytes);
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
m_cachedBytecodeBuffer.set(vm, this, buffer);
|
||||
}
|
||||
|
||||
return m_cachedBytecodeBuffer.get();
|
||||
@@ -389,7 +432,11 @@ void NodeVMSourceTextModule::initializeImportMeta(JSGlobalObject* globalObject)
|
||||
JSModuleEnvironment* moduleEnvironment = m_moduleRecord->moduleEnvironmentMayBeNull();
|
||||
ASSERT(moduleEnvironment != nullptr);
|
||||
|
||||
VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
JSValue metaValue = moduleEnvironment->get(globalObject, globalObject->vm().propertyNames->builtinNames().metaPrivateName());
|
||||
RETURN_IF_EXCEPTION(scope, );
|
||||
ASSERT(metaValue);
|
||||
ASSERT(metaValue.isObject());
|
||||
|
||||
@@ -400,6 +447,7 @@ void NodeVMSourceTextModule::initializeImportMeta(JSGlobalObject* globalObject)
|
||||
args.append(m_moduleWrapper.get());
|
||||
|
||||
JSC::call(globalObject, m_initializeImportMeta.get(), callData, jsUndefined(), args);
|
||||
RETURN_IF_EXCEPTION(scope, );
|
||||
}
|
||||
|
||||
JSObject* NodeVMSourceTextModule::createPrototype(VM& vm, JSGlobalObject* globalObject)
|
||||
|
||||
@@ -67,15 +67,20 @@ NodeVMSyntheticModule* NodeVMSyntheticModule::create(VM& vm, JSGlobalObject* glo
|
||||
WTF::HashSet<String> exportNames;
|
||||
for (unsigned i = 0; i < exportNamesArray->getArrayLength(); i++) {
|
||||
JSValue exportNameValue = exportNamesArray->getIndex(globalObject, i);
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
if (!exportNameValue.isString()) {
|
||||
throwArgumentTypeError(*globalObject, scope, 2, "exportNames"_s, "Module"_s, "Module"_s, "string[]"_s);
|
||||
return nullptr;
|
||||
}
|
||||
exportNames.addVoid(exportNameValue.toWTFString(globalObject));
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
}
|
||||
|
||||
auto* zigGlobalObject = defaultGlobalObject(globalObject);
|
||||
auto* structure = zigGlobalObject->NodeVMSyntheticModuleStructure();
|
||||
auto* ptr = new (NotNull, allocateCell<NodeVMSyntheticModule>(vm)) NodeVMSyntheticModule(vm, structure, identifierValue.toWTFString(globalObject), contextValue, moduleWrapperValue, WTFMove(exportNames), syntheticEvaluationStepsValue);
|
||||
WTF::String identifier = identifierValue.toWTFString(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
auto* ptr = new (NotNull, allocateCell<NodeVMSyntheticModule>(vm)) NodeVMSyntheticModule(vm, structure, WTFMove(identifier), contextValue, moduleWrapperValue, WTFMove(exportNames), syntheticEvaluationStepsValue);
|
||||
ptr->finishCreation(vm);
|
||||
return ptr;
|
||||
}
|
||||
@@ -204,8 +209,11 @@ void NodeVMSyntheticModule::setExport(JSGlobalObject* globalObject, WTF::String
|
||||
}
|
||||
|
||||
ensureModuleRecord(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, );
|
||||
JSModuleNamespaceObject* namespaceObject = m_moduleRecord->getModuleNamespace(globalObject, false);
|
||||
RETURN_IF_EXCEPTION(scope, );
|
||||
namespaceObject->overrideExportValue(globalObject, Identifier::fromString(vm, exportName), value);
|
||||
RETURN_IF_EXCEPTION(scope, );
|
||||
}
|
||||
|
||||
JSObject* NodeVMSyntheticModule::createPrototype(VM& vm, JSGlobalObject* globalObject)
|
||||
|
||||
@@ -4136,22 +4136,26 @@ JSC::JSInternalPromise* GlobalObject::moduleLoaderImportModule(JSGlobalObject* j
|
||||
{
|
||||
auto* globalObject = static_cast<Zig::GlobalObject*>(jsGlobalObject);
|
||||
|
||||
if (JSC::JSInternalPromise* result = NodeVM::importModule(globalObject, moduleNameValue, parameters, sourceOrigin)) {
|
||||
return result;
|
||||
VM& vm = JSC::getVM(globalObject);
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
{
|
||||
JSC::JSInternalPromise* result = NodeVM::importModule(globalObject, moduleNameValue, parameters, sourceOrigin);
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
auto& vm = JSC::getVM(globalObject);
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
JSC::Identifier resolvedIdentifier;
|
||||
|
||||
auto moduleName = moduleNameValue->value(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
if (globalObject->onLoadPlugins.hasVirtualModules()) {
|
||||
if (auto resolution = globalObject->onLoadPlugins.resolveVirtualModule(moduleName, sourceOrigin.url().protocolIsFile() ? sourceOrigin.url().fileSystemPath() : String())) {
|
||||
resolvedIdentifier = JSC::Identifier::fromString(vm, resolution.value());
|
||||
|
||||
auto result = JSC::importModule(globalObject, resolvedIdentifier,
|
||||
JSC::jsUndefined(), parameters, JSC::jsUndefined());
|
||||
auto result = JSC::importModule(globalObject, resolvedIdentifier, JSC::jsUndefined(), parameters, JSC::jsUndefined());
|
||||
if (scope.exception()) [[unlikely]] {
|
||||
auto* promise = JSC::JSInternalPromise::create(vm, globalObject->internalPromiseStructure());
|
||||
return promise->rejectWithCaughtException(globalObject, scope);
|
||||
|
||||
@@ -289,6 +289,7 @@ public:
|
||||
JSC::JSFunction* requireESMFromHijackedExtension() const { return m_commonJSRequireESMFromHijackedExtensionFunction.getInitializedOnMainThread(this); }
|
||||
|
||||
Structure* NodeVMGlobalObjectStructure() const { return m_cachedNodeVMGlobalObjectStructure.getInitializedOnMainThread(this); }
|
||||
Structure* NodeVMSpecialSandboxStructure() const { return m_cachedNodeVMSpecialSandboxStructure.getInitializedOnMainThread(this); }
|
||||
Structure* globalProxyStructure() const { return m_cachedGlobalProxyStructure.getInitializedOnMainThread(this); }
|
||||
JSObject* lazyTestModuleObject() const { return m_lazyTestModuleObject.getInitializedOnMainThread(this); }
|
||||
JSObject* lazyPreloadTestModuleObject() const { return m_lazyPreloadTestModuleObject.getInitializedOnMainThread(this); }
|
||||
@@ -576,6 +577,7 @@ public:
|
||||
V(private, LazyPropertyOfGlobalObject<JSObject>, m_lazyPreloadTestModuleObject) \
|
||||
V(public, LazyPropertyOfGlobalObject<JSObject>, m_testMatcherUtilsObject) \
|
||||
V(public, LazyPropertyOfGlobalObject<Structure>, m_cachedNodeVMGlobalObjectStructure) \
|
||||
V(public, LazyPropertyOfGlobalObject<Structure>, m_cachedNodeVMSpecialSandboxStructure) \
|
||||
V(private, LazyPropertyOfGlobalObject<Structure>, m_cachedGlobalProxyStructure) \
|
||||
V(private, LazyPropertyOfGlobalObject<Structure>, m_commonJSModuleObjectStructure) \
|
||||
V(private, LazyPropertyOfGlobalObject<Structure>, m_JSSocketAddressDTOStructure) \
|
||||
@@ -617,7 +619,9 @@ public:
|
||||
V(public, LazyPropertyOfGlobalObject<JSFloat64Array>, m_statValues) \
|
||||
V(public, LazyPropertyOfGlobalObject<JSBigInt64Array>, m_bigintStatValues) \
|
||||
V(public, LazyPropertyOfGlobalObject<JSFloat64Array>, m_statFsValues) \
|
||||
V(public, LazyPropertyOfGlobalObject<JSBigInt64Array>, m_bigintStatFsValues)
|
||||
V(public, LazyPropertyOfGlobalObject<JSBigInt64Array>, m_bigintStatFsValues) \
|
||||
V(public, LazyPropertyOfGlobalObject<Symbol>, m_nodeVMDontContextify) \
|
||||
V(public, LazyPropertyOfGlobalObject<Symbol>, m_nodeVMUseMainContextDefaultLoader)
|
||||
|
||||
#define DECLARE_GLOBALOBJECT_GC_MEMBER(visibility, T, name) \
|
||||
visibility: \
|
||||
|
||||
@@ -36,6 +36,7 @@ public:
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForRequireResolveFunction;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForBundlerPlugin;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForNodeVMGlobalObject;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForNodeVMSpecialSandbox;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForNodeVMScript;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForNodeVMSourceTextModule;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForNodeVMSyntheticModule;
|
||||
|
||||
@@ -36,6 +36,7 @@ public:
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForRequireResolveFunction;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForBundlerPlugin;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForNodeVMGlobalObject;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForNodeVMSpecialSandbox;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForNodeVMScript;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForNodeVMSourceTextModule;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForNodeVMSyntheticModule;
|
||||
|
||||
@@ -43,14 +43,14 @@ const {
|
||||
Module: ModuleNative,
|
||||
createContext,
|
||||
isContext,
|
||||
// runInNewContext: moduleRunInNewContext,
|
||||
// runInThisContext: moduleRunInThisContext,
|
||||
compileFunction,
|
||||
isModuleNamespaceObject,
|
||||
kUnlinked,
|
||||
kLinked,
|
||||
kEvaluated,
|
||||
kErrored,
|
||||
DONT_CONTEXTIFY,
|
||||
USE_MAIN_CONTEXT_DEFAULT_LOADER,
|
||||
} = vm;
|
||||
|
||||
function runInContext(code, context, options) {
|
||||
@@ -88,7 +88,7 @@ function measureMemory() {
|
||||
}
|
||||
|
||||
function validateContext(contextifiedObject) {
|
||||
if (!isContext(contextifiedObject) && contextifiedObject !== constants.DONT_CONTEXTIFY) {
|
||||
if (contextifiedObject !== constants.DONT_CONTEXTIFY && !isContext(contextifiedObject)) {
|
||||
const error = new Error('The "contextifiedObject" argument must be an vm.Context');
|
||||
error.code = "ERR_INVALID_ARG_TYPE";
|
||||
error.name = "TypeError";
|
||||
@@ -143,7 +143,6 @@ class Module {
|
||||
});
|
||||
}
|
||||
|
||||
let registry: any = { __proto__: null };
|
||||
if (sourceText !== undefined) {
|
||||
this[kNative] = new ModuleNative(
|
||||
identifier,
|
||||
@@ -154,19 +153,8 @@ class Module {
|
||||
options.cachedData,
|
||||
options.initializeImportMeta,
|
||||
this,
|
||||
options.importModuleDynamically ? importModuleDynamicallyWrap(options.importModuleDynamically) : undefined,
|
||||
);
|
||||
registry = {
|
||||
__proto__: null,
|
||||
initializeImportMeta: options.initializeImportMeta,
|
||||
importModuleDynamically: options.importModuleDynamically
|
||||
? importModuleDynamicallyWrap(options.importModuleDynamically)
|
||||
: undefined,
|
||||
};
|
||||
// This will take precedence over the referrer as the object being
|
||||
// passed into the callbacks.
|
||||
registry.callbackReferrer = this;
|
||||
// const { registerModule } = require("internal/modules/esm/utils");
|
||||
// registerModule(this[kNative], registry);
|
||||
} else {
|
||||
$assert(syntheticEvaluationSteps);
|
||||
this[kNative] = new ModuleNative(identifier, context, syntheticExportNames, syntheticEvaluationSteps, this);
|
||||
@@ -442,8 +430,8 @@ class SyntheticModule extends Module {
|
||||
|
||||
const constants = {
|
||||
__proto__: null,
|
||||
USE_MAIN_CONTEXT_DEFAULT_LOADER: Symbol("vm_dynamic_import_main_context_default"),
|
||||
DONT_CONTEXTIFY: Symbol("vm_context_no_contextify"),
|
||||
USE_MAIN_CONTEXT_DEFAULT_LOADER,
|
||||
DONT_CONTEXTIFY,
|
||||
};
|
||||
|
||||
function isModule(object) {
|
||||
@@ -452,7 +440,7 @@ function isModule(object) {
|
||||
|
||||
function importModuleDynamicallyWrap(importModuleDynamically) {
|
||||
const importModuleDynamicallyWrapper = async (...args) => {
|
||||
const m: any = importModuleDynamically.$apply(this, args);
|
||||
const m: any = await importModuleDynamically.$apply(this, args);
|
||||
if (isModuleNamespaceObject(m)) {
|
||||
return m;
|
||||
}
|
||||
|
||||
185
test/js/node/test/parallel/test-vm-context-dont-contextify.js
Normal file
185
test/js/node/test/parallel/test-vm-context-dont-contextify.js
Normal file
@@ -0,0 +1,185 @@
|
||||
'use strict';
|
||||
|
||||
// Check vm.constants.DONT_CONTEXTIFY works.
|
||||
|
||||
const common = require('../common');
|
||||
|
||||
const assert = require('assert');
|
||||
const vm = require('vm');
|
||||
const fixtures = require('../common/fixtures');
|
||||
|
||||
{
|
||||
// Check identity of the returned object.
|
||||
const context = vm.createContext(vm.constants.DONT_CONTEXTIFY);
|
||||
// The globalThis in the new context should be reference equal to the returned object.
|
||||
assert.strictEqual(vm.runInContext('globalThis', context), context);
|
||||
assert(vm.isContext(context));
|
||||
assert.strictEqual(typeof context.Array, 'function'); // Can access builtins directly.
|
||||
assert.deepStrictEqual(Object.keys(context), []); // Properties on the global proxy are not enumerable
|
||||
}
|
||||
|
||||
{
|
||||
// Check that vm.createContext can return the original context if re-passed.
|
||||
const context = vm.createContext(vm.constants.DONT_CONTEXTIFY);
|
||||
const context2 = new vm.createContext(context);
|
||||
assert.strictEqual(context, context2);
|
||||
}
|
||||
|
||||
{
|
||||
// Check that the context is vanilla and that Script.runInContext works.
|
||||
const context = vm.createContext(vm.constants.DONT_CONTEXTIFY);
|
||||
const result =
|
||||
new vm.Script('globalThis.hey = 1; Object.freeze(globalThis); globalThis.process')
|
||||
.runInContext(context);
|
||||
assert.strictEqual(globalThis.hey, undefined); // Should not leak into current context.
|
||||
assert.strictEqual(result, undefined); // Vanilla context has no Node.js globals
|
||||
}
|
||||
|
||||
{
|
||||
// Check Script.runInNewContext works.
|
||||
const result =
|
||||
new vm.Script('globalThis.hey = 1; Object.freeze(globalThis); globalThis.process')
|
||||
.runInNewContext(vm.constants.DONT_CONTEXTIFY);
|
||||
assert.strictEqual(globalThis.hey, undefined); // Should not leak into current context.
|
||||
assert.strictEqual(result, undefined); // Vanilla context has no Node.js globals
|
||||
}
|
||||
|
||||
{
|
||||
// Check that vm.runInNewContext() works
|
||||
const result = vm.runInNewContext(
|
||||
'globalThis.hey = 1; Object.freeze(globalThis); globalThis.process',
|
||||
vm.constants.DONT_CONTEXTIFY);
|
||||
assert.strictEqual(globalThis.hey, undefined); // Should not leak into current context.
|
||||
assert.strictEqual(result, undefined); // Vanilla context has no Node.js globals
|
||||
}
|
||||
|
||||
{
|
||||
// Check that the global object of vanilla contexts work as expected.
|
||||
const context = vm.createContext(vm.constants.DONT_CONTEXTIFY);
|
||||
|
||||
// Check mutation via globalThis.
|
||||
vm.runInContext('globalThis.foo = 1;', context);
|
||||
assert.strictEqual(globalThis.foo, undefined); // Should not pollute the current context.
|
||||
assert.strictEqual(context.foo, 1);
|
||||
assert.strictEqual(vm.runInContext('globalThis.foo', context), 1);
|
||||
assert.strictEqual(vm.runInContext('foo', context), 1);
|
||||
|
||||
// Check mutation from outside.
|
||||
context.foo = 2;
|
||||
assert.strictEqual(context.foo, 2);
|
||||
assert.strictEqual(vm.runInContext('globalThis.foo', context), 2);
|
||||
assert.strictEqual(vm.runInContext('foo', context), 2);
|
||||
|
||||
// Check contextual mutation.
|
||||
vm.runInContext('bar = 1;', context);
|
||||
assert.strictEqual(globalThis.bar, undefined); // Should not pollute the current context.
|
||||
assert.strictEqual(context.bar, 1);
|
||||
assert.strictEqual(vm.runInContext('globalThis.bar', context), 1);
|
||||
assert.strictEqual(vm.runInContext('bar', context), 1);
|
||||
|
||||
// Check adding new property from outside.
|
||||
context.baz = 1;
|
||||
assert.strictEqual(context.baz, 1);
|
||||
assert.strictEqual(vm.runInContext('globalThis.baz', context), 1);
|
||||
assert.strictEqual(vm.runInContext('baz', context), 1);
|
||||
|
||||
// Check mutation via Object.defineProperty().
|
||||
vm.runInContext('Object.defineProperty(globalThis, "qux", {' +
|
||||
'enumerable: false, configurable: false, get() { return 1; } })', context);
|
||||
assert.strictEqual(globalThis.qux, undefined); // Should not pollute the current context.
|
||||
assert.strictEqual(context.qux, 1);
|
||||
assert.strictEqual(vm.runInContext('qux', context), 1);
|
||||
const desc = Object.getOwnPropertyDescriptor(context, 'qux');
|
||||
assert.strictEqual(desc.enumerable, false);
|
||||
assert.strictEqual(desc.configurable, false);
|
||||
assert.strictEqual(typeof desc.get, 'function');
|
||||
assert.throws(() => { context.qux = 1; }, { name: 'TypeError' });
|
||||
assert.throws(() => { Object.defineProperty(context, 'qux', { value: 1 }); }, { name: 'TypeError' });
|
||||
// Setting a value without a setter fails silently.
|
||||
assert.strictEqual(vm.runInContext('qux = 2; qux', context), 1);
|
||||
assert.throws(() => {
|
||||
vm.runInContext('Object.defineProperty(globalThis, "qux", { value: 1 });');
|
||||
}, { name: 'TypeError' });
|
||||
}
|
||||
|
||||
function checkFrozen(context) {
|
||||
// Check mutation via globalThis.
|
||||
vm.runInContext('globalThis.foo = 1', context); // Invoking setters on freezed object fails silently.
|
||||
assert.strictEqual(context.foo, undefined);
|
||||
assert.strictEqual(vm.runInContext('globalThis.foo', context), undefined);
|
||||
assert.throws(() => {
|
||||
vm.runInContext('foo', context); // It should not be looked up contextually.
|
||||
}, {
|
||||
name: 'ReferenceError'
|
||||
});
|
||||
|
||||
// Check mutation from outside.
|
||||
assert.throws(() => {
|
||||
context.foo = 2;
|
||||
}, { name: 'TypeError' });
|
||||
assert.strictEqual(context.foo, undefined);
|
||||
assert.strictEqual(vm.runInContext('globalThis.foo', context), undefined);
|
||||
assert.throws(() => {
|
||||
vm.runInContext('foo', context); // It should not be looked up contextually.
|
||||
}, {
|
||||
name: 'ReferenceError'
|
||||
});
|
||||
|
||||
// Check contextual mutation.
|
||||
vm.runInContext('bar = 1', context); // Invoking setters on freezed object fails silently.
|
||||
assert.strictEqual(context.bar, undefined);
|
||||
assert.strictEqual(vm.runInContext('globalThis.bar', context), undefined);
|
||||
assert.throws(() => {
|
||||
vm.runInContext('bar', context); // It should not be looked up contextually.
|
||||
}, {
|
||||
name: 'ReferenceError'
|
||||
});
|
||||
|
||||
// Check mutation via Object.defineProperty().
|
||||
assert.throws(() => {
|
||||
vm.runInContext('Object.defineProperty(globalThis, "qux", {' +
|
||||
'enumerable: false, configurable: false, get() { return 1; } })', context);
|
||||
}, {
|
||||
name: 'TypeError'
|
||||
});
|
||||
assert.strictEqual(context.qux, undefined);
|
||||
assert.strictEqual(vm.runInContext('globalThis.qux', context), undefined);
|
||||
assert.strictEqual(Object.getOwnPropertyDescriptor(context, 'qux'), undefined);
|
||||
assert.throws(() => { Object.defineProperty(context, 'qux', { value: 1 }); }, { name: 'TypeError' });
|
||||
assert.throws(() => {
|
||||
vm.runInContext('qux', context);
|
||||
}, {
|
||||
name: 'ReferenceError'
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
// Check freezing the vanilla context's global object from within the context.
|
||||
const context = vm.createContext(vm.constants.DONT_CONTEXTIFY);
|
||||
// Only vanilla contexts' globals can be freezed. Contextified global objects cannot be freezed
|
||||
// due to the presence of interceptors.
|
||||
vm.runInContext('Object.freeze(globalThis)', context);
|
||||
checkFrozen(context);
|
||||
}
|
||||
|
||||
{
|
||||
// Check freezing the vanilla context's global object from outside the context.
|
||||
const context = vm.createContext(vm.constants.DONT_CONTEXTIFY);
|
||||
Object.freeze(context);
|
||||
checkFrozen(context);
|
||||
}
|
||||
|
||||
// Check importModuleDynamically works.
|
||||
(async function() {
|
||||
{
|
||||
const moduleUrl = fixtures.fileURL('es-modules', 'message.mjs');
|
||||
const namespace = await import(moduleUrl.href);
|
||||
// Check dynamic import works
|
||||
const context = vm.createContext(vm.constants.DONT_CONTEXTIFY);
|
||||
const script = new vm.Script(`import(${JSON.stringify(moduleUrl)})`, {
|
||||
importModuleDynamically: vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER,
|
||||
});
|
||||
const promise = script.runInContext(context);
|
||||
assert.strictEqual(await promise, namespace);
|
||||
}
|
||||
})().catch(common.mustNotCall());
|
||||
117
test/js/node/test/parallel/test-vm-module-dynamic-import.js
Normal file
117
test/js/node/test/parallel/test-vm-module-dynamic-import.js
Normal file
@@ -0,0 +1,117 @@
|
||||
'use strict';
|
||||
|
||||
// Flags: --experimental-vm-modules
|
||||
|
||||
const common = require('../common');
|
||||
|
||||
const assert = require('assert');
|
||||
const { Script, SourceTextModule } = require('vm');
|
||||
|
||||
async function testNoCallback() {
|
||||
const m = new SourceTextModule(`
|
||||
globalThis.importResult = import("foo");
|
||||
globalThis.importResult.catch(() => {});
|
||||
`);
|
||||
await m.link(common.mustNotCall());
|
||||
await m.evaluate();
|
||||
let threw = false;
|
||||
try {
|
||||
await globalThis.importResult;
|
||||
} catch (err) {
|
||||
threw = true;
|
||||
assert.strictEqual(err.code, 'ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING');
|
||||
}
|
||||
delete globalThis.importResult;
|
||||
assert(threw);
|
||||
}
|
||||
|
||||
async function test() {
|
||||
const foo = new SourceTextModule('export const a = 1;');
|
||||
await foo.link(common.mustNotCall());
|
||||
await foo.evaluate();
|
||||
|
||||
{
|
||||
const s = new Script('import("foo")', {
|
||||
importModuleDynamically: common.mustCall((specifier, wrap) => {
|
||||
assert.strictEqual(specifier, 'foo');
|
||||
assert.strictEqual(wrap, s);
|
||||
return foo;
|
||||
}),
|
||||
});
|
||||
|
||||
const result = s.runInThisContext();
|
||||
assert.strictEqual(await result, foo.namespace);
|
||||
}
|
||||
|
||||
{
|
||||
const m = new SourceTextModule('globalThis.fooResult = import("foo")', {
|
||||
importModuleDynamically: common.mustCall((specifier, wrap) => {
|
||||
assert.strictEqual(specifier, 'foo');
|
||||
assert.strictEqual(wrap, m);
|
||||
return foo;
|
||||
}),
|
||||
});
|
||||
await m.link(common.mustNotCall());
|
||||
await m.evaluate();
|
||||
assert.strictEqual(await globalThis.fooResult, foo.namespace);
|
||||
delete globalThis.fooResult;
|
||||
}
|
||||
|
||||
{
|
||||
const s = new Script('import("foo", { with: { key: "value" } })', {
|
||||
importModuleDynamically: common.mustCall((specifier, wrap, attributes) => {
|
||||
assert.strictEqual(specifier, 'foo');
|
||||
assert.strictEqual(wrap, s);
|
||||
assert.deepStrictEqual(attributes, { __proto__: null, key: 'value' });
|
||||
return foo;
|
||||
}),
|
||||
});
|
||||
|
||||
const result = s.runInThisContext();
|
||||
assert.strictEqual(await result, foo.namespace);
|
||||
}
|
||||
}
|
||||
|
||||
async function testInvalid() {
|
||||
const m = new SourceTextModule('globalThis.fooResult = import("foo")', {
|
||||
importModuleDynamically: common.mustCall((specifier, wrap) => {
|
||||
return 5;
|
||||
}),
|
||||
});
|
||||
await m.link(common.mustNotCall());
|
||||
await m.evaluate();
|
||||
await globalThis.fooResult.catch(common.mustCall((e) => {
|
||||
assert.strictEqual(e.code, 'ERR_VM_MODULE_NOT_MODULE');
|
||||
}));
|
||||
delete globalThis.fooResult;
|
||||
|
||||
const s = new Script('import("bar")', {
|
||||
importModuleDynamically: common.mustCall((specifier, wrap) => {
|
||||
return undefined;
|
||||
}),
|
||||
});
|
||||
let threw = false;
|
||||
try {
|
||||
await s.runInThisContext();
|
||||
} catch (e) {
|
||||
threw = true;
|
||||
assert.strictEqual(e.code, 'ERR_VM_MODULE_NOT_MODULE');
|
||||
}
|
||||
assert(threw);
|
||||
}
|
||||
|
||||
async function testInvalidimportModuleDynamically() {
|
||||
assert.throws(
|
||||
() => new Script(
|
||||
'import("foo")',
|
||||
{ importModuleDynamically: false }),
|
||||
{ code: 'ERR_INVALID_ARG_TYPE' }
|
||||
);
|
||||
}
|
||||
|
||||
(async function() {
|
||||
await testNoCallback();
|
||||
await test();
|
||||
await testInvalid();
|
||||
await testInvalidimportModuleDynamically();
|
||||
}()).then(common.mustCall());
|
||||
@@ -0,0 +1,26 @@
|
||||
'use strict';
|
||||
|
||||
// Flags: --experimental-vm-modules
|
||||
|
||||
const common = require('../common');
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
const { types } = require('util');
|
||||
const { SourceTextModule } = require('vm');
|
||||
|
||||
(async () => {
|
||||
const m = new SourceTextModule('globalThis.importResult = import("");', {
|
||||
importModuleDynamically: common.mustCall(async (specifier, wrap) => {
|
||||
const m = new SourceTextModule('');
|
||||
await m.link(() => 0);
|
||||
await m.evaluate();
|
||||
return m.namespace;
|
||||
}),
|
||||
});
|
||||
await m.link(() => 0);
|
||||
await m.evaluate();
|
||||
const ns = await globalThis.importResult;
|
||||
delete globalThis.importResult;
|
||||
assert.ok(types.isModuleNamespaceObject(ns));
|
||||
})().then(common.mustCall());
|
||||
70
test/js/node/test/parallel/test-vm-module-referrer-realm.mjs
Normal file
70
test/js/node/test/parallel/test-vm-module-referrer-realm.mjs
Normal file
@@ -0,0 +1,70 @@
|
||||
// Flags: --experimental-vm-modules
|
||||
import * as common from '../common/index.mjs';
|
||||
import assert from 'node:assert';
|
||||
import { Script, SourceTextModule, createContext } from 'node:vm';
|
||||
|
||||
async function test() {
|
||||
const foo = new SourceTextModule('export const a = 1;');
|
||||
await foo.link(common.mustNotCall());
|
||||
await foo.evaluate();
|
||||
|
||||
const ctx = createContext({}, {
|
||||
importModuleDynamically: common.mustCall((specifier, wrap) => {
|
||||
assert.strictEqual(specifier, 'foo');
|
||||
assert.strictEqual(wrap, ctx);
|
||||
return foo;
|
||||
}, 2),
|
||||
});
|
||||
{
|
||||
const s = new Script('Promise.resolve("import(\'foo\')").then(eval)', {
|
||||
importModuleDynamically: common.mustNotCall(),
|
||||
});
|
||||
|
||||
const result = s.runInContext(ctx);
|
||||
assert.strictEqual(await result, foo.namespace);
|
||||
}
|
||||
|
||||
{
|
||||
const m = new SourceTextModule('globalThis.fooResult = Promise.resolve("import(\'foo\')").then(eval)', {
|
||||
context: ctx,
|
||||
importModuleDynamically: common.mustNotCall(),
|
||||
});
|
||||
await m.link(common.mustNotCall());
|
||||
await m.evaluate();
|
||||
assert.strictEqual(await ctx.fooResult, foo.namespace);
|
||||
delete ctx.fooResult;
|
||||
}
|
||||
}
|
||||
|
||||
async function testMissing() {
|
||||
const ctx = createContext({});
|
||||
{
|
||||
const s = new Script('Promise.resolve("import(\'foo\')").then(eval)', {
|
||||
importModuleDynamically: common.mustNotCall(),
|
||||
});
|
||||
|
||||
const result = s.runInContext(ctx);
|
||||
await assert.rejects(result, {
|
||||
code: 'ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING',
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
const m = new SourceTextModule('globalThis.fooResult = Promise.resolve("import(\'foo\')").then(eval)', {
|
||||
context: ctx,
|
||||
importModuleDynamically: common.mustNotCall(),
|
||||
});
|
||||
await m.link(common.mustNotCall());
|
||||
await m.evaluate();
|
||||
|
||||
await assert.rejects(ctx.fooResult, {
|
||||
code: 'ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING',
|
||||
});
|
||||
delete ctx.fooResult;
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
test(),
|
||||
testMissing(),
|
||||
]).then(common.mustCall());
|
||||
@@ -0,0 +1,20 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const { Script, compileFunction } = require('vm');
|
||||
const assert = require('assert');
|
||||
|
||||
assert.rejects(async () => {
|
||||
const script = new Script('import("fs")');
|
||||
const imported = script.runInThisContext();
|
||||
await imported;
|
||||
}, {
|
||||
code: 'ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING'
|
||||
}).then(common.mustCall());
|
||||
|
||||
assert.rejects(async () => {
|
||||
const imported = compileFunction('return import("fs")')();
|
||||
await imported;
|
||||
}, {
|
||||
code: 'ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING'
|
||||
}).then(common.mustCall());
|
||||
44
test/js/node/test/sequential/test-vm-timeout-rethrow.js
Normal file
44
test/js/node/test/sequential/test-vm-timeout-rethrow.js
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
'use strict';
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const vm = require('vm');
|
||||
const spawn = require('child_process').spawn;
|
||||
|
||||
if (process.argv[2] === 'child') {
|
||||
const code = 'while(true);';
|
||||
|
||||
const ctx = vm.createContext();
|
||||
|
||||
vm.runInContext(code, ctx, { timeout: 1 });
|
||||
} else {
|
||||
const proc = spawn(process.execPath, process.argv.slice(1).concat('child'));
|
||||
let err = '';
|
||||
proc.stderr.on('data', function(data) {
|
||||
err += data;
|
||||
});
|
||||
|
||||
process.on('exit', function() {
|
||||
assert.match(err, /Script execution timed out after 1ms/);
|
||||
});
|
||||
}
|
||||
@@ -1522,6 +1522,7 @@ test/js/node/test/parallel/test-vm-module-errors.js
|
||||
test/js/node/test/parallel/test-vm-module-import-meta.js
|
||||
test/js/node/test/parallel/test-vm-module-link.js
|
||||
test/js/node/test/parallel/test-vm-module-reevaluate.js
|
||||
test/js/node/test/parallel/test-vm-module-referrer-realm.mjs
|
||||
test/js/node/test/parallel/test-vm-module-synthetic.js
|
||||
test/js/node/test/parallel/test-vm-new-script-context.js
|
||||
test/js/node/test/parallel/test-vm-new-script-new-context.js
|
||||
|
||||
Reference in New Issue
Block a user