summaryrefslogtreecommitdiff
path: root/ext/node/polyfills
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 /ext/node/polyfills
parentc0e9512b39a4ed3713d1fd9b28469d0edf68f578 (diff)
feat: vm rewrite (#24596)
rewrite vm implementation to increase compat. vm.Module+importModuleDynamically callbacks should be added in a followup.
Diffstat (limited to 'ext/node/polyfills')
-rw-r--r--ext/node/polyfills/assert.ts3
-rw-r--r--ext/node/polyfills/vm.js359
-rw-r--r--ext/node/polyfills/vm.ts100
3 files changed, 362 insertions, 100 deletions
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,
-};