summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsnek <snek@deno.com>2024-08-06 14:52:53 +0200
committerGitHub <noreply@github.com>2024-08-06 12:52:53 +0000
commit897159dc6e1b2319cf2f5f09d8d6cecc0d3175fa (patch)
treecfe4a043d1fc102a4e051b99c7fcbef7b79bbb91
parentc0e9512b39a4ed3713d1fd9b28469d0edf68f578 (diff)
feat: vm rewrite (#24596)
rewrite vm implementation to increase compat. vm.Module+importModuleDynamically callbacks should be added in a followup.
-rw-r--r--ext/node/lib.rs31
-rw-r--r--ext/node/ops/mod.rs1
-rw-r--r--ext/node/ops/vm.rs1293
-rw-r--r--ext/node/polyfills/assert.ts3
-rw-r--r--ext/node/polyfills/vm.js359
-rw-r--r--ext/node/polyfills/vm.ts100
-rw-r--r--runtime/snapshot.rs1
-rw-r--r--tests/node_compat/config.jsonc49
-rw-r--r--tests/node_compat/runner/TODO.md42
-rw-r--r--tests/node_compat/test.ts31
-rw-r--r--tests/node_compat/test/common/index.js7
-rw-r--r--tests/node_compat/test/parallel/test-vm-access-process-env.js40
-rw-r--r--tests/node_compat/test/parallel/test-vm-attributes-property-not-on-sandbox.js25
-rw-r--r--tests/node_compat/test/parallel/test-vm-codegen.js108
-rw-r--r--tests/node_compat/test/parallel/test-vm-context-async-script.js42
-rw-r--r--tests/node_compat/test/parallel/test-vm-context-property-forwarding.js72
-rw-r--r--tests/node_compat/test/parallel/test-vm-create-and-run-in-context.js57
-rw-r--r--tests/node_compat/test/parallel/test-vm-create-context-accessors.js56
-rw-r--r--tests/node_compat/test/parallel/test-vm-create-context-arg.js47
-rw-r--r--tests/node_compat/test/parallel/test-vm-create-context-circular-reference.js41
-rw-r--r--tests/node_compat/test/parallel/test-vm-createcacheddata.js29
-rw-r--r--tests/node_compat/test/parallel/test-vm-cross-context.js36
-rw-r--r--tests/node_compat/test/parallel/test-vm-data-property-writable.js35
-rw-r--r--tests/node_compat/test/parallel/test-vm-deleting-property.js22
-rw-r--r--tests/node_compat/test/parallel/test-vm-function-declaration.js56
-rw-r--r--tests/node_compat/test/parallel/test-vm-function-redefinition.js18
-rw-r--r--tests/node_compat/test/parallel/test-vm-getters.js31
-rw-r--r--tests/node_compat/test/parallel/test-vm-global-assignment.js22
-rw-r--r--tests/node_compat/test/parallel/test-vm-global-define-property.js54
-rw-r--r--tests/node_compat/test/parallel/test-vm-global-identity.js39
-rw-r--r--tests/node_compat/test/parallel/test-vm-global-setter.js168
-rw-r--r--tests/node_compat/test/parallel/test-vm-harmony-symbols.js44
-rw-r--r--tests/node_compat/test/parallel/test-vm-indexed-properties.js24
-rw-r--r--tests/node_compat/test/parallel/test-vm-inherited_properties.js45
-rw-r--r--tests/node_compat/test/parallel/test-vm-is-context.js53
-rw-r--r--tests/node_compat/test/parallel/test-vm-low-stack-space.js33
-rw-r--r--tests/node_compat/test/parallel/test-vm-new-script-new-context.js114
-rw-r--r--tests/node_compat/test/parallel/test-vm-not-strict.js44
-rw-r--r--tests/node_compat/test/parallel/test-vm-options-validation.js101
-rw-r--r--tests/node_compat/test/parallel/test-vm-parse-abort-on-uncaught-exception.js25
-rw-r--r--tests/node_compat/test/parallel/test-vm-preserves-property.js32
-rw-r--r--tests/node_compat/test/parallel/test-vm-property-not-on-sandbox.js44
-rw-r--r--tests/node_compat/test/parallel/test-vm-proxies.js25
-rw-r--r--tests/node_compat/test/parallel/test-vm-proxy-failure-CP.js22
-rw-r--r--tests/node_compat/test/parallel/test-vm-script-throw-in-tostring.js21
-rw-r--r--tests/node_compat/test/parallel/test-vm-set-property-proxy.js23
-rw-r--r--tests/node_compat/test/parallel/test-vm-set-proto-null-on-globalthis.js20
-rw-r--r--tests/node_compat/test/parallel/test-vm-source-map-url.js34
-rw-r--r--tests/node_compat/test/parallel/test-vm-strict-mode.js21
-rw-r--r--tests/node_compat/test/parallel/test-vm-symbols.js30
-rw-r--r--tests/node_compat/test/parallel/test-vm-timeout-escape-promise-2.js45
-rw-r--r--tests/node_compat/test/parallel/test-vm-timeout-escape-promise.js46
-rw-r--r--tests/node_compat/test/parallel/test-vm-timeout.js88
-rw-r--r--tests/unit_node/vm_test.ts2
54 files changed, 3597 insertions, 254 deletions
diff --git a/ext/node/lib.rs b/ext/node/lib.rs
index a4a757996..00070fae9 100644
--- a/ext/node/lib.rs
+++ b/ext/node/lib.rs
@@ -303,8 +303,10 @@ deno_core::extension!(deno_node,
ops::vm::op_vm_create_script,
ops::vm::op_vm_create_context,
ops::vm::op_vm_script_run_in_context,
- ops::vm::op_vm_script_run_in_this_context,
ops::vm::op_vm_is_context,
+ ops::vm::op_vm_compile_function,
+ ops::vm::op_vm_script_get_source_map_url,
+ ops::vm::op_vm_script_create_cached_data,
ops::idna::op_node_idna_domain_to_ascii,
ops::idna::op_node_idna_domain_to_unicode,
ops::idna::op_node_idna_punycode_to_ascii,
@@ -620,7 +622,7 @@ deno_core::extension!(deno_node,
"node:util" = "util.ts",
"node:util/types" = "util/types.ts",
"node:v8" = "v8.ts",
- "node:vm" = "vm.ts",
+ "node:vm" = "vm.js",
"node:worker_threads" = "worker_threads.ts",
"node:zlib" = "zlib.ts",
],
@@ -643,6 +645,11 @@ deno_core::extension!(deno_node,
customizer = |ext: &mut deno_core::Extension| {
let mut external_references = Vec::with_capacity(14);
+ vm::QUERY_MAP_FN.with(|query| {
+ external_references.push(ExternalReference {
+ named_query: *query,
+ });
+ });
vm::GETTER_MAP_FN.with(|getter| {
external_references.push(ExternalReference {
named_getter: *getter,
@@ -653,6 +660,11 @@ deno_core::extension!(deno_node,
named_setter: *setter,
});
});
+ vm::DESCRIPTOR_MAP_FN.with(|descriptor| {
+ external_references.push(ExternalReference {
+ named_getter: *descriptor,
+ });
+ });
vm::DELETER_MAP_FN.with(|deleter| {
external_references.push(ExternalReference {
named_deleter: *deleter,
@@ -668,12 +680,12 @@ deno_core::extension!(deno_node,
named_definer: *definer,
});
});
- vm::DESCRIPTOR_MAP_FN.with(|descriptor| {
+
+ vm::INDEXED_QUERY_MAP_FN.with(|query| {
external_references.push(ExternalReference {
- named_getter: *descriptor,
+ indexed_query: *query,
});
});
-
vm::INDEXED_GETTER_MAP_FN.with(|getter| {
external_references.push(ExternalReference {
indexed_getter: *getter,
@@ -684,6 +696,11 @@ deno_core::extension!(deno_node,
indexed_setter: *setter,
});
});
+ vm::INDEXED_DESCRIPTOR_MAP_FN.with(|descriptor| {
+ external_references.push(ExternalReference {
+ indexed_getter: *descriptor,
+ });
+ });
vm::INDEXED_DELETER_MAP_FN.with(|deleter| {
external_references.push(ExternalReference {
indexed_deleter: *deleter,
@@ -694,9 +711,9 @@ deno_core::extension!(deno_node,
indexed_definer: *definer,
});
});
- vm::INDEXED_DESCRIPTOR_MAP_FN.with(|descriptor| {
+ vm::INDEXED_ENUMERATOR_MAP_FN.with(|enumerator| {
external_references.push(ExternalReference {
- indexed_getter: *descriptor,
+ enumerator: *enumerator,
});
});
diff --git a/ext/node/ops/mod.rs b/ext/node/ops/mod.rs
index b51e23ac8..d11cc7461 100644
--- a/ext/node/ops/mod.rs
+++ b/ext/node/ops/mod.rs
@@ -14,7 +14,6 @@ pub mod require;
pub mod util;
pub mod v8;
pub mod vm;
-mod vm_internal;
pub mod winerror;
pub mod worker_threads;
pub mod zlib;
diff --git a/ext/node/ops/vm.rs b/ext/node/ops/vm.rs
index d9a16eeff..e75e05651 100644
--- a/ext/node/ops/vm.rs
+++ b/ext/node/ops/vm.rs
@@ -1,122 +1,1104 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-use deno_core::error::type_error;
-use deno_core::error::AnyError;
+use crate::create_host_defined_options;
use deno_core::op2;
+use deno_core::serde_v8;
use deno_core::v8;
+use deno_core::v8::MapFnTo;
+use deno_core::JsBuffer;
+use std::sync::atomic::AtomicBool;
+use std::sync::atomic::Ordering;
+use std::time::Duration;
-use super::vm_internal as i;
+pub const PRIVATE_SYMBOL_NAME: v8::OneByteConst =
+ v8::String::create_external_onebyte_const(b"node:contextify:context");
-pub use i::create_v8_context;
-pub use i::init_global_template;
-pub use i::ContextInitMode;
-pub use i::VM_CONTEXT_INDEX;
+/// An unbounded script that can be run in a context.
+pub struct ContextifyScript {
+ script: v8::TracedReference<v8::UnboundScript>,
+}
-pub use i::DEFINER_MAP_FN;
-pub use i::DELETER_MAP_FN;
-pub use i::DESCRIPTOR_MAP_FN;
-pub use i::ENUMERATOR_MAP_FN;
-pub use i::GETTER_MAP_FN;
-pub use i::SETTER_MAP_FN;
+impl v8::cppgc::GarbageCollected for ContextifyScript {
+ fn trace(&self, visitor: &v8::cppgc::Visitor) {
+ visitor.trace(&self.script);
+ }
+}
-pub use i::INDEXED_DEFINER_MAP_FN;
-pub use i::INDEXED_DELETER_MAP_FN;
-pub use i::INDEXED_DESCRIPTOR_MAP_FN;
-pub use i::INDEXED_GETTER_MAP_FN;
-pub use i::INDEXED_SETTER_MAP_FN;
+impl ContextifyScript {
+ #[allow(clippy::too_many_arguments)]
+ fn create<'s>(
+ scope: &mut v8::HandleScope<'s>,
+ source: v8::Local<'s, v8::String>,
+ filename: v8::Local<'s, v8::Value>,
+ line_offset: i32,
+ column_offset: i32,
+ cached_data: Option<JsBuffer>,
+ produce_cached_data: bool,
+ parsing_context: Option<v8::Local<'s, v8::Object>>,
+ ) -> Option<CompileResult<'s>> {
+ let context = if let Some(parsing_context) = parsing_context {
+ let Some(context) =
+ ContextifyContext::from_sandbox_obj(scope, parsing_context)
+ else {
+ let message = v8::String::new_external_onebyte_static(
+ scope,
+ b"Invalid sandbox object",
+ )
+ .unwrap();
+ let exception = v8::Exception::type_error(scope, message);
+ scope.throw_exception(exception);
+ return None;
+ };
+ context.context(scope)
+ } else {
+ scope.get_current_context()
+ };
-pub struct Script {
- inner: i::ContextifyScript,
-}
+ let scope = &mut v8::ContextScope::new(scope, context);
+ let host_defined_options = create_host_defined_options(scope);
+ let origin = v8::ScriptOrigin::new(
+ scope,
+ filename,
+ line_offset,
+ column_offset,
+ true,
+ -1,
+ None,
+ false,
+ false,
+ false,
+ Some(host_defined_options),
+ );
-impl deno_core::GarbageCollected for Script {}
+ let mut source = if let Some(cached_data) = cached_data {
+ let cached_data = v8::script_compiler::CachedData::new(&cached_data);
+ v8::script_compiler::Source::new_with_cached_data(
+ source,
+ Some(&origin),
+ cached_data,
+ )
+ } else {
+ v8::script_compiler::Source::new(source, Some(&origin))
+ };
-impl Script {
- fn new(
- scope: &mut v8::HandleScope,
- source: v8::Local<v8::String>,
- ) -> Result<Self, AnyError> {
- Ok(Self {
- inner: i::ContextifyScript::new(scope, source)?,
+ let options = if source.get_cached_data().is_some() {
+ v8::script_compiler::CompileOptions::ConsumeCodeCache
+ } else {
+ v8::script_compiler::CompileOptions::NoCompileOptions
+ };
+
+ let scope = &mut v8::TryCatch::new(scope);
+
+ let Some(unbound_script) = v8::script_compiler::compile_unbound_script(
+ scope,
+ &mut source,
+ options,
+ v8::script_compiler::NoCacheReason::NoReason,
+ ) else {
+ if !scope.has_terminated() {
+ scope.rethrow();
+ }
+ return None;
+ };
+
+ let cached_data = if produce_cached_data {
+ unbound_script.create_code_cache()
+ } else {
+ None
+ };
+
+ let script = v8::TracedReference::new(scope, unbound_script);
+ let this = deno_core::cppgc::make_cppgc_object(scope, Self { script });
+
+ Some(CompileResult {
+ value: serde_v8::Value {
+ v8_value: this.into(),
+ },
+ cached_data: cached_data.as_ref().map(|c| {
+ let backing_store =
+ v8::ArrayBuffer::new_backing_store_from_vec(c.to_vec());
+ v8::ArrayBuffer::with_backing_store(scope, &backing_store.make_shared())
+ .into()
+ }),
+ cached_data_rejected: source
+ .get_cached_data()
+ .map(|c| c.rejected())
+ .unwrap_or(false),
+ cached_data_produced: cached_data.is_some(),
})
}
- fn run_in_this_context<'s>(
+ fn run_in_context<'s>(
&self,
- scope: &'s mut v8::HandleScope,
- ) -> Result<v8::Local<'s, v8::Value>, AnyError> {
- let context = scope.get_current_context();
+ scope: &mut v8::HandleScope<'s>,
+ sandbox: Option<v8::Local<'s, v8::Object>>,
+ timeout: i64,
+ display_errors: bool,
+ break_on_sigint: bool,
+ ) -> Option<v8::Local<'s, v8::Value>> {
+ let (context, microtask_queue) = if let Some(sandbox) = sandbox {
+ let Some(context) = ContextifyContext::from_sandbox_obj(scope, sandbox)
+ else {
+ let message = v8::String::new_external_onebyte_static(
+ scope,
+ b"Invalid sandbox object",
+ )
+ .unwrap();
+ let exception = v8::Exception::type_error(scope, message);
+ scope.throw_exception(exception);
+ return None;
+ };
+ (context.context(scope), context.microtask_queue())
+ } else {
+ (scope.get_current_context(), None)
+ };
+ self.eval_machine(
+ scope,
+ context,
+ timeout,
+ display_errors,
+ break_on_sigint,
+ microtask_queue,
+ )
+ }
+
+ pub fn eval_machine<'s>(
+ &self,
+ scope: &mut v8::HandleScope<'s>,
+ context: v8::Local<v8::Context>,
+ timeout: i64,
+ _display_errors: bool,
+ _break_on_sigint: bool,
+ microtask_queue: Option<&v8::MicrotaskQueue>,
+ ) -> Option<v8::Local<'s, v8::Value>> {
let context_scope = &mut v8::ContextScope::new(scope, context);
- let mut scope = v8::EscapableHandleScope::new(context_scope);
- let result = self
- .inner
- .eval_machine(&mut scope, context)
- .unwrap_or_else(|| v8::undefined(&mut scope).into());
- Ok(scope.escape(result))
+ let scope = &mut v8::EscapableHandleScope::new(context_scope);
+ let scope = &mut v8::TryCatch::new(scope);
+
+ let unbound_script = self.script.get(scope).unwrap();
+ let script = unbound_script.bind_to_current_context(scope);
+
+ let handle = scope.thread_safe_handle();
+
+ let mut run = || {
+ let r = script.run(scope);
+ if r.is_some() {
+ if let Some(mtask_queue) = microtask_queue {
+ mtask_queue.perform_checkpoint(scope);
+ }
+ }
+ r
+ };
+
+ #[allow(clippy::disallowed_types)]
+ let timed_out = std::sync::Arc::new(AtomicBool::new(false));
+ let result = if timeout != -1 {
+ let timed_out = timed_out.clone();
+ let (tx, rx) = std::sync::mpsc::channel();
+ deno_core::unsync::spawn_blocking(move || {
+ if rx
+ .recv_timeout(Duration::from_millis(timeout as _))
+ .is_err()
+ {
+ timed_out.store(true, Ordering::Relaxed);
+ handle.terminate_execution();
+ }
+ });
+ let r = run();
+ let _ = tx.send(());
+ r
+ } else {
+ run()
+ };
+
+ if timed_out.load(Ordering::Relaxed) {
+ if scope.has_terminated() {
+ scope.cancel_terminate_execution();
+ }
+ let message = v8::String::new(
+ scope,
+ &format!("Script execution timed out after {timeout}ms"),
+ )
+ .unwrap();
+ let exception = v8::Exception::error(scope, message);
+ let code_str =
+ v8::String::new_external_onebyte_static(scope, b"code").unwrap();
+ let code = v8::String::new_external_onebyte_static(
+ scope,
+ b"ERR_SCRIPT_EXECUTION_TIMEOUT",
+ )
+ .unwrap();
+ exception
+ .cast::<v8::Object>()
+ .set(scope, code_str.into(), code.into());
+ scope.throw_exception(exception);
+ }
+
+ if scope.has_caught() {
+ // If there was an exception thrown during script execution, re-throw it.
+ if !scope.has_terminated() {
+ scope.rethrow();
+ }
+
+ return None;
+ }
+
+ Some(scope.escape(result?))
}
+}
- fn run_in_context<'s>(
+pub struct ContextifyContext {
+ microtask_queue: *mut v8::MicrotaskQueue,
+ context: v8::TracedReference<v8::Context>,
+ sandbox: v8::TracedReference<v8::Object>,
+}
+
+impl deno_core::GarbageCollected for ContextifyContext {
+ fn trace(&self, visitor: &v8::cppgc::Visitor) {
+ visitor.trace(&self.context);
+ visitor.trace(&self.sandbox);
+ }
+}
+
+impl Drop for ContextifyContext {
+ fn drop(&mut self) {
+ if !self.microtask_queue.is_null() {
+ // SAFETY: If this isn't null, it is a valid MicrotaskQueue.
+ unsafe {
+ std::ptr::drop_in_place(self.microtask_queue);
+ }
+ }
+ }
+}
+
+struct AllowCodeGenWasm(bool);
+
+extern "C" fn allow_wasm_code_gen(
+ context: v8::Local<v8::Context>,
+ _source: v8::Local<v8::String>,
+) -> bool {
+ match context.get_slot::<AllowCodeGenWasm>() {
+ Some(b) => b.0,
+ None => true,
+ }
+}
+
+impl ContextifyContext {
+ pub fn attach(
+ scope: &mut v8::HandleScope,
+ sandbox_obj: v8::Local<v8::Object>,
+ _name: String,
+ _origin: String,
+ allow_code_gen_strings: bool,
+ allow_code_gen_wasm: bool,
+ own_microtask_queue: bool,
+ ) {
+ let main_context = scope.get_current_context();
+
+ let tmp = init_global_template(scope, ContextInitMode::UseSnapshot);
+
+ let microtask_queue = if own_microtask_queue {
+ v8::MicrotaskQueue::new(scope, v8::MicrotasksPolicy::Explicit).into_raw()
+ } else {
+ std::ptr::null_mut()
+ };
+
+ let context = create_v8_context(
+ scope,
+ tmp,
+ ContextInitMode::UseSnapshot,
+ microtask_queue,
+ );
+
+ let context_state = main_context.get_aligned_pointer_from_embedder_data(
+ deno_core::CONTEXT_STATE_SLOT_INDEX,
+ );
+ let module_map = main_context
+ .get_aligned_pointer_from_embedder_data(deno_core::MODULE_MAP_SLOT_INDEX);
+
+ context.set_security_token(main_context.get_security_token(scope));
+ // SAFETY: set embedder data from the creation context
+ unsafe {
+ context.set_aligned_pointer_in_embedder_data(
+ deno_core::CONTEXT_STATE_SLOT_INDEX,
+ context_state,
+ );
+ context.set_aligned_pointer_in_embedder_data(
+ deno_core::MODULE_MAP_SLOT_INDEX,
+ module_map,
+ );
+ }
+
+ scope.set_allow_wasm_code_generation_callback(allow_wasm_code_gen);
+ context.set_allow_generation_from_strings(allow_code_gen_strings);
+ context.set_slot(AllowCodeGenWasm(allow_code_gen_wasm));
+
+ let wrapper = {
+ let context = v8::TracedReference::new(scope, context);
+ let sandbox = v8::TracedReference::new(scope, sandbox_obj);
+ deno_core::cppgc::make_cppgc_object(
+ scope,
+ Self {
+ context,
+ sandbox,
+ microtask_queue,
+ },
+ )
+ };
+ let ptr =
+ deno_core::cppgc::try_unwrap_cppgc_object::<Self>(scope, wrapper.into());
+
+ // SAFETY: We are storing a pointer to the ContextifyContext
+ // in the embedder data of the v8::Context. The contextified wrapper
+ // lives longer than the execution context, so this should be safe.
+ unsafe {
+ context.set_aligned_pointer_in_embedder_data(
+ 3,
+ &*ptr.unwrap() as *const ContextifyContext as _,
+ );
+ }
+
+ let private_str =
+ v8::String::new_from_onebyte_const(scope, &PRIVATE_SYMBOL_NAME);
+ let private_symbol = v8::Private::for_api(scope, private_str);
+
+ sandbox_obj.set_private(scope, private_symbol, wrapper.into());
+ }
+
+ pub fn from_sandbox_obj<'a>(
+ scope: &mut v8::HandleScope<'a>,
+ sandbox_obj: v8::Local<v8::Object>,
+ ) -> Option<&'a Self> {
+ let private_str =
+ v8::String::new_from_onebyte_const(scope, &PRIVATE_SYMBOL_NAME);
+ let private_symbol = v8::Private::for_api(scope, private_str);
+
+ sandbox_obj
+ .get_private(scope, private_symbol)
+ .and_then(|wrapper| {
+ deno_core::cppgc::try_unwrap_cppgc_object::<Self>(scope, wrapper)
+ // SAFETY: the lifetime of the scope does not actually bind to
+ // the lifetime of this reference at all, but the object we read
+ // it from does, so it will be alive at least that long.
+ .map(|r| unsafe { &*(&*r as *const _) })
+ })
+ }
+
+ pub fn is_contextify_context(
+ scope: &mut v8::HandleScope,
+ object: v8::Local<v8::Object>,
+ ) -> bool {
+ Self::from_sandbox_obj(scope, object).is_some()
+ }
+
+ pub fn context<'a>(
+ &self,
+ scope: &mut v8::HandleScope<'a>,
+ ) -> v8::Local<'a, v8::Context> {
+ self.context.get(scope).unwrap()
+ }
+
+ fn global_proxy<'s>(
&self,
scope: &mut v8::HandleScope<'s>,
- sandbox: v8::Local<'s, v8::Value>,
- ) -> Result<v8::Local<'s, v8::Value>, AnyError> {
- let context = if let Ok(sandbox_obj) = sandbox.try_into() {
- let context = i::ContextifyContext::from_sandbox_obj(scope, sandbox_obj)
- .ok_or_else(|| type_error("Invalid sandbox object"))?;
- context.context(scope)
+ ) -> v8::Local<'s, v8::Object> {
+ let ctx = self.context(scope);
+ ctx.global(scope)
+ }
+
+ fn sandbox<'a>(
+ &self,
+ scope: &mut v8::HandleScope<'a>,
+ ) -> v8::Local<'a, v8::Object> {
+ self.sandbox.get(scope).unwrap()
+ }
+
+ fn microtask_queue(&self) -> Option<&v8::MicrotaskQueue> {
+ if self.microtask_queue.is_null() {
+ None
} else {
- scope.get_current_context()
+ // SAFETY: If this isn't null, it is a valid MicrotaskQueue.
+ Some(unsafe { &*self.microtask_queue })
+ }
+ }
+
+ fn get<'a, 'c>(
+ scope: &mut v8::HandleScope<'a>,
+ object: v8::Local<'a, v8::Object>,
+ ) -> Option<&'c ContextifyContext> {
+ let context = object.get_creation_context(scope)?;
+
+ let context_ptr = context.get_aligned_pointer_from_embedder_data(3);
+ if context_ptr.is_null() {
+ return None;
+ }
+ // SAFETY: We are storing a pointer to the ContextifyContext
+ // in the embedder data of the v8::Context during creation.
+ Some(unsafe { &*(context_ptr as *const ContextifyContext) })
+ }
+}
+
+pub const VM_CONTEXT_INDEX: usize = 0;
+
+#[derive(PartialEq)]
+pub enum ContextInitMode {
+ ForSnapshot,
+ UseSnapshot,
+}
+
+pub fn create_v8_context<'a>(
+ scope: &mut v8::HandleScope<'a, ()>,
+ object_template: v8::Local<v8::ObjectTemplate>,
+ mode: ContextInitMode,
+ microtask_queue: *mut v8::MicrotaskQueue,
+) -> v8::Local<'a, v8::Context> {
+ let scope = &mut v8::EscapableHandleScope::new(scope);
+
+ let context = if mode == ContextInitMode::UseSnapshot {
+ v8::Context::from_snapshot(
+ scope,
+ VM_CONTEXT_INDEX,
+ v8::ContextOptions {
+ microtask_queue: Some(microtask_queue),
+ ..Default::default()
+ },
+ )
+ .unwrap()
+ } else {
+ let ctx = v8::Context::new(
+ scope,
+ v8::ContextOptions {
+ global_template: Some(object_template),
+ microtask_queue: Some(microtask_queue),
+ ..Default::default()
+ },
+ );
+ // SAFETY: ContextifyContexts will update this to a pointer to the native object
+ unsafe {
+ ctx.set_aligned_pointer_in_embedder_data(1, std::ptr::null_mut());
+ ctx.set_aligned_pointer_in_embedder_data(2, std::ptr::null_mut());
+ ctx.set_aligned_pointer_in_embedder_data(3, std::ptr::null_mut());
+ ctx.clear_all_slots();
};
+ ctx
+ };
- let context_scope = &mut v8::ContextScope::new(scope, context);
- let mut scope = v8::EscapableHandleScope::new(context_scope);
- let result = self
- .inner
- .eval_machine(&mut scope, context)
- .unwrap_or_else(|| v8::undefined(&mut scope).into());
- Ok(scope.escape(result))
+ scope.escape(context)
+}
+
+#[derive(Debug, Clone)]
+struct SlotContextifyGlobalTemplate(v8::Global<v8::ObjectTemplate>);
+
+pub fn init_global_template<'a>(
+ scope: &mut v8::HandleScope<'a, ()>,
+ mode: ContextInitMode,
+) -> v8::Local<'a, v8::ObjectTemplate> {
+ let maybe_object_template_slot =
+ scope.get_slot::<SlotContextifyGlobalTemplate>();
+
+ if maybe_object_template_slot.is_none() {
+ let global_object_template = init_global_template_inner(scope);
+
+ if mode == ContextInitMode::UseSnapshot {
+ let contextify_global_template_slot = SlotContextifyGlobalTemplate(
+ v8::Global::new(scope, global_object_template),
+ );
+ scope.set_slot(contextify_global_template_slot);
+ }
+ global_object_template
+ } else {
+ let object_template_slot = maybe_object_template_slot
+ .expect("ContextifyGlobalTemplate slot should be already populated.")
+ .clone();
+ v8::Local::new(scope, object_template_slot.0)
}
}
+// Using thread_local! to get around compiler bug.
+//
+// See NOTE in ext/node/global.rs#L12
+thread_local! {
+ pub static QUERY_MAP_FN: v8::NamedPropertyQueryCallback<'static> = property_query.map_fn_to();
+ pub static GETTER_MAP_FN: v8::NamedPropertyGetterCallback<'static> = property_getter.map_fn_to();
+ pub static SETTER_MAP_FN: v8::NamedPropertySetterCallback<'static> = property_setter.map_fn_to();
+ pub static DELETER_MAP_FN: v8::NamedPropertyDeleterCallback<'static> = property_deleter.map_fn_to();
+ pub static ENUMERATOR_MAP_FN: v8::NamedPropertyEnumeratorCallback<'static> = property_enumerator.map_fn_to();
+ pub static DEFINER_MAP_FN: v8::NamedPropertyDefinerCallback<'static> = property_definer.map_fn_to();
+ pub static DESCRIPTOR_MAP_FN: v8::NamedPropertyDescriptorCallback<'static> = property_descriptor.map_fn_to();
+}
+
+thread_local! {
+ pub static INDEXED_GETTER_MAP_FN: v8::IndexedPropertyGetterCallback<'static> = indexed_property_getter.map_fn_to();
+ pub static INDEXED_SETTER_MAP_FN: v8::IndexedPropertySetterCallback<'static> = indexed_property_setter.map_fn_to();
+ pub static INDEXED_DELETER_MAP_FN: v8::IndexedPropertyDeleterCallback<'static> = indexed_property_deleter.map_fn_to();
+ pub static INDEXED_DEFINER_MAP_FN: v8::IndexedPropertyDefinerCallback<'static> = indexed_property_definer.map_fn_to();
+ pub static INDEXED_DESCRIPTOR_MAP_FN: v8::IndexedPropertyDescriptorCallback<'static> = indexed_property_descriptor.map_fn_to();
+ pub static INDEXED_ENUMERATOR_MAP_FN: v8::IndexedPropertyEnumeratorCallback<'static> = indexed_property_enumerator.map_fn_to();
+ pub static INDEXED_QUERY_MAP_FN: v8::IndexedPropertyQueryCallback<'static> = indexed_property_query.map_fn_to();
+}
+
+pub fn init_global_template_inner<'a>(
+ scope: &mut v8::HandleScope<'a, ()>,
+) -> v8::Local<'a, v8::ObjectTemplate> {
+ let global_object_template = v8::ObjectTemplate::new(scope);
+ global_object_template.set_internal_field_count(3);
+
+ let named_property_handler_config = {
+ let mut config = v8::NamedPropertyHandlerConfiguration::new()
+ .flags(v8::PropertyHandlerFlags::HAS_NO_SIDE_EFFECT);
+
+ config = GETTER_MAP_FN.with(|getter| config.getter_raw(*getter));
+ config = SETTER_MAP_FN.with(|setter| config.setter_raw(*setter));
+ config = QUERY_MAP_FN.with(|query| config.query_raw(*query));
+ config = DELETER_MAP_FN.with(|deleter| config.deleter_raw(*deleter));
+ config =
+ ENUMERATOR_MAP_FN.with(|enumerator| config.enumerator_raw(*enumerator));
+ config = DEFINER_MAP_FN.with(|definer| config.definer_raw(*definer));
+ config =
+ DESCRIPTOR_MAP_FN.with(|descriptor| config.descriptor_raw(*descriptor));
+
+ config
+ };
+
+ let indexed_property_handler_config = {
+ let mut config = v8::IndexedPropertyHandlerConfiguration::new()
+ .flags(v8::PropertyHandlerFlags::HAS_NO_SIDE_EFFECT);
+
+ config = INDEXED_GETTER_MAP_FN.with(|getter| config.getter_raw(*getter));
+ config = INDEXED_SETTER_MAP_FN.with(|setter| config.setter_raw(*setter));
+ config = INDEXED_QUERY_MAP_FN.with(|query| config.query_raw(*query));
+ config =
+ INDEXED_DELETER_MAP_FN.with(|deleter| config.deleter_raw(*deleter));
+ config = INDEXED_ENUMERATOR_MAP_FN
+ .with(|enumerator| config.enumerator_raw(*enumerator));
+ config =
+ INDEXED_DEFINER_MAP_FN.with(|definer| config.definer_raw(*definer));
+ config = INDEXED_DESCRIPTOR_MAP_FN
+ .with(|descriptor| config.descriptor_raw(*descriptor));
+
+ config
+ };
+
+ global_object_template
+ .set_named_property_handler(named_property_handler_config);
+ global_object_template
+ .set_indexed_property_handler(indexed_property_handler_config);
+
+ global_object_template
+}
+
+fn property_query<'s>(
+ scope: &mut v8::HandleScope<'s>,
+ property: v8::Local<'s, v8::Name>,
+ args: v8::PropertyCallbackArguments<'s>,
+ mut rv: v8::ReturnValue<v8::Integer>,
+) -> v8::Intercepted {
+ let Some(ctx) = ContextifyContext::get(scope, args.this()) else {
+ return v8::Intercepted::No;
+ };
+
+ let context = ctx.context(scope);
+ let scope = &mut v8::ContextScope::new(scope, context);
+ let sandbox = ctx.sandbox(scope);
+
+ match sandbox.has_real_named_property(scope, property) {
+ None => v8::Intercepted::No,
+ Some(true) => {
+ let Some(attr) =
+ sandbox.get_real_named_property_attributes(scope, property)
+ else {
+ return v8::Intercepted::No;
+ };
+ rv.set_uint32(attr.as_u32());
+ v8::Intercepted::Yes
+ }
+ Some(false) => {
+ match ctx
+ .global_proxy(scope)
+ .has_real_named_property(scope, property)
+ {
+ None => v8::Intercepted::No,
+ Some(true) => {
+ let Some(attr) = ctx
+ .global_proxy(scope)
+ .get_real_named_property_attributes(scope, property)
+ else {
+ return v8::Intercepted::No;
+ };
+ rv.set_uint32(attr.as_u32());
+ v8::Intercepted::Yes
+ }
+ Some(false) => v8::Intercepted::No,
+ }
+ }
+ }
+}
+
+fn property_getter<'s>(
+ scope: &mut v8::HandleScope<'s>,
+ key: v8::Local<'s, v8::Name>,
+ args: v8::PropertyCallbackArguments<'s>,
+ mut ret: v8::ReturnValue,
+) -> v8::Intercepted {
+ let Some(ctx) = ContextifyContext::get(scope, args.this()) else {
+ return v8::Intercepted::No;
+ };
+
+ let sandbox = ctx.sandbox(scope);
+
+ let tc_scope = &mut v8::TryCatch::new(scope);
+ let maybe_rv = sandbox.get_real_named_property(tc_scope, key).or_else(|| {
+ ctx
+ .global_proxy(tc_scope)
+ .get_real_named_property(tc_scope, key)
+ });
+
+ if let Some(mut rv) = maybe_rv {
+ if tc_scope.has_caught() && !tc_scope.has_terminated() {
+ tc_scope.rethrow();
+ }
+
+ if rv == sandbox {
+ rv = ctx.global_proxy(tc_scope).into();
+ }
+
+ ret.set(rv);
+ return v8::Intercepted::Yes;
+ }
+
+ v8::Intercepted::No
+}
+
+fn property_setter<'s>(
+ scope: &mut v8::HandleScope<'s>,
+ key: v8::Local<'s, v8::Name>,
+ value: v8::Local<'s, v8::Value>,
+ args: v8::PropertyCallbackArguments<'s>,
+ _rv: v8::ReturnValue<()>,
+) -> v8::Intercepted {
+ let Some(ctx) = ContextifyContext::get(scope, args.this()) else {
+ return v8::Intercepted::No;
+ };
+
+ let (attributes, is_declared_on_global_proxy) = match ctx
+ .global_proxy(scope)
+ .get_real_named_property_attributes(scope, key)
+ {
+ Some(attr) => (attr, true),
+ None => (v8::PropertyAttribute::NONE, false),
+ };
+ let mut read_only = attributes.is_read_only();
+
+ let (attributes, is_declared_on_sandbox) = match ctx
+ .sandbox(scope)
+ .get_real_named_property_attributes(scope, key)
+ {
+ Some(attr) => (attr, true),
+ None => (v8::PropertyAttribute::NONE, false),
+ };
+ read_only |= attributes.is_read_only();
+
+ if read_only {
+ return v8::Intercepted::No;
+ }
+
+ // true for x = 5
+ // false for this.x = 5
+ // false for Object.defineProperty(this, 'foo', ...)
+ // false for vmResult.x = 5 where vmResult = vm.runInContext();
+ let is_contextual_store = ctx.global_proxy(scope) != args.this();
+
+ // Indicator to not return before setting (undeclared) function declarations
+ // on the sandbox in strict mode, i.e. args.ShouldThrowOnError() = true.
+ // True for 'function f() {}', 'this.f = function() {}',
+ // 'var f = function()'.
+ // In effect only for 'function f() {}' because
+ // var f = function(), is_declared = true
+ // this.f = function() {}, is_contextual_store = false.
+ let is_function = value.is_function();
+
+ let is_declared = is_declared_on_global_proxy || is_declared_on_sandbox;
+ if !is_declared
+ && args.should_throw_on_error()
+ && is_contextual_store
+ && !is_function
+ {
+ return v8::Intercepted::No;
+ }
+
+ if !is_declared && key.is_symbol() {
+ return v8::Intercepted::No;
+ };
+
+ if ctx.sandbox(scope).set(scope, key.into(), value).is_none() {
+ return v8::Intercepted::No;
+ }
+
+ if is_declared_on_sandbox {
+ if let Some(desc) =
+ ctx.sandbox(scope).get_own_property_descriptor(scope, key)
+ {
+ if !desc.is_undefined() {
+ let desc_obj: v8::Local<v8::Object> = desc.try_into().unwrap();
+ // We have to specify the return value for any contextual or get/set
+ // property
+ let get_key =
+ v8::String::new_external_onebyte_static(scope, b"get").unwrap();
+ let set_key =
+ v8::String::new_external_onebyte_static(scope, b"set").unwrap();
+ if desc_obj
+ .has_own_property(scope, get_key.into())
+ .unwrap_or(false)
+ || desc_obj
+ .has_own_property(scope, set_key.into())
+ .unwrap_or(false)
+ {
+ return v8::Intercepted::Yes;
+ }
+ }
+ }
+ }
+
+ v8::Intercepted::No
+}
+
+fn property_descriptor<'s>(
+ scope: &mut v8::HandleScope<'s>,
+ key: v8::Local<'s, v8::Name>,
+ args: v8::PropertyCallbackArguments<'s>,
+ mut rv: v8::ReturnValue,
+) -> v8::Intercepted {
+ let Some(ctx) = ContextifyContext::get(scope, args.this()) else {
+ return v8::Intercepted::No;
+ };
+
+ let context = ctx.context(scope);
+ let sandbox = ctx.sandbox(scope);
+ let scope = &mut v8::ContextScope::new(scope, context);
+
+ if sandbox.has_own_property(scope, key).unwrap_or(false) {
+ if let Some(desc) = sandbox.get_own_property_descriptor(scope, key) {
+ rv.set(desc);
+ return v8::Intercepted::Yes;
+ }
+ }
+
+ v8::Intercepted::No
+}
+
+fn property_definer<'s>(
+ scope: &mut v8::HandleScope<'s>,
+ key: v8::Local<'s, v8::Name>,
+ desc: &v8::PropertyDescriptor,
+ args: v8::PropertyCallbackArguments<'s>,
+ _: v8::ReturnValue<()>,
+) -> v8::Intercepted {
+ let Some(ctx) = ContextifyContext::get(scope, args.this()) else {
+ return v8::Intercepted::No;
+ };
+
+ let context = ctx.context(scope);
+ let scope = &mut v8::ContextScope::new(scope, context);
+
+ let (attributes, is_declared) = match ctx
+ .global_proxy(scope)
+ .get_real_named_property_attributes(scope, key)
+ {
+ Some(attr) => (attr, true),
+ None => (v8::PropertyAttribute::NONE, false),
+ };
+
+ let read_only = attributes.is_read_only();
+ let dont_delete = attributes.is_dont_delete();
+
+ // If the property is set on the global as read_only, don't change it on
+ // the global or sandbox.
+ if is_declared && read_only && dont_delete {
+ return v8::Intercepted::No;
+ }
+
+ let sandbox = ctx.sandbox(scope);
+
+ let define_prop_on_sandbox =
+ |scope: &mut v8::HandleScope,
+ desc_for_sandbox: &mut v8::PropertyDescriptor| {
+ if desc.has_enumerable() {
+ desc_for_sandbox.set_enumerable(desc.enumerable());
+ }
+
+ if desc.has_configurable() {
+ desc_for_sandbox.set_configurable(desc.configurable());
+ }
+
+ sandbox.define_property(scope, key, desc_for_sandbox);
+ };
+
+ if desc.has_get() || desc.has_set() {
+ let mut desc_for_sandbox = v8::PropertyDescriptor::new_from_get_set(
+ if desc.has_get() {
+ desc.get()
+ } else {
+ v8::undefined(scope).into()
+ },
+ if desc.has_set() {
+ desc.set()
+ } else {
+ v8::undefined(scope).into()
+ },
+ );
+
+ define_prop_on_sandbox(scope, &mut desc_for_sandbox);
+ } else {
+ let value = if desc.has_value() {
+ desc.value()
+ } else {
+ v8::undefined(scope).into()
+ };
+
+ if desc.has_writable() {
+ let mut desc_for_sandbox =
+ v8::PropertyDescriptor::new_from_value_writable(value, desc.writable());
+ define_prop_on_sandbox(scope, &mut desc_for_sandbox);
+ } else {
+ let mut desc_for_sandbox = v8::PropertyDescriptor::new_from_value(value);
+ define_prop_on_sandbox(scope, &mut desc_for_sandbox);
+ }
+ }
+
+ v8::Intercepted::Yes
+}
+
+fn property_deleter<'s>(
+ scope: &mut v8::HandleScope<'s>,
+ key: v8::Local<'s, v8::Name>,
+ args: v8::PropertyCallbackArguments<'s>,
+ mut rv: v8::ReturnValue<v8::Boolean>,
+) -> v8::Intercepted {
+ let Some(ctx) = ContextifyContext::get(scope, args.this()) else {
+ return v8::Intercepted::No;
+ };
+
+ let context = ctx.context(scope);
+ let sandbox = ctx.sandbox(scope);
+ let context_scope = &mut v8::ContextScope::new(scope, context);
+ if sandbox.delete(context_scope, key.into()).unwrap_or(false) {
+ return v8::Intercepted::No;
+ }
+
+ rv.set_bool(false);
+ v8::Intercepted::Yes
+}
+
+fn property_enumerator<'s>(
+ scope: &mut v8::HandleScope<'s>,
+ args: v8::PropertyCallbackArguments<'s>,
+ mut rv: v8::ReturnValue<v8::Array>,
+) {
+ let Some(ctx) = ContextifyContext::get(scope, args.this()) else {
+ return;
+ };
+
+ let context = ctx.context(scope);
+ let sandbox = ctx.sandbox(scope);
+ let context_scope = &mut v8::ContextScope::new(scope, context);
+ let Some(properties) = sandbox
+ .get_property_names(context_scope, v8::GetPropertyNamesArgs::default())
+ else {
+ return;
+ };
+
+ rv.set(properties);
+}
+
+fn indexed_property_enumerator<'s>(
+ scope: &mut v8::HandleScope<'s>,
+ args: v8::PropertyCallbackArguments<'s>,
+ mut rv: v8::ReturnValue<v8::Array>,
+) {
+ let Some(ctx) = ContextifyContext::get(scope, args.this()) else {
+ return;
+ };
+ let context = ctx.context(scope);
+ let scope = &mut v8::ContextScope::new(scope, context);
+
+ // By default, GetPropertyNames returns string and number property names, and
+ // doesn't convert the numbers to strings.
+ let Some(properties) = ctx
+ .sandbox(scope)
+ .get_property_names(scope, v8::GetPropertyNamesArgs::default())
+ else {
+ return;
+ };
+
+ let Ok(properties_vec) =
+ serde_v8::from_v8::<Vec<serde_v8::Value>>(scope, properties.into())
+ else {
+ return;
+ };
+
+ let mut indices = vec![];
+ for prop in properties_vec {
+ if prop.v8_value.is_number() {
+ indices.push(prop.v8_value);
+ }
+ }
+
+ rv.set(v8::Array::new_with_elements(scope, &indices));
+}
+
+fn uint32_to_name<'s>(
+ scope: &mut v8::HandleScope<'s>,
+ index: u32,
+) -> v8::Local<'s, v8::Name> {
+ let int = v8::Integer::new_from_unsigned(scope, index);
+ let u32 = v8::Local::<v8::Uint32>::try_from(int).unwrap();
+ u32.to_string(scope).unwrap().into()
+}
+
+fn indexed_property_query<'s>(
+ scope: &mut v8::HandleScope<'s>,
+ index: u32,
+ args: v8::PropertyCallbackArguments<'s>,
+ rv: v8::ReturnValue<v8::Integer>,
+) -> v8::Intercepted {
+ let name = uint32_to_name(scope, index);
+ property_query(scope, name, args, rv)
+}
+
+fn indexed_property_getter<'s>(
+ scope: &mut v8::HandleScope<'s>,
+ index: u32,
+ args: v8::PropertyCallbackArguments<'s>,
+ rv: v8::ReturnValue,
+) -> v8::Intercepted {
+ let key = uint32_to_name(scope, index);
+ property_getter(scope, key, args, rv)
+}
+
+fn indexed_property_setter<'s>(
+ scope: &mut v8::HandleScope<'s>,
+ index: u32,
+ value: v8::Local<'s, v8::Value>,
+ args: v8::PropertyCallbackArguments<'s>,
+ rv: v8::ReturnValue<()>,
+) -> v8::Intercepted {
+ let key = uint32_to_name(scope, index);
+ property_setter(scope, key, value, args, rv)
+}
+
+fn indexed_property_descriptor<'s>(
+ scope: &mut v8::HandleScope<'s>,
+ index: u32,
+ args: v8::PropertyCallbackArguments<'s>,
+ rv: v8::ReturnValue,
+) -> v8::Intercepted {
+ let key = uint32_to_name(scope, index);
+ property_descriptor(scope, key, args, rv)
+}
+
+fn indexed_property_definer<'s>(
+ scope: &mut v8::HandleScope<'s>,
+ index: u32,
+ descriptor: &v8::PropertyDescriptor,
+ args: v8::PropertyCallbackArguments<'s>,
+ rv: v8::ReturnValue<()>,
+) -> v8::Intercepted {
+ let key = uint32_to_name(scope, index);
+ property_definer(scope, key, descriptor, args, rv)
+}
+
+fn indexed_property_deleter<'s>(
+ scope: &mut v8::HandleScope<'s>,
+ index: u32,
+ args: v8::PropertyCallbackArguments<'s>,
+ mut rv: v8::ReturnValue<v8::Boolean>,
+) -> v8::Intercepted {
+ let Some(ctx) = ContextifyContext::get(scope, args.this()) else {
+ return v8::Intercepted::No;
+ };
+
+ let context = ctx.context(scope);
+ let sandbox = ctx.sandbox(scope);
+ let context_scope = &mut v8::ContextScope::new(scope, context);
+ if !sandbox.delete_index(context_scope, index).unwrap_or(false) {
+ return v8::Intercepted::No;
+ }
+
+ // Delete failed on the sandbox, intercept and do not delete on
+ // the global object.
+ rv.set_bool(false);
+ v8::Intercepted::No
+}
+
+#[allow(clippy::too_many_arguments)]
#[op2]
+#[serde]
pub fn op_vm_create_script<'a>(
scope: &mut v8::HandleScope<'a>,
source: v8::Local<'a, v8::String>,
-) -> Result<v8::Local<'a, v8::Object>, AnyError> {
- let script = Script::new(scope, source)?;
- Ok(deno_core::cppgc::make_cppgc_object(scope, script))
+ filename: v8::Local<'a, v8::Value>,
+ line_offset: i32,
+ column_offset: i32,
+ #[buffer] cached_data: Option<JsBuffer>,
+ produce_cached_data: bool,
+ parsing_context: Option<v8::Local<'a, v8::Object>>,
+) -> Option<CompileResult<'a>> {
+ ContextifyScript::create(
+ scope,
+ source,
+ filename,
+ line_offset,
+ column_offset,
+ cached_data,
+ produce_cached_data,
+ parsing_context,
+ )
}
#[op2(reentrant)]
pub fn op_vm_script_run_in_context<'a>(
scope: &mut v8::HandleScope<'a>,
- #[cppgc] script: &Script,
- sandbox: v8::Local<'a, v8::Value>,
-) -> Result<v8::Local<'a, v8::Value>, AnyError> {
- script.run_in_context(scope, sandbox)
-}
-
-#[op2(reentrant)]
-pub fn op_vm_script_run_in_this_context<'a>(
- scope: &'a mut v8::HandleScope,
- #[cppgc] script: &Script,
-) -> Result<v8::Local<'a, v8::Value>, AnyError> {
- script.run_in_this_context(scope)
+ #[cppgc] script: &ContextifyScript,
+ sandbox: Option<v8::Local<'a, v8::Object>>,
+ #[serde] timeout: i64,
+ display_errors: bool,
+ break_on_sigint: bool,
+) -> Option<v8::Local<'a, v8::Value>> {
+ script.run_in_context(
+ scope,
+ sandbox,
+ timeout,
+ display_errors,
+ break_on_sigint,
+ )
}
#[op2]
pub fn op_vm_create_context(
scope: &mut v8::HandleScope,
sandbox_obj: v8::Local<v8::Object>,
+ #[string] name: String,
+ #[string] origin: String,
+ allow_code_gen_strings: bool,
+ allow_code_gen_wasm: bool,
+ own_microtask_queue: bool,
) {
// Don't allow contextifying a sandbox multiple times.
- assert!(!i::ContextifyContext::is_contextify_context(
+ assert!(!ContextifyContext::is_contextify_context(
scope,
sandbox_obj
));
- i::ContextifyContext::attach(scope, sandbox_obj);
+ ContextifyContext::attach(
+ scope,
+ sandbox_obj,
+ name,
+ origin,
+ allow_code_gen_strings,
+ allow_code_gen_wasm,
+ own_microtask_queue,
+ );
}
#[op2]
@@ -127,31 +1109,164 @@ pub fn op_vm_is_context(
sandbox_obj
.try_into()
.map(|sandbox_obj| {
- i::ContextifyContext::is_contextify_context(scope, sandbox_obj)
+ ContextifyContext::is_contextify_context(scope, sandbox_obj)
})
.unwrap_or(false)
}
-#[cfg(test)]
-mod tests {
- use super::*;
- use deno_core::v8;
+#[derive(serde::Serialize)]
+struct CompileResult<'s> {
+ value: serde_v8::Value<'s>,
+ cached_data: Option<serde_v8::Value<'s>>,
+ cached_data_rejected: bool,
+ cached_data_produced: bool,
+}
- #[test]
- fn test_run_in_this_context() {
- let platform = v8::new_default_platform(0, false).make_shared();
- deno_core::JsRuntime::init_platform(Some(platform), false);
+#[allow(clippy::too_many_arguments)]
+#[op2]
+#[serde]
+pub fn op_vm_compile_function<'s>(
+ scope: &mut v8::HandleScope<'s>,
+ source: v8::Local<'s, v8::String>,
+ filename: v8::Local<'s, v8::Value>,
+ line_offset: i32,
+ column_offset: i32,
+ #[buffer] cached_data: Option<JsBuffer>,
+ produce_cached_data: bool,
+ parsing_context: Option<v8::Local<'s, v8::Object>>,
+ context_extensions: Option<v8::Local<'s, v8::Array>>,
+ params: Option<v8::Local<'s, v8::Array>>,
+) -> Option<CompileResult<'s>> {
+ let context = if let Some(parsing_context) = parsing_context {
+ let Some(context) =
+ ContextifyContext::from_sandbox_obj(scope, parsing_context)
+ else {
+ let message = v8::String::new(scope, "Invalid sandbox object").unwrap();
+ let exception = v8::Exception::type_error(scope, message);
+ scope.throw_exception(exception);
+ return None;
+ };
+ context.context(scope)
+ } else {
+ scope.get_current_context()
+ };
- let isolate = &mut v8::Isolate::new(Default::default());
+ let scope = &mut v8::ContextScope::new(scope, context);
+ let host_defined_options = create_host_defined_options(scope);
+ let origin = v8::ScriptOrigin::new(
+ scope,
+ filename,
+ line_offset,
+ column_offset,
+ true,
+ -1,
+ None,
+ false,
+ false,
+ false,
+ Some(host_defined_options),
+ );
- let scope = &mut v8::HandleScope::new(isolate);
- let context = v8::Context::new(scope, Default::default());
- let scope = &mut v8::ContextScope::new(scope, context);
+ let mut source = if let Some(cached_data) = cached_data {
+ let cached_data = v8::script_compiler::CachedData::new(&cached_data);
+ v8::script_compiler::Source::new_with_cached_data(
+ source,
+ Some(&origin),
+ cached_data,
+ )
+ } else {
+ v8::script_compiler::Source::new(source, Some(&origin))
+ };
- let source = v8::String::new(scope, "1 + 2").unwrap();
- let script = Script::new(scope, source).unwrap();
+ let context_extensions = if let Some(context_extensions) = context_extensions
+ {
+ let mut exts = Vec::with_capacity(context_extensions.length() as _);
+ for i in 0..context_extensions.length() {
+ let ext = context_extensions.get_index(scope, i)?.try_into().ok()?;
+ exts.push(ext);
+ }
+ exts
+ } else {
+ vec![]
+ };
- let result = script.run_in_this_context(scope).unwrap();
- assert!(result.is_number());
- }
+ let params = if let Some(params) = params {
+ let mut exts = Vec::with_capacity(params.length() as _);
+ for i in 0..params.length() {
+ let ext = params.get_index(scope, i)?.try_into().ok()?;
+ exts.push(ext);
+ }
+ exts
+ } else {
+ vec![]
+ };
+
+ let options = if source.get_cached_data().is_some() {
+ v8::script_compiler::CompileOptions::ConsumeCodeCache
+ } else {
+ v8::script_compiler::CompileOptions::NoCompileOptions
+ };
+
+ let scope = &mut v8::TryCatch::new(scope);
+
+ let Some(function) = v8::script_compiler::compile_function(
+ scope,
+ &mut source,
+ &params,
+ &context_extensions,
+ options,
+ v8::script_compiler::NoCacheReason::NoReason,
+ ) else {
+ if scope.has_caught() && !scope.has_terminated() {
+ scope.rethrow();
+ }
+ return None;
+ };
+
+ let cached_data = if produce_cached_data {
+ function.create_code_cache()
+ } else {
+ None
+ };
+
+ Some(CompileResult {
+ value: serde_v8::Value {
+ v8_value: function.into(),
+ },
+ cached_data: cached_data.as_ref().map(|c| {
+ let backing_store =
+ v8::ArrayBuffer::new_backing_store_from_vec(c.to_vec());
+ v8::ArrayBuffer::with_backing_store(scope, &backing_store.make_shared())
+ .into()
+ }),
+ cached_data_rejected: source
+ .get_cached_data()
+ .map(|c| c.rejected())
+ .unwrap_or(false),
+ cached_data_produced: cached_data.is_some(),
+ })
+}
+
+#[op2]
+pub fn op_vm_script_get_source_map_url<'s>(
+ scope: &mut v8::HandleScope<'s>,
+ #[cppgc] script: &ContextifyScript,
+) -> v8::Local<'s, v8::Value> {
+ let unbound_script = script.script.get(scope).unwrap();
+ unbound_script.get_source_mapping_url(scope)
+}
+
+#[op2]
+pub fn op_vm_script_create_cached_data<'s>(
+ scope: &mut v8::HandleScope<'s>,
+ #[cppgc] script: &ContextifyScript,
+) -> v8::Local<'s, v8::Value> {
+ let unbound_script = script.script.get(scope).unwrap();
+ let data = match unbound_script.create_code_cache() {
+ Some(c) => c.to_vec(),
+ None => vec![],
+ };
+ let backing_store = v8::ArrayBuffer::new_backing_store_from_vec(data);
+ v8::ArrayBuffer::with_backing_store(scope, &backing_store.make_shared())
+ .into()
}
diff --git a/ext/node/polyfills/assert.ts b/ext/node/polyfills/assert.ts
index 00677e3f7..188c7a0c2 100644
--- a/ext/node/polyfills/assert.ts
+++ b/ext/node/polyfills/assert.ts
@@ -746,6 +746,9 @@ function validateThrownError(
message = error;
error = undefined;
}
+ if (error?.prototype !== undefined && e instanceof error) {
+ return true;
+ }
if (
typeof error === "function" &&
(error === Error || ObjectPrototypeIsPrototypeOf(Error, error))
diff --git a/ext/node/polyfills/vm.js b/ext/node/polyfills/vm.js
new file mode 100644
index 000000000..bc1a25045
--- /dev/null
+++ b/ext/node/polyfills/vm.js
@@ -0,0 +1,359 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license.
+
+import { Buffer } from "node:buffer";
+import { notImplemented } from "ext:deno_node/_utils.ts";
+import {
+ op_vm_compile_function,
+ op_vm_create_context,
+ op_vm_create_script,
+ op_vm_is_context,
+ op_vm_script_create_cached_data,
+ op_vm_script_get_source_map_url,
+ op_vm_script_run_in_context,
+} from "ext:core/ops";
+import {
+ validateArray,
+ validateBoolean,
+ validateBuffer,
+ validateInt32,
+ validateObject,
+ validateOneOf,
+ validateString,
+ validateStringArray,
+ validateUint32,
+} from "ext:deno_node/internal/validators.mjs";
+import { ERR_INVALID_ARG_TYPE } from "ext:deno_node/internal/errors.ts";
+
+import { primordials } from "ext:core/mod.js";
+
+const { Symbol, ArrayPrototypeForEach } = primordials;
+
+const kParsingContext = Symbol("script parsing context");
+
+export class Script {
+ #inner;
+
+ constructor(code, options = {}) {
+ code = `${code}`;
+ if (typeof options === "string") {
+ options = { filename: options };
+ } else {
+ validateObject(options, "options");
+ }
+
+ const {
+ filename = "evalmachine.<anonymous>",
+ lineOffset = 0,
+ columnOffset = 0,
+ cachedData,
+ produceCachedData = false,
+ // importModuleDynamically,
+ [kParsingContext]: parsingContext,
+ } = options;
+
+ validateString(filename, "options.filename");
+ validateInt32(lineOffset, "options.lineOffset");
+ validateInt32(columnOffset, "options.columnOffset");
+ if (cachedData !== undefined) {
+ validateBuffer(cachedData, "options.cachedData");
+ }
+ validateBoolean(produceCachedData, "options.produceCachedData");
+
+ // const hostDefinedOptionId =
+ // getHostDefinedOptionId(importModuleDynamically, filename);
+
+ const result = op_vm_create_script(
+ code,
+ filename,
+ lineOffset,
+ columnOffset,
+ cachedData,
+ produceCachedData,
+ parsingContext,
+ );
+ this.#inner = result.value;
+ this.cachedDataProduced = result.cached_data_produced;
+ this.cachedDataRejected = result.cached_data_rejected;
+ this.cachedData = result.cached_data
+ ? Buffer.from(result.cached_data)
+ : undefined;
+ }
+
+ #runInContext(contextifiedObject, options = {}) {
+ validateObject(options, "options");
+
+ let timeout = options.timeout;
+ if (timeout === undefined) {
+ timeout = -1;
+ } else {
+ validateUint32(timeout, "options.timeout", true);
+ }
+
+ const {
+ displayErrors = true,
+ breakOnSigint = false,
+ } = options;
+
+ validateBoolean(displayErrors, "options.displayErrors");
+ validateBoolean(breakOnSigint, "options.breakOnSigint");
+
+ //if (breakOnSigint && process.listenerCount('SIGINT') > 0) {
+ // return sigintHandlersWrap(super.runInContext, this, args);
+ //}
+
+ return op_vm_script_run_in_context(
+ this.#inner,
+ contextifiedObject,
+ timeout,
+ displayErrors,
+ breakOnSigint,
+ );
+ }
+
+ runInThisContext(options) {
+ return this.#runInContext(null, options);
+ }
+
+ runInContext(contextifiedObject, options) {
+ validateContext(contextifiedObject);
+ return this.#runInContext(contextifiedObject, options);
+ }
+
+ runInNewContext(contextObject, options) {
+ const context = createContext(contextObject, getContextOptions(options));
+ return this.runInContext(context, options);
+ }
+
+ get sourceMapURL() {
+ return op_vm_script_get_source_map_url(this.#inner);
+ }
+
+ createCachedData() {
+ return Buffer.from(op_vm_script_create_cached_data(this.#inner));
+ }
+}
+
+function validateContext(contextifiedObject) {
+ if (!isContext(contextifiedObject)) {
+ throw new ERR_INVALID_ARG_TYPE(
+ "contextifiedObject",
+ "vm.Context",
+ contextifiedObject,
+ );
+ }
+}
+
+function getContextOptions(options) {
+ if (!options) {
+ return {};
+ }
+ const contextOptions = {
+ name: options.contextName,
+ origin: options.contextOrigin,
+ codeGeneration: undefined,
+ microtaskMode: options.microtaskMode,
+ };
+ if (contextOptions.name !== undefined) {
+ validateString(contextOptions.name, "options.contextName");
+ }
+ if (contextOptions.origin !== undefined) {
+ validateString(contextOptions.origin, "options.contextOrigin");
+ }
+ if (options.contextCodeGeneration !== undefined) {
+ validateObject(
+ options.contextCodeGeneration,
+ "options.contextCodeGeneration",
+ );
+ const { strings, wasm } = options.contextCodeGeneration;
+ if (strings !== undefined) {
+ validateBoolean(strings, "options.contextCodeGeneration.strings");
+ }
+ if (wasm !== undefined) {
+ validateBoolean(wasm, "options.contextCodeGeneration.wasm");
+ }
+ contextOptions.codeGeneration = { strings, wasm };
+ }
+ if (options.microtaskMode !== undefined) {
+ validateString(options.microtaskMode, "options.microtaskMode");
+ }
+ return contextOptions;
+}
+
+let defaultContextNameIndex = 1;
+export function createContext(contextObject = {}, options = {}) {
+ if (isContext(contextObject)) {
+ return contextObject;
+ }
+
+ validateObject(options, "options");
+
+ const {
+ name = `VM Context ${defaultContextNameIndex++}`,
+ origin,
+ codeGeneration,
+ microtaskMode,
+ // importModuleDynamically,
+ } = options;
+
+ validateString(name, "options.name");
+ if (origin !== undefined) {
+ validateString(origin, "options.origin");
+ }
+ if (codeGeneration !== undefined) {
+ validateObject(codeGeneration, "options.codeGeneration");
+ }
+
+ let strings = true;
+ let wasm = true;
+ if (codeGeneration !== undefined) {
+ ({ strings = true, wasm = true } = codeGeneration);
+ validateBoolean(strings, "options.codeGeneration.strings");
+ validateBoolean(wasm, "options.codeGeneration.wasm");
+ }
+
+ validateOneOf(microtaskMode, "options.microtaskMode", [
+ "afterEvaluate",
+ undefined,
+ ]);
+ const microtaskQueue = microtaskMode === "afterEvaluate";
+
+ // const hostDefinedOptionId =
+ // getHostDefinedOptionId(importModuleDynamically, name);
+
+ op_vm_create_context(
+ contextObject,
+ name,
+ origin,
+ strings,
+ wasm,
+ microtaskQueue,
+ );
+ // Register the context scope callback after the context was initialized.
+ // registerImportModuleDynamically(contextObject, importModuleDynamically);
+ return contextObject;
+}
+
+export function createScript(code, options) {
+ return new Script(code, options);
+}
+
+export function runInContext(code, contextifiedObject, options) {
+ validateContext(contextifiedObject);
+ if (typeof options === "string") {
+ options = {
+ filename: options,
+ [kParsingContext]: contextifiedObject,
+ };
+ } else {
+ options = {
+ ...options,
+ [kParsingContext]: contextifiedObject,
+ };
+ }
+ return createScript(code, options)
+ .runInContext(contextifiedObject, options);
+}
+
+export function runInNewContext(code, contextObject, options) {
+ if (typeof options === "string") {
+ options = { filename: options };
+ }
+ contextObject = createContext(contextObject, getContextOptions(options));
+ options = { ...options, [kParsingContext]: contextObject };
+ return createScript(code, options).runInNewContext(contextObject, options);
+}
+
+export function runInThisContext(code, options) {
+ if (typeof options === "string") {
+ options = { filename: options };
+ }
+ return createScript(code, options).runInThisContext(options);
+}
+
+export function isContext(object) {
+ validateObject(object, "object", { allowArray: true });
+ return op_vm_is_context(object);
+}
+
+export function compileFunction(code, params, options = {}) {
+ validateString(code, "code");
+ if (params !== undefined) {
+ validateStringArray(params, "params");
+ }
+ const {
+ filename = "",
+ columnOffset = 0,
+ lineOffset = 0,
+ cachedData = undefined,
+ produceCachedData = false,
+ parsingContext = undefined,
+ contextExtensions = [],
+ // importModuleDynamically,
+ } = options;
+
+ validateString(filename, "options.filename");
+ validateInt32(columnOffset, "options.columnOffset");
+ validateInt32(lineOffset, "options.lineOffset");
+ if (cachedData !== undefined) {
+ validateBuffer(cachedData, "options.cachedData");
+ }
+ validateBoolean(produceCachedData, "options.produceCachedData");
+ if (parsingContext !== undefined) {
+ if (
+ typeof parsingContext !== "object" ||
+ parsingContext === null ||
+ !isContext(parsingContext)
+ ) {
+ throw new ERR_INVALID_ARG_TYPE(
+ "options.parsingContext",
+ "Context",
+ parsingContext,
+ );
+ }
+ }
+ validateArray(contextExtensions, "options.contextExtensions");
+ ArrayPrototypeForEach(contextExtensions, (extension, i) => {
+ const name = `options.contextExtensions[${i}]`;
+ validateObject(extension, name, { nullable: true });
+ });
+
+ // const hostDefinedOptionId =
+ // getHostDefinedOptionId(importModuleDynamically, filename);
+
+ const result = op_vm_compile_function(
+ code,
+ filename,
+ lineOffset,
+ columnOffset,
+ cachedData,
+ produceCachedData,
+ parsingContext,
+ contextExtensions,
+ params,
+ );
+
+ result.value.cachedDataProduced = result.cached_data_produced;
+ result.value.cachedDataRejected = result.cached_data_rejected;
+ result.value.cachedData = result.cached_data
+ ? Buffer.from(result.cached_data)
+ : undefined;
+
+ return result.value;
+}
+
+export function measureMemory(_options) {
+ notImplemented("measureMemory");
+}
+
+export default {
+ Script,
+ createContext,
+ createScript,
+ runInContext,
+ runInNewContext,
+ runInThisContext,
+ isContext,
+ compileFunction,
+ measureMemory,
+};
diff --git a/ext/node/polyfills/vm.ts b/ext/node/polyfills/vm.ts
deleted file mode 100644
index 3378e3886..000000000
--- a/ext/node/polyfills/vm.ts
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-
-// deno-lint-ignore-file no-explicit-any
-
-import { notImplemented } from "ext:deno_node/_utils.ts";
-import {
- op_vm_create_context,
- op_vm_create_script,
- op_vm_is_context,
- op_vm_script_run_in_context,
- op_vm_script_run_in_this_context,
-} from "ext:core/ops";
-
-export class Script {
- #inner;
-
- constructor(code: string, _options = {}) {
- this.#inner = op_vm_create_script(code);
- }
-
- runInThisContext(_options: any) {
- return op_vm_script_run_in_this_context(this.#inner);
- }
-
- runInContext(contextifiedObject: any, _options: any) {
- return op_vm_script_run_in_context(this.#inner, contextifiedObject);
- }
-
- runInNewContext(contextObject: any, options: any) {
- const context = createContext(contextObject);
- return this.runInContext(context, options);
- }
-
- createCachedData() {
- notImplemented("Script.prototype.createCachedData");
- }
-}
-
-export function createContext(contextObject: any = {}, _options: any) {
- if (isContext(contextObject)) {
- return contextObject;
- }
-
- op_vm_create_context(contextObject);
- return contextObject;
-}
-
-export function createScript(code: string, options: any) {
- return new Script(code, options);
-}
-
-export function runInContext(
- code: string,
- contextifiedObject: any,
- _options: any,
-) {
- return createScript(code).runInContext(contextifiedObject);
-}
-
-export function runInNewContext(
- code: string,
- contextObject: any,
- options: any,
-) {
- if (options) {
- console.warn("vm.runInNewContext options are currently not supported");
- }
- return createScript(code).runInNewContext(contextObject);
-}
-
-export function runInThisContext(
- code: string,
- options: any,
-) {
- return createScript(code, options).runInThisContext(options);
-}
-
-export function isContext(maybeContext: any) {
- return op_vm_is_context(maybeContext);
-}
-
-export function compileFunction(_code: string, _params: any, _options: any) {
- notImplemented("compileFunction");
-}
-
-export function measureMemory(_options: any) {
- notImplemented("measureMemory");
-}
-
-export default {
- Script,
- createContext,
- createScript,
- runInContext,
- runInNewContext,
- runInThisContext,
- isContext,
- compileFunction,
- measureMemory,
-};
diff --git a/runtime/snapshot.rs b/runtime/snapshot.rs
index da66bff5e..659356ae6 100644
--- a/runtime/snapshot.rs
+++ b/runtime/snapshot.rs
@@ -293,6 +293,7 @@ pub fn create_runtime_snapshot(
scope,
tmpl,
deno_node::ContextInitMode::ForSnapshot,
+ std::ptr::null_mut(),
);
assert_eq!(scope.add_context(ctx), deno_node::VM_CONTEXT_INDEX);
})),
diff --git a/tests/node_compat/config.jsonc b/tests/node_compat/config.jsonc
index d4953075a..72d872812 100644
--- a/tests/node_compat/config.jsonc
+++ b/tests/node_compat/config.jsonc
@@ -669,8 +669,50 @@
"test-util-types-exists.js",
"test-util-types.js",
"test-util.js",
+ "test-vm-access-process-env.js",
+ "test-vm-attributes-property-not-on-sandbox.js",
+ "test-vm-codegen.js",
+ "test-vm-context-async-script.js",
+ "test-vm-context-property-forwarding.js",
+ "test-vm-create-and-run-in-context.js",
+ "test-vm-create-context-accessors.js",
+ "test-vm-create-context-arg.js",
+ "test-vm-create-context-circular-reference.js",
+ "test-vm-createcacheddata.js",
+ "test-vm-cross-context.js",
+ "test-vm-data-property-writable.js",
+ "test-vm-deleting-property.js",
+ "test-vm-function-declaration.js",
+ "test-vm-function-redefinition.js",
+ "test-vm-getters.js",
+ "test-vm-global-assignment.js",
+ "test-vm-global-define-property.js",
+ "test-vm-global-identity.js",
+ "test-vm-global-setter.js",
+ "test-vm-harmony-symbols.js",
+ "test-vm-indexed-properties.js",
+ "test-vm-inherited_properties.js",
+ "test-vm-is-context.js",
+ "test-vm-low-stack-space.js",
+ "test-vm-new-script-new-context.js",
"test-vm-new-script-this-context.js",
+ "test-vm-not-strict.js",
+ "test-vm-options-validation.js",
+ "test-vm-parse-abort-on-uncaught-exception.js",
+ "test-vm-preserves-property.js",
+ "test-vm-property-not-on-sandbox.js",
+ "test-vm-proxies.js",
+ "test-vm-proxy-failure-CP.js",
+ "test-vm-script-throw-in-tostring.js",
+ "test-vm-set-property-proxy.js",
+ "test-vm-set-proto-null-on-globalthis.js",
+ "test-vm-source-map-url.js",
"test-vm-static-this.js",
+ "test-vm-strict-mode.js",
+ "test-vm-symbols.js",
+ "test-vm-timeout-escape-promise-2.js",
+ "test-vm-timeout-escape-promise.js",
+ "test-vm-timeout.js",
"test-webcrypto-sign-verify.js",
"test-whatwg-encoding-custom-api-basics.js",
"test-whatwg-encoding-custom-textdecoder-ignorebom.js",
@@ -716,7 +758,9 @@
"test-tty-stdout-end.js"
],
"pummel": [],
- "sequential": ["test-child-process-exit.js"]
+ "sequential": [
+ "test-child-process-exit.js"
+ ]
},
"windowsIgnore": {
"parallel": [
@@ -744,7 +788,8 @@
"test-net-server-listen-path.js",
"test-net-socket-close-after-end.js",
"test-util-inspect-long-running.js",
- "test-util-inspect.js"
+ "test-util-inspect.js",
+ "test-vm-low-stack-space.js"
]
},
"darwinIgnore": {
diff --git a/tests/node_compat/runner/TODO.md b/tests/node_compat/runner/TODO.md
index e49100981..1656729e2 100644
--- a/tests/node_compat/runner/TODO.md
+++ b/tests/node_compat/runner/TODO.md
@@ -2622,39 +2622,14 @@ NOTE: This file should not be manually edited. Please edit `tests/node_compat/co
- [parallel/test-v8-version-tag.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-v8-version-tag.js)
- [parallel/test-validators.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-validators.js)
- [parallel/test-vfs.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vfs.js)
-- [parallel/test-vm-access-process-env.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-access-process-env.js)
- [parallel/test-vm-api-handles-getter-errors.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-api-handles-getter-errors.js)
-- [parallel/test-vm-attributes-property-not-on-sandbox.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-attributes-property-not-on-sandbox.js)
- [parallel/test-vm-basic.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-basic.js)
- [parallel/test-vm-cached-data.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-cached-data.js)
-- [parallel/test-vm-codegen.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-codegen.js)
-- [parallel/test-vm-context-async-script.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-context-async-script.js)
-- [parallel/test-vm-context-property-forwarding.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-context-property-forwarding.js)
- [parallel/test-vm-context.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-context.js)
-- [parallel/test-vm-create-and-run-in-context.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-create-and-run-in-context.js)
-- [parallel/test-vm-create-context-accessors.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-create-context-accessors.js)
-- [parallel/test-vm-create-context-arg.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-create-context-arg.js)
-- [parallel/test-vm-create-context-circular-reference.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-create-context-circular-reference.js)
-- [parallel/test-vm-createcacheddata.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-createcacheddata.js)
-- [parallel/test-vm-cross-context.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-cross-context.js)
-- [parallel/test-vm-data-property-writable.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-data-property-writable.js)
-- [parallel/test-vm-deleting-property.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-deleting-property.js)
- [parallel/test-vm-dynamic-import-callback-missing-flag.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-dynamic-import-callback-missing-flag.js)
-- [parallel/test-vm-function-declaration.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-function-declaration.js)
-- [parallel/test-vm-function-redefinition.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-function-redefinition.js)
-- [parallel/test-vm-getters.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-getters.js)
-- [parallel/test-vm-global-assignment.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-global-assignment.js)
-- [parallel/test-vm-global-define-property.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-global-define-property.js)
- [parallel/test-vm-global-get-own.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-global-get-own.js)
-- [parallel/test-vm-global-identity.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-global-identity.js)
- [parallel/test-vm-global-non-writable-properties.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-global-non-writable-properties.js)
- [parallel/test-vm-global-property-interceptors.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-global-property-interceptors.js)
-- [parallel/test-vm-global-setter.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-global-setter.js)
-- [parallel/test-vm-harmony-symbols.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-harmony-symbols.js)
-- [parallel/test-vm-indexed-properties.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-indexed-properties.js)
-- [parallel/test-vm-inherited_properties.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-inherited_properties.js)
-- [parallel/test-vm-is-context.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-is-context.js)
-- [parallel/test-vm-low-stack-space.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-low-stack-space.js)
- [parallel/test-vm-measure-memory-lazy.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-measure-memory-lazy.js)
- [parallel/test-vm-measure-memory-multi-context.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-measure-memory-multi-context.js)
- [parallel/test-vm-measure-memory.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-measure-memory.js)
@@ -2667,31 +2642,14 @@ NOTE: This file should not be manually edited. Please edit `tests/node_compat/co
- [parallel/test-vm-module-link.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-module-link.js)
- [parallel/test-vm-module-reevaluate.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-module-reevaluate.js)
- [parallel/test-vm-module-synthetic.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-module-synthetic.js)
-- [parallel/test-vm-new-script-new-context.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-new-script-new-context.js)
- [parallel/test-vm-no-dynamic-import-callback.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-no-dynamic-import-callback.js)
-- [parallel/test-vm-not-strict.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-not-strict.js)
-- [parallel/test-vm-options-validation.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-options-validation.js)
-- [parallel/test-vm-parse-abort-on-uncaught-exception.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-parse-abort-on-uncaught-exception.js)
-- [parallel/test-vm-preserves-property.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-preserves-property.js)
-- [parallel/test-vm-property-not-on-sandbox.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-property-not-on-sandbox.js)
-- [parallel/test-vm-proxies.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-proxies.js)
-- [parallel/test-vm-proxy-failure-CP.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-proxy-failure-CP.js)
- [parallel/test-vm-run-in-new-context.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-run-in-new-context.js)
-- [parallel/test-vm-script-throw-in-tostring.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-script-throw-in-tostring.js)
-- [parallel/test-vm-set-property-proxy.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-set-property-proxy.js)
-- [parallel/test-vm-set-proto-null-on-globalthis.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-set-proto-null-on-globalthis.js)
- [parallel/test-vm-sigint-existing-handler.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-sigint-existing-handler.js)
- [parallel/test-vm-sigint.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-sigint.js)
-- [parallel/test-vm-source-map-url.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-source-map-url.js)
- [parallel/test-vm-strict-assign.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-strict-assign.js)
-- [parallel/test-vm-strict-mode.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-strict-mode.js)
-- [parallel/test-vm-symbols.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-symbols.js)
- [parallel/test-vm-syntax-error-message.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-syntax-error-message.js)
- [parallel/test-vm-syntax-error-stderr.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-syntax-error-stderr.js)
-- [parallel/test-vm-timeout-escape-promise-2.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-timeout-escape-promise-2.js)
- [parallel/test-vm-timeout-escape-promise-module.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-timeout-escape-promise-module.js)
-- [parallel/test-vm-timeout-escape-promise.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-timeout-escape-promise.js)
-- [parallel/test-vm-timeout.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-vm-timeout.js)
- [parallel/test-warn-sigprof.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-warn-sigprof.js)
- [parallel/test-warn-stream-wrap.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-warn-stream-wrap.js)
- [parallel/test-weakref.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-weakref.js)
diff --git a/tests/node_compat/test.ts b/tests/node_compat/test.ts
index c9ec1544d..b5c9514a3 100644
--- a/tests/node_compat/test.ts
+++ b/tests/node_compat/test.ts
@@ -45,6 +45,12 @@ const darwinIgnorePaths = new Set(
const decoder = new TextDecoder();
let testSerialId = 0;
+function parseFlags(source: string): string[] {
+ const line = /^\/\/ Flags: (.+)$/um.exec(source);
+ if (line == null) return [];
+ return line[1].split(" ");
+}
+
async function runTest(t: Deno.TestContext, path: string): Promise<void> {
// If filter patterns are given and any pattern doesn't match
// to the file path, then skip the case
@@ -69,14 +75,23 @@ async function runTest(t: Deno.TestContext, path: string): Promise<void> {
const v8Flags = ["--stack-size=4000"];
const testSource = await Deno.readTextFile(testCase);
const envVars: Record<string, string> = {};
- // TODO(kt3k): Parse `Flags` directive correctly
- if (testSource.includes("Flags: --expose_externalize_string")) {
- v8Flags.push("--expose-externalize-string");
- // TODO(bartlomieju): disable verifying globals if that V8 flag is
- // present. Even though we should be able to pass a list of globals
- // that are allowed, it doesn't work, because the list is expected to
- // contain actual JS objects, not strings :)).
- envVars["NODE_TEST_KNOWN_GLOBALS"] = "0";
+ const knownGlobals: string[] = [];
+ parseFlags(testSource).forEach((flag) => {
+ switch (flag) {
+ case "--expose_externalize_string":
+ v8Flags.push("--expose-externalize-string");
+ knownGlobals.push("createExternalizableString");
+ break;
+ case "--expose-gc":
+ v8Flags.push("--expose-gc");
+ knownGlobals.push("gc");
+ break;
+ default:
+ break;
+ }
+ });
+ if (knownGlobals.length > 0) {
+ envVars["NODE_TEST_KNOWN_GLOBALS"] = knownGlobals.join(",");
}
// TODO(nathanwhit): once we match node's behavior on executing
// `node:test` tests when we run a file, we can remove this
diff --git a/tests/node_compat/test/common/index.js b/tests/node_compat/test/common/index.js
index ebac56ac5..d2165aecd 100644
--- a/tests/node_compat/test/common/index.js
+++ b/tests/node_compat/test/common/index.js
@@ -16,7 +16,6 @@ const path = require("path");
const util = require("util");
const tmpdir = require("./tmpdir");
-
function platformTimeout(ms) {
return ms;
}
@@ -90,7 +89,7 @@ function allowGlobals(...allowlist) {
if (process.env.NODE_TEST_KNOWN_GLOBALS !== '0') {
if (process.env.NODE_TEST_KNOWN_GLOBALS) {
- const knownFromEnv = process.env.NODE_TEST_KNOWN_GLOBALS.split(',');
+ const knownFromEnv = process.env.NODE_TEST_KNOWN_GLOBALS.split(',').map((name) => global[name]);
allowGlobals(...knownFromEnv);
}
@@ -445,13 +444,13 @@ const pwdCommand = isWindows ?
function spawnPromisified(...args) {
let stderr = '';
let stdout = '';
-
+
const child = spawn(...args);
child.stderr.setEncoding('utf8');
child.stderr.on('data', (data) => { stderr += data; });
child.stdout.setEncoding('utf8');
child.stdout.on('data', (data) => { stdout += data; });
-
+
return new Promise((resolve, reject) => {
child.on('close', (code, signal) => {
resolve({
diff --git a/tests/node_compat/test/parallel/test-vm-access-process-env.js b/tests/node_compat/test/parallel/test-vm-access-process-env.js
new file mode 100644
index 000000000..95f555dac
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-access-process-env.js
@@ -0,0 +1,40 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+// 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';
+// Tests that node does neither crash nor throw an error when accessing
+// process.env when inside a VM context.
+// See https://github.com/nodejs/node-v0.x-archive/issues/7511.
+
+require('../common');
+const assert = require('assert');
+const vm = require('vm');
+
+const context = vm.createContext({ process });
+const result = vm.runInContext('process.env["PATH"]', context);
+assert.notStrictEqual(undefined, result);
diff --git a/tests/node_compat/test/parallel/test-vm-attributes-property-not-on-sandbox.js b/tests/node_compat/test/parallel/test-vm-attributes-property-not-on-sandbox.js
new file mode 100644
index 000000000..940fd4e7f
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-attributes-property-not-on-sandbox.js
@@ -0,0 +1,25 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+require('../common');
+const assert = require('assert');
+const vm = require('vm');
+
+// Assert that accessor descriptors are not flattened on the sandbox.
+// Issue: https://github.com/nodejs/node/issues/2734
+const sandbox = {};
+vm.createContext(sandbox);
+const code = `Object.defineProperty(
+ this,
+ 'foo',
+ { get: function() {return 17} }
+ );
+ var desc = Object.getOwnPropertyDescriptor(this, 'foo');`;
+
+vm.runInContext(code, sandbox);
+assert.strictEqual(typeof sandbox.desc.get, 'function');
diff --git a/tests/node_compat/test/parallel/test-vm-codegen.js b/tests/node_compat/test/parallel/test-vm-codegen.js
new file mode 100644
index 000000000..fff9c287f
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-codegen.js
@@ -0,0 +1,108 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+
+require('../common');
+const assert = require('assert');
+
+const { createContext, runInContext, runInNewContext } = require('vm');
+
+const WASM_BYTES = Buffer.from(
+ [0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]);
+
+{
+ const ctx = createContext({ WASM_BYTES });
+ const test = 'eval(""); new WebAssembly.Module(WASM_BYTES);';
+ runInContext(test, ctx);
+
+ runInNewContext(test, { WASM_BYTES }, {
+ contextCodeGeneration: undefined,
+ });
+}
+
+{
+ const ctx = createContext({}, {
+ codeGeneration: {
+ strings: false,
+ },
+ });
+
+ const EvalError = runInContext('EvalError', ctx);
+ assert.throws(() => {
+ runInContext('eval("x")', ctx);
+ }, EvalError);
+}
+
+{
+ const ctx = createContext({ WASM_BYTES }, {
+ codeGeneration: {
+ wasm: false,
+ },
+ });
+
+ const CompileError = runInContext('WebAssembly.CompileError', ctx);
+ assert.throws(() => {
+ runInContext('new WebAssembly.Module(WASM_BYTES)', ctx);
+ }, CompileError);
+}
+
+assert.throws(() => {
+ runInNewContext('eval("x")', {}, {
+ contextCodeGeneration: {
+ strings: false,
+ },
+ });
+}, {
+ name: 'EvalError'
+});
+
+assert.throws(() => {
+ runInNewContext('new WebAssembly.Module(WASM_BYTES)', { WASM_BYTES }, {
+ contextCodeGeneration: {
+ wasm: false,
+ },
+ });
+}, {
+ name: 'CompileError'
+});
+
+assert.throws(() => {
+ createContext({}, {
+ codeGeneration: {
+ strings: 0,
+ },
+ });
+}, {
+ code: 'ERR_INVALID_ARG_TYPE',
+});
+
+assert.throws(() => {
+ runInNewContext('eval("x")', {}, {
+ contextCodeGeneration: {
+ wasm: 1,
+ },
+ });
+}, {
+ code: 'ERR_INVALID_ARG_TYPE'
+});
+
+assert.throws(() => {
+ createContext({}, {
+ codeGeneration: 1,
+ });
+}, {
+ code: 'ERR_INVALID_ARG_TYPE',
+});
+
+assert.throws(() => {
+ createContext({}, {
+ codeGeneration: null,
+ });
+}, {
+ code: 'ERR_INVALID_ARG_TYPE',
+});
diff --git a/tests/node_compat/test/parallel/test-vm-context-async-script.js b/tests/node_compat/test/parallel/test-vm-context-async-script.js
new file mode 100644
index 000000000..271567ccf
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-context-async-script.js
@@ -0,0 +1,42 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+// 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';
+const common = require('../common');
+const assert = require('assert');
+const vm = require('vm');
+
+const sandbox = { setTimeout };
+
+const ctx = vm.createContext(sandbox);
+
+vm.runInContext('setTimeout(function() { x = 3; }, 0);', ctx);
+setTimeout(common.mustCall(() => {
+ assert.strictEqual(sandbox.x, 3);
+ assert.strictEqual(ctx.x, 3);
+}), 1);
diff --git a/tests/node_compat/test/parallel/test-vm-context-property-forwarding.js b/tests/node_compat/test/parallel/test-vm-context-property-forwarding.js
new file mode 100644
index 000000000..f50737771
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-context-property-forwarding.js
@@ -0,0 +1,72 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+// 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 sandbox = { x: 3 };
+
+const ctx = vm.createContext(sandbox);
+
+assert.strictEqual(vm.runInContext('x;', ctx), 3);
+vm.runInContext('y = 4;', ctx);
+assert.strictEqual(sandbox.y, 4);
+assert.strictEqual(ctx.y, 4);
+
+// Test `IndexedPropertyGetterCallback` and `IndexedPropertyDeleterCallback`
+const x = { get 1() { return 5; } };
+const pd_expected = Object.getOwnPropertyDescriptor(x, 1);
+const ctx2 = vm.createContext(x);
+const pd_actual = Object.getOwnPropertyDescriptor(ctx2, 1);
+
+assert.deepStrictEqual(pd_actual, pd_expected);
+assert.strictEqual(ctx2[1], 5);
+delete ctx2[1];
+assert.strictEqual(ctx2[1], undefined);
+
+// https://github.com/nodejs/node/issues/33806
+{
+ const ctx = vm.createContext();
+
+ Object.defineProperty(ctx, 'prop', {
+ get() {
+ return undefined;
+ },
+ set(val) {
+ throw new Error('test error');
+ },
+ });
+
+ assert.throws(() => {
+ vm.runInContext('prop = 42', ctx);
+ }, {
+ message: 'test error',
+ });
+}
diff --git a/tests/node_compat/test/parallel/test-vm-create-and-run-in-context.js b/tests/node_compat/test/parallel/test-vm-create-and-run-in-context.js
new file mode 100644
index 000000000..0674c0b7b
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-create-and-run-in-context.js
@@ -0,0 +1,57 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+// 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';
+// Flags: --expose-gc
+require('../common');
+const assert = require('assert');
+
+const vm = require('vm');
+
+// Run in a new empty context
+let context = vm.createContext();
+let result = vm.runInContext('"passed";', context);
+assert.strictEqual(result, 'passed');
+
+// Create a new pre-populated context
+context = vm.createContext({ 'foo': 'bar', 'thing': 'lala' });
+assert.strictEqual(context.foo, 'bar');
+assert.strictEqual(context.thing, 'lala');
+
+// Test updating context
+result = vm.runInContext('var foo = 3;', context);
+assert.strictEqual(context.foo, 3);
+assert.strictEqual(context.thing, 'lala');
+
+// https://github.com/nodejs/node/issues/5768
+// Run in contextified sandbox without referencing the context
+const sandbox = { x: 1 };
+vm.createContext(sandbox);
+global.gc();
+vm.runInContext('x = 2', sandbox);
+// Should not crash.
diff --git a/tests/node_compat/test/parallel/test-vm-create-context-accessors.js b/tests/node_compat/test/parallel/test-vm-create-context-accessors.js
new file mode 100644
index 000000000..4b683d687
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-create-context-accessors.js
@@ -0,0 +1,56 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+// 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');
+
+let ctx = {};
+
+Object.defineProperty(ctx, 'getter', {
+ get: function() {
+ return 'ok';
+ }
+});
+
+let val;
+Object.defineProperty(ctx, 'setter', {
+ set: function(_val) {
+ val = _val;
+ },
+ get: function() {
+ return `ok=${val}`;
+ }
+});
+
+ctx = vm.createContext(ctx);
+
+const result = vm.runInContext('setter = "test";[getter,setter]', ctx);
+assert.strictEqual(result[0], 'ok');
+assert.strictEqual(result[1], 'ok=test');
diff --git a/tests/node_compat/test/parallel/test-vm-create-context-arg.js b/tests/node_compat/test/parallel/test-vm-create-context-arg.js
new file mode 100644
index 000000000..6eb0f7cf9
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-create-context-arg.js
@@ -0,0 +1,47 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+// 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');
+
+assert.throws(() => {
+ vm.createContext('string is not supported');
+}, {
+ code: 'ERR_INVALID_ARG_TYPE',
+ name: 'TypeError'
+});
+
+// Should not throw.
+vm.createContext({ a: 1 });
+vm.createContext([0, 1, 2, 3]);
+
+const sandbox = {};
+vm.createContext(sandbox);
+vm.createContext(sandbox);
diff --git a/tests/node_compat/test/parallel/test-vm-create-context-circular-reference.js b/tests/node_compat/test/parallel/test-vm-create-context-circular-reference.js
new file mode 100644
index 000000000..95056a3d9
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-create-context-circular-reference.js
@@ -0,0 +1,41 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+// 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');
+
+let sbx = {};
+sbx.window = sbx;
+
+sbx = vm.createContext(sbx);
+
+sbx.test = 123;
+
+assert.strictEqual(sbx.window.window.window.window.window.test, 123);
diff --git a/tests/node_compat/test/parallel/test-vm-createcacheddata.js b/tests/node_compat/test/parallel/test-vm-createcacheddata.js
new file mode 100644
index 000000000..0e786364d
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-createcacheddata.js
@@ -0,0 +1,29 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+
+require('../common');
+
+const { Script } = require('vm');
+const assert = require('assert');
+
+const source = 'function x() {} const y = x();';
+
+const script = new Script(source);
+let cachedData = script.createCachedData();
+assert(cachedData instanceof Buffer);
+
+assert(!new Script(source, { cachedData }).cachedDataRejected);
+
+script.runInNewContext();
+
+for (let i = 0; i < 10; i += 1) {
+ cachedData = script.createCachedData();
+
+ assert(!new Script(source, { cachedData }).cachedDataRejected);
+}
diff --git a/tests/node_compat/test/parallel/test-vm-cross-context.js b/tests/node_compat/test/parallel/test-vm-cross-context.js
new file mode 100644
index 000000000..3a1f1678e
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-cross-context.js
@@ -0,0 +1,36 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+// 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 vm = require('vm');
+const ctx = vm.createContext(global);
+
+// Should not throw.
+vm.runInContext('!function() { var x = console.log; }()', ctx);
diff --git a/tests/node_compat/test/parallel/test-vm-data-property-writable.js b/tests/node_compat/test/parallel/test-vm-data-property-writable.js
new file mode 100644
index 000000000..1ce764801
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-data-property-writable.js
@@ -0,0 +1,35 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+// Refs: https://github.com/nodejs/node/issues/10223
+
+require('../common');
+const vm = require('vm');
+const assert = require('assert');
+
+const context = vm.createContext({});
+
+let code = `
+ Object.defineProperty(this, 'foo', {value: 5});
+ Object.getOwnPropertyDescriptor(this, 'foo');
+`;
+
+let desc = vm.runInContext(code, context);
+
+assert.strictEqual(desc.writable, false);
+
+// Check that interceptors work for symbols.
+code = `
+ const bar = Symbol('bar');
+ Object.defineProperty(this, bar, {value: 6});
+ Object.getOwnPropertyDescriptor(this, bar);
+`;
+
+desc = vm.runInContext(code, context);
+
+assert.strictEqual(desc.value, 6);
diff --git a/tests/node_compat/test/parallel/test-vm-deleting-property.js b/tests/node_compat/test/parallel/test-vm-deleting-property.js
new file mode 100644
index 000000000..df5ac859a
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-deleting-property.js
@@ -0,0 +1,22 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+// Refs: https://github.com/nodejs/node/issues/6287
+
+require('../common');
+const assert = require('assert');
+const vm = require('vm');
+
+const context = vm.createContext();
+const res = vm.runInContext(`
+ this.x = 'prop';
+ delete this.x;
+ Object.getOwnPropertyDescriptor(this, 'x');
+`, context);
+
+assert.strictEqual(res, undefined);
diff --git a/tests/node_compat/test/parallel/test-vm-function-declaration.js b/tests/node_compat/test/parallel/test-vm-function-declaration.js
new file mode 100644
index 000000000..209720c75
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-function-declaration.js
@@ -0,0 +1,56 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+// 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 o = vm.createContext({ console });
+
+// Function declaration and expression should both be copied to the
+// sandboxed context.
+let code = 'let a = function() {};\n';
+code += 'function b(){}\n';
+code += 'var c = function() {};\n';
+code += 'var d = () => {};\n';
+code += 'let e = () => {};\n';
+
+// Grab the global b function as the completion value, to ensure that
+// we are getting the global function, and not some other thing
+code += '(function(){return this})().b;\n';
+
+const res = vm.runInContext(code, o, 'test');
+assert.strictEqual(typeof res, 'function');
+assert.strictEqual(res.name, 'b');
+assert.strictEqual(typeof o.a, 'undefined');
+assert.strictEqual(typeof o.b, 'function');
+assert.strictEqual(typeof o.c, 'function');
+assert.strictEqual(typeof o.d, 'function');
+assert.strictEqual(typeof o.e, 'undefined');
+assert.strictEqual(res, o.b);
diff --git a/tests/node_compat/test/parallel/test-vm-function-redefinition.js b/tests/node_compat/test/parallel/test-vm-function-redefinition.js
new file mode 100644
index 000000000..7bc62ac0e
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-function-redefinition.js
@@ -0,0 +1,18 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+// Refs: https://github.com/nodejs/node/issues/548
+require('../common');
+const assert = require('assert');
+const vm = require('vm');
+const context = vm.createContext();
+
+vm.runInContext('function test() { return 0; }', context);
+vm.runInContext('function test() { return 1; }', context);
+const result = vm.runInContext('test()', context);
+assert.strictEqual(result, 1);
diff --git a/tests/node_compat/test/parallel/test-vm-getters.js b/tests/node_compat/test/parallel/test-vm-getters.js
new file mode 100644
index 000000000..b9c28014a
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-getters.js
@@ -0,0 +1,31 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+// Refs: https://github.com/nodejs/node/issues/2734
+require('../common');
+const assert = require('assert');
+const vm = require('vm');
+const sandbox = {};
+
+Object.defineProperty(sandbox, 'prop', {
+ get() {
+ return 'foo';
+ }
+});
+
+const descriptor = Object.getOwnPropertyDescriptor(sandbox, 'prop');
+const context = vm.createContext(sandbox);
+const code = 'Object.getOwnPropertyDescriptor(this, "prop");';
+const result = vm.runInContext(code, context);
+
+// Ref: https://github.com/nodejs/node/issues/11803
+
+assert.deepStrictEqual(Object.keys(result), Object.keys(descriptor));
+for (const prop of Object.keys(result)) {
+ assert.strictEqual(result[prop], descriptor[prop]);
+}
diff --git a/tests/node_compat/test/parallel/test-vm-global-assignment.js b/tests/node_compat/test/parallel/test-vm-global-assignment.js
new file mode 100644
index 000000000..c8fc516d6
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-global-assignment.js
@@ -0,0 +1,22 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+// Regression test for https://github.com/nodejs/node/issues/10806
+
+require('../common');
+const assert = require('assert');
+const vm = require('vm');
+const ctx = vm.createContext({ open() { } });
+const window = vm.runInContext('this', ctx);
+const other = 123;
+
+assert.notStrictEqual(window.open, other);
+window.open = other;
+assert.strictEqual(window.open, other);
+window.open = other;
+assert.strictEqual(window.open, other);
diff --git a/tests/node_compat/test/parallel/test-vm-global-define-property.js b/tests/node_compat/test/parallel/test-vm-global-define-property.js
new file mode 100644
index 000000000..28f5070c2
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-global-define-property.js
@@ -0,0 +1,54 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+// 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 code =
+ 'Object.defineProperty(this, "f", {\n' +
+ ' get: function() { return x; },\n' +
+ ' set: function(k) { x = k; },\n' +
+ ' configurable: true,\n' +
+ ' enumerable: true\n' +
+ '});\n' +
+ 'g = f;\n' +
+ 'f;\n';
+
+const x = {};
+const o = vm.createContext({ console, x });
+
+const res = vm.runInContext(code, o, 'test');
+
+assert(res);
+assert.strictEqual(typeof res, 'object');
+assert.strictEqual(res, x);
+assert.strictEqual(o.f, res);
+assert.deepStrictEqual(Object.keys(o), ['console', 'x', 'f', 'g']);
diff --git a/tests/node_compat/test/parallel/test-vm-global-identity.js b/tests/node_compat/test/parallel/test-vm-global-identity.js
new file mode 100644
index 000000000..5413ca94a
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-global-identity.js
@@ -0,0 +1,39 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+// 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 ctx = vm.createContext();
+ctx.window = ctx;
+
+const thisVal = vm.runInContext('this;', ctx);
+const windowVal = vm.runInContext('window;', ctx);
+assert.strictEqual(thisVal, windowVal);
diff --git a/tests/node_compat/test/parallel/test-vm-global-setter.js b/tests/node_compat/test/parallel/test-vm-global-setter.js
new file mode 100644
index 000000000..8f1f862f3
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-global-setter.js
@@ -0,0 +1,168 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+const common = require('../common');
+const assert = require('assert');
+const vm = require('vm');
+
+const getSetSymbolReceivingFunction = Symbol('sym-1');
+const getSetSymbolReceivingNumber = Symbol('sym-2');
+const symbolReceivingNumber = Symbol('sym-3');
+const unknownSymbolReceivingNumber = Symbol('sym-4');
+
+const window = createWindow();
+
+const descriptor1 = Object.getOwnPropertyDescriptor(
+ window.globalProxy,
+ 'getSetPropReceivingFunction'
+);
+assert.strictEqual(typeof descriptor1.get, 'function');
+assert.strictEqual(typeof descriptor1.set, 'function');
+assert.strictEqual(descriptor1.configurable, true);
+
+const descriptor2 = Object.getOwnPropertyDescriptor(
+ window.globalProxy,
+ 'getSetPropReceivingNumber'
+);
+assert.strictEqual(typeof descriptor2.get, 'function');
+assert.strictEqual(typeof descriptor2.set, 'function');
+assert.strictEqual(descriptor2.configurable, true);
+
+const descriptor3 = Object.getOwnPropertyDescriptor(
+ window.globalProxy,
+ 'propReceivingNumber'
+);
+assert.strictEqual(descriptor3.value, 44);
+
+const descriptor4 = Object.getOwnPropertyDescriptor(
+ window.globalProxy,
+ 'unknownPropReceivingNumber'
+);
+assert.strictEqual(descriptor4, undefined);
+
+const descriptor5 = Object.getOwnPropertyDescriptor(
+ window.globalProxy,
+ getSetSymbolReceivingFunction
+);
+assert.strictEqual(typeof descriptor5.get, 'function');
+assert.strictEqual(typeof descriptor5.set, 'function');
+assert.strictEqual(descriptor5.configurable, true);
+
+const descriptor6 = Object.getOwnPropertyDescriptor(
+ window.globalProxy,
+ getSetSymbolReceivingNumber
+);
+assert.strictEqual(typeof descriptor6.get, 'function');
+assert.strictEqual(typeof descriptor6.set, 'function');
+assert.strictEqual(descriptor6.configurable, true);
+
+const descriptor7 = Object.getOwnPropertyDescriptor(
+ window.globalProxy,
+ symbolReceivingNumber
+);
+assert.strictEqual(descriptor7.value, 48);
+
+const descriptor8 = Object.getOwnPropertyDescriptor(
+ window.globalProxy,
+ unknownSymbolReceivingNumber
+);
+assert.strictEqual(descriptor8, undefined);
+
+const descriptor9 = Object.getOwnPropertyDescriptor(
+ window.globalProxy,
+ 'getSetPropThrowing'
+);
+assert.strictEqual(typeof descriptor9.get, 'function');
+assert.strictEqual(typeof descriptor9.set, 'function');
+assert.strictEqual(descriptor9.configurable, true);
+
+const descriptor10 = Object.getOwnPropertyDescriptor(
+ window.globalProxy,
+ 'nonWritableProp'
+);
+assert.strictEqual(descriptor10.value, 51);
+assert.strictEqual(descriptor10.writable, false);
+
+// Regression test for GH-42962. This assignment should not throw.
+window.globalProxy.getSetPropReceivingFunction = () => {};
+assert.strictEqual(window.globalProxy.getSetPropReceivingFunction, 42);
+
+window.globalProxy.getSetPropReceivingNumber = 143;
+assert.strictEqual(window.globalProxy.getSetPropReceivingNumber, 43);
+
+window.globalProxy.propReceivingNumber = 144;
+assert.strictEqual(window.globalProxy.propReceivingNumber, 144);
+
+window.globalProxy.unknownPropReceivingNumber = 145;
+assert.strictEqual(window.globalProxy.unknownPropReceivingNumber, 145);
+
+window.globalProxy[getSetSymbolReceivingFunction] = () => {};
+assert.strictEqual(window.globalProxy[getSetSymbolReceivingFunction], 46);
+
+window.globalProxy[getSetSymbolReceivingNumber] = 147;
+assert.strictEqual(window.globalProxy[getSetSymbolReceivingNumber], 47);
+
+window.globalProxy[symbolReceivingNumber] = 148;
+assert.strictEqual(window.globalProxy[symbolReceivingNumber], 148);
+
+window.globalProxy[unknownSymbolReceivingNumber] = 149;
+assert.strictEqual(window.globalProxy[unknownSymbolReceivingNumber], 149);
+
+assert.throws(
+ () => (window.globalProxy.getSetPropThrowing = 150),
+ new Error('setter called')
+);
+assert.strictEqual(window.globalProxy.getSetPropThrowing, 50);
+
+assert.throws(
+ () => (window.globalProxy.nonWritableProp = 151),
+ new TypeError('Cannot redefine property: nonWritableProp')
+);
+assert.strictEqual(window.globalProxy.nonWritableProp, 51);
+
+function createWindow() {
+ const obj = {};
+ vm.createContext(obj);
+ Object.defineProperty(obj, 'getSetPropReceivingFunction', {
+ get: common.mustCall(() => 42),
+ set: common.mustCall(),
+ configurable: true,
+ });
+ Object.defineProperty(obj, 'getSetPropReceivingNumber', {
+ get: common.mustCall(() => 43),
+ set: common.mustCall(),
+ configurable: true,
+ });
+ obj.propReceivingNumber = 44;
+ Object.defineProperty(obj, getSetSymbolReceivingFunction, {
+ get: common.mustCall(() => 46),
+ set: common.mustCall(),
+ configurable: true,
+ });
+ Object.defineProperty(obj, getSetSymbolReceivingNumber, {
+ get: common.mustCall(() => 47),
+ set: common.mustCall(),
+ configurable: true,
+ });
+ obj[symbolReceivingNumber] = 48;
+ Object.defineProperty(obj, 'getSetPropThrowing', {
+ get: common.mustCall(() => 50),
+ set: common.mustCall(() => {
+ throw new Error('setter called');
+ }),
+ configurable: true,
+ });
+ Object.defineProperty(obj, 'nonWritableProp', {
+ value: 51,
+ writable: false,
+ });
+
+ obj.globalProxy = vm.runInContext('this', obj);
+
+ return obj;
+}
diff --git a/tests/node_compat/test/parallel/test-vm-harmony-symbols.js b/tests/node_compat/test/parallel/test-vm-harmony-symbols.js
new file mode 100644
index 000000000..d4713a2e2
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-harmony-symbols.js
@@ -0,0 +1,44 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+// 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');
+
+// The sandbox should have its own Symbol constructor.
+let sandbox = {};
+vm.runInNewContext('this.Symbol = Symbol', sandbox);
+assert.strictEqual(typeof sandbox.Symbol, 'function');
+assert.notStrictEqual(sandbox.Symbol, Symbol);
+
+// Unless we copy the Symbol constructor explicitly, of course.
+sandbox = { Symbol };
+vm.runInNewContext('this.Symbol = Symbol', sandbox);
+assert.strictEqual(typeof sandbox.Symbol, 'function');
+assert.strictEqual(sandbox.Symbol, Symbol);
diff --git a/tests/node_compat/test/parallel/test-vm-indexed-properties.js b/tests/node_compat/test/parallel/test-vm-indexed-properties.js
new file mode 100644
index 000000000..332905ff7
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-indexed-properties.js
@@ -0,0 +1,24 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+
+require('../common');
+const assert = require('assert');
+const vm = require('vm');
+
+const code = `Object.defineProperty(this, 99, {
+ value: 20,
+ enumerable: true
+ });`;
+
+
+const sandbox = {};
+const ctx = vm.createContext(sandbox);
+vm.runInContext(code, ctx);
+
+assert.strictEqual(sandbox[99], 20);
diff --git a/tests/node_compat/test/parallel/test-vm-inherited_properties.js b/tests/node_compat/test/parallel/test-vm-inherited_properties.js
new file mode 100644
index 000000000..4994a8cea
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-inherited_properties.js
@@ -0,0 +1,45 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+
+require('../common');
+
+const vm = require('vm');
+const assert = require('assert');
+
+let base = {
+ propBase: 1
+};
+
+let sandbox = Object.create(base, {
+ propSandbox: { value: 3 }
+});
+
+const context = vm.createContext(sandbox);
+
+let result = vm.runInContext('Object.hasOwnProperty(this, "propBase");',
+ context);
+
+assert.strictEqual(result, false);
+
+// Ref: https://github.com/nodejs/node/issues/5350
+base = { __proto__: null };
+base.x = 1;
+base.y = 2;
+
+sandbox = { __proto__: base };
+sandbox.z = 3;
+
+assert.deepStrictEqual(Object.keys(sandbox), ['z']);
+
+const code = 'x = 0; z = 4;';
+result = vm.runInNewContext(code, sandbox);
+assert.strictEqual(result, 4);
+
+// Check that y is not an own property.
+assert.deepStrictEqual(Object.keys(sandbox), ['z', 'x']);
diff --git a/tests/node_compat/test/parallel/test-vm-is-context.js b/tests/node_compat/test/parallel/test-vm-is-context.js
new file mode 100644
index 000000000..fdbaa36d6
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-is-context.js
@@ -0,0 +1,53 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+// 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');
+
+for (const valToTest of [
+ 'string', null, undefined, 8.9, Symbol('sym'), true,
+]) {
+ assert.throws(() => {
+ vm.isContext(valToTest);
+ }, {
+ code: 'ERR_INVALID_ARG_TYPE',
+ name: 'TypeError'
+ });
+}
+
+assert.strictEqual(vm.isContext({}), false);
+assert.strictEqual(vm.isContext([]), false);
+
+assert.strictEqual(vm.isContext(vm.createContext()), true);
+assert.strictEqual(vm.isContext(vm.createContext([])), true);
+
+const sandbox = { foo: 'bar' };
+vm.createContext(sandbox);
+assert.strictEqual(vm.isContext(sandbox), true);
diff --git a/tests/node_compat/test/parallel/test-vm-low-stack-space.js b/tests/node_compat/test/parallel/test-vm-low-stack-space.js
new file mode 100644
index 000000000..d10137724
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-low-stack-space.js
@@ -0,0 +1,33 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+require('../common');
+const assert = require('assert');
+const vm = require('vm');
+
+function a() {
+ try {
+ return a();
+ } catch {
+ // Throw an exception as near to the recursion-based RangeError as possible.
+ return vm.runInThisContext('() => 42')();
+ }
+}
+
+assert.strictEqual(a(), 42);
+
+function b() {
+ try {
+ return b();
+ } catch {
+ // This writes a lot of noise to stderr, but it still works.
+ return vm.runInNewContext('() => 42')();
+ }
+}
+
+assert.strictEqual(b(), 42);
diff --git a/tests/node_compat/test/parallel/test-vm-new-script-new-context.js b/tests/node_compat/test/parallel/test-vm-new-script-new-context.js
new file mode 100644
index 000000000..aada16262
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-new-script-new-context.js
@@ -0,0 +1,114 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+// 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 Script = require('vm').Script;
+
+{
+ const script = new Script('\'passed\';');
+ const result1 = script.runInNewContext();
+ const result2 = script.runInNewContext();
+ assert.strictEqual(result1, 'passed');
+ assert.strictEqual(result2, 'passed');
+}
+
+{
+ const script = new Script('throw new Error(\'test\');');
+ assert.throws(() => {
+ script.runInNewContext();
+ }, /^Error: test$/);
+}
+
+{
+ const script = new Script('foo.bar = 5;');
+ assert.throws(() => {
+ script.runInNewContext();
+ }, /^ReferenceError: foo is not defined$/);
+}
+
+{
+ global.hello = 5;
+ const script = new Script('hello = 2');
+ script.runInNewContext();
+ assert.strictEqual(global.hello, 5);
+
+ // Cleanup
+ delete global.hello;
+}
+
+{
+ global.code = 'foo = 1;' +
+ 'bar = 2;' +
+ 'if (baz !== 3) throw new Error(\'test fail\');';
+ global.foo = 2;
+ global.obj = { foo: 0, baz: 3 };
+ const script = new Script(global.code);
+ /* eslint-disable no-unused-vars */
+ const baz = script.runInNewContext(global.obj);
+ /* eslint-enable no-unused-vars */
+ assert.strictEqual(global.obj.foo, 1);
+ assert.strictEqual(global.obj.bar, 2);
+ assert.strictEqual(global.foo, 2);
+
+ // cleanup
+ delete global.code;
+ delete global.foo;
+ delete global.obj;
+}
+
+{
+ const script = new Script('f()');
+ function changeFoo() { global.foo = 100; }
+ script.runInNewContext({ f: changeFoo });
+ assert.strictEqual(global.foo, 100);
+
+ // cleanup
+ delete global.foo;
+}
+
+{
+ const script = new Script('f.a = 2');
+ const f = { a: 1 };
+ script.runInNewContext({ f });
+ assert.strictEqual(f.a, 2);
+
+ assert.throws(() => {
+ script.runInNewContext();
+ }, /^ReferenceError: f is not defined$/);
+}
+
+{
+ const script = new Script('');
+ assert.throws(() => {
+ script.runInNewContext.call('\'hello\';');
+ }, /^TypeError: this\.runInContext is not a function$/);
+}
diff --git a/tests/node_compat/test/parallel/test-vm-not-strict.js b/tests/node_compat/test/parallel/test-vm-not-strict.js
new file mode 100644
index 000000000..c5dee3a2c
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-not-strict.js
@@ -0,0 +1,44 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+/* eslint-disable strict, no-var, no-delete-var, no-undef, node-core/required-modules, node-core/require-common-first */
+// Importing common would break the execution. Indeed running `vm.runInThisContext` alters the global context
+// when declaring new variables with `var`. The other rules (strict, no-var, no-delete-var) have been disabled
+// in order to be able to test this specific not-strict case playing with `var` and `delete`.
+// Related to bug report: https://github.com/nodejs/node/issues/43129
+var assert = require('assert');
+var vm = require('vm');
+
+var data = [];
+var a = 'direct';
+delete a;
+data.push(a);
+
+var item2 = vm.runInThisContext(`
+var unusedB = 1;
+var data = [];
+var b = "this";
+delete b;
+data.push(b);
+data[0]
+`);
+data.push(item2);
+
+vm.runInContext(
+ `
+var unusedC = 1;
+var c = "new";
+delete c;
+data.push(c);
+`,
+ vm.createContext({ data: data })
+);
+
+assert.deepStrictEqual(data, ['direct', 'this', 'new']);
+
+assert.strictEqual(typeof unusedB, 'number'); // Declared within runInThisContext
+assert.strictEqual(typeof unusedC, 'undefined'); // Declared within runInContext
diff --git a/tests/node_compat/test/parallel/test-vm-options-validation.js b/tests/node_compat/test/parallel/test-vm-options-validation.js
new file mode 100644
index 000000000..d1b215ed7
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-options-validation.js
@@ -0,0 +1,101 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+
+require('../common');
+const assert = require('assert');
+const vm = require('vm');
+
+const invalidArgType = {
+ name: 'TypeError',
+ code: 'ERR_INVALID_ARG_TYPE'
+};
+
+const outOfRange = {
+ name: 'RangeError',
+ code: 'ERR_OUT_OF_RANGE'
+};
+
+assert.throws(() => {
+ new vm.Script('void 0', 42);
+}, invalidArgType);
+
+[null, {}, [1], 'bad', true].forEach((value) => {
+ assert.throws(() => {
+ new vm.Script('void 0', { lineOffset: value });
+ }, invalidArgType);
+
+ assert.throws(() => {
+ new vm.Script('void 0', { columnOffset: value });
+ }, invalidArgType);
+});
+
+[0.1, 2 ** 32].forEach((value) => {
+ assert.throws(() => {
+ new vm.Script('void 0', { lineOffset: value });
+ }, outOfRange);
+
+ assert.throws(() => {
+ new vm.Script('void 0', { columnOffset: value });
+ }, outOfRange);
+});
+
+assert.throws(() => {
+ new vm.Script('void 0', { lineOffset: Number.MAX_SAFE_INTEGER });
+}, outOfRange);
+
+assert.throws(() => {
+ new vm.Script('void 0', { columnOffset: Number.MAX_SAFE_INTEGER });
+}, outOfRange);
+
+assert.throws(() => {
+ new vm.Script('void 0', { filename: 123 });
+}, invalidArgType);
+
+assert.throws(() => {
+ new vm.Script('void 0', { produceCachedData: 1 });
+}, invalidArgType);
+
+[[0], {}, true, 'bad', 42].forEach((value) => {
+ assert.throws(() => {
+ new vm.Script('void 0', { cachedData: value });
+ }, invalidArgType);
+});
+
+{
+ const script = new vm.Script('void 0');
+ const sandbox = vm.createContext();
+
+ function assertErrors(options, errCheck) {
+ assert.throws(() => {
+ script.runInThisContext(options);
+ }, errCheck);
+
+ assert.throws(() => {
+ script.runInContext(sandbox, options);
+ }, errCheck);
+
+ assert.throws(() => {
+ script.runInNewContext({}, options);
+ }, errCheck);
+ }
+
+ [null, 'bad', 42].forEach((value) => {
+ assertErrors(value, invalidArgType);
+ });
+ [{}, [1], 'bad', null].forEach((value) => {
+ assertErrors({ timeout: value }, invalidArgType);
+ });
+ [-1, 0, NaN].forEach((value) => {
+ assertErrors({ timeout: value }, outOfRange);
+ });
+ [{}, [1], 'bad', 1, null].forEach((value) => {
+ assertErrors({ displayErrors: value }, invalidArgType);
+ assertErrors({ breakOnSigint: value }, invalidArgType);
+ });
+}
diff --git a/tests/node_compat/test/parallel/test-vm-parse-abort-on-uncaught-exception.js b/tests/node_compat/test/parallel/test-vm-parse-abort-on-uncaught-exception.js
new file mode 100644
index 000000000..e8cae690a
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-parse-abort-on-uncaught-exception.js
@@ -0,0 +1,25 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+// Flags: --abort-on-uncaught-exception
+'use strict';
+require('../common');
+const vm = require('vm');
+
+// Regression test for https://github.com/nodejs/node/issues/13258
+
+try {
+ new vm.Script({ toString() { throw new Error('foo'); } }, {});
+} catch {
+ // Continue regardless of error.
+}
+
+try {
+ new vm.Script('[', {});
+} catch {
+ // Continue regardless of error.
+}
diff --git a/tests/node_compat/test/parallel/test-vm-preserves-property.js b/tests/node_compat/test/parallel/test-vm-preserves-property.js
new file mode 100644
index 000000000..28f662a1a
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-preserves-property.js
@@ -0,0 +1,32 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+
+require('../common');
+const assert = require('assert');
+
+const vm = require('vm');
+
+const x = {};
+Object.defineProperty(x, 'prop', {
+ configurable: false,
+ enumerable: false,
+ writable: false,
+ value: 'val'
+});
+const o = vm.createContext(x);
+
+const code = 'Object.getOwnPropertyDescriptor(this, "prop")';
+const res = vm.runInContext(code, o, 'test');
+
+assert(res);
+assert.strictEqual(typeof res, 'object');
+assert.strictEqual(res.value, 'val');
+assert.strictEqual(res.configurable, false);
+assert.strictEqual(res.enumerable, false);
+assert.strictEqual(res.writable, false);
diff --git a/tests/node_compat/test/parallel/test-vm-property-not-on-sandbox.js b/tests/node_compat/test/parallel/test-vm-property-not-on-sandbox.js
new file mode 100644
index 000000000..360a5dabf
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-property-not-on-sandbox.js
@@ -0,0 +1,44 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+require('../common');
+const assert = require('assert');
+const vm = require('vm');
+
+// This, admittedly contrived, example tests an edge cases of the vm module.
+//
+// The GetterCallback explicitly checks the global_proxy() if a property is
+// not found on the sandbox. In the following test, the explicit check
+// inside the callback yields different results than deferring the
+// check until after the callback. The check is deferred if the
+// callback does not intercept, i.e., if args.GetReturnValue().Set() is
+// not called.
+
+// Check that the GetterCallback explicitly calls GetRealNamedProperty()
+// on the global proxy if the property is not found on the sandbox.
+//
+// foo is not defined on the sandbox until we call CopyProperties().
+// In the GetterCallback, we do not find the property on the sandbox and
+// get the property from the global proxy. Since the return value is
+// the sandbox, we replace it by
+// the global_proxy to keep the correct identities.
+//
+// This test case is partially inspired by
+// https://github.com/nodejs/node/issues/855
+const sandbox = { console };
+sandbox.document = { defaultView: sandbox };
+vm.createContext(sandbox);
+const code = `Object.defineProperty(
+ this,
+ 'foo',
+ { get: function() {return document.defaultView} }
+ );
+ var result = foo === this;`;
+
+vm.runInContext(code, sandbox);
+assert.strictEqual(sandbox.result, true);
diff --git a/tests/node_compat/test/parallel/test-vm-proxies.js b/tests/node_compat/test/parallel/test-vm-proxies.js
new file mode 100644
index 000000000..c485e0a62
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-proxies.js
@@ -0,0 +1,25 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+
+require('../common');
+const assert = require('assert');
+const vm = require('vm');
+
+// src/node_contextify.cc filters out the Proxy object from the parent
+// context. Make sure that the new context has a Proxy object of its own.
+let sandbox = {};
+vm.runInNewContext('this.Proxy = Proxy', sandbox);
+assert.strictEqual(typeof sandbox.Proxy, 'function');
+assert.notStrictEqual(sandbox.Proxy, Proxy);
+
+// Unless we copy the Proxy object explicitly, of course.
+sandbox = { Proxy };
+vm.runInNewContext('this.Proxy = Proxy', sandbox);
+assert.strictEqual(typeof sandbox.Proxy, 'function');
+assert.strictEqual(sandbox.Proxy, Proxy);
diff --git a/tests/node_compat/test/parallel/test-vm-proxy-failure-CP.js b/tests/node_compat/test/parallel/test-vm-proxy-failure-CP.js
new file mode 100644
index 000000000..2f503bd31
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-proxy-failure-CP.js
@@ -0,0 +1,22 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+require('../common');
+const vm = require('vm');
+
+// Check that we do not accidentally query attributes.
+// Issue: https://github.com/nodejs/node/issues/11902
+const handler = {
+ getOwnPropertyDescriptor: (target, prop) => {
+ throw new Error('whoops');
+ }
+};
+const sandbox = new Proxy({ foo: 'bar' }, handler);
+const context = vm.createContext(sandbox);
+
+vm.runInContext('', context);
diff --git a/tests/node_compat/test/parallel/test-vm-script-throw-in-tostring.js b/tests/node_compat/test/parallel/test-vm-script-throw-in-tostring.js
new file mode 100644
index 000000000..c13573086
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-script-throw-in-tostring.js
@@ -0,0 +1,21 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+
+require('../common');
+const assert = require('assert');
+
+const vm = require('vm');
+
+assert.throws(() => {
+ new vm.Script({
+ toString() {
+ throw new Error();
+ }
+ });
+}, Error);
diff --git a/tests/node_compat/test/parallel/test-vm-set-property-proxy.js b/tests/node_compat/test/parallel/test-vm-set-property-proxy.js
new file mode 100644
index 000000000..61f80902c
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-set-property-proxy.js
@@ -0,0 +1,23 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+const common = require('../common');
+const assert = require('assert');
+const vm = require('vm');
+
+// Regression test for https://github.com/nodejs/node/issues/34606
+
+const handler = {
+ getOwnPropertyDescriptor: common.mustCallAtLeast(() => {
+ return {};
+ })
+};
+
+const proxy = new Proxy({}, handler);
+assert.throws(() => vm.runInNewContext('p = 6', proxy),
+ /getOwnPropertyDescriptor/);
diff --git a/tests/node_compat/test/parallel/test-vm-set-proto-null-on-globalthis.js b/tests/node_compat/test/parallel/test-vm-set-proto-null-on-globalthis.js
new file mode 100644
index 000000000..779864668
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-set-proto-null-on-globalthis.js
@@ -0,0 +1,20 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+require('../common');
+
+// Setting __proto__ on vm context's globalThis should not cause a crash
+// Regression test for https://github.com/nodejs/node/issues/47798
+
+const vm = require('vm');
+const context = vm.createContext();
+
+const contextGlobalThis = vm.runInContext('this', context);
+
+// Should not crash.
+contextGlobalThis.__proto__ = null; // eslint-disable-line no-proto
diff --git a/tests/node_compat/test/parallel/test-vm-source-map-url.js b/tests/node_compat/test/parallel/test-vm-source-map-url.js
new file mode 100644
index 000000000..fb91ff1fc
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-source-map-url.js
@@ -0,0 +1,34 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+
+require('../common');
+const assert = require('assert');
+const vm = require('vm');
+
+function checkSourceMapUrl(source, expectedSourceMapURL) {
+ const script = new vm.Script(source);
+ assert.strictEqual(script.sourceMapURL, expectedSourceMapURL);
+}
+
+// No magic comment
+checkSourceMapUrl(`
+function myFunc() {}
+`, undefined);
+
+// Malformed magic comment
+checkSourceMapUrl(`
+function myFunc() {}
+// sourceMappingURL=sourcemap.json
+`, undefined);
+
+// Expected magic comment
+checkSourceMapUrl(`
+function myFunc() {}
+//# sourceMappingURL=sourcemap.json
+`, 'sourcemap.json');
diff --git a/tests/node_compat/test/parallel/test-vm-strict-mode.js b/tests/node_compat/test/parallel/test-vm-strict-mode.js
new file mode 100644
index 000000000..e759bd2c4
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-strict-mode.js
@@ -0,0 +1,21 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+// https://github.com/nodejs/node/issues/12300
+
+require('../common');
+const assert = require('assert');
+const vm = require('vm');
+
+const ctx = vm.createContext({ x: 42 });
+
+// This might look as if x has not been declared, but x is defined on the
+// sandbox and the assignment should not throw.
+vm.runInContext('"use strict"; x = 1', ctx);
+
+assert.strictEqual(ctx.x, 1);
diff --git a/tests/node_compat/test/parallel/test-vm-symbols.js b/tests/node_compat/test/parallel/test-vm-symbols.js
new file mode 100644
index 000000000..fbaff6a82
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-symbols.js
@@ -0,0 +1,30 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+
+require('../common');
+const assert = require('assert');
+
+const vm = require('vm');
+
+const symbol = Symbol();
+
+function Document() {
+ this[symbol] = 'foo';
+}
+
+Document.prototype.getSymbolValue = function() {
+ return this[symbol];
+};
+
+const context = new Document();
+vm.createContext(context);
+
+assert.strictEqual(context.getSymbolValue(), 'foo');
+
+assert.strictEqual(vm.runInContext('this.getSymbolValue()', context), 'foo');
diff --git a/tests/node_compat/test/parallel/test-vm-timeout-escape-promise-2.js b/tests/node_compat/test/parallel/test-vm-timeout-escape-promise-2.js
new file mode 100644
index 000000000..19ba0a682
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-timeout-escape-promise-2.js
@@ -0,0 +1,45 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+// https://github.com/nodejs/node/issues/3020
+// Promises used to allow code to escape the timeout
+// set for runInContext, runInNewContext, and runInThisContext.
+
+require('../common');
+const assert = require('assert');
+const vm = require('vm');
+
+const NS_PER_MS = 1000000n;
+
+const hrtime = process.hrtime.bigint;
+
+function loop() {
+ const start = hrtime();
+ while (1) {
+ const current = hrtime();
+ const span = (current - start) / NS_PER_MS;
+ if (span >= 2000n) {
+ throw new Error(
+ `escaped timeout at ${span} milliseconds!`);
+ }
+ }
+}
+
+assert.throws(() => {
+ vm.runInNewContext(
+ 'Promise.resolve().then(() => loop());',
+ {
+ hrtime,
+ loop
+ },
+ { timeout: 10, microtaskMode: 'afterEvaluate' }
+ );
+}, {
+ code: 'ERR_SCRIPT_EXECUTION_TIMEOUT',
+ message: 'Script execution timed out after 10ms'
+});
diff --git a/tests/node_compat/test/parallel/test-vm-timeout-escape-promise.js b/tests/node_compat/test/parallel/test-vm-timeout-escape-promise.js
new file mode 100644
index 000000000..c2393a852
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-timeout-escape-promise.js
@@ -0,0 +1,46 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+
+// https://github.com/nodejs/node/issues/3020
+// Promises used to allow code to escape the timeout
+// set for runInContext, runInNewContext, and runInThisContext.
+
+require('../common');
+const assert = require('assert');
+const vm = require('vm');
+
+const NS_PER_MS = 1000000n;
+
+const hrtime = process.hrtime.bigint;
+
+function loop() {
+ const start = hrtime();
+ while (1) {
+ const current = hrtime();
+ const span = (current - start) / NS_PER_MS;
+ if (span >= 2000n) {
+ throw new Error(
+ `escaped timeout at ${span} milliseconds!`);
+ }
+ }
+}
+
+assert.throws(() => {
+ vm.runInNewContext(
+ 'Promise.resolve().then(() => loop()); loop();',
+ {
+ hrtime,
+ loop
+ },
+ { timeout: 5, microtaskMode: 'afterEvaluate' }
+ );
+}, {
+ code: 'ERR_SCRIPT_EXECUTION_TIMEOUT',
+ message: 'Script execution timed out after 5ms'
+});
diff --git a/tests/node_compat/test/parallel/test-vm-timeout.js b/tests/node_compat/test/parallel/test-vm-timeout.js
new file mode 100644
index 000000000..d34520640
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-vm-timeout.js
@@ -0,0 +1,88 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+// 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');
+
+// Timeout of 100ms executing endless loop
+assert.throws(
+ function() {
+ vm.runInThisContext('while(true) {}', { timeout: 100 });
+ },
+ {
+ code: 'ERR_SCRIPT_EXECUTION_TIMEOUT',
+ message: 'Script execution timed out after 100ms'
+ });
+
+// Timeout of 1000ms, script finishes first
+vm.runInThisContext('', { timeout: 1000 });
+
+// Nested vm timeouts, inner timeout propagates out
+assert.throws(
+ function() {
+ const context = {
+ log: console.log,
+ runInVM: function(timeout) {
+ vm.runInNewContext('while(true) {}', context, { timeout });
+ }
+ };
+ vm.runInNewContext('runInVM(10)', context, { timeout: 10000 });
+ throw new Error('Test 5 failed');
+ },
+ {
+ code: 'ERR_SCRIPT_EXECUTION_TIMEOUT',
+ message: 'Script execution timed out after 10ms'
+ });
+
+// Nested vm timeouts, outer timeout is shorter and fires first.
+assert.throws(
+ function() {
+ const context = {
+ runInVM: function(timeout) {
+ vm.runInNewContext('while(true) {}', context, { timeout });
+ }
+ };
+ vm.runInNewContext('runInVM(10000)', context, { timeout: 100 });
+ throw new Error('Test 6 failed');
+ },
+ {
+ code: 'ERR_SCRIPT_EXECUTION_TIMEOUT',
+ message: 'Script execution timed out after 100ms'
+ });
+
+// Nested vm timeouts, inner script throws an error.
+assert.throws(function() {
+ const context = {
+ runInVM: function(timeout) {
+ vm.runInNewContext('throw new Error(\'foobar\')', context, { timeout });
+ }
+ };
+ vm.runInNewContext('runInVM(10000)', context, { timeout: 100000 });
+}, /foobar/);
diff --git a/tests/unit_node/vm_test.ts b/tests/unit_node/vm_test.ts
index 4c370931d..85b955663 100644
--- a/tests/unit_node/vm_test.ts
+++ b/tests/unit_node/vm_test.ts
@@ -128,7 +128,7 @@ Deno.test({
const obj = {};
assertEquals(isContext(obj), false);
assertEquals(isContext(globalThis), false);
- const sandbox = runInNewContext("{}");
+ const sandbox = runInNewContext("({})");
assertEquals(isContext(sandbox), false);
},
});