mirror of
https://github.com/denoland/deno.git
synced 2025-02-08 07:16:56 -05:00
fix(ext/node): node:vm
contexts (#23202)
Implement contextified objects in `node:vm` Fixes https://github.com/denoland/deno/issues/23186 Fixes https://github.com/denoland/deno/issues/22395 Fixes https://github.com/denoland/deno/issues/20607 Fixes https://github.com/denoland/deno/issues/18299 Fixes https://github.com/denoland/deno/issues/19395 Fixes https://github.com/denoland/deno/issues/18315 Fixes https://github.com/denoland/deno/issues/18319 Fixes https://github.com/denoland/deno/issues/23183
This commit is contained in:
parent
4ad011e829
commit
51f77463d0
6 changed files with 849 additions and 37 deletions
|
@ -260,7 +260,11 @@ deno_core::extension!(deno_node,
|
||||||
ops::winerror::op_node_sys_to_uv_error,
|
ops::winerror::op_node_sys_to_uv_error,
|
||||||
ops::v8::op_v8_cached_data_version_tag,
|
ops::v8::op_v8_cached_data_version_tag,
|
||||||
ops::v8::op_v8_get_heap_statistics,
|
ops::v8::op_v8_get_heap_statistics,
|
||||||
ops::v8::op_vm_run_in_new_context,
|
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::idna::op_node_idna_domain_to_ascii,
|
ops::idna::op_node_idna_domain_to_ascii,
|
||||||
ops::idna::op_node_idna_domain_to_unicode,
|
ops::idna::op_node_idna_domain_to_unicode,
|
||||||
ops::idna::op_node_idna_punycode_to_ascii,
|
ops::idna::op_node_idna_punycode_to_ascii,
|
||||||
|
|
|
@ -10,6 +10,8 @@ pub mod os;
|
||||||
pub mod require;
|
pub mod require;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
pub mod v8;
|
pub mod v8;
|
||||||
|
pub mod vm;
|
||||||
|
mod vm_internal;
|
||||||
pub mod winerror;
|
pub mod winerror;
|
||||||
pub mod worker_threads;
|
pub mod worker_threads;
|
||||||
pub mod zlib;
|
pub mod zlib;
|
||||||
|
|
138
ext/node/ops/vm.rs
Normal file
138
ext/node/ops/vm.rs
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
use deno_core::error::type_error;
|
||||||
|
use deno_core::error::AnyError;
|
||||||
|
use deno_core::op2;
|
||||||
|
use deno_core::v8;
|
||||||
|
|
||||||
|
use super::vm_internal as i;
|
||||||
|
|
||||||
|
pub struct Script {
|
||||||
|
inner: i::ContextifyScript,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Script {
|
||||||
|
fn new(
|
||||||
|
scope: &mut v8::HandleScope,
|
||||||
|
source: v8::Local<v8::String>,
|
||||||
|
) -> Result<Self, AnyError> {
|
||||||
|
Ok(Self {
|
||||||
|
inner: i::ContextifyScript::new(scope, source)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_in_this_context<'s>(
|
||||||
|
&self,
|
||||||
|
scope: &'s mut v8::HandleScope,
|
||||||
|
) -> Result<v8::Local<'s, v8::Value>, AnyError> {
|
||||||
|
let context = scope.get_current_context();
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_in_context<'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)
|
||||||
|
} else {
|
||||||
|
scope.get_current_context()
|
||||||
|
};
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[op2]
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[op2]
|
||||||
|
pub fn op_vm_create_context(
|
||||||
|
scope: &mut v8::HandleScope,
|
||||||
|
sandbox_obj: v8::Local<v8::Object>,
|
||||||
|
) {
|
||||||
|
// Don't allow contextifying a sandbox multiple times.
|
||||||
|
assert!(!i::ContextifyContext::is_contextify_context(
|
||||||
|
scope,
|
||||||
|
sandbox_obj
|
||||||
|
));
|
||||||
|
|
||||||
|
i::ContextifyContext::attach(scope, sandbox_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[op2]
|
||||||
|
pub fn op_vm_is_context(
|
||||||
|
scope: &mut v8::HandleScope,
|
||||||
|
sandbox_obj: v8::Local<v8::Value>,
|
||||||
|
) -> bool {
|
||||||
|
sandbox_obj
|
||||||
|
.try_into()
|
||||||
|
.map(|sandbox_obj| {
|
||||||
|
i::ContextifyContext::is_contextify_context(scope, sandbox_obj)
|
||||||
|
})
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use deno_core::v8;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_run_in_this_context() {
|
||||||
|
let platform = v8::new_default_platform(0, false).make_shared();
|
||||||
|
v8::V8::initialize_platform(platform);
|
||||||
|
v8::V8::initialize();
|
||||||
|
|
||||||
|
let isolate = &mut v8::Isolate::new(Default::default());
|
||||||
|
|
||||||
|
let scope = &mut v8::HandleScope::new(isolate);
|
||||||
|
let context = v8::Context::new(scope);
|
||||||
|
let scope = &mut v8::ContextScope::new(scope, context);
|
||||||
|
|
||||||
|
let source = v8::String::new(scope, "1 + 2").unwrap();
|
||||||
|
let script = Script::new(scope, source).unwrap();
|
||||||
|
|
||||||
|
let result = script.run_in_this_context(scope).unwrap();
|
||||||
|
assert!(result.is_number());
|
||||||
|
}
|
||||||
|
}
|
599
ext/node/ops/vm_internal.rs
Normal file
599
ext/node/ops/vm_internal.rs
Normal file
|
@ -0,0 +1,599 @@
|
||||||
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
use deno_core::error::type_error;
|
||||||
|
use deno_core::error::AnyError;
|
||||||
|
use deno_core::v8;
|
||||||
|
use deno_core::v8::MapFnTo;
|
||||||
|
|
||||||
|
pub const PRIVATE_SYMBOL_NAME: v8::OneByteConst =
|
||||||
|
v8::String::create_external_onebyte_const(b"node:contextify:context");
|
||||||
|
|
||||||
|
/// An unbounded script that can be run in a context.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ContextifyScript {
|
||||||
|
script: v8::Global<v8::UnboundScript>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContextifyScript {
|
||||||
|
pub fn new(
|
||||||
|
scope: &mut v8::HandleScope,
|
||||||
|
source_str: v8::Local<v8::String>,
|
||||||
|
) -> Result<Self, AnyError> {
|
||||||
|
let source = v8::script_compiler::Source::new(source_str, None);
|
||||||
|
|
||||||
|
let unbound_script = v8::script_compiler::compile_unbound_script(
|
||||||
|
scope,
|
||||||
|
source,
|
||||||
|
v8::script_compiler::CompileOptions::NoCompileOptions,
|
||||||
|
v8::script_compiler::NoCacheReason::NoReason,
|
||||||
|
)
|
||||||
|
.ok_or_else(|| type_error("Failed to compile script"))?;
|
||||||
|
let script = v8::Global::new(scope, unbound_script);
|
||||||
|
Ok(Self { script })
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(littledivy): Support `options`
|
||||||
|
pub fn eval_machine<'s>(
|
||||||
|
&self,
|
||||||
|
scope: &mut v8::HandleScope<'s>,
|
||||||
|
_context: v8::Local<v8::Context>,
|
||||||
|
) -> Option<v8::Local<'s, v8::Value>> {
|
||||||
|
let tc_scope = &mut v8::TryCatch::new(scope);
|
||||||
|
|
||||||
|
let unbound_script = v8::Local::new(tc_scope, self.script.clone());
|
||||||
|
let script = unbound_script.bind_to_current_context(tc_scope);
|
||||||
|
|
||||||
|
let result = script.run(tc_scope);
|
||||||
|
|
||||||
|
if tc_scope.has_caught() {
|
||||||
|
// If there was an exception thrown during script execution, re-throw it.
|
||||||
|
if !tc_scope.has_terminated() {
|
||||||
|
tc_scope.rethrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(result.unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ContextifyContext {
|
||||||
|
context: v8::Global<v8::Context>,
|
||||||
|
sandbox: v8::Global<v8::Object>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContextifyContext {
|
||||||
|
pub fn attach(
|
||||||
|
scope: &mut v8::HandleScope,
|
||||||
|
sandbox_obj: v8::Local<v8::Object>,
|
||||||
|
) {
|
||||||
|
let tmp = init_global_template(scope);
|
||||||
|
|
||||||
|
let context = create_v8_context(scope, tmp, None);
|
||||||
|
Self::from_context(scope, context, sandbox_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_context(
|
||||||
|
scope: &mut v8::HandleScope,
|
||||||
|
v8_context: v8::Local<v8::Context>,
|
||||||
|
sandbox_obj: v8::Local<v8::Object>,
|
||||||
|
) {
|
||||||
|
let main_context = scope.get_current_context();
|
||||||
|
v8_context.set_security_token(main_context.get_security_token(scope));
|
||||||
|
|
||||||
|
let context = v8::Global::new(scope, v8_context);
|
||||||
|
let sandbox = v8::Global::new(scope, sandbox_obj);
|
||||||
|
let wrapper =
|
||||||
|
deno_core::cppgc::make_cppgc_object(scope, Self { context, sandbox });
|
||||||
|
let ptr = deno_core::cppgc::try_unwrap_cppgc_object::<Self>(wrapper.into())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
v8_context.set_aligned_pointer_in_embedder_data(
|
||||||
|
0,
|
||||||
|
ptr 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,
|
||||||
|
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>(wrapper)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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> {
|
||||||
|
v8::Local::new(scope, &self.context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn global_proxy<'s>(
|
||||||
|
&self,
|
||||||
|
scope: &mut v8::HandleScope<'s>,
|
||||||
|
) -> 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> {
|
||||||
|
v8::Local::new(scope, &self.sandbox)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get<'a, 'c>(
|
||||||
|
scope: &mut v8::HandleScope<'a>,
|
||||||
|
object: v8::Local<'a, v8::Object>,
|
||||||
|
) -> Option<&'c ContextifyContext> {
|
||||||
|
let Some(context) = object.get_creation_context(scope) else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
let context_ptr = context.get_aligned_pointer_from_embedder_data(0);
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
fn create_v8_context<'a>(
|
||||||
|
scope: &mut v8::HandleScope<'a>,
|
||||||
|
object_template: v8::Local<v8::ObjectTemplate>,
|
||||||
|
snapshot_data: Option<&'static [u8]>,
|
||||||
|
) -> v8::Local<'a, v8::Context> {
|
||||||
|
let scope = &mut v8::EscapableHandleScope::new(scope);
|
||||||
|
|
||||||
|
let context = if let Some(_snapshot_data) = snapshot_data {
|
||||||
|
v8::Context::from_snapshot(scope, VM_CONTEXT_INDEX).unwrap()
|
||||||
|
} else {
|
||||||
|
v8::Context::new_from_template(scope, object_template)
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.escape(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct SlotContextifyGlobalTemplate(v8::Global<v8::ObjectTemplate>);
|
||||||
|
|
||||||
|
fn init_global_template<'a>(
|
||||||
|
scope: &mut v8::HandleScope<'a>,
|
||||||
|
) -> v8::Local<'a, v8::ObjectTemplate> {
|
||||||
|
let mut maybe_object_template_slot =
|
||||||
|
scope.get_slot::<SlotContextifyGlobalTemplate>();
|
||||||
|
|
||||||
|
if maybe_object_template_slot.is_none() {
|
||||||
|
init_global_template_inner(scope);
|
||||||
|
maybe_object_template_slot =
|
||||||
|
scope.get_slot::<SlotContextifyGlobalTemplate>();
|
||||||
|
}
|
||||||
|
let object_template_slot = maybe_object_template_slot
|
||||||
|
.expect("ContextifyGlobalTemplate slot should be already populated.")
|
||||||
|
.clone();
|
||||||
|
v8::Local::new(scope, object_template_slot.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn c_noop(_: *const v8::FunctionCallbackInfo) {}
|
||||||
|
|
||||||
|
// Using thread_local! to get around compiler bug.
|
||||||
|
//
|
||||||
|
// See NOTE in ext/node/global.rs#L12
|
||||||
|
thread_local! {
|
||||||
|
pub static GETTER_MAP_FN: v8::GenericNamedPropertyGetterCallback<'static> = property_getter.map_fn_to();
|
||||||
|
pub static SETTER_MAP_FN: v8::GenericNamedPropertySetterCallback<'static> = property_setter.map_fn_to();
|
||||||
|
pub static DELETER_MAP_FN: v8::GenericNamedPropertyGetterCallback<'static> = property_deleter.map_fn_to();
|
||||||
|
pub static ENUMERATOR_MAP_FN: v8::GenericNamedPropertyEnumeratorCallback<'static> = property_enumerator.map_fn_to();
|
||||||
|
pub static DEFINER_MAP_FN: v8::GenericNamedPropertyDefinerCallback<'static> = property_definer.map_fn_to();
|
||||||
|
pub static DESCRIPTOR_MAP_FN: v8::GenericNamedPropertyGetterCallback<'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::IndexedPropertyGetterCallback<'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::IndexedPropertyGetterCallback<'static> = indexed_property_descriptor.map_fn_to();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_global_template_inner(scope: &mut v8::HandleScope) {
|
||||||
|
let global_func_template =
|
||||||
|
v8::FunctionTemplate::builder_raw(c_noop).build(scope);
|
||||||
|
let global_object_template = global_func_template.instance_template(scope);
|
||||||
|
|
||||||
|
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 = 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_DELETER_MAP_FN.with(|deleter| config.deleter_raw(*deleter));
|
||||||
|
config =
|
||||||
|
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);
|
||||||
|
let contextify_global_template_slot = SlotContextifyGlobalTemplate(
|
||||||
|
v8::Global::new(scope, global_object_template),
|
||||||
|
);
|
||||||
|
scope.set_slot(contextify_global_template_slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn property_getter<'s>(
|
||||||
|
scope: &mut v8::HandleScope<'s>,
|
||||||
|
key: v8::Local<'s, v8::Name>,
|
||||||
|
args: v8::PropertyCallbackArguments<'s>,
|
||||||
|
mut ret: v8::ReturnValue,
|
||||||
|
) {
|
||||||
|
let ctx = ContextifyContext::get(scope, args.this()).unwrap();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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>,
|
||||||
|
mut rv: v8::ReturnValue,
|
||||||
|
) {
|
||||||
|
let ctx = ContextifyContext::get(scope, args.this()).unwrap();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !is_declared && key.is_symbol() {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if ctx.sandbox(scope).set(scope, key.into(), value).is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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"get").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)
|
||||||
|
{
|
||||||
|
rv.set(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn property_deleter<'s>(
|
||||||
|
scope: &mut v8::HandleScope<'s>,
|
||||||
|
key: v8::Local<'s, v8::Name>,
|
||||||
|
args: v8::PropertyCallbackArguments<'s>,
|
||||||
|
mut rv: v8::ReturnValue,
|
||||||
|
) {
|
||||||
|
let ctx = ContextifyContext::get(scope, args.this()).unwrap();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv.set_bool(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn property_enumerator<'s>(
|
||||||
|
scope: &mut v8::HandleScope<'s>,
|
||||||
|
args: v8::PropertyCallbackArguments<'s>,
|
||||||
|
mut rv: v8::ReturnValue,
|
||||||
|
) {
|
||||||
|
let ctx = ContextifyContext::get(scope, args.this()).unwrap();
|
||||||
|
|
||||||
|
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.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn property_definer<'s>(
|
||||||
|
scope: &mut v8::HandleScope<'s>,
|
||||||
|
key: v8::Local<'s, v8::Name>,
|
||||||
|
desc: &v8::PropertyDescriptor,
|
||||||
|
args: v8::PropertyCallbackArguments<'s>,
|
||||||
|
_: v8::ReturnValue,
|
||||||
|
) {
|
||||||
|
let ctx = ContextifyContext::get(scope, args.this()).unwrap();
|
||||||
|
|
||||||
|
let context = ctx.context(scope);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sandbox = ctx.sandbox(scope);
|
||||||
|
let scope = &mut v8::ContextScope::new(scope, context);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn property_descriptor<'s>(
|
||||||
|
scope: &mut v8::HandleScope<'s>,
|
||||||
|
key: v8::Local<'s, v8::Name>,
|
||||||
|
args: v8::PropertyCallbackArguments<'s>,
|
||||||
|
mut rv: v8::ReturnValue,
|
||||||
|
) {
|
||||||
|
let ctx = ContextifyContext::get(scope, args.this()).unwrap();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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_getter<'s>(
|
||||||
|
scope: &mut v8::HandleScope<'s>,
|
||||||
|
index: u32,
|
||||||
|
args: v8::PropertyCallbackArguments<'s>,
|
||||||
|
rv: v8::ReturnValue,
|
||||||
|
) {
|
||||||
|
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,
|
||||||
|
) {
|
||||||
|
let key = uint32_to_name(scope, index);
|
||||||
|
property_setter(scope, key, value, args, rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn indexed_property_deleter<'s>(
|
||||||
|
scope: &mut v8::HandleScope<'s>,
|
||||||
|
index: u32,
|
||||||
|
args: v8::PropertyCallbackArguments<'s>,
|
||||||
|
mut rv: v8::ReturnValue,
|
||||||
|
) {
|
||||||
|
let ctx = ContextifyContext::get(scope, args.this()).unwrap();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete failed on the sandbox, intercept and do not delete on
|
||||||
|
// the global object.
|
||||||
|
rv.set_bool(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn indexed_property_definer<'s>(
|
||||||
|
scope: &mut v8::HandleScope<'s>,
|
||||||
|
index: u32,
|
||||||
|
descriptor: &v8::PropertyDescriptor,
|
||||||
|
args: v8::PropertyCallbackArguments<'s>,
|
||||||
|
rv: v8::ReturnValue,
|
||||||
|
) {
|
||||||
|
let key = uint32_to_name(scope, index);
|
||||||
|
property_definer(scope, key, descriptor, args, rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn indexed_property_descriptor<'s>(
|
||||||
|
scope: &mut v8::HandleScope<'s>,
|
||||||
|
index: u32,
|
||||||
|
args: v8::PropertyCallbackArguments<'s>,
|
||||||
|
rv: v8::ReturnValue,
|
||||||
|
) {
|
||||||
|
let key = uint32_to_name(scope, index);
|
||||||
|
property_descriptor(scope, key, args, rv);
|
||||||
|
}
|
|
@ -1,46 +1,48 @@
|
||||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
// TODO(petamoriken): enable prefer-primordials for node polyfills
|
// deno-lint-ignore-file no-explicit-any
|
||||||
// deno-lint-ignore-file no-explicit-any prefer-primordials
|
|
||||||
|
|
||||||
import { core } from "ext:core/mod.js";
|
|
||||||
import { notImplemented } from "ext:deno_node/_utils.ts";
|
import { notImplemented } from "ext:deno_node/_utils.ts";
|
||||||
import { op_vm_run_in_new_context } from "ext:core/ops";
|
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 {
|
export class Script {
|
||||||
code: string;
|
#inner;
|
||||||
|
|
||||||
constructor(code: string, _options = {}) {
|
constructor(code: string, _options = {}) {
|
||||||
this.code = `${code}`;
|
this.#inner = op_vm_create_script(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
runInThisContext(_options: any) {
|
runInThisContext(_options: any) {
|
||||||
const [result, error] = core.evalContext(this.code, "data:");
|
return op_vm_script_run_in_this_context(this.#inner);
|
||||||
if (error) {
|
|
||||||
throw error.thrown;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
runInContext(_contextifiedObject: any, _options: any) {
|
runInContext(contextifiedObject: any, _options: any) {
|
||||||
notImplemented("Script.prototype.runInContext");
|
return op_vm_script_run_in_context(this.#inner, contextifiedObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
runInNewContext(contextObject: any, options: any) {
|
runInNewContext(contextObject: any, options: any) {
|
||||||
if (options) {
|
const context = createContext(contextObject);
|
||||||
console.warn(
|
return this.runInContext(context, options);
|
||||||
"Script.runInNewContext options are currently not supported",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return op_vm_run_in_new_context(this.code, contextObject);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createCachedData() {
|
createCachedData() {
|
||||||
notImplemented("Script.prototyp.createCachedData");
|
notImplemented("Script.prototype.createCachedData");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createContext(_contextObject: any, _options: any) {
|
export function createContext(contextObject: any = {}, _options: any) {
|
||||||
notImplemented("createContext");
|
if (isContext(contextObject)) {
|
||||||
|
return contextObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
op_vm_create_context(contextObject);
|
||||||
|
return contextObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createScript(code: string, options: any) {
|
export function createScript(code: string, options: any) {
|
||||||
|
@ -48,11 +50,11 @@ export function createScript(code: string, options: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function runInContext(
|
export function runInContext(
|
||||||
_code: string,
|
code: string,
|
||||||
_contextifiedObject: any,
|
contextifiedObject: any,
|
||||||
_options: any,
|
_options: any,
|
||||||
) {
|
) {
|
||||||
notImplemented("runInContext");
|
return createScript(code).runInContext(contextifiedObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function runInNewContext(
|
export function runInNewContext(
|
||||||
|
@ -63,7 +65,7 @@ export function runInNewContext(
|
||||||
if (options) {
|
if (options) {
|
||||||
console.warn("vm.runInNewContext options are currently not supported");
|
console.warn("vm.runInNewContext options are currently not supported");
|
||||||
}
|
}
|
||||||
return op_vm_run_in_new_context(code, contextObject);
|
return createScript(code).runInNewContext(contextObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function runInThisContext(
|
export function runInThisContext(
|
||||||
|
@ -73,9 +75,8 @@ export function runInThisContext(
|
||||||
return createScript(code, options).runInThisContext(options);
|
return createScript(code, options).runInThisContext(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isContext(_maybeContext: any) {
|
export function isContext(maybeContext: any) {
|
||||||
// TODO(@littledivy): Currently we do not expose contexts so this is always false.
|
return op_vm_is_context(maybeContext);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function compileFunction(_code: string, _params: any, _options: any) {
|
export function compileFunction(_code: string, _params: any, _options: any) {
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||||
import { isContext, runInNewContext } from "node:vm";
|
|
||||||
import { assertEquals, assertThrows } from "@std/assert/mod.ts";
|
import { assertEquals, assertThrows } from "@std/assert/mod.ts";
|
||||||
|
import {
|
||||||
|
createContext,
|
||||||
|
isContext,
|
||||||
|
runInContext,
|
||||||
|
runInNewContext,
|
||||||
|
runInThisContext,
|
||||||
|
Script,
|
||||||
|
} from "node:vm";
|
||||||
|
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "vm runInNewContext",
|
name: "vm runInNewContext",
|
||||||
|
@ -10,16 +17,76 @@ Deno.test({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Deno.test({
|
||||||
|
name: "vm new Script()",
|
||||||
|
fn() {
|
||||||
|
const script = new Script(`
|
||||||
|
function add(a, b) {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
const x = add(1, 2);
|
||||||
|
x
|
||||||
|
`);
|
||||||
|
|
||||||
|
const value = script.runInThisContext();
|
||||||
|
assertEquals(value, 3);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// https://github.com/denoland/deno/issues/23186
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "vm runInNewContext sandbox",
|
name: "vm runInNewContext sandbox",
|
||||||
fn() {
|
fn() {
|
||||||
assertThrows(() => runInNewContext("Deno"));
|
const sandbox = { fromAnotherRealm: false };
|
||||||
// deno-lint-ignore no-var
|
runInNewContext("fromAnotherRealm = {}", sandbox);
|
||||||
var a = 1;
|
|
||||||
assertThrows(() => runInNewContext("a + 1"));
|
|
||||||
|
|
||||||
runInNewContext("a = 2");
|
assertEquals(typeof sandbox.fromAnotherRealm, "object");
|
||||||
assertEquals(a, 1);
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// https://github.com/denoland/deno/issues/22395
|
||||||
|
Deno.test({
|
||||||
|
name: "vm runInewContext with context object",
|
||||||
|
fn() {
|
||||||
|
const context = { a: 1, b: 2 };
|
||||||
|
const result = runInNewContext("a + b", context);
|
||||||
|
assertEquals(result, 3);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// https://github.com/denoland/deno/issues/18299
|
||||||
|
Deno.test({
|
||||||
|
name: "vm createContext and runInContext",
|
||||||
|
fn() {
|
||||||
|
// @ts-expect-error implicit any
|
||||||
|
globalThis.globalVar = 3;
|
||||||
|
|
||||||
|
const context = { globalVar: 1 };
|
||||||
|
createContext(context);
|
||||||
|
runInContext("globalVar *= 2", context);
|
||||||
|
assertEquals(context.globalVar, 2);
|
||||||
|
// @ts-expect-error implicit any
|
||||||
|
assertEquals(globalThis.globalVar, 3);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test({
|
||||||
|
name: "vm runInThisContext Error rethrow",
|
||||||
|
fn() {
|
||||||
|
assertThrows(
|
||||||
|
() => {
|
||||||
|
runInThisContext("throw new Error('error')");
|
||||||
|
},
|
||||||
|
Error,
|
||||||
|
"error",
|
||||||
|
);
|
||||||
|
assertThrows(
|
||||||
|
() => {
|
||||||
|
runInThisContext("throw new TypeError('type error')");
|
||||||
|
},
|
||||||
|
TypeError,
|
||||||
|
"type error",
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -53,6 +120,7 @@ Deno.test({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// https://github.com/denoland/deno/issues/18315
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "vm isContext",
|
name: "vm isContext",
|
||||||
fn() {
|
fn() {
|
||||||
|
|
Loading…
Add table
Reference in a new issue