mirror of
https://github.com/denoland/deno.git
synced 2024-12-23 15:49:44 -05:00
fix(ext/node): basic vm.runInNewContext implementation (#21527)
Simple implementation to support webpack (& Next.js):
8766092180/lib/javascript/JavascriptParser.js (L4329)
This commit is contained in:
parent
0bee37a5e2
commit
02e138dca9
6 changed files with 131 additions and 7 deletions
|
@ -82,6 +82,7 @@ util::unit_test_factory!(
|
|||
tty_test,
|
||||
util_test,
|
||||
v8_test,
|
||||
vm_test,
|
||||
worker_threads_test,
|
||||
zlib_test
|
||||
]
|
||||
|
|
57
cli/tests/unit_node/vm_test.ts
Normal file
57
cli/tests/unit_node/vm_test.ts
Normal file
|
@ -0,0 +1,57 @@
|
|||
// 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"));
|
||||
// deno-lint-ignore no-var
|
||||
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);
|
||||
}
|
||||
},
|
||||
});
|
|
@ -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;
|
||||
|
@ -243,6 +244,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,
|
||||
|
|
|
@ -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,50 @@ pub fn op_v8_get_heap_statistics(
|
|||
buffer[12] = stats.used_global_handles_size() as f64;
|
||||
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)
|
||||
}
|
||||
|
||||
#[op2]
|
||||
pub fn op_vm_run_in_new_context<'a>(
|
||||
scope: &mut v8::HandleScope<'a>,
|
||||
script: v8::Local<v8::String>,
|
||||
ctx_val: v8::Local<v8::Value>,
|
||||
) -> Result<v8::Local<'a, v8::Value>, AnyError> {
|
||||
let _ctx_obj = if ctx_val.is_undefined() || ctx_val.is_null() {
|
||||
v8::Object::new(scope)
|
||||
} else {
|
||||
ctx_val.try_into()?
|
||||
};
|
||||
|
||||
let ctx = make_context(scope);
|
||||
|
||||
let scope = &mut v8::ContextScope::new(scope, ctx);
|
||||
|
||||
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();
|
||||
|
||||
v8::undefined(tc_scope).into()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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,13 @@ export class Script {
|
|||
notImplemented("Script.prototype.runInContext");
|
||||
}
|
||||
|
||||
runInNewContext(_contextObject: any, _options: any) {
|
||||
notImplemented("Script.prototype.runInNewContext");
|
||||
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);
|
||||
}
|
||||
|
||||
createCachedData() {
|
||||
|
@ -51,11 +57,14 @@ export function runInContext(
|
|||
}
|
||||
|
||||
export function runInNewContext(
|
||||
_code: string,
|
||||
_contextObject: any,
|
||||
_options: any,
|
||||
code: string,
|
||||
contextObject: any,
|
||||
options: any,
|
||||
) {
|
||||
notImplemented("runInNewContext");
|
||||
if (options) {
|
||||
console.warn("vm.runInNewContext options are currently not supported");
|
||||
}
|
||||
return ops.op_vm_run_in_new_context(code, contextObject);
|
||||
}
|
||||
|
||||
export function runInThisContext(
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue