Skip to content

Commit

Permalink
fix(ext/node): node:vm contexts (denoland#23202)
Browse files Browse the repository at this point in the history
Implement contextified objects in `node:vm`

Fixes denoland#23186
Fixes denoland#22395
Fixes denoland#20607
Fixes denoland#18299
Fixes denoland#19395
Fixes denoland#18315
Fixes denoland#18319
Fixes denoland#23183
  • Loading branch information
littledivy committed Apr 19, 2024
1 parent 884eb01 commit 69b5fba
Show file tree
Hide file tree
Showing 6 changed files with 849 additions and 37 deletions.
6 changes: 5 additions & 1 deletion ext/node/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,11 @@ deno_core::extension!(deno_node,
ops::winerror::op_node_sys_to_uv_error,
ops::v8::op_v8_cached_data_version_tag,
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_unicode,
ops::idna::op_node_idna_punycode_to_ascii,
Expand Down
2 changes: 2 additions & 0 deletions ext/node/ops/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ pub mod process;
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;
138 changes: 138 additions & 0 deletions ext/node/ops/vm.rs
Original file line number Diff line number Diff line change
@@ -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());
}
}
Loading

0 comments on commit 69b5fba

Please sign in to comment.