From 469b6de004405221e94139267e7c24e87317b7c4 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sun, 10 Dec 2023 18:58:22 +0530 Subject: [PATCH 1/5] fix(ext/node): basic vm.runInNewContext implementation --- ext/node/lib.rs | 1 + ext/node/ops/v8.rs | 24 ++++++++++++++++++++++++ ext/node/polyfills/vm.ts | 11 ++++++----- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/ext/node/lib.rs b/ext/node/lib.rs index 547f1d60a1ce43..c282c3b47679d2 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -243,6 +243,7 @@ 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::idna::op_node_idna_domain_to_ascii, ops::idna::op_node_idna_domain_to_unicode, ops::idna::op_node_idna_punycode_decode, diff --git a/ext/node/ops/v8.rs b/ext/node/ops/v8.rs index dbb84e9329ba1f..d304d19613f327 100644 --- a/ext/node/ops/v8.rs +++ b/ext/node/ops/v8.rs @@ -1,4 +1,5 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +use deno_core::error::AnyError; use deno_core::op2; use deno_core::v8; @@ -30,3 +31,26 @@ pub fn op_v8_get_heap_statistics( buffer[12] = stats.used_global_handles_size() as f64; buffer[13] = stats.external_memory() as f64; } + +#[op2] +pub fn op_vm_run_in_new_context<'a>( + scope: &mut v8::HandleScope<'a>, + script: v8::Local, + ctx_val: v8::Local, +) -> Result, AnyError> { + let _ctx_obj = if ctx_val.is_undefined() || ctx_val.is_null() { + v8::Object::new(scope) + } else { + ctx_val.try_into()? + }; + + let template = v8::ObjectTemplate::new(scope); + let ctx = v8::Context::new_from_template(scope, template); + + let scope = &mut v8::ContextScope::new(scope, ctx); + + let script = v8::Script::compile(scope, script, None).unwrap(); + let result = script.run(scope).unwrap(); + + Ok(result) +} diff --git a/ext/node/polyfills/vm.ts b/ext/node/polyfills/vm.ts index 39cd1ce36086ea..3a32f8ccc8e2db 100644 --- a/ext/node/polyfills/vm.ts +++ b/ext/node/polyfills/vm.ts @@ -6,6 +6,7 @@ import { notImplemented } from "ext:deno_node/_utils.ts"; const { core } = globalThis.__bootstrap; +const ops = core.ops; export class Script { code: string; @@ -25,8 +26,8 @@ export class Script { notImplemented("Script.prototype.runInContext"); } - runInNewContext(_contextObject: any, _options: any) { - notImplemented("Script.prototype.runInNewContext"); + runInNewContext(contextObject: any, _options: any) { + return ops.op_vm_run_in_new_context(this.code, contextObject); } createCachedData() { @@ -51,11 +52,11 @@ export function runInContext( } export function runInNewContext( - _code: string, - _contextObject: any, + code: string, + contextObject: any, _options: any, ) { - notImplemented("runInNewContext"); + return ops.op_vm_run_in_new_context(code, contextObject); } export function runInThisContext( From eab995f719401614627f7d0305c466d62e05a92a Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Mon, 11 Dec 2023 11:32:15 +0530 Subject: [PATCH 2/5] Add tests --- cli/tests/unit_node/vm_test.ts | 56 ++++++++++++++++++++++++++++++++++ ext/node/lib.rs | 1 + ext/node/ops/v8.rs | 34 ++++++++++++++++++--- runtime/snapshot.rs | 9 +++++- 4 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 cli/tests/unit_node/vm_test.ts diff --git a/cli/tests/unit_node/vm_test.ts b/cli/tests/unit_node/vm_test.ts new file mode 100644 index 00000000000000..8b45270d4d9be3 --- /dev/null +++ b/cli/tests/unit_node/vm_test.ts @@ -0,0 +1,56 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { runInNewContext } from "node:vm"; +import { + assertEquals, + assertThrows, +} from "../../../test_util/std/assert/mod.ts"; + +Deno.test({ + name: "vm runInNewContext", + fn() { + const two = runInNewContext("1 + 1"); + assertEquals(two, 2); + }, +}); + +Deno.test({ + name: "vm runInNewContext sandbox", + fn() { + assertThrows(() => runInNewContext("Deno")); + var a = 1; + assertThrows(() => runInNewContext("a + 1")); + + runInNewContext("a = 2"); + assertEquals(a, 1); + }, +}); + +// https://github.com/webpack/webpack/blob/87660921808566ef3b8796f8df61bd79fc026108/lib/javascript/JavascriptParser.js#L4329 +Deno.test({ + name: "vm runInNewContext webpack magic comments", + fn() { + const webpackCommentRegExp = new RegExp( + /(^|\W)webpack[A-Z]{1,}[A-Za-z]{1,}:/, + ); + const comments = [ + 'webpackChunkName: "test"', + 'webpackMode: "lazy"', + "webpackPrefetch: true", + "webpackPreload: true", + "webpackProvidedExports: true", + 'webpackChunkLoading: "require"', + 'webpackExports: ["default", "named"]', + ]; + + for (const comment of comments) { + const result = webpackCommentRegExp.test(comment); + assertEquals(result, true); + + const [[key, value]]: [string, string][] = Object.entries( + runInNewContext(`(function(){return {${comment}};})()`), + ); + const expectedKey = comment.split(":")[0].trim(); + assertEquals(key, expectedKey); + } + }, +}); diff --git a/ext/node/lib.rs b/ext/node/lib.rs index c282c3b47679d2..56f4b0ee075dfd 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -30,6 +30,7 @@ mod path; mod polyfill; mod resolution; +pub use ops::v8::VM_CONTEXT_INDEX; pub use package_json::PackageJson; pub use path::PathClean; pub use polyfill::is_builtin_node_module; diff --git a/ext/node/ops/v8.rs b/ext/node/ops/v8.rs index d304d19613f327..983039ee130e03 100644 --- a/ext/node/ops/v8.rs +++ b/ext/node/ops/v8.rs @@ -32,6 +32,16 @@ pub fn op_v8_get_heap_statistics( buffer[13] = stats.external_memory() as f64; } +pub const VM_CONTEXT_INDEX: usize = 0; + +fn make_context<'a>( + scope: &mut v8::HandleScope<'a>, +) -> v8::Local<'a, v8::Context> { + let scope = &mut v8::EscapableHandleScope::new(scope); + let context = v8::Context::from_snapshot(scope, VM_CONTEXT_INDEX).unwrap(); + scope.escape(context).into() +} + #[op2] pub fn op_vm_run_in_new_context<'a>( scope: &mut v8::HandleScope<'a>, @@ -44,13 +54,27 @@ pub fn op_vm_run_in_new_context<'a>( ctx_val.try_into()? }; - let template = v8::ObjectTemplate::new(scope); - let ctx = v8::Context::new_from_template(scope, template); + let ctx = make_context(scope); let scope = &mut v8::ContextScope::new(scope, ctx); - let script = v8::Script::compile(scope, script, None).unwrap(); - let result = script.run(scope).unwrap(); + let tc_scope = &mut v8::TryCatch::new(scope); + let script = match v8::Script::compile(tc_scope, script, None) { + Some(s) => s, + None => { + assert!(tc_scope.has_caught()); + tc_scope.rethrow(); + return Ok(v8::undefined(tc_scope).into()); + } + }; + + Ok(match script.run(tc_scope) { + Some(result) => result, + None => { + assert!(tc_scope.has_caught()); + tc_scope.rethrow(); - Ok(result) + v8::undefined(tc_scope).into() + } + }) } diff --git a/runtime/snapshot.rs b/runtime/snapshot.rs index c2e8b0df246f8d..6a9fb4a2b783ff 100644 --- a/runtime/snapshot.rs +++ b/runtime/snapshot.rs @@ -7,6 +7,7 @@ use crate::shared::runtime; use deno_cache::SqliteBackedCache; use deno_core::error::AnyError; use deno_core::snapshot_util::*; +use deno_core::v8; use deno_core::Extension; use deno_http::DefaultHttpPropertyExtractor; use std::path::Path; @@ -261,7 +262,13 @@ pub fn create_runtime_snapshot( startup_snapshot: None, extensions, compression_cb: None, - with_runtime_cb: None, + with_runtime_cb: Some(Box::new(|rt| { + let isolate = rt.v8_isolate(); + let scope = &mut v8::HandleScope::new(isolate); + + let ctx = v8::Context::new(scope); + assert_eq!(scope.add_context(ctx), deno_node::VM_CONTEXT_INDEX); + })), skip_op_registration: false, }); for path in output.files_loaded_during_snapshot { From c043a34d27caadddeef705c260fe6e49504b920a Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Mon, 11 Dec 2023 11:35:32 +0530 Subject: [PATCH 3/5] Fix lint --- cli/tests/unit_node/vm_test.ts | 3 ++- ext/node/ops/v8.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cli/tests/unit_node/vm_test.ts b/cli/tests/unit_node/vm_test.ts index 8b45270d4d9be3..c43495e1d38dd3 100644 --- a/cli/tests/unit_node/vm_test.ts +++ b/cli/tests/unit_node/vm_test.ts @@ -17,6 +17,7 @@ Deno.test({ name: "vm runInNewContext sandbox", fn() { assertThrows(() => runInNewContext("Deno")); + // deno-lint-ignore no-var var a = 1; assertThrows(() => runInNewContext("a + 1")); @@ -46,7 +47,7 @@ Deno.test({ const result = webpackCommentRegExp.test(comment); assertEquals(result, true); - const [[key, value]]: [string, string][] = Object.entries( + const [[key, _value]]: [string, string][] = Object.entries( runInNewContext(`(function(){return {${comment}};})()`), ); const expectedKey = comment.split(":")[0].trim(); diff --git a/ext/node/ops/v8.rs b/ext/node/ops/v8.rs index 983039ee130e03..17af493589a892 100644 --- a/ext/node/ops/v8.rs +++ b/ext/node/ops/v8.rs @@ -39,7 +39,7 @@ fn make_context<'a>( ) -> v8::Local<'a, v8::Context> { let scope = &mut v8::EscapableHandleScope::new(scope); let context = v8::Context::from_snapshot(scope, VM_CONTEXT_INDEX).unwrap(); - scope.escape(context).into() + scope.escape(context) } #[op2] From b20d03362e79294bc599e326b3e165f1b284300a Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Mon, 11 Dec 2023 11:37:10 +0530 Subject: [PATCH 4/5] Review --- ext/node/polyfills/vm.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ext/node/polyfills/vm.ts b/ext/node/polyfills/vm.ts index 3a32f8ccc8e2db..45c67526ed4e09 100644 --- a/ext/node/polyfills/vm.ts +++ b/ext/node/polyfills/vm.ts @@ -26,7 +26,12 @@ export class Script { notImplemented("Script.prototype.runInContext"); } - runInNewContext(contextObject: any, _options: any) { + runInNewContext(contextObject: any, options: any) { + if (options) { + console.warn( + "Script.runInNewContext options are currently not supported", + ); + } return ops.op_vm_run_in_new_context(this.code, contextObject); } @@ -54,8 +59,11 @@ export function runInContext( export function runInNewContext( code: string, contextObject: any, - _options: any, + options: any, ) { + if (options) { + console.warn("vm.runInNewContext options are currently not supported"); + } return ops.op_vm_run_in_new_context(code, contextObject); } From e1c8901f638ab9712d7156b741bcd3ddfc6062ad Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Mon, 11 Dec 2023 12:13:08 +0530 Subject: [PATCH 5/5] update for glob --- cli/tests/integration/node_unit_tests.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/cli/tests/integration/node_unit_tests.rs b/cli/tests/integration/node_unit_tests.rs index 9aad274e38389e..14847e9db4f00e 100644 --- a/cli/tests/integration/node_unit_tests.rs +++ b/cli/tests/integration/node_unit_tests.rs @@ -82,6 +82,7 @@ util::unit_test_factory!( tty_test, util_test, v8_test, + vm_test, worker_threads_test, zlib_test ]