diff --git a/Cargo.lock b/Cargo.lock index 9e85d56a4b..b7fea19cec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -357,15 +357,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" -[[package]] -name = "basic-toml" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c0de75129aa8d0cceaf750b89013f0e08804d6ec61416da787b35ad0d7cddf1" -dependencies = [ - "serde", -] - [[package]] name = "bencher" version = "0.1.5" @@ -607,12 +598,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" -[[package]] -name = "cooked-waker" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147be55d677052dabc6b22252d5dd0fd4c29c8c27aa4f2fbef0f94aa003b406f" - [[package]] name = "core-foundation" version = "0.9.3" @@ -973,12 +958,12 @@ dependencies = [ [[package]] name = "deno_core" -version = "0.191.0" +version = "0.193.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d7eeb863655f377ffe22d5a90f01d1b57b4d4ad0acbfeec266d5d3faf4cd1cf" dependencies = [ "anyhow", "bytes", - "cooked-waker", - "deno_ast", "deno_ops", "futures", "indexmap", @@ -1346,14 +1331,14 @@ dependencies = [ [[package]] name = "deno_ops" -version = "0.69.0" +version = "0.71.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dd95626e113f292ce758f216bb12e50c9c58e886195bcd85231e715c73d6470" dependencies = [ "deno-proc-macro-rules", "lazy-regex", "once_cell", "pmutil", - "pretty_assertions", - "prettyplease", "proc-macro-crate", "proc-macro2 1.0.60", "quote 1.0.28", @@ -1362,9 +1347,7 @@ dependencies = [ "strum_macros", "syn 1.0.109", "syn 2.0.18", - "testing_macros", "thiserror", - "trybuild", "v8", ] @@ -3679,16 +3662,6 @@ dependencies = [ "yansi", ] -[[package]] -name = "prettyplease" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" -dependencies = [ - "proc-macro2 1.0.60", - "syn 1.0.109", -] - [[package]] name = "primeorder" version = "0.13.1" @@ -4393,15 +4366,15 @@ dependencies = [ [[package]] name = "serde_v8" -version = "0.102.0" +version = "0.104.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2481189a5019f43a6b68620193578623701323754409555c36588c81ee2487de" dependencies = [ - "bencher", "bytes", "derive_more", "num-bigint", "serde", "serde_bytes", - "serde_json", "smallvec", "thiserror", "v8", @@ -5244,23 +5217,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "testing_macros" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d536c776d47c59f8f47fbf4e7062b23095be9fce218d11d9c9fb988b579dfa" -dependencies = [ - "anyhow", - "glob", - "once_cell", - "pmutil", - "proc-macro2 1.0.60", - "quote 1.0.28", - "regex", - "relative-path", - "syn 1.0.109", -] - [[package]] name = "text-size" version = "1.1.0" @@ -5650,21 +5606,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" -[[package]] -name = "trybuild" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3115bddce1b5f52dd4b5e0ec8298a66ce733e4cc6759247dc2d1c11508ec38" -dependencies = [ - "basic-toml", - "glob", - "once_cell", - "serde", - "serde_derive", - "serde_json", - "termcolor", -] - [[package]] name = "twox-hash" version = "1.6.3" diff --git a/Cargo.toml b/Cargo.toml index 0cd91de48e..5cc401a95d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,10 +6,7 @@ members = [ "bench_util", "cli", "cli/napi/sym", - "core", - "ops", "runtime", - "serde_v8", "test_ffi", "test_napi", "test_util", @@ -44,9 +41,10 @@ repository = "https://github.com/denoland/deno" v8 = { version = "0.74.1", default-features = false } deno_ast = { version = "0.27.0", features = ["transpiling"] } -deno_core = { version = "0.191.0", path = "./core" } -deno_ops = { version = "0.69.0", path = "./ops" } -serde_v8 = { version = "0.102.0", path = "./serde_v8" } +deno_core = { version = "0.193.0" } +deno_ops = { version = "0.70.0" } +serde_v8 = { version = "0.103.0" } + deno_runtime = { version = "0.117.0", path = "./runtime" } napi_sym = { version = "0.39.0", path = "./cli/napi/sym" } deno_bench_util = { version = "0.103.0", path = "./bench_util" } @@ -132,8 +130,6 @@ signature = "=1.6.4" slab = "0.4" smallvec = "1.8" socket2 = "0.4.7" -strum = { version = "0.24", features = ["derive"] } -strum_macros = "0.24" tar = "=0.4.38" tempfile = "3.4.0" thiserror = "1.0.40" diff --git a/cli/tests/integration/check_tests.rs b/cli/tests/integration/check_tests.rs index 41c568ad39..ab17713f25 100644 --- a/cli/tests/integration/check_tests.rs +++ b/cli/tests/integration/check_tests.rs @@ -194,35 +194,6 @@ fn typecheck_declarations_unstable() { output.assert_exit_code(0); } -#[test] -fn typecheck_core() { - let context = TestContext::default(); - let deno_dir = context.deno_dir(); - let test_file = deno_dir.path().join("test_deno_core_types.ts"); - std::fs::write( - &test_file, - format!( - "import \"{}\";", - deno_core::resolve_path( - &util::root_path() - .join("core") - .join("lib.deno_core.d.ts") - .to_string(), - &std::env::current_dir().unwrap() - ) - .unwrap() - ), - ) - .unwrap(); - - let args = vec!["run".to_string(), test_file.to_string()]; - let output = context.new_command().args_vec(args).split_output().run(); - - println!("stdout: {}", output.stdout()); - println!("stderr: {}", output.stderr()); - output.assert_exit_code(0); -} - #[test] fn ts_no_recheck_on_redirect() { let test_context = TestContext::default(); diff --git a/cli/tests/unit/stat_test.ts b/cli/tests/unit/stat_test.ts index 69730d439b..75693541d5 100644 --- a/cli/tests/unit/stat_test.ts +++ b/cli/tests/unit/stat_test.ts @@ -135,11 +135,11 @@ Deno.test({ permissions: { read: true } }, function lstatSyncSuccess() { assert(!modulesInfoByUrl.isDirectory); assert(modulesInfoByUrl.isSymlink); - const coreInfo = Deno.lstatSync("core"); + const coreInfo = Deno.lstatSync("cli"); assert(coreInfo.isDirectory); assert(!coreInfo.isSymlink); - const coreInfoByUrl = Deno.lstatSync(pathToAbsoluteFileUrl("core")); + const coreInfoByUrl = Deno.lstatSync(pathToAbsoluteFileUrl("cli")); assert(coreInfoByUrl.isDirectory); assert(!coreInfoByUrl.isSymlink); }); @@ -261,11 +261,11 @@ Deno.test({ permissions: { read: true } }, async function lstatSuccess() { assert(!modulesInfoByUrl.isDirectory); assert(modulesInfoByUrl.isSymlink); - const coreInfo = await Deno.lstat("core"); + const coreInfo = await Deno.lstat("cli"); assert(coreInfo.isDirectory); assert(!coreInfo.isSymlink); - const coreInfoByUrl = await Deno.lstat(pathToAbsoluteFileUrl("core")); + const coreInfoByUrl = await Deno.lstat(pathToAbsoluteFileUrl("cli")); assert(coreInfoByUrl.isDirectory); assert(!coreInfoByUrl.isSymlink); }); diff --git a/core/00_primordials.js b/core/00_primordials.js deleted file mode 100644 index 60474e649d..0000000000 --- a/core/00_primordials.js +++ /dev/null @@ -1,615 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -// Based on https://github.com/nodejs/node/blob/889ad35d3d41e376870f785b0c1b669cb732013d/lib/internal/per_context/primordials.js -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. -// This file subclasses and stores the JS builtins that come from the VM -// so that Node.js's builtin modules do not need to later look these up from -// the global proxy, which can be mutated by users. - -// Use of primordials have sometimes a dramatic impact on performance, please -// benchmark all changes made in performance-sensitive areas of the codebase. -// See: https://github.com/nodejs/node/pull/38248 - -// deno-lint-ignore-file prefer-primordials - -"use strict"; - -(() => { - const primordials = {}; - - const { - defineProperty: ReflectDefineProperty, - getOwnPropertyDescriptor: ReflectGetOwnPropertyDescriptor, - ownKeys: ReflectOwnKeys, - } = Reflect; - - // `uncurryThis` is equivalent to `func => Function.prototype.call.bind(func)`. - // It is using `bind.bind(call)` to avoid using `Function.prototype.bind` - // and `Function.prototype.call` after it may have been mutated by users. - const { apply, bind, call } = Function.prototype; - const uncurryThis = bind.bind(call); - primordials.uncurryThis = uncurryThis; - - // `applyBind` is equivalent to `func => Function.prototype.apply.bind(func)`. - // It is using `bind.bind(apply)` to avoid using `Function.prototype.bind` - // and `Function.prototype.apply` after it may have been mutated by users. - const applyBind = bind.bind(apply); - primordials.applyBind = applyBind; - - // Methods that accept a variable number of arguments, and thus it's useful to - // also create `${prefix}${key}Apply`, which uses `Function.prototype.apply`, - // instead of `Function.prototype.call`, and thus doesn't require iterator - // destructuring. - const varargsMethods = [ - // 'ArrayPrototypeConcat' is omitted, because it performs the spread - // on its own for arrays and array-likes with a truthy - // @@isConcatSpreadable symbol property. - "ArrayOf", - "ArrayPrototypePush", - "ArrayPrototypeUnshift", - // 'FunctionPrototypeCall' is omitted, since there's 'ReflectApply' - // and 'FunctionPrototypeApply'. - "MathHypot", - "MathMax", - "MathMin", - "StringPrototypeConcat", - "TypedArrayOf", - ]; - - function getNewKey(key) { - return typeof key === "symbol" - ? `Symbol${key.description[7].toUpperCase()}${key.description.slice(8)}` - : `${key[0].toUpperCase()}${key.slice(1)}`; - } - - function copyAccessor(dest, prefix, key, { enumerable, get, set }) { - ReflectDefineProperty(dest, `${prefix}Get${key}`, { - value: uncurryThis(get), - enumerable, - }); - if (set !== undefined) { - ReflectDefineProperty(dest, `${prefix}Set${key}`, { - value: uncurryThis(set), - enumerable, - }); - } - } - - function copyPropsRenamed(src, dest, prefix) { - for (const key of ReflectOwnKeys(src)) { - const newKey = getNewKey(key); - const desc = ReflectGetOwnPropertyDescriptor(src, key); - if ("get" in desc) { - copyAccessor(dest, prefix, newKey, desc); - } else { - const name = `${prefix}${newKey}`; - ReflectDefineProperty(dest, name, desc); - if (varargsMethods.includes(name)) { - ReflectDefineProperty(dest, `${name}Apply`, { - // `src` is bound as the `this` so that the static `this` points - // to the object it was defined on, - // e.g.: `ArrayOfApply` gets a `this` of `Array`: - value: applyBind(desc.value, src), - }); - } - } - } - } - - function copyPropsRenamedBound(src, dest, prefix) { - for (const key of ReflectOwnKeys(src)) { - const newKey = getNewKey(key); - const desc = ReflectGetOwnPropertyDescriptor(src, key); - if ("get" in desc) { - copyAccessor(dest, prefix, newKey, desc); - } else { - const { value } = desc; - if (typeof value === "function") { - desc.value = value.bind(src); - } - - const name = `${prefix}${newKey}`; - ReflectDefineProperty(dest, name, desc); - if (varargsMethods.includes(name)) { - ReflectDefineProperty(dest, `${name}Apply`, { - value: applyBind(value, src), - }); - } - } - } - } - - function copyPrototype(src, dest, prefix) { - for (const key of ReflectOwnKeys(src)) { - const newKey = getNewKey(key); - const desc = ReflectGetOwnPropertyDescriptor(src, key); - if ("get" in desc) { - copyAccessor(dest, prefix, newKey, desc); - } else { - const { value } = desc; - if (typeof value === "function") { - desc.value = uncurryThis(value); - } - - const name = `${prefix}${newKey}`; - ReflectDefineProperty(dest, name, desc); - if (varargsMethods.includes(name)) { - ReflectDefineProperty(dest, `${name}Apply`, { - value: applyBind(value), - }); - } - } - } - } - - // Create copies of configurable value properties of the global object - [ - "Proxy", - "globalThis", - ].forEach((name) => { - primordials[name] = globalThis[name]; - }); - - // Create copy of isNaN - primordials[isNaN.name] = isNaN; - - // Create copies of URI handling functions - [ - decodeURI, - decodeURIComponent, - encodeURI, - encodeURIComponent, - ].forEach((fn) => { - primordials[fn.name] = fn; - }); - - // Create copies of the namespace objects - [ - "JSON", - "Math", - "Proxy", - "Reflect", - ].forEach((name) => { - copyPropsRenamed(globalThis[name], primordials, name); - }); - - // Create copies of intrinsic objects - [ - "AggregateError", - "Array", - "ArrayBuffer", - "BigInt", - "BigInt64Array", - "BigUint64Array", - "Boolean", - "DataView", - "Date", - "Error", - "EvalError", - "FinalizationRegistry", - "Float32Array", - "Float64Array", - "Function", - "Int16Array", - "Int32Array", - "Int8Array", - "Map", - "Number", - "Object", - "RangeError", - "ReferenceError", - "RegExp", - "Set", - "String", - "Symbol", - "SyntaxError", - "TypeError", - "URIError", - "Uint16Array", - "Uint32Array", - "Uint8Array", - "Uint8ClampedArray", - "WeakMap", - "WeakRef", - "WeakSet", - ].forEach((name) => { - const original = globalThis[name]; - primordials[name] = original; - copyPropsRenamed(original, primordials, name); - copyPrototype(original.prototype, primordials, `${name}Prototype`); - }); - - // Create copies of intrinsic objects that require a valid `this` to call - // static methods. - // Refs: https://www.ecma-international.org/ecma-262/#sec-promise.all - [ - "Promise", - ].forEach((name) => { - const original = globalThis[name]; - primordials[name] = original; - copyPropsRenamedBound(original, primordials, name); - copyPrototype(original.prototype, primordials, `${name}Prototype`); - }); - - // Create copies of abstract intrinsic objects that are not directly exposed - // on the global object. - // Refs: https://tc39.es/ecma262/#sec-%typedarray%-intrinsic-object - [ - { name: "TypedArray", original: Reflect.getPrototypeOf(Uint8Array) }, - { - name: "ArrayIterator", - original: { - prototype: Reflect.getPrototypeOf(Array.prototype[Symbol.iterator]()), - }, - }, - { - name: "SetIterator", - original: { - prototype: Reflect.getPrototypeOf(new Set()[Symbol.iterator]()), - }, - }, - { - name: "MapIterator", - original: { - prototype: Reflect.getPrototypeOf(new Map()[Symbol.iterator]()), - }, - }, - { - name: "StringIterator", - original: { - prototype: Reflect.getPrototypeOf(String.prototype[Symbol.iterator]()), - }, - }, - { name: "Generator", original: Reflect.getPrototypeOf(function* () {}) }, - { - name: "AsyncGenerator", - original: Reflect.getPrototypeOf(async function* () {}), - }, - ].forEach(({ name, original }) => { - primordials[name] = original; - // The static %TypedArray% methods require a valid `this`, but can't be bound, - // as they need a subclass constructor as the receiver: - copyPrototype(original, primordials, name); - copyPrototype(original.prototype, primordials, `${name}Prototype`); - }); - - const { - ArrayPrototypeForEach, - ArrayPrototypeJoin, - ArrayPrototypeMap, - FunctionPrototypeCall, - ObjectDefineProperty, - ObjectFreeze, - ObjectPrototypeIsPrototypeOf, - ObjectSetPrototypeOf, - Promise, - PromisePrototype, - PromisePrototypeThen, - SymbolIterator, - TypedArrayPrototypeJoin, - } = primordials; - - // Because these functions are used by `makeSafe`, which is exposed - // on the `primordials` object, it's important to use const references - // to the primordials that they use: - const createSafeIterator = (factory, next) => { - class SafeIterator { - constructor(iterable) { - this._iterator = factory(iterable); - } - next() { - return next(this._iterator); - } - [SymbolIterator]() { - return this; - } - } - ObjectSetPrototypeOf(SafeIterator.prototype, null); - ObjectFreeze(SafeIterator.prototype); - ObjectFreeze(SafeIterator); - return SafeIterator; - }; - - const SafeArrayIterator = createSafeIterator( - primordials.ArrayPrototypeSymbolIterator, - primordials.ArrayIteratorPrototypeNext, - ); - primordials.SafeArrayIterator = SafeArrayIterator; - primordials.SafeSetIterator = createSafeIterator( - primordials.SetPrototypeSymbolIterator, - primordials.SetIteratorPrototypeNext, - ); - primordials.SafeMapIterator = createSafeIterator( - primordials.MapPrototypeSymbolIterator, - primordials.MapIteratorPrototypeNext, - ); - primordials.SafeStringIterator = createSafeIterator( - primordials.StringPrototypeSymbolIterator, - primordials.StringIteratorPrototypeNext, - ); - - const copyProps = (src, dest) => { - ArrayPrototypeForEach(ReflectOwnKeys(src), (key) => { - if (!ReflectGetOwnPropertyDescriptor(dest, key)) { - ReflectDefineProperty( - dest, - key, - ReflectGetOwnPropertyDescriptor(src, key), - ); - } - }); - }; - - /** - * @type {typeof primordials.makeSafe} - */ - const makeSafe = (unsafe, safe) => { - if (SymbolIterator in unsafe.prototype) { - const dummy = new unsafe(); - let next; // We can reuse the same `next` method. - - ArrayPrototypeForEach(ReflectOwnKeys(unsafe.prototype), (key) => { - if (!ReflectGetOwnPropertyDescriptor(safe.prototype, key)) { - const desc = ReflectGetOwnPropertyDescriptor(unsafe.prototype, key); - if ( - typeof desc.value === "function" && - desc.value.length === 0 && - SymbolIterator in (FunctionPrototypeCall(desc.value, dummy) ?? {}) - ) { - const createIterator = uncurryThis(desc.value); - next ??= uncurryThis(createIterator(dummy).next); - const SafeIterator = createSafeIterator(createIterator, next); - desc.value = function () { - return new SafeIterator(this); - }; - } - ReflectDefineProperty(safe.prototype, key, desc); - } - }); - } else { - copyProps(unsafe.prototype, safe.prototype); - } - copyProps(unsafe, safe); - - ObjectSetPrototypeOf(safe.prototype, null); - ObjectFreeze(safe.prototype); - ObjectFreeze(safe); - return safe; - }; - primordials.makeSafe = makeSafe; - - // Subclass the constructors because we need to use their prototype - // methods later. - // Defining the `constructor` is necessary here to avoid the default - // constructor which uses the user-mutable `%ArrayIteratorPrototype%.next`. - primordials.SafeMap = makeSafe( - Map, - class SafeMap extends Map { - constructor(i) { - if (i == null) { - super(); - return; - } - super(new SafeArrayIterator(i)); - } - }, - ); - primordials.SafeWeakMap = makeSafe( - WeakMap, - class SafeWeakMap extends WeakMap { - constructor(i) { - if (i == null) { - super(); - return; - } - super(new SafeArrayIterator(i)); - } - }, - ); - - primordials.SafeSet = makeSafe( - Set, - class SafeSet extends Set { - constructor(i) { - if (i == null) { - super(); - return; - } - super(new SafeArrayIterator(i)); - } - }, - ); - primordials.SafeWeakSet = makeSafe( - WeakSet, - class SafeWeakSet extends WeakSet { - constructor(i) { - if (i == null) { - super(); - return; - } - super(new SafeArrayIterator(i)); - } - }, - ); - - primordials.SafeRegExp = makeSafe( - RegExp, - class SafeRegExp extends RegExp { - constructor(pattern, flags) { - super(pattern, flags); - } - }, - ); - - primordials.SafeFinalizationRegistry = makeSafe( - FinalizationRegistry, - class SafeFinalizationRegistry extends FinalizationRegistry { - constructor(cleanupCallback) { - super(cleanupCallback); - } - }, - ); - - primordials.SafeWeakRef = makeSafe( - WeakRef, - class SafeWeakRef extends WeakRef { - constructor(target) { - super(target); - } - }, - ); - - const SafePromise = makeSafe( - Promise, - class SafePromise extends Promise { - constructor(executor) { - super(executor); - } - }, - ); - - primordials.ArrayPrototypeToString = (thisArray) => - ArrayPrototypeJoin(thisArray); - - primordials.TypedArrayPrototypeToString = (thisArray) => - TypedArrayPrototypeJoin(thisArray); - - primordials.PromisePrototypeCatch = (thisPromise, onRejected) => - PromisePrototypeThen(thisPromise, undefined, onRejected); - - const arrayToSafePromiseIterable = (array) => - new SafeArrayIterator( - ArrayPrototypeMap( - array, - (p) => { - if (ObjectPrototypeIsPrototypeOf(PromisePrototype, p)) { - return new SafePromise((c, d) => PromisePrototypeThen(p, c, d)); - } - return p; - }, - ), - ); - - /** - * Creates a Promise that is resolved with an array of results when all of the - * provided Promises resolve, or rejected when any Promise is rejected. - * @template T - * @param {Array>} values - * @returns {Promise[]>} - */ - primordials.SafePromiseAll = (values) => - // Wrapping on a new Promise is necessary to not expose the SafePromise - // prototype to user-land. - new Promise((a, b) => - SafePromise.all(arrayToSafePromiseIterable(values)).then(a, b) - ); - - // NOTE: Uncomment the following functions when you need to use them - - // /** - // * Creates a Promise that is resolved with an array of results when all - // * of the provided Promises resolve or reject. - // * @template T - // * @param {Array>} values - // * @returns {Promise[]>} - // */ - // primordials.SafePromiseAllSettled = (values) => - // // Wrapping on a new Promise is necessary to not expose the SafePromise - // // prototype to user-land. - // new Promise((a, b) => - // SafePromise.allSettled(arrayToSafePromiseIterable(values)).then(a, b) - // ); - - // /** - // * The any function returns a promise that is fulfilled by the first given - // * promise to be fulfilled, or rejected with an AggregateError containing - // * an array of rejection reasons if all of the given promises are rejected. - // * It resolves all elements of the passed iterable to promises as it runs - // * this algorithm. - // * @template T - // * @param {T} values - // * @returns {Promise>} - // */ - // primordials.SafePromiseAny = (values) => - // // Wrapping on a new Promise is necessary to not expose the SafePromise - // // prototype to user-land. - // new Promise((a, b) => - // SafePromise.any(arrayToSafePromiseIterable(values)).then(a, b) - // ); - - // /** - // * Creates a Promise that is resolved or rejected when any of the provided - // * Promises are resolved or rejected. - // * @template T - // * @param {T} values - // * @returns {Promise>} - // */ - // primordials.SafePromiseRace = (values) => - // // Wrapping on a new Promise is necessary to not expose the SafePromise - // // prototype to user-land. - // new Promise((a, b) => - // SafePromise.race(arrayToSafePromiseIterable(values)).then(a, b) - // ); - - /** - * Attaches a callback that is invoked when the Promise is settled (fulfilled or - * rejected). The resolved value cannot be modified from the callback. - * Prefer using async functions when possible. - * @param {Promise} thisPromise - * @param {() => void) | undefined | null} onFinally The callback to execute - * when the Promise is settled (fulfilled or rejected). - * @returns A Promise for the completion of the callback. - */ - primordials.SafePromisePrototypeFinally = (thisPromise, onFinally) => - // Wrapping on a new Promise is necessary to not expose the SafePromise - // prototype to user-land. - new Promise((a, b) => - new SafePromise((a, b) => PromisePrototypeThen(thisPromise, a, b)) - .finally(onFinally) - .then(a, b) - ); - - // Create getter and setter for `queueMicrotask`, it hasn't been bound yet. - let queueMicrotask = undefined; - ObjectDefineProperty(primordials, "queueMicrotask", { - get() { - return queueMicrotask; - }, - }); - primordials.setQueueMicrotask = (value) => { - if (queueMicrotask !== undefined) { - throw new Error("queueMicrotask is already defined"); - } - queueMicrotask = value; - }; - - // Renaming from `eval` is necessary because otherwise it would perform direct - // evaluation, allowing user-land access to local variables. - // This is because the identifier `eval` is somewhat treated as a keyword - primordials.indirectEval = eval; - - ObjectSetPrototypeOf(primordials, null); - ObjectFreeze(primordials); - - // Provide bootstrap namespace - globalThis.__bootstrap = { primordials }; -})(); diff --git a/core/01_core.js b/core/01_core.js deleted file mode 100644 index 674e772ae6..0000000000 --- a/core/01_core.js +++ /dev/null @@ -1,878 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -"use strict"; - -((window) => { - const { - Array, - ArrayPrototypeFill, - ArrayPrototypeMap, - ArrayPrototypePush, - Error, - ErrorCaptureStackTrace, - MapPrototypeDelete, - MapPrototypeGet, - MapPrototypeHas, - MapPrototypeSet, - ObjectAssign, - ObjectDefineProperty, - ObjectFreeze, - ObjectFromEntries, - ObjectKeys, - Promise, - PromiseReject, - PromiseResolve, - PromisePrototypeThen, - Proxy, - RangeError, - ReferenceError, - ReflectHas, - ReflectApply, - SafeArrayIterator, - SafeMap, - SafePromisePrototypeFinally, - StringPrototypeSlice, - StringPrototypeSplit, - SymbolFor, - SyntaxError, - TypeError, - URIError, - setQueueMicrotask, - } = window.__bootstrap.primordials; - const { ops, asyncOps } = window.Deno.core; - - const build = { - target: "unknown", - arch: "unknown", - os: "unknown", - vendor: "unknown", - env: undefined, - }; - - function setBuildInfo(target) { - const { 0: arch, 1: vendor, 2: os, 3: env } = StringPrototypeSplit( - target, - "-", - 4, - ); - build.target = target; - build.arch = arch; - build.vendor = vendor; - build.os = os; - build.env = env; - ObjectFreeze(build); - } - - const errorMap = {}; - // Builtin v8 / JS errors - registerErrorClass("Error", Error); - registerErrorClass("RangeError", RangeError); - registerErrorClass("ReferenceError", ReferenceError); - registerErrorClass("SyntaxError", SyntaxError); - registerErrorClass("TypeError", TypeError); - registerErrorClass("URIError", URIError); - - let nextPromiseId = 1; - const promiseMap = new SafeMap(); - const RING_SIZE = 4 * 1024; - const NO_PROMISE = null; // Alias to null is faster than plain nulls - const promiseRing = ArrayPrototypeFill(new Array(RING_SIZE), NO_PROMISE); - // TODO(bartlomieju): it future use `v8::Private` so it's not visible - // to users. Currently missing bindings. - const promiseIdSymbol = SymbolFor("Deno.core.internalPromiseId"); - - let opCallTracingEnabled = false; - const opCallTraces = new SafeMap(); - - function enableOpCallTracing() { - opCallTracingEnabled = true; - } - - function isOpCallTracingEnabled() { - return opCallTracingEnabled; - } - - function movePromise(promiseId) { - const idx = promiseId % RING_SIZE; - // Move old promise from ring to map - const oldPromise = promiseRing[idx]; - if (oldPromise !== NO_PROMISE) { - const oldPromiseId = promiseId - RING_SIZE; - MapPrototypeSet(promiseMap, oldPromiseId, oldPromise); - } - return promiseRing[idx] = NO_PROMISE; - } - - function setPromise(promiseId) { - const idx = promiseId % RING_SIZE; - // Move old promise from ring to map - const oldPromise = promiseRing[idx]; - if (oldPromise !== NO_PROMISE) { - const oldPromiseId = promiseId - RING_SIZE; - MapPrototypeSet(promiseMap, oldPromiseId, oldPromise); - } - // Set new promise - return promiseRing[idx] = newPromise(); - } - - function getPromise(promiseId) { - // Check if out of ring bounds, fallback to map - const outOfBounds = promiseId < nextPromiseId - RING_SIZE; - if (outOfBounds) { - const promise = MapPrototypeGet(promiseMap, promiseId); - MapPrototypeDelete(promiseMap, promiseId); - return promise; - } - // Otherwise take from ring - const idx = promiseId % RING_SIZE; - const promise = promiseRing[idx]; - promiseRing[idx] = NO_PROMISE; - return promise; - } - - function newPromise() { - let resolve, reject; - const promise = new Promise((resolve_, reject_) => { - resolve = resolve_; - reject = reject_; - }); - promise.resolve = resolve; - promise.reject = reject; - return promise; - } - - function hasPromise(promiseId) { - // Check if out of ring bounds, fallback to map - const outOfBounds = promiseId < nextPromiseId - RING_SIZE; - if (outOfBounds) { - return MapPrototypeHas(promiseMap, promiseId); - } - // Otherwise check it in ring - const idx = promiseId % RING_SIZE; - return promiseRing[idx] != NO_PROMISE; - } - - const macrotaskCallbacks = []; - const nextTickCallbacks = []; - - function setMacrotaskCallback(cb) { - ArrayPrototypePush(macrotaskCallbacks, cb); - } - - function setNextTickCallback(cb) { - ArrayPrototypePush(nextTickCallbacks, cb); - } - - // This function has variable number of arguments. The last argument describes - // if there's a "next tick" scheduled by the Node.js compat layer. Arguments - // before last are alternating integers and any values that describe the - // responses of async ops. - function eventLoopTick() { - // First respond to all pending ops. - for (let i = 0; i < arguments.length - 1; i += 2) { - const promiseId = arguments[i]; - const res = arguments[i + 1]; - const promise = getPromise(promiseId); - promise.resolve(res); - } - // Drain nextTick queue if there's a tick scheduled. - if (arguments[arguments.length - 1]) { - for (let i = 0; i < nextTickCallbacks.length; i++) { - nextTickCallbacks[i](); - } - } else { - ops.op_run_microtasks(); - } - // Finally drain macrotask queue. - for (let i = 0; i < macrotaskCallbacks.length; i++) { - const cb = macrotaskCallbacks[i]; - while (true) { - const res = cb(); - - // If callback returned `undefined` then it has no work to do, we don't - // need to perform microtask checkpoint. - if (res === undefined) { - break; - } - - ops.op_run_microtasks(); - // If callback returned `true` then it has no more work to do, stop - // calling it then. - if (res === true) { - break; - } - } - } - } - - function registerErrorClass(className, errorClass) { - registerErrorBuilder(className, (msg) => new errorClass(msg)); - } - - function registerErrorBuilder(className, errorBuilder) { - if (typeof errorMap[className] !== "undefined") { - throw new TypeError(`Error class for "${className}" already registered`); - } - errorMap[className] = errorBuilder; - } - - function buildCustomError(className, message, code) { - let error; - try { - error = errorMap[className]?.(message); - } catch (e) { - throw new Error( - `Unable to build custom error for "${className}"\n ${e.message}`, - ); - } - // Strip buildCustomError() calls from stack trace - if (typeof error == "object") { - ErrorCaptureStackTrace(error, buildCustomError); - if (code) { - error.code = code; - } - } - return error; - } - - function unwrapOpError(hideFunction) { - return (res) => { - // .$err_class_name is a special key that should only exist on errors - const className = res?.$err_class_name; - if (!className) { - return res; - } - - const errorBuilder = errorMap[className]; - const err = errorBuilder ? errorBuilder(res.message) : new Error( - `Unregistered error class: "${className}"\n ${res.message}\n Classes of errors returned from ops should be registered via Deno.core.registerErrorClass().`, - ); - // Set .code if error was a known OS error, see error_codes.rs - if (res.code) { - err.code = res.code; - } - // Strip unwrapOpResult() and errorBuilder() calls from stack trace - ErrorCaptureStackTrace(err, hideFunction); - throw err; - }; - } - - function unwrapOpResultNewPromise(id, res, hideFunction) { - // .$err_class_name is a special key that should only exist on errors - if (res?.$err_class_name) { - const className = res.$err_class_name; - const errorBuilder = errorMap[className]; - const err = errorBuilder ? errorBuilder(res.message) : new Error( - `Unregistered error class: "${className}"\n ${res.message}\n Classes of errors returned from ops should be registered via Deno.core.registerErrorClass().`, - ); - // Set .code if error was a known OS error, see error_codes.rs - if (res.code) { - err.code = res.code; - } - // Strip unwrapOpResult() and errorBuilder() calls from stack trace - ErrorCaptureStackTrace(err, hideFunction); - return PromiseReject(err); - } - const promise = PromiseResolve(res); - promise[promiseIdSymbol] = id; - return promise; - } - - /* -Basic codegen. - -TODO(mmastrac): automate this (handlebars?) - -let s = ""; -const vars = "abcdefghijklm"; -for (let i = 0; i < 10; i++) { - let args = ""; - for (let j = 0; j < i; j++) { - args += `${vars[j]},`; - } - s += ` - case ${i}: - fn = function async_op_${i}(${args}) { - const id = nextPromiseId++; - try { - const maybeResult = originalOp(id, ${args}); - if (maybeResult !== undefined) { - movePromise(id); - return unwrapOpResultNewPromise(id, maybeResult, async_op_${i}); - } - } catch (err) { - movePromise(id); - ErrorCaptureStackTrace(err, async_op_${i}); - return PromiseReject(err); - } - let promise = PromisePrototypeThen(setPromise(id), unwrapOpError(eventLoopTick)); - promise = handleOpCallTracing(opName, id, promise); - promise[promiseIdSymbol] = id; - return promise; - }; - break; - `; -} - */ - - // This function is called once per async stub - function asyncStub(opName, args) { - setUpAsyncStub(opName); - return ReflectApply(ops[opName], undefined, args); - } - - function setUpAsyncStub(opName) { - const originalOp = asyncOps[opName]; - let fn; - // The body of this switch statement can be generated using the script above. - switch (originalOp.length - 1) { - case 0: - fn = function async_op_0() { - const id = nextPromiseId++; - try { - const maybeResult = originalOp(id); - if (maybeResult !== undefined) { - movePromise(id); - return unwrapOpResultNewPromise(id, maybeResult, async_op_0); - } - } catch (err) { - movePromise(id); - ErrorCaptureStackTrace(err, async_op_0); - return PromiseReject(err); - } - let promise = PromisePrototypeThen( - setPromise(id), - unwrapOpError(eventLoopTick), - ); - promise = handleOpCallTracing(opName, id, promise); - promise[promiseIdSymbol] = id; - return promise; - }; - break; - - case 1: - fn = function async_op_1(a) { - const id = nextPromiseId++; - try { - const maybeResult = originalOp(id, a); - if (maybeResult !== undefined) { - movePromise(id); - return unwrapOpResultNewPromise(id, maybeResult, async_op_1); - } - } catch (err) { - movePromise(id); - ErrorCaptureStackTrace(err, async_op_1); - return PromiseReject(err); - } - let promise = PromisePrototypeThen( - setPromise(id), - unwrapOpError(eventLoopTick), - ); - promise = handleOpCallTracing(opName, id, promise); - promise[promiseIdSymbol] = id; - return promise; - }; - break; - - case 2: - fn = function async_op_2(a, b) { - const id = nextPromiseId++; - try { - const maybeResult = originalOp(id, a, b); - if (maybeResult !== undefined) { - movePromise(id); - return unwrapOpResultNewPromise(id, maybeResult, async_op_2); - } - } catch (err) { - movePromise(id); - ErrorCaptureStackTrace(err, async_op_2); - return PromiseReject(err); - } - let promise = PromisePrototypeThen( - setPromise(id), - unwrapOpError(eventLoopTick), - ); - promise = handleOpCallTracing(opName, id, promise); - promise[promiseIdSymbol] = id; - return promise; - }; - break; - - case 3: - fn = function async_op_3(a, b, c) { - const id = nextPromiseId++; - try { - const maybeResult = originalOp(id, a, b, c); - if (maybeResult !== undefined) { - movePromise(id); - return unwrapOpResultNewPromise(id, maybeResult, async_op_3); - } - } catch (err) { - movePromise(id); - ErrorCaptureStackTrace(err, async_op_3); - return PromiseReject(err); - } - let promise = PromisePrototypeThen( - setPromise(id), - unwrapOpError(eventLoopTick), - ); - promise = handleOpCallTracing(opName, id, promise); - promise[promiseIdSymbol] = id; - return promise; - }; - break; - - case 4: - fn = function async_op_4(a, b, c, d) { - const id = nextPromiseId++; - try { - const maybeResult = originalOp(id, a, b, c, d); - if (maybeResult !== undefined) { - movePromise(id); - return unwrapOpResultNewPromise(id, maybeResult, async_op_4); - } - } catch (err) { - movePromise(id); - ErrorCaptureStackTrace(err, async_op_4); - return PromiseReject(err); - } - let promise = PromisePrototypeThen( - setPromise(id), - unwrapOpError(eventLoopTick), - ); - promise = handleOpCallTracing(opName, id, promise); - promise[promiseIdSymbol] = id; - return promise; - }; - break; - - case 5: - fn = function async_op_5(a, b, c, d, e) { - const id = nextPromiseId++; - try { - const maybeResult = originalOp(id, a, b, c, d, e); - if (maybeResult !== undefined) { - movePromise(id); - return unwrapOpResultNewPromise(id, maybeResult, async_op_5); - } - } catch (err) { - movePromise(id); - ErrorCaptureStackTrace(err, async_op_5); - return PromiseReject(err); - } - let promise = PromisePrototypeThen( - setPromise(id), - unwrapOpError(eventLoopTick), - ); - promise = handleOpCallTracing(opName, id, promise); - promise[promiseIdSymbol] = id; - return promise; - }; - break; - - case 6: - fn = function async_op_6(a, b, c, d, e, f) { - const id = nextPromiseId++; - try { - const maybeResult = originalOp(id, a, b, c, d, e, f); - if (maybeResult !== undefined) { - movePromise(id); - return unwrapOpResultNewPromise(id, maybeResult, async_op_6); - } - } catch (err) { - movePromise(id); - ErrorCaptureStackTrace(err, async_op_6); - return PromiseReject(err); - } - let promise = PromisePrototypeThen( - setPromise(id), - unwrapOpError(eventLoopTick), - ); - promise = handleOpCallTracing(opName, id, promise); - promise[promiseIdSymbol] = id; - return promise; - }; - break; - - case 7: - fn = function async_op_7(a, b, c, d, e, f, g) { - const id = nextPromiseId++; - try { - const maybeResult = originalOp(id, a, b, c, d, e, f, g); - if (maybeResult !== undefined) { - movePromise(id); - return unwrapOpResultNewPromise(id, maybeResult, async_op_7); - } - } catch (err) { - movePromise(id); - ErrorCaptureStackTrace(err, async_op_7); - return PromiseReject(err); - } - let promise = PromisePrototypeThen( - setPromise(id), - unwrapOpError(eventLoopTick), - ); - promise = handleOpCallTracing(opName, id, promise); - promise[promiseIdSymbol] = id; - return promise; - }; - break; - - case 8: - fn = function async_op_8(a, b, c, d, e, f, g, h) { - const id = nextPromiseId++; - try { - const maybeResult = originalOp(id, a, b, c, d, e, f, g, h); - if (maybeResult !== undefined) { - movePromise(id); - return unwrapOpResultNewPromise(id, maybeResult, async_op_8); - } - } catch (err) { - movePromise(id); - ErrorCaptureStackTrace(err, async_op_8); - return PromiseReject(err); - } - let promise = PromisePrototypeThen( - setPromise(id), - unwrapOpError(eventLoopTick), - ); - promise = handleOpCallTracing(opName, id, promise); - promise[promiseIdSymbol] = id; - return promise; - }; - break; - - case 9: - fn = function async_op_9(a, b, c, d, e, f, g, h, i) { - const id = nextPromiseId++; - try { - const maybeResult = originalOp(id, a, b, c, d, e, f, g, h, i); - if (maybeResult !== undefined) { - movePromise(id); - return unwrapOpResultNewPromise(id, maybeResult, async_op_9); - } - } catch (err) { - movePromise(id); - ErrorCaptureStackTrace(err, async_op_9); - return PromiseReject(err); - } - let promise = PromisePrototypeThen( - setPromise(id), - unwrapOpError(eventLoopTick), - ); - promise = handleOpCallTracing(opName, id, promise); - promise[promiseIdSymbol] = id; - return promise; - }; - break; - - default: - throw new Error( - `Too many arguments for async op codegen (length of ${opName} was ${ - originalOp.length - 1 - })`, - ); - } - ObjectDefineProperty(fn, "name", { - value: opName, - configurable: false, - writable: false, - }); - return (ops[opName] = fn); - } - - function opAsync(name, ...args) { - const id = nextPromiseId++; - try { - const maybeResult = asyncOps[name](id, ...new SafeArrayIterator(args)); - if (maybeResult !== undefined) { - movePromise(id); - return unwrapOpResultNewPromise(id, maybeResult, opAsync); - } - } catch (err) { - movePromise(id); - if (!ReflectHas(asyncOps, name)) { - return PromiseReject(new TypeError(`${name} is not a registered op`)); - } - ErrorCaptureStackTrace(err, opAsync); - return PromiseReject(err); - } - let promise = PromisePrototypeThen( - setPromise(id), - unwrapOpError(eventLoopTick), - ); - promise = handleOpCallTracing(name, id, promise); - promise[promiseIdSymbol] = id; - return promise; - } - - function handleOpCallTracing(opName, promiseId, p) { - if (opCallTracingEnabled) { - const stack = StringPrototypeSlice(new Error().stack, 6); - MapPrototypeSet(opCallTraces, promiseId, { opName, stack }); - return SafePromisePrototypeFinally( - p, - () => MapPrototypeDelete(opCallTraces, promiseId), - ); - } else { - return p; - } - } - - function refOp(promiseId) { - if (!hasPromise(promiseId)) { - return; - } - ops.op_ref_op(promiseId); - } - - function unrefOp(promiseId) { - if (!hasPromise(promiseId)) { - return; - } - ops.op_unref_op(promiseId); - } - - function resources() { - return ObjectFromEntries(ops.op_resources()); - } - - function metrics() { - const { 0: aggregate, 1: perOps } = ops.op_metrics(); - aggregate.ops = ObjectFromEntries(ArrayPrototypeMap( - ops.op_op_names(), - (opName, opId) => [opName, perOps[opId]], - )); - return aggregate; - } - - let reportExceptionCallback = undefined; - - // Used to report errors thrown from functions passed to `queueMicrotask()`. - // The callback will be passed the thrown error. For example, you can use this - // to dispatch an error event to the global scope. - // In other words, set the implementation for - // https://html.spec.whatwg.org/multipage/webappapis.html#report-the-exception - function setReportExceptionCallback(cb) { - if (typeof cb != "function") { - throw new TypeError("expected a function"); - } - reportExceptionCallback = cb; - } - - function queueMicrotask(cb) { - if (typeof cb != "function") { - throw new TypeError("expected a function"); - } - return ops.op_queue_microtask(() => { - try { - cb(); - } catch (error) { - if (reportExceptionCallback) { - reportExceptionCallback(error); - } else { - throw error; - } - } - }); - } - - // Some "extensions" rely on "BadResource" and "Interrupted" errors in the - // JS code (eg. "deno_net") so they are provided in "Deno.core" but later - // reexported on "Deno.errors" - class BadResource extends Error { - constructor(msg) { - super(msg); - this.name = "BadResource"; - } - } - const BadResourcePrototype = BadResource.prototype; - - class Interrupted extends Error { - constructor(msg) { - super(msg); - this.name = "Interrupted"; - } - } - const InterruptedPrototype = Interrupted.prototype; - - const promiseHooks = [ - [], // init - [], // before - [], // after - [], // resolve - ]; - - function setPromiseHooks(init, before, after, resolve) { - const hooks = [init, before, after, resolve]; - for (let i = 0; i < hooks.length; i++) { - const hook = hooks[i]; - // Skip if no callback was provided for this hook type. - if (hook == null) { - continue; - } - // Verify that the type of `hook` is a function. - if (typeof hook !== "function") { - throw new TypeError(`Expected function at position ${i}`); - } - // Add the hook to the list. - ArrayPrototypePush(promiseHooks[i], hook); - } - - const wrappedHooks = ArrayPrototypeMap(promiseHooks, (hooks) => { - switch (hooks.length) { - case 0: - return undefined; - case 1: - return hooks[0]; - case 2: - return create2xHookWrapper(hooks[0], hooks[1]); - case 3: - return create3xHookWrapper(hooks[0], hooks[1], hooks[2]); - default: - return createHookListWrapper(hooks); - } - - // The following functions are used to create wrapper functions that call - // all the hooks in a list of a certain length. The reason to use a - // function that creates a wrapper is to minimize the number of objects - // captured in the closure. - function create2xHookWrapper(hook1, hook2) { - return function (promise, parent) { - hook1(promise, parent); - hook2(promise, parent); - }; - } - function create3xHookWrapper(hook1, hook2, hook3) { - return function (promise, parent) { - hook1(promise, parent); - hook2(promise, parent); - hook3(promise, parent); - }; - } - function createHookListWrapper(hooks) { - return function (promise, parent) { - for (let i = 0; i < hooks.length; i++) { - const hook = hooks[i]; - hook(promise, parent); - } - }; - } - }); - - ops.op_set_promise_hooks( - wrappedHooks[0], - wrappedHooks[1], - wrappedHooks[2], - wrappedHooks[3], - ); - } - - // Eagerly initialize ops for snapshot purposes - for (const opName of new SafeArrayIterator(ObjectKeys(asyncOps))) { - setUpAsyncStub(opName); - } - - function ensureFastOps() { - return new Proxy({}, { - get(_target, opName) { - if (ops[opName] === undefined) { - throw new Error(`Unknown or disabled op '${opName}'`); - } - if (asyncOps[opName] !== undefined) { - return setUpAsyncStub(opName); - } else { - return ops[opName]; - } - }, - }); - } - - const { - op_close: close, - op_try_close: tryClose, - op_read: read, - op_read_all: readAll, - op_write: write, - op_write_all: writeAll, - op_read_sync: readSync, - op_write_sync: writeSync, - op_shutdown: shutdown, - } = ensureFastOps(); - - // Extra Deno.core.* exports - const core = ObjectAssign(globalThis.Deno.core, { - asyncStub, - ensureFastOps, - opAsync, - resources, - metrics, - registerErrorBuilder, - registerErrorClass, - buildCustomError, - eventLoopTick, - BadResource, - BadResourcePrototype, - Interrupted, - InterruptedPrototype, - enableOpCallTracing, - isOpCallTracingEnabled, - opCallTraces, - refOp, - unrefOp, - setReportExceptionCallback, - setPromiseHooks, - close, - tryClose, - read, - readAll, - write, - writeAll, - readSync, - writeSync, - shutdown, - print: (msg, isErr) => ops.op_print(msg, isErr), - setMacrotaskCallback, - setNextTickCallback, - runMicrotasks: () => ops.op_run_microtasks(), - hasTickScheduled: () => ops.op_has_tick_scheduled(), - setHasTickScheduled: (bool) => ops.op_set_has_tick_scheduled(bool), - evalContext: ( - source, - specifier, - ) => ops.op_eval_context(source, specifier), - createHostObject: () => ops.op_create_host_object(), - encode: (text) => ops.op_encode(text), - decode: (buffer) => ops.op_decode(buffer), - serialize: ( - value, - options, - errorCallback, - ) => ops.op_serialize(value, options, errorCallback), - deserialize: (buffer, options) => ops.op_deserialize(buffer, options), - getPromiseDetails: (promise) => ops.op_get_promise_details(promise), - getProxyDetails: (proxy) => ops.op_get_proxy_details(proxy), - isProxy: (value) => ops.op_is_proxy(value), - memoryUsage: () => ops.op_memory_usage(), - setWasmStreamingCallback: (fn) => ops.op_set_wasm_streaming_callback(fn), - abortWasmStreaming: ( - rid, - error, - ) => ops.op_abort_wasm_streaming(rid, error), - destructureError: (error) => ops.op_destructure_error(error), - opNames: () => ops.op_op_names(), - eventLoopHasMoreWork: () => ops.op_event_loop_has_more_work(), - setPromiseRejectCallback: (fn) => ops.op_set_promise_reject_callback(fn), - byteLength: (str) => ops.op_str_byte_length(str), - build, - setBuildInfo, - }); - - ObjectAssign(globalThis.__bootstrap, { core }); - const internals = {}; - ObjectAssign(globalThis.__bootstrap, { internals }); - ObjectAssign(globalThis.Deno, { core }); - - // Direct bindings on `globalThis` - ObjectAssign(globalThis, { queueMicrotask }); - setQueueMicrotask(queueMicrotask); -})(globalThis); diff --git a/core/02_error.js b/core/02_error.js deleted file mode 100644 index b29dc9b4ee..0000000000 --- a/core/02_error.js +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -"use strict"; - -((window) => { - const core = Deno.core; - const ops = core.ops; - const { - Error, - ObjectFreeze, - ObjectAssign, - StringPrototypeStartsWith, - StringPrototypeEndsWith, - ObjectDefineProperties, - ArrayPrototypePush, - ArrayPrototypeMap, - ArrayPrototypeJoin, - } = window.__bootstrap.primordials; - - // Keep in sync with `cli/fmt_errors.rs`. - function formatLocation(cse) { - if (cse.isNative) { - return "native"; - } - let result = ""; - if (cse.fileName) { - result += ops.op_format_file_name(cse.fileName); - } else { - if (cse.isEval) { - if (cse.evalOrigin == null) { - throw new Error("assert evalOrigin"); - } - result += `${cse.evalOrigin}, `; - } - result += ""; - } - if (cse.lineNumber != null) { - result += `:${cse.lineNumber}`; - if (cse.columnNumber != null) { - result += `:${cse.columnNumber}`; - } - } - return result; - } - - // Keep in sync with `cli/fmt_errors.rs`. - function formatCallSiteEval(cse) { - let result = ""; - if (cse.isAsync) { - result += "async "; - } - if (cse.isPromiseAll) { - result += `Promise.all (index ${cse.promiseIndex})`; - return result; - } - const isMethodCall = !(cse.isToplevel || cse.isConstructor); - if (isMethodCall) { - if (cse.functionName) { - if (cse.typeName) { - if (!StringPrototypeStartsWith(cse.functionName, cse.typeName)) { - result += `${cse.typeName}.`; - } - } - result += cse.functionName; - if (cse.methodName) { - if (!StringPrototypeEndsWith(cse.functionName, cse.methodName)) { - result += ` [as ${cse.methodName}]`; - } - } - } else { - if (cse.typeName) { - result += `${cse.typeName}.`; - } - if (cse.methodName) { - result += cse.methodName; - } else { - result += ""; - } - } - } else if (cse.isConstructor) { - result += "new "; - if (cse.functionName) { - result += cse.functionName; - } else { - result += ""; - } - } else if (cse.functionName) { - result += cse.functionName; - } else { - result += formatLocation(cse); - return result; - } - - result += ` (${formatLocation(cse)})`; - return result; - } - - function evaluateCallSite(callSite) { - return { - this: callSite.getThis(), - typeName: callSite.getTypeName(), - function: callSite.getFunction(), - functionName: callSite.getFunctionName(), - methodName: callSite.getMethodName(), - fileName: callSite.getFileName(), - lineNumber: callSite.getLineNumber(), - columnNumber: callSite.getColumnNumber(), - evalOrigin: callSite.getEvalOrigin(), - isToplevel: callSite.isToplevel(), - isEval: callSite.isEval(), - isNative: callSite.isNative(), - isConstructor: callSite.isConstructor(), - isAsync: callSite.isAsync(), - isPromiseAll: callSite.isPromiseAll(), - promiseIndex: callSite.getPromiseIndex(), - }; - } - - function sourceMapCallSiteEval(cse) { - if (cse.fileName && cse.lineNumber != null && cse.columnNumber != null) { - return { ...cse, ...ops.op_apply_source_map(cse) }; - } - return cse; - } - - /** A function that can be used as `Error.prepareStackTrace`. */ - function prepareStackTrace(error, callSites) { - let callSiteEvals = ArrayPrototypeMap(callSites, evaluateCallSite); - callSiteEvals = ArrayPrototypeMap(callSiteEvals, sourceMapCallSiteEval); - ObjectDefineProperties(error, { - __callSiteEvals: { __proto__: null, value: [], configurable: true }, - }); - const formattedCallSites = []; - for (let i = 0; i < callSiteEvals.length; ++i) { - const cse = callSiteEvals[i]; - ArrayPrototypePush(error.__callSiteEvals, cse); - ArrayPrototypePush(formattedCallSites, formatCallSiteEval(cse)); - } - const message = error.message !== undefined ? error.message : ""; - const name = error.name !== undefined ? error.name : "Error"; - let messageLine; - if (name != "" && message != "") { - messageLine = `${name}: ${message}`; - } else if ((name || message) != "") { - messageLine = name || message; - } else { - messageLine = ""; - } - return messageLine + - ArrayPrototypeJoin( - ArrayPrototypeMap(formattedCallSites, (s) => `\n at ${s}`), - "", - ); - } - - ObjectAssign(globalThis.__bootstrap.core, { prepareStackTrace }); - ObjectFreeze(globalThis.__bootstrap.core); -})(this); diff --git a/core/Cargo.toml b/core/Cargo.toml deleted file mode 100644 index e8e659726d..0000000000 --- a/core/Cargo.toml +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -[package] -name = "deno_core" -version = "0.191.0" -authors.workspace = true -edition.workspace = true -license.workspace = true -readme = "README.md" -repository.workspace = true -description = "A modern JavaScript/TypeScript runtime built with V8, Rust, and Tokio" - -[lib] -path = "lib.rs" - -[features] -default = ["v8_use_custom_libcxx"] -v8_use_custom_libcxx = ["v8/use_custom_libcxx"] -include_js_files_for_snapshotting = [] - -[dependencies] -anyhow.workspace = true -bytes.workspace = true -deno_ops.workspace = true -futures.workspace = true -# Stay on 1.6 to avoid a dependency cycle in ahash https://github.com/tkaitchuck/aHash/issues/95 -# Projects not depending on ahash are unaffected as cargo will pull any 1.X that is >= 1.6. -indexmap = "1.6" -libc.workspace = true -log.workspace = true -once_cell.workspace = true -parking_lot.workspace = true -pin-project.workspace = true -serde.workspace = true -serde_json = { workspace = true, features = ["preserve_order"] } -serde_v8.workspace = true -smallvec.workspace = true -sourcemap = "6.1" -tokio.workspace = true -url.workspace = true -v8.workspace = true - -[[example]] -name = "http_bench_json_ops" -path = "examples/http_bench_json_ops/main.rs" - -# These dependencies are only used for the 'http_bench_*_ops' examples. -[dev-dependencies] -cooked-waker = "5" -deno_ast.workspace = true diff --git a/core/README.md b/core/README.md deleted file mode 100644 index 9b4f33fa0e..0000000000 --- a/core/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# Deno Core Crate - -[![crates](https://img.shields.io/crates/v/deno_core.svg)](https://crates.io/crates/deno_core) -[![docs](https://docs.rs/deno_core/badge.svg)](https://docs.rs/deno_core) - -The main dependency of this crate is -[rusty_v8](https://github.com/denoland/rusty_v8), which provides the V8-Rust -bindings. - -This Rust crate contains the essential V8 bindings for Deno's command-line -interface (Deno CLI). The main abstraction here is the JsRuntime which provides -a way to execute JavaScript. - -The JsRuntime implements an event loop abstraction for the executed code that -keeps track of all pending tasks (async ops, dynamic module loads). It is user's -responsibility to drive that loop by using `JsRuntime::run_event_loop` method - -it must be executed in the context of Rust's future executor (eg. tokio, smol). - -Rust functions can be registered in JavaScript using `deno_core::Extension`. Use -the `Deno.core.ops.op_name()` and `Deno.core.opAsync("op_name", ...)` functions -to trigger the op function callback. A conventional way to write ops is using -the [`deno_ops`](https://github.com/denoland/deno/blob/main/ops) crate. - -Documentation for this crate is thin at the moment. Please see -[hello_world.rs](https://github.com/denoland/deno/blob/main/core/examples/hello_world.rs) -and -[http_bench_json_ops/main.rs](https://github.com/denoland/deno/blob/main/core/examples/http_bench_json_ops/main.rs) -as examples of usage. - -TypeScript support and lots of other functionality are not available at this -layer. See the [CLI](https://github.com/denoland/deno/tree/main/cli) for that. diff --git a/core/async_cancel.rs b/core/async_cancel.rs deleted file mode 100644 index 5573e543df..0000000000 --- a/core/async_cancel.rs +++ /dev/null @@ -1,793 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use std::any::type_name; -use std::borrow::Cow; -use std::error::Error; -use std::fmt; -use std::fmt::Display; -use std::fmt::Formatter; -use std::io; -use std::pin::Pin; -use std::rc::Rc; - -use futures::future::FusedFuture; -use futures::future::Future; -use futures::future::TryFuture; -use futures::task::Context; -use futures::task::Poll; -use pin_project::pin_project; - -use crate::RcLike; -use crate::Resource; - -use self::internal as i; - -#[derive(Debug, Default)] -pub struct CancelHandle { - node: i::Node, -} - -impl CancelHandle { - pub fn new() -> Self { - Default::default() - } - - pub fn new_rc() -> Rc { - Rc::new(Self::new()) - } - - /// Cancel all cancelable futures that are bound to this handle. Note that - /// this method does not require a mutable reference to the `CancelHandle`. - pub fn cancel(&self) { - self.node.cancel(); - } - - pub fn is_canceled(&self) -> bool { - self.node.is_canceled() - } -} - -#[pin_project(project = CancelableProjection)] -#[derive(Debug)] -pub enum Cancelable { - Pending { - #[pin] - future: F, - #[pin] - registration: i::Registration, - }, - Terminated, -} - -impl Future for Cancelable { - type Output = Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { - let poll_result = match self.as_mut().project() { - CancelableProjection::Pending { - future, - registration, - } => Self::poll_pending(future, registration, cx), - CancelableProjection::Terminated => { - panic!("{}::poll() called after completion", type_name::()) - } - }; - // Fuse: if this Future is completed or canceled, make sure the inner - // `future` and `registration` fields are dropped in order to unlink it from - // its cancel handle. - if matches!(poll_result, Poll::Ready(_)) { - self.set(Cancelable::Terminated) - } - poll_result - } -} - -impl FusedFuture for Cancelable { - fn is_terminated(&self) -> bool { - matches!(self, Self::Terminated) - } -} - -impl Resource for CancelHandle { - fn name(&self) -> Cow { - "cancellation".into() - } - - fn close(self: Rc) { - self.cancel(); - } -} - -#[pin_project(project = TryCancelableProjection)] -#[derive(Debug)] -pub struct TryCancelable { - #[pin] - inner: Cancelable, -} - -impl Future for TryCancelable -where - F: Future>, - Canceled: Into, -{ - type Output = F::Output; - - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - let TryCancelableProjection { inner } = self.project(); - match inner.poll(cx) { - Poll::Pending => Poll::Pending, - Poll::Ready(Ok(result)) => Poll::Ready(result), - Poll::Ready(Err(err)) => Poll::Ready(Err(err.into())), - } - } -} - -impl FusedFuture for TryCancelable -where - F: Future>, - Canceled: Into, -{ - fn is_terminated(&self) -> bool { - self.inner.is_terminated() - } -} - -pub trait CancelFuture -where - Self: Future + Sized, -{ - fn or_cancel>( - self, - cancel_handle: H, - ) -> Cancelable { - Cancelable::new(self, cancel_handle.into()) - } -} - -impl CancelFuture for F where F: Future {} - -pub trait CancelTryFuture -where - Self: TryFuture + Sized, - Canceled: Into, -{ - fn try_or_cancel>( - self, - cancel_handle: H, - ) -> TryCancelable { - TryCancelable::new(self, cancel_handle.into()) - } -} - -impl CancelTryFuture for F -where - F: TryFuture, - Canceled: Into, -{ -} - -#[derive(Copy, Clone, Default, Debug, Eq, Hash, PartialEq)] -pub struct Canceled; - -impl Display for Canceled { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "operation canceled") - } -} - -impl Error for Canceled {} - -impl From for io::Error { - fn from(_: Canceled) -> Self { - io::Error::new(io::ErrorKind::Interrupted, Canceled) - } -} - -mod internal { - use super::CancelHandle; - use super::Cancelable; - use super::Canceled; - use super::TryCancelable; - use crate::RcRef; - use futures::future::Future; - use futures::task::Context; - use futures::task::Poll; - use futures::task::Waker; - use pin_project::pin_project; - use std::any::Any; - use std::cell::UnsafeCell; - use std::marker::PhantomPinned; - use std::mem::replace; - use std::pin::Pin; - use std::ptr::NonNull; - use std::rc::Rc; - use std::rc::Weak; - - impl Cancelable { - pub(super) fn new(future: F, cancel_handle: RcRef) -> Self { - let head_node = RcRef::map(cancel_handle, |r| &r.node); - let registration = Registration::WillRegister { head_node }; - Self::Pending { - future, - registration, - } - } - - pub(super) fn poll_pending( - future: Pin<&mut F>, - mut registration: Pin<&mut Registration>, - cx: &mut Context, - ) -> Poll> { - // Do a cancellation check _before_ polling the inner future. If it has - // already been canceled the inner future will not be polled. - let node = match &*registration { - Registration::WillRegister { head_node } => head_node, - Registration::Registered { node } => node, - }; - if node.is_canceled() { - return Poll::Ready(Err(Canceled)); - } - - match future.poll(cx) { - Poll::Ready(res) => return Poll::Ready(Ok(res)), - Poll::Pending => {} - } - - // Register this future with its `CancelHandle`, saving the `Waker` that - // can be used to make the runtime poll this future when it is canceled. - // When already registered, update the stored `Waker` if necessary. - let head_node = match &*registration { - Registration::WillRegister { .. } => { - match registration.as_mut().project_replace(Default::default()) { - RegistrationProjectionOwned::WillRegister { head_node } => { - Some(head_node) - } - _ => unreachable!(), - } - } - _ => None, - }; - let node = match registration.project() { - RegistrationProjection::Registered { node } => node, - _ => unreachable!(), - }; - node.register(cx.waker(), head_node)?; - - Poll::Pending - } - } - - impl TryCancelable { - pub(super) fn new(future: F, cancel_handle: RcRef) -> Self { - Self { - inner: Cancelable::new(future, cancel_handle), - } - } - } - - #[pin_project(project = RegistrationProjection, - project_replace = RegistrationProjectionOwned)] - #[derive(Debug)] - pub enum Registration { - WillRegister { - head_node: RcRef, - }, - Registered { - #[pin] - node: Node, - }, - } - - impl Default for Registration { - fn default() -> Self { - Self::Registered { - node: Default::default(), - } - } - } - - #[derive(Debug)] - pub struct Node { - inner: UnsafeCell, - _pin: PhantomPinned, - } - - impl Node { - /// If necessary, register a `Cancelable` node with a `CancelHandle`, and - /// save or update the `Waker` that can wake with this cancelable future. - pub fn register( - &self, - waker: &Waker, - head_rc: Option>, - ) -> Result<(), Canceled> { - match head_rc.as_ref().map(RcRef::split) { - Some((head, rc)) => { - // Register this `Cancelable` node with a `CancelHandle` head node. - assert_ne!(self, head); - // TODO(piscisaureus): safety comment - #[allow(clippy::undocumented_unsafe_blocks)] - let self_inner = unsafe { &mut *self.inner.get() }; - // TODO(piscisaureus): safety comment - #[allow(clippy::undocumented_unsafe_blocks)] - let head_inner = unsafe { &mut *head.inner.get() }; - self_inner.link(waker, head_inner, rc) - } - None => { - // This `Cancelable` has already been linked to a `CancelHandle` head - // node; just update our stored `Waker` if necessary. - // TODO(piscisaureus): safety comment - #[allow(clippy::undocumented_unsafe_blocks)] - let inner = unsafe { &mut *self.inner.get() }; - inner.update_waker(waker) - } - } - } - - pub fn cancel(&self) { - // TODO(piscisaureus): safety comment - #[allow(clippy::undocumented_unsafe_blocks)] - let inner = unsafe { &mut *self.inner.get() }; - inner.cancel(); - } - - pub fn is_canceled(&self) -> bool { - // TODO(piscisaureus): safety comment - #[allow(clippy::undocumented_unsafe_blocks)] - let inner = unsafe { &mut *self.inner.get() }; - inner.is_canceled() - } - } - - impl Default for Node { - fn default() -> Self { - Self { - inner: UnsafeCell::new(NodeInner::Unlinked), - _pin: PhantomPinned, - } - } - } - - impl Drop for Node { - fn drop(&mut self) { - // TODO(piscisaureus): safety comment - #[allow(clippy::undocumented_unsafe_blocks)] - let inner = unsafe { &mut *self.inner.get() }; - inner.unlink(); - } - } - - impl PartialEq for Node { - fn eq(&self, other: &Self) -> bool { - std::ptr::eq(self, other) - } - } - - #[derive(Debug)] - enum NodeInner { - Unlinked, - Linked { - kind: NodeKind, - prev: NonNull, - next: NonNull, - }, - Canceled, - } - - impl NodeInner { - fn as_non_null(&mut self) -> NonNull { - NonNull::from(self) - } - - fn link( - &mut self, - waker: &Waker, - head: &mut Self, - rc_pin: &Rc, - ) -> Result<(), Canceled> { - // The future should not have been linked to a cancel handle before. - assert!(matches!(self, NodeInner::Unlinked)); - - match head { - NodeInner::Unlinked => { - *head = NodeInner::Linked { - kind: NodeKind::head(rc_pin), - prev: self.as_non_null(), - next: self.as_non_null(), - }; - *self = NodeInner::Linked { - kind: NodeKind::item(waker), - prev: head.as_non_null(), - next: head.as_non_null(), - }; - Ok(()) - } - NodeInner::Linked { - kind: NodeKind::Head { .. }, - prev: next_prev_nn, - .. - } => { - // TODO(piscisaureus): safety comment - #[allow(clippy::undocumented_unsafe_blocks)] - let prev = unsafe { &mut *next_prev_nn.as_ptr() }; - match prev { - NodeInner::Linked { - kind: NodeKind::Item { .. }, - next: prev_next_nn, - .. - } => { - *self = NodeInner::Linked { - kind: NodeKind::item(waker), - prev: replace(next_prev_nn, self.as_non_null()), - next: replace(prev_next_nn, self.as_non_null()), - }; - Ok(()) - } - _ => unreachable!(), - } - } - NodeInner::Canceled => Err(Canceled), - _ => unreachable!(), - } - } - - fn update_waker(&mut self, new_waker: &Waker) -> Result<(), Canceled> { - match self { - NodeInner::Unlinked => Ok(()), - NodeInner::Linked { - kind: NodeKind::Item { waker }, - .. - } => { - if !waker.will_wake(new_waker) { - *waker = new_waker.clone(); - } - Ok(()) - } - NodeInner::Canceled => Err(Canceled), - _ => unreachable!(), - } - } - - /// If this node is linked to other nodes, remove it from the chain. This - /// method is called (only) by the drop handler for `Node`. It is suitable - /// for both 'head' and 'item' nodes. - fn unlink(&mut self) { - if let NodeInner::Linked { - prev: mut prev_nn, - next: mut next_nn, - .. - } = replace(self, NodeInner::Unlinked) - { - if prev_nn == next_nn { - // There were only two nodes in this chain; after unlinking ourselves - // the other node is no longer linked. - // TODO(piscisaureus): safety comment - #[allow(clippy::undocumented_unsafe_blocks)] - let other = unsafe { prev_nn.as_mut() }; - *other = NodeInner::Unlinked; - } else { - // The chain had more than two nodes. - // TODO(piscisaureus): safety comment - #[allow(clippy::undocumented_unsafe_blocks)] - match unsafe { prev_nn.as_mut() } { - NodeInner::Linked { - next: prev_next_nn, .. - } => { - *prev_next_nn = next_nn; - } - _ => unreachable!(), - } - // TODO(piscisaureus): safety comment - #[allow(clippy::undocumented_unsafe_blocks)] - match unsafe { next_nn.as_mut() } { - NodeInner::Linked { - prev: next_prev_nn, .. - } => { - *next_prev_nn = prev_nn; - } - _ => unreachable!(), - } - } - } - } - - /// Mark this node and all linked nodes for cancellation. Note that `self` - /// must refer to a head (`CancelHandle`) node. - fn cancel(&mut self) { - let mut head_nn = NonNull::from(self); - - // TODO(piscisaureus): safety comment - #[allow(clippy::undocumented_unsafe_blocks)] - // Mark the head node as canceled. - let mut item_nn = - match replace(unsafe { head_nn.as_mut() }, NodeInner::Canceled) { - NodeInner::Linked { - kind: NodeKind::Head { .. }, - next: next_nn, - .. - } => next_nn, - NodeInner::Unlinked | NodeInner::Canceled => return, - _ => unreachable!(), - }; - - // Cancel all item nodes in the chain, waking each stored `Waker`. - while item_nn != head_nn { - // TODO(piscisaureus): safety comment - #[allow(clippy::undocumented_unsafe_blocks)] - match replace(unsafe { item_nn.as_mut() }, NodeInner::Canceled) { - NodeInner::Linked { - kind: NodeKind::Item { waker }, - next: next_nn, - .. - } => { - waker.wake(); - item_nn = next_nn; - } - _ => unreachable!(), - } - } - } - - /// Returns true if this node has been marked for cancellation. This method - /// may be used with both head (`CancelHandle`) and item (`Cancelable`) - /// nodes. - fn is_canceled(&self) -> bool { - match self { - NodeInner::Unlinked | NodeInner::Linked { .. } => false, - NodeInner::Canceled => true, - } - } - } - - #[derive(Debug)] - enum NodeKind { - /// In a chain of linked nodes, the "head" node is owned by the - /// `CancelHandle`. A chain usually contains at most one head node; however - /// when a `CancelHandle` is dropped before the futures associated with it - /// are dropped, a chain may temporarily contain no head node at all. - Head { - /// The `weak_pin` field adds adds a weak reference to the `Rc` guarding - /// the heap allocation that contains the `CancelHandle`. Without this - /// extra weak reference, `Rc::get_mut()` might succeed and allow the - /// `CancelHandle` to be moved when it isn't safe to do so. - _weak_pin: Weak, - }, - /// All item nodes in a chain are associated with a `Cancelable` head node. - Item { - /// If this future indeed does get canceled, the waker is needed to make - /// sure that the canceled future gets polled as soon as possible. - waker: Waker, - }, - } - - impl NodeKind { - fn head(rc_pin: &Rc) -> Self { - let _weak_pin = Rc::downgrade(rc_pin); - Self::Head { _weak_pin } - } - - fn item(waker: &Waker) -> Self { - let waker = waker.clone(); - Self::Item { waker } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use anyhow::Error; - use futures::future::pending; - use futures::future::poll_fn; - use futures::future::ready; - use futures::future::FutureExt; - use futures::future::TryFutureExt; - use futures::pending; - use futures::select; - use futures::task::noop_waker_ref; - use futures::task::Context; - use futures::task::Poll; - use std::convert::Infallible as Never; - use std::io; - use tokio::net::TcpStream; - use tokio::spawn; - use tokio::task::yield_now; - - fn box_fused<'a, F: FusedFuture + 'a>( - future: F, - ) -> Pin + 'a>> { - Box::pin(future) - } - - async fn ready_in_n(name: &str, count: usize) -> &str { - let mut remaining = count as isize; - poll_fn(|_| { - assert!(remaining >= 0); - if remaining == 0 { - Poll::Ready(name) - } else { - remaining -= 1; - Poll::Pending - } - }) - .await - } - - #[test] - fn cancel_future() { - let cancel_now = CancelHandle::new_rc(); - let cancel_at_0 = CancelHandle::new_rc(); - let cancel_at_1 = CancelHandle::new_rc(); - let cancel_at_4 = CancelHandle::new_rc(); - let cancel_never = CancelHandle::new_rc(); - - cancel_now.cancel(); - - let mut futures = vec![ - box_fused(ready("A").or_cancel(&cancel_now)), - box_fused(ready("B").or_cancel(&cancel_at_0)), - box_fused(ready("C").or_cancel(&cancel_at_1)), - box_fused( - ready_in_n("D", 0) - .or_cancel(&cancel_never) - .try_or_cancel(&cancel_now), - ), - box_fused( - ready_in_n("E", 1) - .or_cancel(&cancel_at_1) - .try_or_cancel(&cancel_at_1), - ), - box_fused(ready_in_n("F", 2).or_cancel(&cancel_at_1)), - box_fused(ready_in_n("G", 3).or_cancel(&cancel_at_4)), - box_fused(ready_in_n("H", 4).or_cancel(&cancel_at_4)), - box_fused(ready_in_n("I", 5).or_cancel(&cancel_at_4)), - box_fused(ready_in_n("J", 5).map(Ok)), - box_fused(ready_in_n("K", 5).or_cancel(cancel_never)), - ]; - - let mut cx = Context::from_waker(noop_waker_ref()); - - for i in 0..=5 { - match i { - 0 => cancel_at_0.cancel(), - 1 => cancel_at_1.cancel(), - 4 => cancel_at_4.cancel(), - 2 | 3 | 5 => {} - _ => unreachable!(), - } - - let results = futures - .iter_mut() - .filter(|fut| !fut.is_terminated()) - .filter_map(|fut| match fut.poll_unpin(&mut cx) { - Poll::Pending => None, - Poll::Ready(res) => Some(res), - }) - .collect::>(); - - match i { - 0 => assert_eq!( - results, - [Err(Canceled), Err(Canceled), Ok("C"), Err(Canceled)] - ), - 1 => assert_eq!(results, [Err(Canceled), Err(Canceled)]), - 2 => assert_eq!(results, []), - 3 => assert_eq!(results, [Ok("G")]), - 4 => assert_eq!(results, [Err(Canceled), Err(Canceled)]), - 5 => assert_eq!(results, [Ok("J"), Ok("K")]), - _ => unreachable!(), - } - } - - assert!(!futures.into_iter().any(|fut| !fut.is_terminated())); - - let cancel_handles = [cancel_now, cancel_at_0, cancel_at_1, cancel_at_4]; - assert!(!cancel_handles.iter().any(|c| !c.is_canceled())); - } - - #[tokio::test] - async fn cancel_try_future() { - { - // Cancel a spawned task before it actually runs. - let cancel_handle = Rc::new(CancelHandle::new()); - let future = spawn(async { panic!("the task should not be spawned") }) - .map_err(Error::from) - .try_or_cancel(&cancel_handle); - cancel_handle.cancel(); - let error = future.await.unwrap_err(); - assert!(error.downcast_ref::().is_some()); - assert_eq!(error.to_string().as_str(), "operation canceled"); - } - - { - // Cancel a network I/O future right after polling it. - let cancel_handle = Rc::new(CancelHandle::new()); - let result = loop { - select! { - r = TcpStream::connect("1.2.3.4:12345") - .try_or_cancel(&cancel_handle) => break r, - default => cancel_handle.cancel(), - }; - }; - let error = result.unwrap_err(); - assert_eq!(error.kind(), io::ErrorKind::Interrupted); - assert_eq!(error.to_string().as_str(), "operation canceled"); - } - } - - #[tokio::test] - async fn future_cancels_itself_before_completion() { - // A future cancels itself before it reaches completion. This future should - // indeed get canceled and should not be polled again. - let cancel_handle = CancelHandle::new_rc(); - let result = async { - cancel_handle.cancel(); - yield_now().await; - unreachable!(); - } - .or_cancel(&cancel_handle) - .await; - assert_eq!(result.unwrap_err(), Canceled); - } - - #[tokio::test] - async fn future_cancels_itself_and_hangs() { - // A future cancels itself, after which it returns `Poll::Pending` without - // setting up a waker that would allow it to make progress towards - // completion. Nevertheless, the `Cancelable` wrapper future must finish. - let cancel_handle = CancelHandle::new_rc(); - let result = async { - yield_now().await; - cancel_handle.cancel(); - pending!(); - unreachable!(); - } - .or_cancel(&cancel_handle) - .await; - assert_eq!(result.unwrap_err(), Canceled); - } - - #[tokio::test] - async fn future_cancels_itself_and_completes() { - // A TryFuture attempts to cancel itself while it is getting polled, and - // yields a result from the very same `poll()` call. Because this future - // actually reaches completion, the attempted cancellation has no effect. - let cancel_handle = CancelHandle::new_rc(); - let result = async { - yield_now().await; - cancel_handle.cancel(); - Ok::<_, io::Error>("done") - } - .try_or_cancel(&cancel_handle) - .await; - assert_eq!(result.unwrap(), "done"); - } - - #[test] - fn cancel_handle_pinning() { - let mut cancel_handle = CancelHandle::new_rc(); - - // There is only one reference to `cancel_handle`, so `Rc::get_mut()` should - // succeed. - assert!(Rc::get_mut(&mut cancel_handle).is_some()); - - let mut future = pending::().or_cancel(&cancel_handle); - // SAFETY: `Cancelable` pins the future - let future = unsafe { Pin::new_unchecked(&mut future) }; - - // There are two `Rc` references now, so this fails. - assert!(Rc::get_mut(&mut cancel_handle).is_none()); - - let mut cx = Context::from_waker(noop_waker_ref()); - assert!(future.poll(&mut cx).is_pending()); - - // Polling `future` has established a link between the future and - // `cancel_handle`, so both values should be pinned at this point. - assert!(Rc::get_mut(&mut cancel_handle).is_none()); - - cancel_handle.cancel(); - - // Canceling or dropping the associated future(s) unlinks them from the - // cancel handle, therefore `cancel_handle` can now safely be moved again. - assert!(Rc::get_mut(&mut cancel_handle).is_some()); - } -} diff --git a/core/async_cell.rs b/core/async_cell.rs deleted file mode 100644 index 97d70699d1..0000000000 --- a/core/async_cell.rs +++ /dev/null @@ -1,780 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use std::any::type_name; -use std::any::Any; -use std::borrow::Borrow; -use std::cell::Cell; -use std::cell::UnsafeCell; -use std::collections::VecDeque; -use std::fmt; -use std::fmt::Debug; -use std::fmt::Formatter; -use std::ops::Deref; -use std::rc::Rc; - -use self::internal as i; - -pub type AsyncRef = i::AsyncBorrowImpl; -pub type AsyncMut = i::AsyncBorrowImpl; - -pub type AsyncRefFuture = i::AsyncBorrowFutureImpl; -pub type AsyncMutFuture = i::AsyncBorrowFutureImpl; - -pub struct AsyncRefCell { - value: UnsafeCell, - borrow_count: Cell, - waiters: Cell>>, - turn: Cell, -} - -impl AsyncRefCell { - /// Create a new `AsyncRefCell` that encapsulates the specified value. - /// Note that in order to borrow the inner value, the `AsyncRefCell` - /// needs to be wrapped in an `Rc` or an `RcRef`. These can be created - /// either manually, or by using the convenience method - /// `AsyncRefCell::new_rc()`. - pub fn new(value: T) -> Self { - Self { - value: UnsafeCell::new(value), - borrow_count: Default::default(), - waiters: Default::default(), - turn: Default::default(), - } - } - - pub fn new_rc(value: T) -> Rc { - Rc::new(Self::new(value)) - } - - pub fn as_ptr(&self) -> *mut T { - self.value.get() - } - - pub fn into_inner(self) -> T { - assert!(self.borrow_count.get().is_empty()); - self.value.into_inner() - } -} - -impl Debug for AsyncRefCell { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "AsyncRefCell<{}>", type_name::()) - } -} - -impl Default for AsyncRefCell { - fn default() -> Self { - Self::new(Default::default()) - } -} - -impl AsyncRefCell { - pub fn default_rc() -> Rc { - Rc::new(Default::default()) - } -} - -impl From for AsyncRefCell { - fn from(value: T) -> Self { - Self::new(value) - } -} - -impl AsyncRefCell { - pub fn borrow(self: &Rc) -> AsyncRefFuture { - AsyncRefFuture::new(self) - } - - pub fn borrow_mut(self: &Rc) -> AsyncMutFuture { - AsyncMutFuture::new(self) - } - - pub fn try_borrow(self: &Rc) -> Option> { - Self::borrow_sync(self) - } - - pub fn try_borrow_mut(self: &Rc) -> Option> { - Self::borrow_sync(self) - } -} - -impl RcRef> { - pub fn borrow(&self) -> AsyncRefFuture { - AsyncRefFuture::new(self) - } - - pub fn borrow_mut(&self) -> AsyncMutFuture { - AsyncMutFuture::new(self) - } - - pub fn try_borrow(&self) -> Option> { - AsyncRefCell::::borrow_sync(self) - } - - pub fn try_borrow_mut(&self) -> Option> { - AsyncRefCell::::borrow_sync(self) - } -} - -/// An `RcRef` encapsulates a reference counted pointer, just like a regular -/// `std::rc::Rc`. However, unlike a regular `Rc`, it can be remapped so that -/// it dereferences to any value that's reachable through the reference-counted -/// pointer. This is achieved through the associated method, `RcRef::map()`, -/// similar to how `std::cell::Ref::map()` works. Example: -/// -/// ```rust -/// # use std::rc::Rc; -/// # use deno_core::RcRef; -/// -/// struct Stuff { -/// foo: u32, -/// bar: String, -/// } -/// -/// let stuff_rc = Rc::new(Stuff { -/// foo: 42, -/// bar: "hello".to_owned(), -/// }); -/// -/// // `foo_rc` and `bar_rc` dereference to different types, however -/// // they share a reference count. -/// let foo_rc: RcRef = RcRef::map(stuff_rc.clone(), |v| &v.foo); -/// let bar_rc: RcRef = RcRef::map(stuff_rc, |v| &v.bar); -/// ``` -#[derive(Debug)] -pub struct RcRef { - rc: Rc, - value: *const T, -} - -impl RcRef { - pub fn new(value: T) -> Self { - Self::from(Rc::new(value)) - } - - pub fn map, F: FnOnce(&S) -> &T>( - source: R, - map_fn: F, - ) -> RcRef { - let RcRef:: { rc, value } = source.into(); - // TODO(piscisaureus): safety comment - #[allow(clippy::undocumented_unsafe_blocks)] - let value = map_fn(unsafe { &*value }); - RcRef { rc, value } - } - - pub(crate) fn split(rc_ref: &Self) -> (&T, &Rc) { - let &Self { ref rc, value } = rc_ref; - // TODO(piscisaureus): safety comment - #[allow(clippy::undocumented_unsafe_blocks)] - (unsafe { &*value }, rc) - } -} - -impl Default for RcRef { - fn default() -> Self { - Self::new(Default::default()) - } -} - -impl Clone for RcRef { - fn clone(&self) -> Self { - Self { - rc: self.rc.clone(), - value: self.value, - } - } -} - -impl From<&RcRef> for RcRef { - fn from(rc_ref: &RcRef) -> Self { - rc_ref.clone() - } -} - -impl From> for RcRef { - fn from(rc: Rc) -> Self { - Self { - value: &*rc, - rc: rc as Rc<_>, - } - } -} - -impl From<&Rc> for RcRef { - fn from(rc: &Rc) -> Self { - rc.clone().into() - } -} - -impl Deref for RcRef { - type Target = T; - fn deref(&self) -> &Self::Target { - // TODO(piscisaureus): safety comment - #[allow(clippy::undocumented_unsafe_blocks)] - unsafe { - &*self.value - } - } -} - -impl Borrow for RcRef { - fn borrow(&self) -> &T { - self - } -} - -impl AsRef for RcRef { - fn as_ref(&self) -> &T { - self - } -} - -/// The `RcLike` trait provides an abstraction over `std::rc::Rc` and `RcRef`, -/// so that applicable methods can operate on either type. -pub trait RcLike: AsRef + Into> {} - -impl RcLike for Rc {} -impl RcLike for RcRef {} -impl RcLike for &Rc {} -impl RcLike for &RcRef {} - -mod internal { - use super::AsyncRefCell; - use super::RcLike; - use super::RcRef; - use futures::future::Future; - use futures::ready; - use futures::task::Context; - use futures::task::Poll; - use futures::task::Waker; - use std::borrow::Borrow; - use std::borrow::BorrowMut; - use std::fmt::Debug; - use std::marker::PhantomData; - use std::ops::Deref; - use std::ops::DerefMut; - use std::pin::Pin; - - impl AsyncRefCell { - /// Borrow the cell's contents synchronously without creating an - /// intermediate future. If the cell has already been borrowed and either - /// the existing or the requested borrow is exclusive, this function returns - /// `None`. - pub fn borrow_sync>>( - cell: R, - ) -> Option> { - let cell_ref = cell.as_ref(); - // Don't allow synchronous borrows to cut in line; if there are any - // enqueued waiters, return `None`, even if the current borrow is a shared - // one and the requested borrow is too. - // TODO(piscisaureus): safety comment - #[allow(clippy::undocumented_unsafe_blocks)] - let waiters = unsafe { &mut *cell_ref.waiters.as_ptr() }; - if waiters.is_empty() { - // There are no enqueued waiters, but it is still possible that the cell - // is currently borrowed. If there are no current borrows, or both the - // existing and requested ones are shared, `try_add()` returns the - // adjusted borrow count. - let new_borrow_count = - cell_ref.borrow_count.get().try_add(M::borrow_mode())?; - cell_ref.borrow_count.set(new_borrow_count); - Some(AsyncBorrowImpl::::new(cell.into())) - } else { - None - } - } - - fn drop_borrow(&self) { - let new_borrow_count = self.borrow_count.get().remove(M::borrow_mode()); - self.borrow_count.set(new_borrow_count); - - if new_borrow_count.is_empty() { - self.wake_waiters() - } - } - - fn create_waiter(&self) -> usize { - let waiter = Waiter::new(M::borrow_mode()); - let turn = self.turn.get(); - let index = { - // TODO(piscisaureus): safety comment - #[allow(clippy::undocumented_unsafe_blocks)] - let waiters = unsafe { &mut *self.waiters.as_ptr() }; - waiters.push_back(Some(waiter)); - waiters.len() - 1 - }; - if index == 0 { - // SAFETY: the `waiters` reference used above *must* be dropped here. - self.wake_waiters() - } - // Return the new waiter's id. - turn + index - } - - fn poll_waiter( - &self, - id: usize, - cx: &mut Context, - ) -> Poll<()> { - let borrow_count = self.borrow_count.get(); - let turn = self.turn.get(); - if id < turn { - // This waiter made it to the front of the line; we reserved a borrow - // for it, woke its Waker, and removed the waiter from the queue. - // Assertion: BorrowCount::remove() will panic if `mode` is incorrect. - let _ = borrow_count.remove(M::borrow_mode()); - Poll::Ready(()) - } else { - // This waiter is still in line and has not yet been woken. - // TODO(piscisaureus): safety comment - #[allow(clippy::undocumented_unsafe_blocks)] - let waiters = unsafe { &mut *self.waiters.as_ptr() }; - // Sanity check: id cannot be higher than the last queue element. - assert!(id < turn + waiters.len()); - // Sanity check: since we always call wake_waiters() when the queue head - // is updated, it should be impossible to add it to the current borrow. - assert!(id > turn || borrow_count.try_add(M::borrow_mode()).is_none()); - // Save or update the waiter's Waker. - let waiter_mut = waiters[id - turn].as_mut().unwrap(); - waiter_mut.set_waker(cx.waker()); - Poll::Pending - } - } - - fn wake_waiters(&self) { - let mut borrow_count = self.borrow_count.get(); - // TODO(piscisaureus): safety comment - #[allow(clippy::undocumented_unsafe_blocks)] - let waiters = unsafe { &mut *self.waiters.as_ptr() }; - let mut turn = self.turn.get(); - - loop { - let waiter_entry = match waiters.front().map(Option::as_ref) { - None => break, // Queue empty. - Some(w) => w, - }; - let borrow_mode = match waiter_entry { - None => { - // Queue contains a hole. This happens when a Waiter is dropped - // before it makes it to the front of the queue. - waiters.pop_front(); - turn += 1; - continue; - } - Some(waiter) => waiter.borrow_mode(), - }; - // See if the waiter at the front of the queue can borrow the cell's - // value now. If it does, `try_add()` returns the new borrow count, - // effectively "reserving" the borrow until the associated - // AsyncBorrowFutureImpl future gets polled and produces the actual - // borrow. - borrow_count = match borrow_count.try_add(borrow_mode) { - None => break, // Can't borrow yet. - Some(b) => b, - }; - // Drop from queue. - let mut waiter = waiters.pop_front().unwrap().unwrap(); - turn += 1; - // Wake this waiter, so the AsyncBorrowFutureImpl future gets polled. - if let Some(waker) = waiter.take_waker() { - waker.wake() - } - } - // Save updated counters. - self.borrow_count.set(borrow_count); - self.turn.set(turn); - } - - fn drop_waiter(&self, id: usize) { - let turn = self.turn.get(); - if id < turn { - // We already made a borrow count reservation for this waiter but the - // borrow will never be picked up and consequently, never dropped. - // Therefore, call the borrow drop handler here. - self.drop_borrow::(); - } else { - // This waiter is still in the queue, take it out and leave a "hole". - // TODO(piscisaureus): safety comment - #[allow(clippy::undocumented_unsafe_blocks)] - let waiters = unsafe { &mut *self.waiters.as_ptr() }; - waiters[id - turn].take().unwrap(); - } - - if id == turn { - // Since the first entry in the waiter queue was touched we have to - // reprocess the waiter queue. - self.wake_waiters() - } - } - } - - pub struct AsyncBorrowFutureImpl { - cell: Option>>, - id: usize, - _phantom: PhantomData, - } - - impl AsyncBorrowFutureImpl { - pub fn new>>(cell: R) -> Self { - Self { - id: cell.as_ref().create_waiter::(), - cell: Some(cell.into()), - _phantom: PhantomData, - } - } - } - - impl Future for AsyncBorrowFutureImpl { - type Output = AsyncBorrowImpl; - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - ready!(self.cell.as_ref().unwrap().poll_waiter::(self.id, cx)); - // TODO(piscisaureus): safety comment - #[allow(clippy::undocumented_unsafe_blocks)] - let self_mut = unsafe { Pin::get_unchecked_mut(self) }; - let cell = self_mut.cell.take().unwrap(); - Poll::Ready(AsyncBorrowImpl::::new(cell)) - } - } - - impl Drop for AsyncBorrowFutureImpl { - fn drop(&mut self) { - // The expected mode of operation is that this future gets polled until it - // is ready and yields a value of type `AsyncBorrowImpl`, which has a drop - // handler that adjusts the `AsyncRefCell` borrow counter. However if the - // `cell` field still holds a value at this point, it means that the - // future was never polled to completion and no `AsyncBorrowImpl` was ever - // created, so we have to adjust the borrow count here. - if let Some(cell) = self.cell.take() { - cell.drop_waiter::(self.id) - } - } - } - - pub struct AsyncBorrowImpl { - cell: RcRef>, - _phantom: PhantomData, - } - - impl AsyncBorrowImpl { - fn new(cell: RcRef>) -> Self { - Self { - cell, - _phantom: PhantomData, - } - } - } - - impl Deref for AsyncBorrowImpl { - type Target = T; - fn deref(&self) -> &Self::Target { - // TODO(piscisaureus): safety comment - #[allow(clippy::undocumented_unsafe_blocks)] - unsafe { - &*self.cell.as_ptr() - } - } - } - - impl Borrow for AsyncBorrowImpl { - fn borrow(&self) -> &T { - self - } - } - - impl AsRef for AsyncBorrowImpl { - fn as_ref(&self) -> &T { - self - } - } - - impl DerefMut for AsyncBorrowImpl { - fn deref_mut(&mut self) -> &mut Self::Target { - // TODO(piscisaureus): safety comment - #[allow(clippy::undocumented_unsafe_blocks)] - unsafe { - &mut *self.cell.as_ptr() - } - } - } - - impl BorrowMut for AsyncBorrowImpl { - fn borrow_mut(&mut self) -> &mut T { - self - } - } - - impl AsMut for AsyncBorrowImpl { - fn as_mut(&mut self) -> &mut T { - self - } - } - - impl Drop for AsyncBorrowImpl { - fn drop(&mut self) { - self.cell.drop_borrow::() - } - } - - #[derive(Copy, Clone, Debug, Eq, PartialEq)] - pub enum BorrowMode { - Shared, - Exclusive, - } - - pub trait BorrowModeTrait: Copy { - fn borrow_mode() -> BorrowMode; - } - - #[derive(Copy, Clone, Debug)] - pub struct Shared; - - impl BorrowModeTrait for Shared { - fn borrow_mode() -> BorrowMode { - BorrowMode::Shared - } - } - - #[derive(Copy, Clone, Debug)] - pub struct Exclusive; - - impl BorrowModeTrait for Exclusive { - fn borrow_mode() -> BorrowMode { - BorrowMode::Exclusive - } - } - - #[derive(Copy, Clone, Debug, Eq, PartialEq)] - pub enum BorrowCount { - Shared(usize), - Exclusive, - } - - impl Default for BorrowCount { - fn default() -> Self { - Self::Shared(0) - } - } - - impl BorrowCount { - pub fn is_empty(self) -> bool { - matches!(self, BorrowCount::Shared(0)) - } - - pub fn try_add(self, mode: BorrowMode) -> Option { - match (self, mode) { - (BorrowCount::Shared(refs), BorrowMode::Shared) => { - Some(BorrowCount::Shared(refs + 1)) - } - (BorrowCount::Shared(0), BorrowMode::Exclusive) => { - Some(BorrowCount::Exclusive) - } - _ => None, - } - } - - #[allow(dead_code)] - pub fn add(self, mode: BorrowMode) -> BorrowCount { - match self.try_add(mode) { - Some(value) => value, - None => panic!("Can't add {mode:?} to {self:?}"), - } - } - - pub fn try_remove(self, mode: BorrowMode) -> Option { - match (self, mode) { - (BorrowCount::Shared(refs), BorrowMode::Shared) if refs > 0 => { - Some(BorrowCount::Shared(refs - 1)) - } - (BorrowCount::Exclusive, BorrowMode::Exclusive) => { - Some(BorrowCount::Shared(0)) - } - _ => None, - } - } - - pub fn remove(self, mode: BorrowMode) -> BorrowCount { - match self.try_remove(mode) { - Some(value) => value, - None => panic!("Can't remove {mode:?} from {self:?}"), - } - } - } - - /// The `waiters` queue that is associated with an individual `AsyncRefCell` - /// contains elements of the `Waiter` type. - pub struct Waiter { - borrow_mode: BorrowMode, - waker: Option, - } - - impl Waiter { - pub fn new(borrow_mode: BorrowMode) -> Self { - Self { - borrow_mode, - waker: None, - } - } - - pub fn borrow_mode(&self) -> BorrowMode { - self.borrow_mode - } - - pub fn set_waker(&mut self, new_waker: &Waker) { - if self - .waker - .as_ref() - .filter(|waker| waker.will_wake(new_waker)) - .is_none() - { - self.waker.replace(new_waker.clone()); - } - } - - pub fn take_waker(&mut self) -> Option { - self.waker.take() - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[derive(Default)] - struct Thing { - touch_count: usize, - _private: (), - } - - impl Thing { - pub fn look(&self) -> usize { - self.touch_count - } - - pub fn touch(&mut self) -> usize { - self.touch_count += 1; - self.touch_count - } - } - - #[tokio::test] - async fn async_ref_cell_borrow() { - let cell = AsyncRefCell::::default_rc(); - - let fut1 = cell.borrow(); - let fut2 = cell.borrow_mut(); - let fut3 = cell.borrow(); - let fut4 = cell.borrow(); - let fut5 = cell.borrow(); - let fut6 = cell.borrow(); - let fut7 = cell.borrow_mut(); - let fut8 = cell.borrow(); - - // The `try_borrow` and `try_borrow_mut` methods should always return `None` - // if there's a queue of async borrowers. - assert!(cell.try_borrow().is_none()); - assert!(cell.try_borrow_mut().is_none()); - - assert_eq!(fut1.await.look(), 0); - - assert_eq!(fut2.await.touch(), 1); - - { - let ref5 = fut5.await; - let ref4 = fut4.await; - let ref3 = fut3.await; - let ref6 = fut6.await; - assert_eq!(ref3.look(), 1); - assert_eq!(ref4.look(), 1); - assert_eq!(ref5.look(), 1); - assert_eq!(ref6.look(), 1); - } - - { - let mut ref7 = fut7.await; - assert_eq!(ref7.look(), 1); - assert_eq!(ref7.touch(), 2); - } - - { - let ref8 = fut8.await; - assert_eq!(ref8.look(), 2); - } - } - - #[test] - fn async_ref_cell_try_borrow() { - let cell = AsyncRefCell::::default_rc(); - - { - let ref1 = cell.try_borrow().unwrap(); - assert_eq!(ref1.look(), 0); - assert!(cell.try_borrow_mut().is_none()); - } - - { - let mut ref2 = cell.try_borrow_mut().unwrap(); - assert_eq!(ref2.touch(), 1); - assert!(cell.try_borrow().is_none()); - assert!(cell.try_borrow_mut().is_none()); - } - - { - let ref3 = cell.try_borrow().unwrap(); - let ref4 = cell.try_borrow().unwrap(); - let ref5 = cell.try_borrow().unwrap(); - let ref6 = cell.try_borrow().unwrap(); - assert_eq!(ref3.look(), 1); - assert_eq!(ref4.look(), 1); - assert_eq!(ref5.look(), 1); - assert_eq!(ref6.look(), 1); - assert!(cell.try_borrow_mut().is_none()); - } - - { - let mut ref7 = cell.try_borrow_mut().unwrap(); - assert_eq!(ref7.look(), 1); - assert_eq!(ref7.touch(), 2); - assert!(cell.try_borrow().is_none()); - assert!(cell.try_borrow_mut().is_none()); - } - - { - let ref8 = cell.try_borrow().unwrap(); - assert_eq!(ref8.look(), 2); - assert!(cell.try_borrow_mut().is_none()); - assert!(cell.try_borrow().is_some()); - } - } - - #[derive(Default)] - struct ThreeThings { - pub thing1: AsyncRefCell, - pub thing2: AsyncRefCell, - pub thing3: AsyncRefCell, - } - - #[tokio::test] - async fn rc_ref_map() { - let three_cells = Rc::new(ThreeThings::default()); - - let rc1 = RcRef::map(three_cells.clone(), |things| &things.thing1); - let rc2 = RcRef::map(three_cells.clone(), |things| &things.thing2); - let rc3 = RcRef::map(three_cells, |things| &things.thing3); - - let mut ref1 = rc1.borrow_mut().await; - let ref2 = rc2.borrow().await; - let mut ref3 = rc3.borrow_mut().await; - - assert_eq!(ref1.look(), 0); - assert_eq!(ref3.touch(), 1); - assert_eq!(ref1.touch(), 1); - assert_eq!(ref2.look(), 0); - assert_eq!(ref3.touch(), 2); - assert_eq!(ref1.look(), 1); - assert_eq!(ref1.touch(), 2); - assert_eq!(ref3.touch(), 3); - assert_eq!(ref1.touch(), 3); - } -} diff --git a/core/error.rs b/core/error.rs deleted file mode 100644 index 55fdcaa7ce..0000000000 --- a/core/error.rs +++ /dev/null @@ -1,719 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use std::borrow::Cow; -use std::collections::HashSet; -use std::fmt; -use std::fmt::Debug; -use std::fmt::Display; -use std::fmt::Formatter; - -use anyhow::Error; - -use crate::runtime::JsRealm; -use crate::runtime::JsRuntime; -use crate::source_map::apply_source_map; -use crate::source_map::get_source_line; -use crate::url::Url; - -/// A generic wrapper that can encapsulate any concrete error type. -// TODO(ry) Deprecate AnyError and encourage deno_core::anyhow::Error instead. -pub type AnyError = anyhow::Error; - -pub type JsErrorCreateFn = dyn Fn(JsError) -> Error; -pub type GetErrorClassFn = &'static dyn for<'e> Fn(&'e Error) -> &'static str; - -/// Creates a new error with a caller-specified error class name and message. -pub fn custom_error( - class: &'static str, - message: impl Into>, -) -> Error { - CustomError { - class, - message: message.into(), - } - .into() -} - -pub fn generic_error(message: impl Into>) -> Error { - custom_error("Error", message) -} - -pub fn type_error(message: impl Into>) -> Error { - custom_error("TypeError", message) -} - -pub fn range_error(message: impl Into>) -> Error { - custom_error("RangeError", message) -} - -pub fn invalid_hostname(hostname: &str) -> Error { - type_error(format!("Invalid hostname: '{hostname}'")) -} - -pub fn uri_error(message: impl Into>) -> Error { - custom_error("URIError", message) -} - -pub fn bad_resource(message: impl Into>) -> Error { - custom_error("BadResource", message) -} - -pub fn bad_resource_id() -> Error { - custom_error("BadResource", "Bad resource ID") -} - -pub fn not_supported() -> Error { - custom_error("NotSupported", "The operation is not supported") -} - -pub fn resource_unavailable() -> Error { - custom_error( - "Busy", - "Resource is unavailable because it is in use by a promise", - ) -} - -/// A simple error type that lets the creator specify both the error message and -/// the error class name. This type is private; externally it only ever appears -/// wrapped in an `anyhow::Error`. To retrieve the error class name from a wrapped -/// `CustomError`, use the function `get_custom_error_class()`. -#[derive(Debug)] -struct CustomError { - class: &'static str, - message: Cow<'static, str>, -} - -impl Display for CustomError { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.write_str(&self.message) - } -} - -impl std::error::Error for CustomError {} - -/// If this error was crated with `custom_error()`, return the specified error -/// class name. In all other cases this function returns `None`. -pub fn get_custom_error_class(error: &Error) -> Option<&'static str> { - error.downcast_ref::().map(|e| e.class) -} - -pub fn to_v8_error<'a>( - scope: &mut v8::HandleScope<'a>, - get_class: GetErrorClassFn, - error: &Error, -) -> v8::Local<'a, v8::Value> { - let tc_scope = &mut v8::TryCatch::new(scope); - let cb = JsRealm::state_from_scope(tc_scope) - .borrow() - .js_build_custom_error_cb - .clone() - .expect("Custom error builder must be set"); - let cb = cb.open(tc_scope); - let this = v8::undefined(tc_scope).into(); - let class = v8::String::new(tc_scope, get_class(error)).unwrap(); - let message = v8::String::new(tc_scope, &format!("{error:#}")).unwrap(); - let mut args = vec![class.into(), message.into()]; - if let Some(code) = crate::error_codes::get_error_code(error) { - args.push(v8::String::new(tc_scope, code).unwrap().into()); - } - let maybe_exception = cb.call(tc_scope, this, &args); - - match maybe_exception { - Some(exception) => exception, - None => { - let mut msg = - "Custom error class must have a builder registered".to_string(); - if tc_scope.has_caught() { - let e = tc_scope.exception().unwrap(); - let js_error = JsError::from_v8_exception(tc_scope, e); - msg = format!("{}: {}", msg, js_error.exception_message); - } - panic!("{}", msg); - } - } -} - -/// A `JsError` represents an exception coming from V8, with stack frames and -/// line numbers. The deno_cli crate defines another `JsError` type, which wraps -/// the one defined here, that adds source map support and colorful formatting. -/// When updating this struct, also update errors_are_equal_without_cause() in -/// fmt_error.rs. -#[derive(Debug, PartialEq, Clone, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -pub struct JsError { - pub name: Option, - pub message: Option, - pub stack: Option, - pub cause: Option>, - pub exception_message: String, - pub frames: Vec, - pub source_line: Option, - pub source_line_frame_index: Option, - pub aggregated: Option>, -} - -#[derive(Debug, Eq, PartialEq, Clone, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -pub struct JsStackFrame { - pub type_name: Option, - pub function_name: Option, - pub method_name: Option, - pub file_name: Option, - pub line_number: Option, - pub column_number: Option, - pub eval_origin: Option, - // Warning! isToplevel has inconsistent snake<>camel case, "typo" originates in v8: - // https://source.chromium.org/search?q=isToplevel&sq=&ss=chromium%2Fchromium%2Fsrc:v8%2F - #[serde(rename = "isToplevel")] - pub is_top_level: Option, - pub is_eval: bool, - pub is_native: bool, - pub is_constructor: bool, - pub is_async: bool, - pub is_promise_all: bool, - pub promise_index: Option, -} - -impl JsStackFrame { - pub fn from_location( - file_name: Option, - line_number: Option, - column_number: Option, - ) -> Self { - Self { - type_name: None, - function_name: None, - method_name: None, - file_name, - line_number, - column_number, - eval_origin: None, - is_top_level: None, - is_eval: false, - is_native: false, - is_constructor: false, - is_async: false, - is_promise_all: false, - promise_index: None, - } - } - - /// Gets the source mapped stack frame corresponding to the - /// (script_resource_name, line_number, column_number) from a v8 message. - /// For non-syntax errors, it should also correspond to the first stack frame. - pub fn from_v8_message<'a>( - scope: &'a mut v8::HandleScope, - message: v8::Local<'a, v8::Message>, - ) -> Option { - let f = message.get_script_resource_name(scope)?; - let f: v8::Local = f.try_into().ok()?; - let f = f.to_rust_string_lossy(scope); - let l = message.get_line_number(scope)? as i64; - // V8's column numbers are 0-based, we want 1-based. - let c = message.get_start_column() as i64 + 1; - let state_rc = JsRuntime::state_from(scope); - let (getter, cache) = { - let state = state_rc.borrow(); - ( - state.source_map_getter.clone(), - state.source_map_cache.clone(), - ) - }; - - if let Some(source_map_getter) = getter { - let mut cache = cache.borrow_mut(); - let (f, l, c) = - apply_source_map(f, l, c, &mut cache, &**source_map_getter); - Some(JsStackFrame::from_location(Some(f), Some(l), Some(c))) - } else { - Some(JsStackFrame::from_location(Some(f), Some(l), Some(c))) - } - } - - pub fn maybe_format_location(&self) -> Option { - Some(format!( - "{}:{}:{}", - self.file_name.as_ref()?, - self.line_number?, - self.column_number? - )) - } -} - -fn get_property<'a>( - scope: &mut v8::HandleScope<'a>, - object: v8::Local, - key: &str, -) -> Option> { - let key = v8::String::new(scope, key).unwrap(); - object.get(scope, key.into()) -} - -#[derive(Default, serde::Deserialize)] -pub(crate) struct NativeJsError { - pub name: Option, - pub message: Option, - // Warning! .stack is special so handled by itself - // stack: Option, -} - -impl JsError { - pub fn from_v8_exception( - scope: &mut v8::HandleScope, - exception: v8::Local, - ) -> Self { - Self::inner_from_v8_exception(scope, exception, Default::default()) - } - - pub fn from_v8_message<'a>( - scope: &'a mut v8::HandleScope, - msg: v8::Local<'a, v8::Message>, - ) -> Self { - // Create a new HandleScope because we're creating a lot of new local - // handles below. - let scope = &mut v8::HandleScope::new(scope); - - let exception_message = msg.get(scope).to_rust_string_lossy(scope); - - // Convert them into Vec - let mut frames: Vec = vec![]; - let mut source_line = None; - let mut source_line_frame_index = None; - - if let Some(stack_frame) = JsStackFrame::from_v8_message(scope, msg) { - frames = vec![stack_frame]; - } - { - let state_rc = JsRuntime::state_from(scope); - let (getter, cache) = { - let state = state_rc.borrow(); - ( - state.source_map_getter.clone(), - state.source_map_cache.clone(), - ) - }; - if let Some(source_map_getter) = getter { - let mut cache = cache.borrow_mut(); - for (i, frame) in frames.iter().enumerate() { - if let (Some(file_name), Some(line_number)) = - (&frame.file_name, frame.line_number) - { - if !file_name.trim_start_matches('[').starts_with("ext:") { - source_line = get_source_line( - file_name, - line_number, - &mut cache, - &**source_map_getter, - ); - source_line_frame_index = Some(i); - break; - } - } - } - } - } - - Self { - name: None, - message: None, - exception_message, - cause: None, - source_line, - source_line_frame_index, - frames, - stack: None, - aggregated: None, - } - } - - fn inner_from_v8_exception<'a>( - scope: &'a mut v8::HandleScope, - exception: v8::Local<'a, v8::Value>, - mut seen: HashSet>, - ) -> Self { - // Create a new HandleScope because we're creating a lot of new local - // handles below. - let scope = &mut v8::HandleScope::new(scope); - - let msg = v8::Exception::create_message(scope, exception); - - let mut exception_message = None; - let context_state_rc = JsRealm::state_from_scope(scope); - - let js_format_exception_cb = - context_state_rc.borrow().js_format_exception_cb.clone(); - if let Some(format_exception_cb) = js_format_exception_cb { - let format_exception_cb = format_exception_cb.open(scope); - let this = v8::undefined(scope).into(); - let formatted = format_exception_cb.call(scope, this, &[exception]); - if let Some(formatted) = formatted { - if formatted.is_string() { - exception_message = Some(formatted.to_rust_string_lossy(scope)); - } - } - } - - if is_instance_of_error(scope, exception) { - let v8_exception = exception; - // The exception is a JS Error object. - let exception: v8::Local = exception.try_into().unwrap(); - let cause = get_property(scope, exception, "cause"); - let e: NativeJsError = - serde_v8::from_v8(scope, exception.into()).unwrap_or_default(); - // Get the message by formatting error.name and error.message. - let name = e.name.clone().unwrap_or_else(|| "Error".to_string()); - let message_prop = e.message.clone().unwrap_or_default(); - let exception_message = exception_message.unwrap_or_else(|| { - if !name.is_empty() && !message_prop.is_empty() { - format!("Uncaught {name}: {message_prop}") - } else if !name.is_empty() { - format!("Uncaught {name}") - } else if !message_prop.is_empty() { - format!("Uncaught {message_prop}") - } else { - "Uncaught".to_string() - } - }); - let cause = cause.and_then(|cause| { - if cause.is_undefined() || seen.contains(&exception) { - None - } else { - seen.insert(exception); - Some(Box::new(JsError::inner_from_v8_exception( - scope, cause, seen, - ))) - } - }); - - // Access error.stack to ensure that prepareStackTrace() has been called. - // This should populate error.__callSiteEvals. - let stack = get_property(scope, exception, "stack"); - let stack: Option> = - stack.and_then(|s| s.try_into().ok()); - let stack = stack.map(|s| s.to_rust_string_lossy(scope)); - - // Read an array of structured frames from error.__callSiteEvals. - let frames_v8 = get_property(scope, exception, "__callSiteEvals"); - // Ignore non-array values - let frames_v8: Option> = - frames_v8.and_then(|a| a.try_into().ok()); - - // Convert them into Vec - let mut frames: Vec = match frames_v8 { - Some(frames_v8) => serde_v8::from_v8(scope, frames_v8.into()).unwrap(), - None => vec![], - }; - let mut source_line = None; - let mut source_line_frame_index = None; - - // When the stack frame array is empty, but the source location given by - // (script_resource_name, line_number, start_column + 1) exists, this is - // likely a syntax error. For the sake of formatting we treat it like it - // was given as a single stack frame. - if frames.is_empty() { - if let Some(stack_frame) = JsStackFrame::from_v8_message(scope, msg) { - frames = vec![stack_frame]; - } - } - { - let state_rc = JsRuntime::state_from(scope); - let (getter, cache) = { - let state = state_rc.borrow(); - ( - state.source_map_getter.clone(), - state.source_map_cache.clone(), - ) - }; - if let Some(source_map_getter) = getter { - let mut cache = cache.borrow_mut(); - - for (i, frame) in frames.iter().enumerate() { - if let (Some(file_name), Some(line_number)) = - (&frame.file_name, frame.line_number) - { - if !file_name.trim_start_matches('[').starts_with("ext:") { - source_line = get_source_line( - file_name, - line_number, - &mut cache, - &**source_map_getter, - ); - source_line_frame_index = Some(i); - break; - } - } - } - } else if let Some(frame) = frames.first() { - if let Some(file_name) = &frame.file_name { - if !file_name.trim_start_matches('[').starts_with("ext:") { - source_line = msg - .get_source_line(scope) - .map(|v| v.to_rust_string_lossy(scope)); - source_line_frame_index = Some(0); - } - } - } - } - - let mut aggregated: Option> = None; - if is_aggregate_error(scope, v8_exception) { - // Read an array of stored errors, this is only defined for `AggregateError` - let aggregated_errors = get_property(scope, exception, "errors"); - let aggregated_errors: Option> = - aggregated_errors.and_then(|a| a.try_into().ok()); - - if let Some(errors) = aggregated_errors { - if errors.length() > 0 { - let mut agg = vec![]; - for i in 0..errors.length() { - let error = errors.get_index(scope, i).unwrap(); - let js_error = Self::from_v8_exception(scope, error); - agg.push(js_error); - } - aggregated = Some(agg); - } - } - }; - - Self { - name: e.name, - message: e.message, - exception_message, - cause, - source_line, - source_line_frame_index, - frames, - stack, - aggregated, - } - } else { - let exception_message = exception_message - .unwrap_or_else(|| msg.get(scope).to_rust_string_lossy(scope)); - // The exception is not a JS Error object. - // Get the message given by V8::Exception::create_message(), and provide - // empty frames. - Self { - name: None, - message: None, - exception_message, - cause: None, - source_line: None, - source_line_frame_index: None, - frames: vec![], - stack: None, - aggregated: None, - } - } - } -} - -impl std::error::Error for JsError {} - -impl Display for JsError { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - if let Some(stack) = &self.stack { - let stack_lines = stack.lines(); - if stack_lines.count() > 1 { - return write!(f, "{stack}"); - } - } - write!(f, "{}", self.exception_message)?; - let location = self.frames.first().and_then(|f| f.maybe_format_location()); - if let Some(location) = location { - write!(f, "\n at {location}")?; - } - Ok(()) - } -} - -// TODO(piscisaureus): rusty_v8 should implement the Error trait on -// values of type v8::Global. -pub(crate) fn to_v8_type_error( - scope: &mut v8::HandleScope, - err: Error, -) -> v8::Global { - let err_string = err.to_string(); - let error_chain = err - .chain() - .skip(1) - .filter(|e| e.to_string() != err_string) - .map(|e| e.to_string()) - .collect::>(); - - let message = if !error_chain.is_empty() { - format!( - "{}\n Caused by:\n {}", - err_string, - error_chain.join("\n ") - ) - } else { - err_string - }; - - let message = v8::String::new(scope, &message).unwrap(); - let exception = v8::Exception::type_error(scope, message); - v8::Global::new(scope, exception) -} - -/// Implements `value instanceof primordials.Error` in JS. Similar to -/// `Value::is_native_error()` but more closely matches the semantics -/// of `instanceof`. `Value::is_native_error()` also checks for static class -/// inheritance rather than just scanning the prototype chain, which doesn't -/// work with our WebIDL implementation of `DOMException`. -pub(crate) fn is_instance_of_error( - scope: &mut v8::HandleScope, - value: v8::Local, -) -> bool { - if !value.is_object() { - return false; - } - let message = v8::String::empty(scope); - let error_prototype = v8::Exception::error(scope, message) - .to_object(scope) - .unwrap() - .get_prototype(scope) - .unwrap(); - let mut maybe_prototype = - value.to_object(scope).unwrap().get_prototype(scope); - while let Some(prototype) = maybe_prototype { - if !prototype.is_object() { - return false; - } - if prototype.strict_equals(error_prototype) { - return true; - } - maybe_prototype = prototype - .to_object(scope) - .and_then(|o| o.get_prototype(scope)); - } - false -} - -/// Implements `value instanceof primordials.AggregateError` in JS, -/// by walking the prototype chain, and comparing each links constructor `name` property. -/// -/// NOTE: There is currently no way to detect `AggregateError` via `rusty_v8`, -/// as v8 itself doesn't expose `v8__Exception__AggregateError`, -/// and we cannot create bindings for it. This forces us to rely on `name` inference. -pub(crate) fn is_aggregate_error( - scope: &mut v8::HandleScope, - value: v8::Local, -) -> bool { - let mut maybe_prototype = Some(value); - while let Some(prototype) = maybe_prototype { - if !prototype.is_object() { - return false; - } - - let prototype = prototype.to_object(scope).unwrap(); - let prototype_name = match get_property(scope, prototype, "constructor") { - Some(constructor) => { - let ctor = constructor.to_object(scope).unwrap(); - get_property(scope, ctor, "name").map(|v| v.to_rust_string_lossy(scope)) - } - None => return false, - }; - - if prototype_name == Some(String::from("AggregateError")) { - return true; - } - - maybe_prototype = prototype.get_prototype(scope); - } - - false -} - -const DATA_URL_ABBREV_THRESHOLD: usize = 150; - -pub fn format_file_name(file_name: &str) -> String { - abbrev_file_name(file_name).unwrap_or_else(|| file_name.to_string()) -} - -fn abbrev_file_name(file_name: &str) -> Option { - if file_name.len() <= DATA_URL_ABBREV_THRESHOLD { - return None; - } - let url = Url::parse(file_name).ok()?; - if url.scheme() != "data" { - return None; - } - let (head, tail) = url.path().split_once(',')?; - let len = tail.len(); - let start = tail.get(0..20)?; - let end = tail.get(len - 20..)?; - Some(format!("{}:{},{}......{}", url.scheme(), head, start, end)) -} - -pub(crate) fn exception_to_err_result( - scope: &mut v8::HandleScope, - exception: v8::Local, - in_promise: bool, -) -> Result { - let state_rc = JsRuntime::state_from(scope); - - let was_terminating_execution = scope.is_execution_terminating(); - // Disable running microtasks for a moment. When upgrading to V8 v11.4 - // we discovered that canceling termination here will cause the queued - // microtasks to run which breaks some tests. - scope.set_microtasks_policy(v8::MicrotasksPolicy::Explicit); - // If TerminateExecution was called, cancel isolate termination so that the - // exception can be created. Note that `scope.is_execution_terminating()` may - // have returned false if TerminateExecution was indeed called but there was - // no JS to execute after the call. - scope.cancel_terminate_execution(); - let mut exception = exception; - { - // If termination is the result of a `op_dispatch_exception` call, we want - // to use the exception that was passed to it rather than the exception that - // was passed to this function. - let state = state_rc.borrow(); - exception = if let Some(exception) = &state.dispatched_exception { - v8::Local::new(scope, exception.clone()) - } else if was_terminating_execution && exception.is_null_or_undefined() { - let message = v8::String::new(scope, "execution terminated").unwrap(); - v8::Exception::error(scope, message) - } else { - exception - }; - } - - let mut js_error = JsError::from_v8_exception(scope, exception); - if in_promise { - js_error.exception_message = format!( - "Uncaught (in promise) {}", - js_error.exception_message.trim_start_matches("Uncaught ") - ); - } - - if was_terminating_execution { - // Resume exception termination. - scope.terminate_execution(); - } - scope.set_microtasks_policy(v8::MicrotasksPolicy::Auto); - - Err(js_error.into()) -} - -pub fn throw_type_error(scope: &mut v8::HandleScope, message: impl AsRef) { - let message = v8::String::new(scope, message.as_ref()).unwrap(); - let exception = v8::Exception::type_error(scope, message); - scope.throw_exception(exception); -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_bad_resource() { - let err = bad_resource("Resource has been closed"); - assert_eq!(err.to_string(), "Resource has been closed"); - } - - #[test] - fn test_bad_resource_id() { - let err = bad_resource_id(); - assert_eq!(err.to_string(), "Bad resource ID"); - } -} diff --git a/core/error_codes.rs b/core/error_codes.rs deleted file mode 100644 index ebe0366099..0000000000 --- a/core/error_codes.rs +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use anyhow::Error; - -pub fn get_error_code(err: &Error) -> Option<&'static str> { - err - .downcast_ref::() - .map(|e| match e.raw_os_error() { - Some(code) => get_os_error_code(code), - None => get_io_error_code(e), - }) - .and_then(|code| match code.is_empty() { - true => None, - false => Some(code), - }) -} - -fn get_io_error_code(err: &std::io::Error) -> &'static str { - // not exhaustive but simple and possibly sufficient once `io_error_more` is stabilized (https://github.com/rust-lang/rust/issues/86442) - // inversion of https://github.com/rust-lang/rust/blob/dca3f1b786efd27be3b325ed1e01e247aa589c3b/library/std/src/sys/unix/mod.rs#L138-L185 - // TODO(@AaronO): revisit as `io_error_more` lands in rust stable - use std::io::ErrorKind; - match err.kind() { - // ErrorKind::ArgumentListTooLong => "E2BIG", - ErrorKind::AddrInUse => "EADDRINUSE", - ErrorKind::AddrNotAvailable => "EADDRNOTAVAIL", - // ErrorKind::ResourceBusy => "EBUSY", - ErrorKind::ConnectionAborted => "ECONNABORTED", - ErrorKind::ConnectionRefused => "ECONNREFUSED", - ErrorKind::ConnectionReset => "ECONNRESET", - // ErrorKind::Deadlock => "EDEADLK", - // ErrorKind::FilesystemQuotaExceeded => "EDQUOT", - ErrorKind::AlreadyExists => "EEXIST", - // ErrorKind::FileTooLarge => "EFBIG", - // ErrorKind::HostUnreachable => "EHOSTUNREACH", - ErrorKind::Interrupted => "EINTR", - ErrorKind::InvalidInput => "EINVAL", - // ErrorKind::IsADirectory => "EISDIR", - // ErrorKind::FilesystemLoop => "ELOOP", - ErrorKind::NotFound => "ENOENT", - ErrorKind::OutOfMemory => "ENOMEM", - // ErrorKind::StorageFull => "ENOSPC", - ErrorKind::Unsupported => "ENOSYS", - // ErrorKind::TooManyLinks => "EMLINK", - // ErrorKind::FilenameTooLong => "ENAMETOOLONG", - // ErrorKind::NetworkDown => "ENETDOWN", - // ErrorKind::NetworkUnreachable => "ENETUNREACH", - ErrorKind::NotConnected => "ENOTCONN", - // ErrorKind::NotADirectory => "ENOTDIR", - // ErrorKind::DirectoryNotEmpty => "ENOTEMPTY", - ErrorKind::BrokenPipe => "EPIPE", - // ErrorKind::ReadOnlyFilesystem => "EROFS", - // ErrorKind::NotSeekable => "ESPIPE", - // ErrorKind::StaleNetworkFileHandle => "ESTALE", - ErrorKind::TimedOut => "ETIMEDOUT", - // ErrorKind::ExecutableFileBusy => "ETXTBSY", - // ErrorKind::CrossesDevices => "EXDEV", - ErrorKind::PermissionDenied => "EACCES", // NOTE: Collides with EPERM ... - ErrorKind::WouldBlock => "EWOULDBLOCK", // NOTE: Collides with EAGAIN ... - _ => "", - } -} - -/// Maps OS errno codes to string names -/// derived from libuv: https://github.com/libuv/libuv/blob/26b2e5dbb6301756644d6e4cf6ca9c49c00513d3/include/uv/errno.h -/// generated with tools/codegen_error_codes.js -#[cfg(unix)] -fn get_os_error_code(errno: i32) -> &'static str { - match errno { - libc::E2BIG => "E2BIG", - libc::EACCES => "EACCES", - libc::EADDRINUSE => "EADDRINUSE", - libc::EADDRNOTAVAIL => "EADDRNOTAVAIL", - libc::EAFNOSUPPORT => "EAFNOSUPPORT", - libc::EAGAIN => "EAGAIN", - libc::EALREADY => "EALREADY", - libc::EBADF => "EBADF", - libc::EBUSY => "EBUSY", - libc::ECANCELED => "ECANCELED", - libc::ECONNABORTED => "ECONNABORTED", - libc::ECONNREFUSED => "ECONNREFUSED", - libc::ECONNRESET => "ECONNRESET", - libc::EEXIST => "EEXIST", - libc::EFAULT => "EFAULT", - libc::EHOSTUNREACH => "EHOSTUNREACH", - libc::EINVAL => "EINVAL", - libc::EIO => "EIO", - libc::EISCONN => "EISCONN", - libc::EISDIR => "EISDIR", - libc::ELOOP => "ELOOP", - libc::EMFILE => "EMFILE", - libc::EMSGSIZE => "EMSGSIZE", - libc::ENAMETOOLONG => "ENAMETOOLONG", - libc::ENETUNREACH => "ENETUNREACH", - libc::ENOBUFS => "ENOBUFS", - libc::ENOENT => "ENOENT", - libc::ENOMEM => "ENOMEM", - libc::ENOSPC => "ENOSPC", - libc::ENOTCONN => "ENOTCONN", - libc::ENOTDIR => "ENOTDIR", - libc::ENOTEMPTY => "ENOTEMPTY", - libc::ENOTSOCK => "ENOTSOCK", - libc::ENOTSUP => "ENOTSUP", - libc::EPERM => "EPERM", - libc::EPIPE => "EPIPE", - libc::EPROTONOSUPPORT => "EPROTONOSUPPORT", - libc::EROFS => "EROFS", - libc::ETIMEDOUT => "ETIMEDOUT", - libc::EXDEV => "EXDEV", - libc::ESOCKTNOSUPPORT => "ESOCKTNOSUPPORT", - _ => "", - } -} - -#[cfg(windows)] -fn get_os_error_code(errno: i32) -> &'static str { - match errno { - 998 => "EACCES", // ERROR_NOACCESS - 10013 => "EACCES", // WSAEACCES - 1920 => "EACCES", // ERROR_CANT_ACCESS_FILE - 1227 => "EADDRINUSE", // ERROR_ADDRESS_ALREADY_ASSOCIATED - 10048 => "EADDRINUSE", // WSAEADDRINUSE - 10049 => "EADDRNOTAVAIL", // WSAEADDRNOTAVAIL - 10047 => "EAFNOSUPPORT", // WSAEAFNOSUPPORT - 10035 => "EAGAIN", // WSAEWOULDBLOCK - 10037 => "EALREADY", // WSAEALREADY - 1004 => "EBADF", // ERROR_INVALID_FLAGS - 6 => "EBADF", // ERROR_INVALID_HANDLE - 33 => "EBUSY", // ERROR_LOCK_VIOLATION - 231 => "EBUSY", // ERROR_PIPE_BUSY - 32 => "EBUSY", // ERROR_SHARING_VIOLATION - 995 => "ECANCELED", // ERROR_OPERATION_ABORTED - 10004 => "ECANCELED", // WSAEINTR - 1236 => "ECONNABORTED", // ERROR_CONNECTION_ABORTED - 10053 => "ECONNABORTED", // WSAECONNABORTED - 1225 => "ECONNREFUSED", // ERROR_CONNECTION_REFUSED - 10061 => "ECONNREFUSED", // WSAECONNREFUSED - 64 => "ECONNRESET", // ERROR_NETNAME_DELETED - 10054 => "ECONNRESET", // WSAECONNRESET - 183 => "EEXIST", // ERROR_ALREADY_EXISTS - 80 => "EEXIST", // ERROR_FILE_EXISTS - 111 => "EFAULT", // ERROR_BUFFER_OVERFLOW - 10014 => "EFAULT", // WSAEFAULT - 1232 => "EHOSTUNREACH", // ERROR_HOST_UNREACHABLE - 10065 => "EHOSTUNREACH", // WSAEHOSTUNREACH - 122 => "EINVAL", // ERROR_INSUFFICIENT_BUFFER - 13 => "EINVAL", // ERROR_INVALID_DATA - 87 => "EINVAL", // ERROR_INVALID_PARAMETER - 1464 => "EINVAL", // ERROR_SYMLINK_NOT_SUPPORTED - 10022 => "EINVAL", // WSAEINVAL - 10046 => "EINVAL", // WSAEPFNOSUPPORT - 1102 => "EIO", // ERROR_BEGINNING_OF_MEDIA - 1111 => "EIO", // ERROR_BUS_RESET - 23 => "EIO", // ERROR_CRC - 1166 => "EIO", // ERROR_DEVICE_DOOR_OPEN - 1165 => "EIO", // ERROR_DEVICE_REQUIRES_CLEANING - 1393 => "EIO", // ERROR_DISK_CORRUPT - 1129 => "EIO", // ERROR_EOM_OVERFLOW - 1101 => "EIO", // ERROR_FILEMARK_DETECTED - 31 => "EIO", // ERROR_GEN_FAILURE - 1106 => "EIO", // ERROR_INVALID_BLOCK_LENGTH - 1117 => "EIO", // ERROR_IO_DEVICE - 1104 => "EIO", // ERROR_NO_DATA_DETECTED - 205 => "EIO", // ERROR_NO_SIGNAL_SENT - 110 => "EIO", // ERROR_OPEN_FAILED - 1103 => "EIO", // ERROR_SETMARK_DETECTED - 156 => "EIO", // ERROR_SIGNAL_REFUSED - 10056 => "EISCONN", // WSAEISCONN - 1921 => "ELOOP", // ERROR_CANT_RESOLVE_FILENAME - 4 => "EMFILE", // ERROR_TOO_MANY_OPEN_FILES - 10024 => "EMFILE", // WSAEMFILE - 10040 => "EMSGSIZE", // WSAEMSGSIZE - 206 => "ENAMETOOLONG", // ERROR_FILENAME_EXCED_RANGE - 1231 => "ENETUNREACH", // ERROR_NETWORK_UNREACHABLE - 10051 => "ENETUNREACH", // WSAENETUNREACH - 10055 => "ENOBUFS", // WSAENOBUFS - 161 => "ENOENT", // ERROR_BAD_PATHNAME - 267 => "ENOENT", // ERROR_DIRECTORY - 203 => "ENOENT", // ERROR_ENVVAR_NOT_FOUND - 2 => "ENOENT", // ERROR_FILE_NOT_FOUND - 123 => "ENOENT", // ERROR_INVALID_NAME - 15 => "ENOENT", // ERROR_INVALID_DRIVE - 4392 => "ENOENT", // ERROR_INVALID_REPARSE_DATA - 126 => "ENOENT", // ERROR_MOD_NOT_FOUND - 3 => "ENOENT", // ERROR_PATH_NOT_FOUND - 11001 => "ENOENT", // WSAHOST_NOT_FOUND - 11004 => "ENOENT", // WSANO_DATA - 8 => "ENOMEM", // ERROR_NOT_ENOUGH_MEMORY - 14 => "ENOMEM", // ERROR_OUTOFMEMORY - 82 => "ENOSPC", // ERROR_CANNOT_MAKE - 112 => "ENOSPC", // ERROR_DISK_FULL - 277 => "ENOSPC", // ERROR_EA_TABLE_FULL - 1100 => "ENOSPC", // ERROR_END_OF_MEDIA - 39 => "ENOSPC", // ERROR_HANDLE_DISK_FULL - 2250 => "ENOTCONN", // ERROR_NOT_CONNECTED - 10057 => "ENOTCONN", // WSAENOTCONN - 145 => "ENOTEMPTY", // ERROR_DIR_NOT_EMPTY - 10038 => "ENOTSOCK", // WSAENOTSOCK - 50 => "ENOTSUP", // ERROR_NOT_SUPPORTED - 5 => "EPERM", // ERROR_ACCESS_DENIED - 1314 => "EPERM", // ERROR_PRIVILEGE_NOT_HELD - 230 => "EPIPE", // ERROR_BAD_PIPE - 232 => "EPIPE", // ERROR_NO_DATA - 233 => "EPIPE", // ERROR_PIPE_NOT_CONNECTED - 10058 => "EPIPE", // WSAESHUTDOWN - 10043 => "EPROTONOSUPPORT", // WSAEPROTONOSUPPORT - 19 => "EROFS", // ERROR_WRITE_PROTECT - 121 => "ETIMEDOUT", // ERROR_SEM_TIMEOUT - 10060 => "ETIMEDOUT", // WSAETIMEDOUT - 17 => "EXDEV", // ERROR_NOT_SAME_DEVICE - 1 => "EISDIR", // ERROR_INVALID_FUNCTION - 208 => "E2BIG", // ERROR_META_EXPANSION_TOO_LONG - 10044 => "ESOCKTNOSUPPORT", // WSAESOCKTNOSUPPORT - _ => "", - } -} diff --git a/core/examples/disable_ops.rs b/core/examples/disable_ops.rs deleted file mode 100644 index c75af1c3ff..0000000000 --- a/core/examples/disable_ops.rs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -//! This example shows you how to define ops in Rust and then call them from -//! JavaScript. - -use deno_core::Extension; -use deno_core::JsRuntime; -use deno_core::RuntimeOptions; - -fn main() { - let my_ext = Extension::builder("my_ext") - .middleware(|op| match op.name { - "op_print" => op.disable(), - _ => op, - }) - .build(); - - // Initialize a runtime instance - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![my_ext], - ..Default::default() - }); - - // Deno.core.print() will now be a NOP - runtime - .execute_script_static("", r#"Deno.core.print("I'm broken")"#) - .unwrap(); -} diff --git a/core/examples/eval_js_value.rs b/core/examples/eval_js_value.rs deleted file mode 100644 index a752d71001..0000000000 --- a/core/examples/eval_js_value.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -//! This example shows you how to evaluate JavaScript expression and deserialize -//! return value into a Rust object. - -// NOTE: -// Here we are deserializing to `serde_json::Value` but you can -// deserialize to any other type that implements the `Deserialize` trait. - -use deno_core::v8; -use deno_core::JsRuntime; -use deno_core::RuntimeOptions; - -fn main() { - let mut runtime = JsRuntime::new(RuntimeOptions::default()); - - // Evaluate some code - let code = "let a = 1+4; a*2"; - let output: serde_json::Value = - eval(&mut runtime, code).expect("Eval failed"); - - println!("Output: {output:?}"); - - let expected_output = serde_json::json!(10); - assert_eq!(expected_output, output); -} - -fn eval( - context: &mut JsRuntime, - code: &'static str, -) -> Result { - let res = context.execute_script_static("", code); - match res { - Ok(global) => { - let scope = &mut context.handle_scope(); - let local = v8::Local::new(scope, global); - // Deserialize a `v8` object into a Rust type using `serde_v8`, - // in this case deserialize to a JSON `Value`. - let deserialized_value = - serde_v8::from_v8::(scope, local); - - match deserialized_value { - Ok(value) => Ok(value), - Err(err) => Err(format!("Cannot deserialize value: {err:?}")), - } - } - Err(err) => Err(format!("Evaling error: {err:?}")), - } -} diff --git a/core/examples/fs_module_loader.rs b/core/examples/fs_module_loader.rs deleted file mode 100644 index 737ff1d5cc..0000000000 --- a/core/examples/fs_module_loader.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use anyhow::Context; -use deno_core::anyhow::Error; -use deno_core::FsModuleLoader; -use deno_core::JsRuntime; -use deno_core::RuntimeOptions; -use std::rc::Rc; - -fn main() -> Result<(), Error> { - let args: Vec = std::env::args().collect(); - if args.len() < 2 { - println!("Usage: target/examples/debug/fs_module_loader "); - std::process::exit(1); - } - let main_url = &args[1]; - println!("Run {main_url}"); - - let mut js_runtime = JsRuntime::new(RuntimeOptions { - module_loader: Some(Rc::new(FsModuleLoader)), - ..Default::default() - }); - - let runtime = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build()?; - - let main_module = deno_core::resolve_path( - main_url, - &std::env::current_dir().context("Unable to get CWD")?, - )?; - - let future = async move { - let mod_id = js_runtime.load_main_module(&main_module, None).await?; - let result = js_runtime.mod_evaluate(mod_id); - js_runtime.run_event_loop(false).await?; - result.await? - }; - runtime.block_on(future) -} diff --git a/core/examples/hello_world.rs b/core/examples/hello_world.rs deleted file mode 100644 index cce6e2218b..0000000000 --- a/core/examples/hello_world.rs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -//! This example shows you how to define ops in Rust and then call them from -//! JavaScript. - -use deno_core::op; -use deno_core::Extension; -use deno_core::JsRuntime; -use deno_core::RuntimeOptions; - -// This is a hack to make the `#[op]` macro work with -// deno_core examples. -// You can remove this: -use deno_core::*; - -#[op] -fn op_sum(nums: Vec) -> Result { - // Sum inputs - let sum = nums.iter().fold(0.0, |a, v| a + v); - // return as a Result - Ok(sum) -} - -fn main() { - // Build a deno_core::Extension providing custom ops - let ext = Extension::builder("my_ext") - .ops(vec![ - // An op for summing an array of numbers - // The op-layer automatically deserializes inputs - // and serializes the returned Result & value - op_sum::decl(), - ]) - .build(); - - // Initialize a runtime instance - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![ext], - ..Default::default() - }); - - // Now we see how to invoke the op we just defined. The runtime automatically - // contains a Deno.core object with several functions for interacting with it. - // You can find its definition in core.js. - runtime - .execute_script_static( - "", - r#" -// Print helper function, calling Deno.core.print() -function print(value) { - Deno.core.print(value.toString()+"\n"); -} - -const arr = [1, 2, 3]; -print("The sum of"); -print(arr); -print("is"); -print(Deno.core.ops.op_sum(arr)); - -// And incorrect usage -try { - print(Deno.core.ops.op_sum(0)); -} catch(e) { - print('Exception:'); - print(e); -} -"#, - ) - .unwrap(); -} diff --git a/core/examples/http_bench_json_ops/http_bench_json_ops.js b/core/examples/http_bench_json_ops/http_bench_json_ops.js deleted file mode 100644 index a840e4e9f9..0000000000 --- a/core/examples/http_bench_json_ops/http_bench_json_ops.js +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -// This is not a real HTTP server. We read blindly one time into 'requestBuf', -// then write this fixed 'responseBuf'. The point of this benchmark is to -// exercise the event loop in a simple yet semi-realistic way. - -// deno-lint-ignore-file camelcase - -const { op_listen } = Deno.core.ops; -const { - op_accept, - op_read_socket, -} = Deno.core.ensureFastOps(); - -const requestBuf = new Uint8Array(64 * 1024); -const responseBuf = new Uint8Array( - "HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World\n" - .split("") - .map((c) => c.charCodeAt(0)), -); - -async function serve(rid) { - try { - while (true) { - await op_read_socket(rid, requestBuf); - if (!ops.op_try_write(rid, responseBuf)) { - await Deno.core.writeAll(rid, responseBuf); - } - } - } catch { - // pass - } - Deno.core.close(rid); -} - -async function main() { - /** Listens on 0.0.0.0:4570, returns rid. */ - const listenerRid = op_listen(); - Deno.core.print(`http_bench_ops listening on http://127.0.0.1:4570/\n`); - - while (true) { - const rid = await op_accept(listenerRid); - serve(rid); - } -} - -main(); diff --git a/core/examples/http_bench_json_ops/main.rs b/core/examples/http_bench_json_ops/main.rs deleted file mode 100644 index a4d6afe319..0000000000 --- a/core/examples/http_bench_json_ops/main.rs +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use deno_core::anyhow::Error; -use deno_core::op; -use deno_core::AsyncRefCell; -use deno_core::AsyncResult; -use deno_core::JsBuffer; -use deno_core::JsRuntimeForSnapshot; -use deno_core::OpState; -use deno_core::Resource; -use deno_core::ResourceId; -use std::cell::RefCell; -use std::env; -use std::net::SocketAddr; -use std::rc::Rc; -use tokio::io::AsyncReadExt; -use tokio::io::AsyncWriteExt; - -// This is a hack to make the `#[op]` macro work with -// deno_core examples. -// You can remove this: -use deno_core::*; - -// Note: a `tokio::net::TcpListener` doesn't need to be wrapped in a cell, -// because it only supports one op (`accept`) which does not require a mutable -// reference to the listener. -struct TcpListener { - inner: tokio::net::TcpListener, -} - -impl TcpListener { - async fn accept(self: Rc) -> Result { - let stream = self.inner.accept().await?.0.into(); - Ok(stream) - } -} - -impl Resource for TcpListener { - fn close(self: Rc) {} -} - -impl TryFrom for TcpListener { - type Error = std::io::Error; - fn try_from( - std_listener: std::net::TcpListener, - ) -> Result { - tokio::net::TcpListener::try_from(std_listener).map(|tokio_listener| Self { - inner: tokio_listener, - }) - } -} - -struct TcpStream { - rd: AsyncRefCell, - wr: AsyncRefCell, -} - -impl TcpStream { - async fn read(self: Rc, data: &mut [u8]) -> Result { - let mut rd = RcRef::map(&self, |r| &r.rd).borrow_mut().await; - let nread = rd.read(data).await?; - Ok(nread) - } - - async fn write(self: Rc, data: &[u8]) -> Result { - let mut wr = RcRef::map(self, |r| &r.wr).borrow_mut().await; - let nwritten = wr.write(data).await?; - Ok(nwritten) - } - - fn try_write(self: Rc, data: &[u8]) -> Result { - let wr = RcRef::map(self, |r| &r.wr) - .try_borrow_mut() - .ok_or_else(|| Error::msg("Failed to acquire lock on TcpStream"))?; - let nwritten = wr.try_write(data)?; - Ok(nwritten) - } -} - -impl Resource for TcpStream { - deno_core::impl_readable_byob!(); - deno_core::impl_writable!(); - - fn close(self: Rc) {} -} - -impl From for TcpStream { - fn from(s: tokio::net::TcpStream) -> Self { - let (rd, wr) = s.into_split(); - Self { - rd: rd.into(), - wr: wr.into(), - } - } -} - -fn create_js_runtime() -> JsRuntimeForSnapshot { - let ext = deno_core::Extension::builder("my_ext") - .ops(vec![ - op_listen::decl(), - op_accept::decl(), - op_try_write::decl(), - op_read_socket::decl(), - ]) - .build(); - - JsRuntimeForSnapshot::new( - deno_core::RuntimeOptions { - extensions: vec![ext], - ..Default::default() - }, - Default::default(), - ) -} - -#[op] -async fn op_read_socket( - state: Rc>, - rid: ResourceId, - mut data: JsBuffer, -) -> Result { - let resource = state.borrow_mut().resource_table.get::(rid)?; - let nread = resource.read(&mut data).await?; - Ok(nread as u32) -} - -#[op] -fn op_listen(state: &mut OpState) -> Result { - let addr = "127.0.0.1:4570".parse::().unwrap(); - let std_listener = std::net::TcpListener::bind(addr)?; - std_listener.set_nonblocking(true)?; - let listener = TcpListener::try_from(std_listener)?; - let rid = state.resource_table.add(listener); - Ok(rid) -} - -#[op] -async fn op_accept( - state: Rc>, - rid: ResourceId, -) -> Result { - let listener = state.borrow().resource_table.get::(rid)?; - let stream = listener.accept().await?; - let rid = state.borrow_mut().resource_table.add(stream); - Ok(rid) -} - -#[op(fast)] -fn op_try_write( - state: &mut OpState, - rid: u32, - value: &[u8], -) -> Result { - let stream = state.resource_table.get::(rid)?; - Ok(stream.try_write(value).is_ok()) -} - -fn main() { - // NOTE: `--help` arg will display V8 help and exit - deno_core::v8_set_flags(env::args().collect()); - - let mut js_runtime = create_js_runtime(); - let runtime = tokio::runtime::Builder::new_current_thread() - .enable_io() - .build() - .unwrap(); - let future = async move { - js_runtime - .execute_script( - "http_bench_json_ops.js", - include_ascii_string!("http_bench_json_ops.js"), - ) - .unwrap(); - js_runtime.run_event_loop(false).await - }; - runtime.block_on(future).unwrap(); -} diff --git a/core/examples/panik.rs b/core/examples/panik.rs deleted file mode 100644 index 54b46d3371..0000000000 --- a/core/examples/panik.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -//! This example shows that op-panics currently result in UB (likely "failed to initiate panic") -//! without a custom panic hook that aborts the process or -C panic=abort. -//! -//! This happens due to the UB of panicking in an extern "C", -//! given how ops are reduced via rusty_v8::MapFnTo -//! See: -//! - https://github.com/rust-lang/rust/issues/74990 -//! - https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html - -use deno_core::op; -use deno_core::Extension; -use deno_core::JsRuntime; -use deno_core::RuntimeOptions; - -// This is a hack to make the `#[op]` macro work with -// deno_core examples. -// You can remove this: -use deno_core::*; - -fn main() { - #[op] - fn op_panik() { - panic!("panik !!!") - } - - let extensions = vec![Extension::builder("my_ext") - .ops(vec![op_panik::decl()]) - .build()]; - let mut rt = JsRuntime::new(RuntimeOptions { - extensions, - ..Default::default() - }); - rt.execute_script_static("panik", "Deno.core.ops.op_panik()") - .unwrap(); -} diff --git a/core/examples/schedule_task.rs b/core/examples/schedule_task.rs deleted file mode 100644 index 348ba76667..0000000000 --- a/core/examples/schedule_task.rs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use deno_core::anyhow::Error; -use deno_core::op; -use deno_core::Extension; -use deno_core::JsRuntime; -use deno_core::OpState; -use deno_core::RuntimeOptions; -use futures::channel::mpsc; -use futures::stream::StreamExt; -use std::task::Poll; - -// This is a hack to make the `#[op]` macro work with -// deno_core examples. -// You can remove this: -use deno_core::*; - -type Task = Box; - -fn main() { - let my_ext = Extension::builder("my_ext") - .ops(vec![op_schedule_task::decl()]) - .event_loop_middleware(|state_rc, cx| { - let mut state = state_rc.borrow_mut(); - let recv = state.borrow_mut::>(); - let mut ref_loop = false; - while let Poll::Ready(Some(call)) = recv.poll_next_unpin(cx) { - call(); - ref_loop = true; // `call` can callback into runtime and schedule new callbacks :-) - } - ref_loop - }) - .state(move |state| { - let (tx, rx) = mpsc::unbounded::(); - state.put(tx); - state.put(rx); - }) - .build(); - - // Initialize a runtime instance - let mut js_runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![my_ext], - ..Default::default() - }); - let runtime = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .unwrap(); - - let future = async move { - // Schedule 10 tasks. - js_runtime - .execute_script_static( - "", - r#"for (let i = 1; i <= 10; i++) Deno.core.ops.op_schedule_task(i);"#, - ) - .unwrap(); - js_runtime.run_event_loop(false).await - }; - runtime.block_on(future).unwrap(); -} - -#[op] -fn op_schedule_task(state: &mut OpState, i: u8) -> Result<(), Error> { - let tx = state.borrow_mut::>(); - tx.unbounded_send(Box::new(move || println!("Hello, world! x{i}"))) - .expect("unbounded_send failed"); - Ok(()) -} diff --git a/core/examples/ts_module_loader.rs b/core/examples/ts_module_loader.rs deleted file mode 100644 index 6adb27977c..0000000000 --- a/core/examples/ts_module_loader.rs +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -//! This example shows how to use swc to transpile TypeScript and JSX/TSX -//! modules. -//! -//! It will only transpile, not typecheck (like Deno's `--no-check` flag). - -use std::pin::Pin; -use std::rc::Rc; - -use anyhow::anyhow; -use anyhow::bail; -use anyhow::Context; -use anyhow::Error; -use deno_ast::MediaType; -use deno_ast::ParseParams; -use deno_ast::SourceTextInfo; -use deno_core::error::AnyError; -use deno_core::resolve_import; -use deno_core::resolve_path; -use deno_core::JsRuntime; -use deno_core::ModuleLoader; -use deno_core::ModuleSource; -use deno_core::ModuleSourceFuture; -use deno_core::ModuleSpecifier; -use deno_core::ModuleType; -use deno_core::ResolutionKind; -use deno_core::RuntimeOptions; -use futures::FutureExt; - -struct TypescriptModuleLoader; - -impl ModuleLoader for TypescriptModuleLoader { - fn resolve( - &self, - specifier: &str, - referrer: &str, - _kind: ResolutionKind, - ) -> Result { - Ok(resolve_import(specifier, referrer)?) - } - - fn load( - &self, - module_specifier: &ModuleSpecifier, - _maybe_referrer: Option<&ModuleSpecifier>, - _is_dyn_import: bool, - ) -> Pin> { - fn load( - module_specifier: &ModuleSpecifier, - ) -> Result { - let path = module_specifier - .to_file_path() - .map_err(|_| anyhow!("Only file:// URLs are supported."))?; - - let media_type = MediaType::from_path(&path); - let (module_type, should_transpile) = match MediaType::from_path(&path) { - MediaType::JavaScript | MediaType::Mjs | MediaType::Cjs => { - (ModuleType::JavaScript, false) - } - MediaType::Jsx => (ModuleType::JavaScript, true), - MediaType::TypeScript - | MediaType::Mts - | MediaType::Cts - | MediaType::Dts - | MediaType::Dmts - | MediaType::Dcts - | MediaType::Tsx => (ModuleType::JavaScript, true), - MediaType::Json => (ModuleType::Json, false), - _ => bail!("Unknown extension {:?}", path.extension()), - }; - - let code = std::fs::read_to_string(&path)?; - let code = if should_transpile { - let parsed = deno_ast::parse_module(ParseParams { - specifier: module_specifier.to_string(), - text_info: SourceTextInfo::from_string(code), - media_type, - capture_tokens: false, - scope_analysis: false, - maybe_syntax: None, - })?; - parsed.transpile(&Default::default())?.text - } else { - code - }; - Ok(ModuleSource::new( - module_type, - code.into(), - module_specifier, - )) - } - - futures::future::ready(load(module_specifier)).boxed_local() - } -} - -fn main() -> Result<(), Error> { - let args: Vec = std::env::args().collect(); - if args.len() < 2 { - println!("Usage: target/examples/debug/ts_module_loader "); - std::process::exit(1); - } - let main_url = &args[1]; - println!("Run {main_url}"); - - let mut js_runtime = JsRuntime::new(RuntimeOptions { - module_loader: Some(Rc::new(TypescriptModuleLoader)), - ..Default::default() - }); - - let main_module = resolve_path( - main_url, - &std::env::current_dir().context("Unable to get CWD")?, - )?; - - let future = async move { - let mod_id = js_runtime.load_main_module(&main_module, None).await?; - let result = js_runtime.mod_evaluate(mod_id); - js_runtime.run_event_loop(false).await?; - result.await? - }; - - tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .unwrap() - .block_on(future) -} diff --git a/core/examples/wasm.js b/core/examples/wasm.js deleted file mode 100644 index cb6a4af522..0000000000 --- a/core/examples/wasm.js +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -// asc wasm.ts --exportStart --initialMemory 6400 -O -o wasm.wasm -// deno-fmt-ignore -const bytes = new Uint8Array([ - 0, 97, 115, 109, 1, 0, 0, 0, 1, 4, 1, 96, 0, 0, 2, - 15, 1, 3, 111, 112, 115, 7, 111, 112, 95, 119, 97, 115, 109, 0, - 0, 3, 3, 2, 0, 0, 5, 4, 1, 0, 128, 50, 7, 36, 4, - 7, 111, 112, 95, 119, 97, 115, 109, 0, 0, 4, 99, 97, 108, 108, - 0, 1, 6, 109, 101, 109, 111, 114, 121, 2, 0, 6, 95, 115, 116, - 97, 114, 116, 0, 2, 10, 10, 2, 4, 0, 16, 0, 11, 3, 0, - 1, 11 - ]); - -const { ops } = Deno.core; - -const module = new WebAssembly.Module(bytes); -const instance = new WebAssembly.Instance(module, { ops }); -ops.op_set_wasm_mem(instance.exports.memory); - -instance.exports.call(); - -const memory = instance.exports.memory; -const view = new Uint8Array(memory.buffer); - -if (view[0] !== 69) { - throw new Error("Expected first byte to be 69"); -} diff --git a/core/examples/wasm.rs b/core/examples/wasm.rs deleted file mode 100644 index 5d5c5f6ff0..0000000000 --- a/core/examples/wasm.rs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use deno_core::op; -use deno_core::Extension; -use deno_core::JsRuntime; -use deno_core::RuntimeOptions; -use std::mem::transmute; -use std::ptr::NonNull; - -// This is a hack to make the `#[op]` macro work with -// deno_core examples. -// You can remove this: - -use deno_core::*; - -struct WasmMemory(NonNull); - -fn wasm_memory_unchecked(state: &mut OpState) -> &mut [u8] { - let WasmMemory(global) = state.borrow::(); - // SAFETY: `v8::Local` is always non-null pointer; the `HandleScope` is - // already on the stack, but we don't have access to it. - let memory_object = unsafe { - transmute::, v8::Local>( - *global, - ) - }; - let backing_store = memory_object.buffer().get_backing_store(); - let ptr = backing_store.data().unwrap().as_ptr() as *mut u8; - let len = backing_store.byte_length(); - // SAFETY: `ptr` is a valid pointer to `len` bytes. - unsafe { std::slice::from_raw_parts_mut(ptr, len) } -} - -#[op(wasm)] -fn op_wasm(state: &mut OpState, memory: Option<&mut [u8]>) { - let memory = memory.unwrap_or_else(|| wasm_memory_unchecked(state)); - memory[0] = 69; -} - -#[op(v8)] -fn op_set_wasm_mem( - scope: &mut v8::HandleScope, - state: &mut OpState, - memory: serde_v8::Value, -) { - let memory = - v8::Local::::try_from(memory.v8_value).unwrap(); - let global = v8::Global::new(scope, memory); - state.put(WasmMemory(global.into_raw())); -} - -fn main() { - // Build a deno_core::Extension providing custom ops - let ext = Extension::builder("my_ext") - .ops(vec![op_wasm::decl(), op_set_wasm_mem::decl()]) - .build(); - - // Initialize a runtime instance - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![ext], - ..Default::default() - }); - - runtime - .execute_script("", include_ascii_string!("wasm.js")) - .unwrap(); -} diff --git a/core/examples/wasm.ts b/core/examples/wasm.ts deleted file mode 100644 index 4cf364c3a5..0000000000 --- a/core/examples/wasm.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -export declare function op_wasm(): void; - -export function call(): void { - op_wasm(); -} diff --git a/core/extensions.rs b/core/extensions.rs deleted file mode 100644 index dac19ec50d..0000000000 --- a/core/extensions.rs +++ /dev/null @@ -1,645 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use crate::modules::ModuleCode; -use crate::OpState; -use anyhow::Context as _; -use anyhow::Error; -use std::cell::RefCell; -use std::path::PathBuf; -use std::rc::Rc; -use std::task::Context; -use v8::fast_api::FastFunction; - -#[derive(Clone, Debug)] -pub enum ExtensionFileSourceCode { - /// Source code is included in the binary produced. Either by being defined - /// inline, or included using `include_str!()`. If you are snapshotting, this - /// will result in two copies of the source code being included - one in the - /// snapshot, the other the static string in the `Extension`. - IncludedInBinary(&'static str), - - // Source code is loaded from a file on disk. It's meant to be used if the - // embedder is creating snapshots. Files will be loaded from the filesystem - // during the build time and they will only be present in the V8 snapshot. - LoadedFromFsDuringSnapshot(PathBuf), -} - -#[derive(Clone, Debug)] -pub struct ExtensionFileSource { - pub specifier: &'static str, - pub code: ExtensionFileSourceCode, -} - -impl ExtensionFileSource { - fn find_non_ascii(s: &str) -> String { - s.chars().filter(|c| !c.is_ascii()).collect::() - } - - pub fn load(&self) -> Result { - match &self.code { - ExtensionFileSourceCode::IncludedInBinary(code) => { - debug_assert!( - code.is_ascii(), - "Extension code must be 7-bit ASCII: {} (found {})", - self.specifier, - Self::find_non_ascii(code) - ); - Ok(ModuleCode::from_static(code)) - } - ExtensionFileSourceCode::LoadedFromFsDuringSnapshot(path) => { - let msg = || format!("Failed to read \"{}\"", path.display()); - let s = std::fs::read_to_string(path).with_context(msg)?; - debug_assert!( - s.is_ascii(), - "Extension code must be 7-bit ASCII: {} (found {})", - self.specifier, - Self::find_non_ascii(&s) - ); - Ok(s.into()) - } - } - } -} - -pub type OpFnRef = v8::FunctionCallback; -pub type OpMiddlewareFn = dyn Fn(OpDecl) -> OpDecl; -pub type OpStateFn = dyn FnOnce(&mut OpState); -pub type OpEventLoopFn = dyn Fn(Rc>, &mut Context) -> bool; - -/// Trait implemented by all generated ops. -pub trait Op { - const NAME: &'static str; - const DECL: OpDecl; -} - -pub struct OpDecl { - pub name: &'static str, - pub v8_fn_ptr: OpFnRef, - pub enabled: bool, - pub is_async: bool, - pub is_unstable: bool, - pub is_v8: bool, - pub arg_count: u8, - pub fast_fn: Option, -} - -impl OpDecl { - pub fn enabled(self, enabled: bool) -> Self { - Self { enabled, ..self } - } - - pub fn disable(self) -> Self { - self.enabled(false) - } -} - -/// Declares a block of Deno `#[op]`s. The first parameter determines the name of the -/// op declaration block, and is usually `deno_ops`. This block generates a function that -/// returns a [`Vec`]. -/// -/// This can be either a compact form like: -/// -/// ```no_compile -/// # use deno_core::*; -/// #[op] -/// fn op_xyz() {} -/// -/// deno_core::ops!(deno_ops, [ -/// op_xyz -/// ]); -/// -/// // Use the ops: -/// deno_ops() -/// ``` -/// -/// ... or a parameterized form like so that allows passing a number of type parameters -/// to each `#[op]`: -/// -/// ```no_compile -/// # use deno_core::*; -/// #[op] -/// fn op_xyz

() where P: Clone {} -/// -/// deno_core::ops!(deno_ops, -/// parameters = [P: Clone], -/// ops = [ -/// op_xyz

-/// ] -/// ); -/// -/// // Use the ops, with `String` as the parameter `P`: -/// deno_ops::() -/// ``` -#[macro_export] -macro_rules! ops { - ($name:ident, parameters = [ $( $param:ident : $type:ident ),+ ], ops = [ $( $(#[$m:meta])* $( $op:ident )::+ $( < $op_param:ident > )? ),+ $(,)? ]) => { - pub(crate) fn $name < $( $param : $type + 'static ),+ > () -> Vec<$crate::OpDecl> { - vec![ - $( - $( #[ $m ] )* - $( $op )::+ :: decl $( :: <$op_param> )? () , - )+ - ] - } - }; - ($name:ident, [ $( $(#[$m:meta])* $( $op:ident )::+ ),+ $(,)? ] ) => { - pub(crate) fn $name() -> Vec<$crate::OpDecl> { - vec![ - $( $( #[ $m ] )* $( $op )::+ :: decl(), )+ - ] - } - } -} - -/// Defines a Deno extension. The first parameter is the name of the extension symbol namespace to create. This is the symbol you -/// will use to refer to the extension. -/// -/// Most extensions will define a combination of ops and ESM files, like so: -/// -/// ```no_compile -/// #[op] -/// fn op_xyz() { -/// } -/// -/// deno_core::extension!( -/// my_extension, -/// ops = [ op_xyz ], -/// esm = [ "my_script.js" ], -/// ); -/// ``` -/// -/// The following options are available for the [`extension`] macro: -/// -/// * deps: a comma-separated list of module dependencies, eg: `deps = [ my_other_extension ]` -/// * parameters: a comma-separated list of parameters and base traits, eg: `parameters = [ P: MyTrait ]` -/// * bounds: a comma-separated list of additional type bounds, eg: `bounds = [ P::MyAssociatedType: MyTrait ]` -/// * ops: a comma-separated list of [`OpDecl`]s to provide, eg: `ops = [ op_foo, op_bar ]` -/// * esm: a comma-separated list of ESM module filenames (see [`include_js_files`]), eg: `esm = [ dir "dir", "my_file.js" ]` -/// * js: a comma-separated list of JS filenames (see [`include_js_files`]), eg: `js = [ dir "dir", "my_file.js" ]` -/// * config: a structure-like definition for configuration parameters which will be required when initializing this extension, eg: `config = { my_param: Option }` -/// * middleware: an [`OpDecl`] middleware function with the signature `fn (OpDecl) -> OpDecl` -/// * state: a state initialization function, with the signature `fn (&mut OpState, ...) -> ()`, where `...` are parameters matching the fields of the config struct -/// * event_loop_middleware: an event-loop middleware function (see [`ExtensionBuilder::event_loop_middleware`]) -#[macro_export] -macro_rules! extension { - ( - $name:ident - $(, deps = [ $( $dep:ident ),* ] )? - $(, parameters = [ $( $param:ident : $type:ident ),+ ] )? - $(, bounds = [ $( $bound:path : $bound_type:ident ),+ ] )? - $(, ops_fn = $ops_symbol:ident $( < $ops_param:ident > )? )? - $(, ops = [ $( $(#[$m:meta])* $( $op:ident )::+ $( < $( $op_param:ident ),* > )? ),+ $(,)? ] )? - $(, esm_entry_point = $esm_entry_point:literal )? - $(, esm = [ $( dir $dir_esm:literal , )? $( $esm:literal ),* $(,)? ] )? - $(, js = [ $( dir $dir_js:literal , )? $( $js:literal ),* $(,)? ] )? - $(, options = { $( $options_id:ident : $options_type:ty ),* $(,)? } )? - $(, middleware = $middleware_fn:expr )? - $(, state = $state_fn:expr )? - $(, event_loop_middleware = $event_loop_middleware_fn:ident )? - $(, customizer = $customizer_fn:expr )? - $(,)? - ) => { - /// Extension struct for - #[doc = stringify!($name)] - /// . - #[allow(non_camel_case_types)] - pub struct $name { - } - - impl $name { - #[inline(always)] - fn ext() -> $crate::ExtensionBuilder { - $crate::Extension::builder_with_deps(stringify!($name), &[ $( $( stringify!($dep) ),* )? ]) - } - - /// If ESM or JS was specified, add those files to the extension. - #[inline(always)] - #[allow(unused_variables)] - fn with_js(ext: &mut $crate::ExtensionBuilder) { - $( ext.esm( - $crate::include_js_files!( $name $( dir $dir_esm , )? $( $esm , )* ) - ); )? - $( - ext.esm_entry_point($esm_entry_point); - )? - $( ext.js( - $crate::include_js_files!( $name $( dir $dir_js , )? $( $js , )* ) - ); )? - } - - // If ops were specified, add those ops to the extension. - #[inline(always)] - #[allow(unused_variables)] - fn with_ops $( < $( $param : $type + 'static ),+ > )?(ext: &mut $crate::ExtensionBuilder) - $( where $( $bound : $bound_type ),+ )? - { - // If individual ops are specified, roll them up into a vector and apply them - $( - ext.ops(vec![ - $( - $( #[ $m ] )* - $( $op )::+ $( :: < $($op_param),* > )? :: decl () - ),+ - ]); - )? - - // Otherwise use the ops_fn, if provided - $crate::extension!(! __ops__ ext $( $ops_symbol $( < $ops_param > )? )? __eot__); - } - - // Includes the state and middleware functions, if defined. - #[inline(always)] - #[allow(unused_variables)] - fn with_state_and_middleware$( < $( $param : $type + 'static ),+ > )?(ext: &mut $crate::ExtensionBuilder, $( $( $options_id : $options_type ),* )? ) - $( where $( $bound : $bound_type ),+ )? - { - $crate::extension!(! __config__ ext $( parameters = [ $( $param : $type ),* ] )? $( config = { $( $options_id : $options_type ),* } )? $( state_fn = $state_fn )? ); - - $( - ext.event_loop_middleware($event_loop_middleware_fn); - )? - - $( - ext.middleware($middleware_fn); - )? - } - - #[inline(always)] - #[allow(unused_variables)] - #[allow(clippy::redundant_closure_call)] - fn with_customizer(ext: &mut $crate::ExtensionBuilder) { - $( ($customizer_fn)(ext); )? - } - - #[allow(dead_code)] - pub fn init_js_only $( < $( $param : $type + 'static ),* > )? () -> $crate::Extension - $( where $( $bound : $bound_type ),+ )? - { - let mut ext = Self::ext(); - // If esm or JS was specified, add JS files - Self::with_js(&mut ext); - Self::with_ops $( ::< $( $param ),+ > )?(&mut ext); - Self::with_customizer(&mut ext); - ext.take() - } - - #[allow(dead_code)] - pub fn init_ops_and_esm $( < $( $param : $type + 'static ),+ > )? ( $( $( $options_id : $options_type ),* )? ) -> $crate::Extension - $( where $( $bound : $bound_type ),+ )? - { - let mut ext = Self::ext(); - // If esm or JS was specified, add JS files - Self::with_js(&mut ext); - Self::with_ops $( ::< $( $param ),+ > )?(&mut ext); - Self::with_state_and_middleware $( ::< $( $param ),+ > )?(&mut ext, $( $( $options_id , )* )? ); - Self::with_customizer(&mut ext); - ext.take() - } - - #[allow(dead_code)] - pub fn init_ops $( < $( $param : $type + 'static ),+ > )? ( $( $( $options_id : $options_type ),* )? ) -> $crate::Extension - $( where $( $bound : $bound_type ),+ )? - { - let mut ext = Self::ext(); - Self::with_ops $( ::< $( $param ),+ > )?(&mut ext); - Self::with_state_and_middleware $( ::< $( $param ),+ > )?(&mut ext, $( $( $options_id , )* )? ); - Self::with_customizer(&mut ext); - ext.take() - } - } - }; - - // This branch of the macro generates a config object that calls the state function with itself. - (! __config__ $ext:ident $( parameters = [ $( $param:ident : $type:ident ),+ ] )? config = { $( $options_id:ident : $options_type:ty ),* } $( state_fn = $state_fn:expr )? ) => { - { - #[doc(hidden)] - struct Config $( < $( $param : $type + 'static ),+ > )? { - $( pub $options_id : $options_type , )* - $( __phantom_data: ::std::marker::PhantomData<($( $param ),+)>, )? - } - let config = Config { - $( $options_id , )* - $( __phantom_data: ::std::marker::PhantomData::<($( $param ),+)>::default() )? - }; - - let state_fn: fn(&mut $crate::OpState, Config $( < $( $param ),+ > )? ) = $( $state_fn )?; - $ext.state(move |state: &mut $crate::OpState| { - state_fn(state, config); - }); - } - }; - - (! __config__ $ext:ident $( parameters = [ $( $param:ident : $type:ident ),+ ] )? $( state_fn = $state_fn:expr )? ) => { - $( $ext.state($state_fn); )? - }; - - (! __ops__ $ext:ident __eot__) => { - }; - - (! __ops__ $ext:ident $ops_symbol:ident __eot__) => { - $ext.ops($ops_symbol()) - }; - - (! __ops__ $ext:ident $ops_symbol:ident < $ops_param:ident > __eot__) => { - $ext.ops($ops_symbol::<$ops_param>()) - }; -} - -#[derive(Default)] -pub struct Extension { - pub(crate) name: &'static str, - js_files: Vec, - esm_files: Vec, - esm_entry_point: Option<&'static str>, - ops: Option>, - opstate_fn: Option>, - middleware_fn: Option>, - event_loop_middleware: Option>, - initialized: bool, - enabled: bool, - deps: Option<&'static [&'static str]>, -} - -// Note: this used to be a trait, but we "downgraded" it to a single concrete type -// for the initial iteration, it will likely become a trait in the future -impl Extension { - pub fn builder(name: &'static str) -> ExtensionBuilder { - ExtensionBuilder { - name, - ..Default::default() - } - } - - pub fn builder_with_deps( - name: &'static str, - deps: &'static [&'static str], - ) -> ExtensionBuilder { - ExtensionBuilder { - name, - deps, - ..Default::default() - } - } - - /// Check if dependencies have been loaded, and errors if either: - /// - The extension is depending on itself or an extension with the same name. - /// - A dependency hasn't been loaded yet. - pub fn check_dependencies(&self, previous_exts: &[Extension]) { - if let Some(deps) = self.deps { - 'dep_loop: for dep in deps { - if dep == &self.name { - panic!("Extension '{}' is either depending on itself or there is another extension with the same name", self.name); - } - - for ext in previous_exts { - if dep == &ext.name { - continue 'dep_loop; - } - } - - panic!("Extension '{}' is missing dependency '{dep}'", self.name); - } - } - } - - /// returns JS source code to be loaded into the isolate (either at snapshotting, - /// or at startup). as a vector of a tuple of the file name, and the source code. - pub fn get_js_sources(&self) -> &Vec { - &self.js_files - } - - pub fn get_esm_sources(&self) -> &Vec { - &self.esm_files - } - - pub fn get_esm_entry_point(&self) -> Option<&'static str> { - self.esm_entry_point - } - - /// Called at JsRuntime startup to initialize ops in the isolate. - pub fn init_ops(&mut self) -> Option> { - // TODO(@AaronO): maybe make op registration idempotent - if self.initialized { - panic!("init_ops called twice: not idempotent or correct"); - } - self.initialized = true; - - let mut ops = self.ops.take()?; - for op in ops.iter_mut() { - op.enabled = self.enabled && op.enabled; - } - Some(ops) - } - - /// Allows setting up the initial op-state of an isolate at startup. - pub fn init_state(&mut self, state: &mut OpState) { - if let Some(op_fn) = self.opstate_fn.take() { - op_fn(state); - } - } - - /// init_middleware lets us middleware op registrations, it's called before init_ops - pub fn init_middleware(&mut self) -> Option> { - self.middleware_fn.take() - } - - pub fn init_event_loop_middleware(&mut self) -> Option> { - self.event_loop_middleware.take() - } - - pub fn run_event_loop_middleware( - &self, - op_state_rc: Rc>, - cx: &mut Context, - ) -> bool { - self - .event_loop_middleware - .as_ref() - .map(|f| f(op_state_rc, cx)) - .unwrap_or(false) - } - - pub fn enabled(self, enabled: bool) -> Self { - Self { enabled, ..self } - } - - pub fn disable(self) -> Self { - self.enabled(false) - } -} - -// Provides a convenient builder pattern to declare Extensions -#[derive(Default)] -pub struct ExtensionBuilder { - js: Vec, - esm: Vec, - esm_entry_point: Option<&'static str>, - ops: Vec, - state: Option>, - middleware: Option>, - event_loop_middleware: Option>, - name: &'static str, - deps: &'static [&'static str], -} - -impl ExtensionBuilder { - pub fn js(&mut self, js_files: Vec) -> &mut Self { - self.js.extend(js_files); - self - } - - pub fn esm(&mut self, esm_files: Vec) -> &mut Self { - self.esm.extend(esm_files); - self - } - - pub fn esm_entry_point(&mut self, entry_point: &'static str) -> &mut Self { - self.esm_entry_point = Some(entry_point); - self - } - - pub fn ops(&mut self, ops: Vec) -> &mut Self { - self.ops.extend(ops); - self - } - - pub fn state(&mut self, opstate_fn: F) -> &mut Self - where - F: FnOnce(&mut OpState) + 'static, - { - self.state = Some(Box::new(opstate_fn)); - self - } - - pub fn middleware(&mut self, middleware_fn: F) -> &mut Self - where - F: Fn(OpDecl) -> OpDecl + 'static, - { - self.middleware = Some(Box::new(middleware_fn)); - self - } - - pub fn event_loop_middleware(&mut self, middleware_fn: F) -> &mut Self - where - F: Fn(Rc>, &mut Context) -> bool + 'static, - { - self.event_loop_middleware = Some(Box::new(middleware_fn)); - self - } - - /// Consume the [`ExtensionBuilder`] and return an [`Extension`]. - pub fn take(self) -> Extension { - let ops = Some(self.ops); - let deps = Some(self.deps); - Extension { - js_files: self.js, - esm_files: self.esm, - esm_entry_point: self.esm_entry_point, - ops, - opstate_fn: self.state, - middleware_fn: self.middleware, - event_loop_middleware: self.event_loop_middleware, - initialized: false, - enabled: true, - name: self.name, - deps, - } - } - - pub fn build(&mut self) -> Extension { - let ops = Some(std::mem::take(&mut self.ops)); - let deps = Some(std::mem::take(&mut self.deps)); - Extension { - js_files: std::mem::take(&mut self.js), - esm_files: std::mem::take(&mut self.esm), - esm_entry_point: self.esm_entry_point.take(), - ops, - opstate_fn: self.state.take(), - middleware_fn: self.middleware.take(), - event_loop_middleware: self.event_loop_middleware.take(), - initialized: false, - enabled: true, - name: self.name, - deps, - } - } -} - -/// Helps embed JS files in an extension. Returns a vector of -/// `ExtensionFileSource`, that represent the filename and source code. All -/// specified files are rewritten into "ext:/". -/// -/// An optional "dir" option can be specified to prefix all files with a -/// directory name. -/// -/// Example (for "my_extension"): -/// ```ignore -/// include_js_files!( -/// "01_hello.js", -/// "02_goodbye.js", -/// ) -/// // Produces following specifiers: -/// - "ext:my_extension/01_hello.js" -/// - "ext:my_extension/02_goodbye.js" -/// -/// /// Example with "dir" option (for "my_extension"): -/// ```ignore -/// include_js_files!( -/// dir "js", -/// "01_hello.js", -/// "02_goodbye.js", -/// ) -/// // Produces following specifiers: -/// - "ext:my_extension/js/01_hello.js" -/// - "ext:my_extension/js/02_goodbye.js" -/// ``` -#[cfg(not(feature = "include_js_files_for_snapshotting"))] -#[macro_export] -macro_rules! include_js_files { - ($name:ident dir $dir:literal, $($file:literal,)+) => { - vec![ - $($crate::ExtensionFileSource { - specifier: concat!("ext:", stringify!($name), "/", $file), - code: $crate::ExtensionFileSourceCode::IncludedInBinary( - include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/", $dir, "/", $file)) - ), - },)+ - ] - }; - - ($name:ident $($file:literal,)+) => { - vec![ - $($crate::ExtensionFileSource { - specifier: concat!("ext:", stringify!($name), "/", $file), - code: $crate::ExtensionFileSourceCode::IncludedInBinary( - include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/", $file)) - ), - },)+ - ] - }; -} - -#[cfg(feature = "include_js_files_for_snapshotting")] -#[macro_export] -macro_rules! include_js_files { - ($name:ident dir $dir:literal, $($file:literal,)+) => { - vec![ - $($crate::ExtensionFileSource { - specifier: concat!("ext:", stringify!($name), "/", $file), - code: $crate::ExtensionFileSourceCode::LoadedFromFsDuringSnapshot( - std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join($dir).join($file) - ), - },)+ - ] - }; - - ($name:ident $($file:literal,)+) => { - vec![ - $($crate::ExtensionFileSource { - specifier: concat!("ext:", stringify!($name), "/", $file), - code: $crate::ExtensionFileSourceCode::LoadedFromFsDuringSnapshot( - std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join($file) - ), - },)+ - ] - }; -} diff --git a/core/fast_string.rs b/core/fast_string.rs deleted file mode 100644 index 95dfb4939b..0000000000 --- a/core/fast_string.rs +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use std::borrow::Borrow; -use std::fmt::Debug; -use std::hash::Hash; -use std::sync::Arc; -use url::Url; -use v8::NewStringType; - -/// Module names and code can be sourced from strings or bytes that are either owned or borrowed. This enumeration allows us -/// to perform a minimal amount of cloning and format-shifting of the underlying data. -/// -/// Note that any [`FastString`] created from a `'static` byte array or string must contain ASCII characters. -/// -/// Examples of ways to construct a [`FastString`]: -/// -/// ```rust -/// # use deno_core::{ascii_str, FastString}; -/// -/// let code: FastString = ascii_str!("a string"); -/// let code: FastString = format!("a string").into(); -/// ``` -pub enum FastString { - /// Created from static data. - Static(&'static str), - - /// Created from static data, known to contain only ASCII chars. - StaticAscii(&'static str), - - /// An owned chunk of data. Note that we use `Box` rather than `Vec` to avoid the - /// storage overhead. - Owned(Box), - - // Scripts loaded from the `deno_graph` infrastructure. - Arc(Arc), -} - -impl FastString { - /// Compile-time function to determine if a string is ASCII. Note that UTF-8 chars - /// longer than one byte have the high-bit set and thus, are not ASCII. - const fn is_ascii(s: &'static [u8]) -> bool { - let mut i = 0; - while i < s.len() { - if !s[i].is_ascii() { - return false; - } - i += 1; - } - true - } - - /// Create a [`FastString`] from a static string. The string may contain non-ASCII characters, and if - /// so, will take the slower path when used in v8. - pub const fn from_static(s: &'static str) -> Self { - if Self::is_ascii(s.as_bytes()) { - Self::StaticAscii(s) - } else { - Self::Static(s) - } - } - - /// Create a [`FastString`] from a static string. If the string contains non-ASCII characters, the compiler - /// will abort. - pub const fn ensure_static_ascii(s: &'static str) -> Self { - if Self::is_ascii(s.as_bytes()) { - Self::StaticAscii(s) - } else { - panic!("This string contained non-ASCII characters and cannot be created with ensure_static_ascii") - } - } - - /// Creates a cheap copy of this [`FastString`], potentially transmuting it to a faster form. Note that this - /// is not a clone operation as it consumes the old [`FastString`]. - pub fn into_cheap_copy(self) -> (Self, Self) { - match self { - Self::Static(s) => (Self::Static(s), Self::Static(s)), - Self::StaticAscii(s) => (Self::StaticAscii(s), Self::StaticAscii(s)), - Self::Arc(s) => (Self::Arc(s.clone()), Self::Arc(s)), - Self::Owned(s) => { - let s: Arc = s.into(); - (Self::Arc(s.clone()), Self::Arc(s)) - } - } - } - - pub const fn try_static_ascii(&self) -> Option<&'static [u8]> { - match self { - Self::StaticAscii(s) => Some(s.as_bytes()), - _ => None, - } - } - - pub fn as_bytes(&self) -> &[u8] { - // TODO(mmastrac): This can be const eventually (waiting for Arc const deref) - match self { - Self::Arc(s) => s.as_bytes(), - Self::Owned(s) => s.as_bytes(), - Self::Static(s) => s.as_bytes(), - Self::StaticAscii(s) => s.as_bytes(), - } - } - - pub fn as_str(&self) -> &str { - // TODO(mmastrac): This can be const eventually (waiting for Arc const deref) - match self { - Self::Arc(s) => s, - Self::Owned(s) => s, - Self::Static(s) => s, - Self::StaticAscii(s) => s, - } - } - - /// Create a v8 string from this [`FastString`]. If the string is static and contains only ASCII characters, - /// an external one-byte static is created. - pub fn v8<'a>( - &self, - scope: &mut v8::HandleScope<'a>, - ) -> v8::Local<'a, v8::String> { - match self.try_static_ascii() { - Some(s) => v8::String::new_external_onebyte_static(scope, s).unwrap(), - None => { - v8::String::new_from_utf8(scope, self.as_bytes(), NewStringType::Normal) - .unwrap() - } - } - } - - /// Truncates a [`FastString`] value, possibly re-allocating or memcpy'ing. May be slow. - pub fn truncate(&mut self, index: usize) { - match self { - Self::Static(b) => *self = Self::Static(&b[..index]), - Self::StaticAscii(b) => *self = Self::StaticAscii(&b[..index]), - Self::Owned(b) => *self = Self::Owned(b[..index].to_owned().into()), - // We can't do much if we have an Arc, so we'll just take ownership of the truncated version - Self::Arc(s) => *self = s[..index].to_owned().into(), - } - } -} - -impl Hash for FastString { - fn hash(&self, state: &mut H) { - self.as_str().hash(state) - } -} - -impl AsRef for FastString { - fn as_ref(&self) -> &str { - self.as_str() - } -} - -impl Borrow for FastString { - fn borrow(&self) -> &str { - self.as_str() - } -} - -impl Debug for FastString { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - Debug::fmt(self.as_str(), f) - } -} - -impl Default for FastString { - fn default() -> Self { - Self::StaticAscii("") - } -} - -impl PartialEq for FastString { - fn eq(&self, other: &Self) -> bool { - self.as_bytes() == other.as_bytes() - } -} - -impl Eq for FastString {} - -/// [`FastString`] can be made cheaply from [`Url`] as we know it's owned and don't need to do an -/// ASCII check. -impl From for FastString { - fn from(value: Url) -> Self { - let s: String = value.into(); - s.into() - } -} - -/// [`FastString`] can be made cheaply from [`String`] as we know it's owned and don't need to do an -/// ASCII check. -impl From for FastString { - fn from(value: String) -> Self { - FastString::Owned(value.into_boxed_str()) - } -} - -/// [`FastString`] can be made cheaply from [`Arc`] as we know it's shared and don't need to do an -/// ASCII check. -impl From> for FastString { - fn from(value: Arc) -> Self { - FastString::Arc(value) - } -} - -/// Include a fast string in the binary. This string is asserted at compile-time to be 7-bit ASCII for optimal -/// v8 performance. -#[macro_export] -macro_rules! include_ascii_string { - ($file:literal) => { - $crate::FastString::ensure_static_ascii(include_str!($file)) - }; -} - -/// Include a fast string in the binary from a string literal. This string is asserted at compile-time to be -/// 7-bit ASCII for optimal v8 performance. -#[macro_export] -macro_rules! ascii_str { - ($str:literal) => { - $crate::FastString::ensure_static_ascii($str) - }; -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn truncate() { - let mut s = "123456".to_owned(); - s.truncate(3); - - let mut code: FastString = FastString::from_static("123456"); - code.truncate(3); - assert_eq!(s, code.as_ref()); - - let mut code: FastString = "123456".to_owned().into(); - code.truncate(3); - assert_eq!(s, code.as_ref()); - - let arc_str: Arc = "123456".into(); - let mut code: FastString = arc_str.into(); - code.truncate(3); - assert_eq!(s, code.as_ref()); - } -} diff --git a/core/flags.rs b/core/flags.rs deleted file mode 100644 index f68d0a4222..0000000000 --- a/core/flags.rs +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -/// Pass the command line arguments to v8. -/// Returns a vector of command line arguments that V8 did not understand. -pub fn v8_set_flags(args: Vec) -> Vec { - v8::V8::set_flags_from_command_line(args) -} diff --git a/core/gotham_state.rs b/core/gotham_state.rs deleted file mode 100644 index 422499f2ff..0000000000 --- a/core/gotham_state.rs +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -// Forked from Gotham: -// https://github.com/gotham-rs/gotham/blob/bcbbf8923789e341b7a0e62c59909428ca4e22e2/gotham/src/state/mod.rs -// Copyright 2017 Gotham Project Developers. MIT license. - -use log::trace; -use std::any::type_name; -use std::any::Any; -use std::any::TypeId; -use std::collections::BTreeMap; - -#[derive(Default)] -pub struct GothamState { - data: BTreeMap>, -} - -impl GothamState { - /// Puts a value into the `GothamState` storage. One value of each type is retained. - /// Successive calls to `put` will overwrite the existing value of the same - /// type. - pub fn put(&mut self, t: T) { - let type_id = TypeId::of::(); - trace!(" inserting record to state for type_id `{:?}`", type_id); - self.data.insert(type_id, Box::new(t)); - } - - /// Determines if the current value exists in `GothamState` storage. - pub fn has(&self) -> bool { - let type_id = TypeId::of::(); - self.data.get(&type_id).is_some() - } - - /// Tries to borrow a value from the `GothamState` storage. - pub fn try_borrow(&self) -> Option<&T> { - let type_id = TypeId::of::(); - trace!(" borrowing state data for type_id `{:?}`", type_id); - self.data.get(&type_id).and_then(|b| b.downcast_ref()) - } - - /// Borrows a value from the `GothamState` storage. - pub fn borrow(&self) -> &T { - self.try_borrow().unwrap_or_else(|| missing::()) - } - - /// Tries to mutably borrow a value from the `GothamState` storage. - pub fn try_borrow_mut(&mut self) -> Option<&mut T> { - let type_id = TypeId::of::(); - trace!(" mutably borrowing state data for type_id `{:?}`", type_id); - self.data.get_mut(&type_id).and_then(|b| b.downcast_mut()) - } - - /// Mutably borrows a value from the `GothamState` storage. - pub fn borrow_mut(&mut self) -> &mut T { - self.try_borrow_mut().unwrap_or_else(|| missing::()) - } - - /// Tries to move a value out of the `GothamState` storage and return ownership. - pub fn try_take(&mut self) -> Option { - let type_id = TypeId::of::(); - trace!( - " taking ownership from state data for type_id `{:?}`", - type_id - ); - self - .data - .remove(&type_id) - .and_then(|b| b.downcast().ok()) - .map(|b| *b) - } - - /// Moves a value out of the `GothamState` storage and returns ownership. - /// - /// # Panics - /// - /// If a value of type `T` is not present in `GothamState`. - pub fn take(&mut self) -> T { - self.try_take().unwrap_or_else(|| missing::()) - } -} - -fn missing() -> ! { - panic!( - "required type {} is not present in GothamState container", - type_name::() - ); -} - -#[cfg(test)] -mod tests { - use super::GothamState; - - struct MyStruct { - value: i32, - } - - struct AnotherStruct { - value: &'static str, - } - - type Alias1 = String; - type Alias2 = String; - - #[test] - fn put_borrow1() { - let mut state = GothamState::default(); - state.put(MyStruct { value: 1 }); - assert_eq!(state.borrow::().value, 1); - } - - #[test] - fn put_borrow2() { - let mut state = GothamState::default(); - assert!(!state.has::()); - state.put(AnotherStruct { value: "a string" }); - assert!(state.has::()); - assert!(!state.has::()); - state.put(MyStruct { value: 100 }); - assert!(state.has::()); - assert_eq!(state.borrow::().value, 100); - assert_eq!(state.borrow::().value, "a string"); - } - - #[test] - fn try_borrow() { - let mut state = GothamState::default(); - state.put(MyStruct { value: 100 }); - assert!(state.try_borrow::().is_some()); - assert_eq!(state.try_borrow::().unwrap().value, 100); - assert!(state.try_borrow::().is_none()); - } - - #[test] - fn try_borrow_mut() { - let mut state = GothamState::default(); - state.put(MyStruct { value: 100 }); - if let Some(a) = state.try_borrow_mut::() { - a.value += 10; - } - assert_eq!(state.borrow::().value, 110); - } - - #[test] - fn borrow_mut() { - let mut state = GothamState::default(); - state.put(MyStruct { value: 100 }); - { - let a = state.borrow_mut::(); - a.value += 10; - } - assert_eq!(state.borrow::().value, 110); - assert!(state.try_borrow_mut::().is_none()); - } - - #[test] - fn try_take() { - let mut state = GothamState::default(); - state.put(MyStruct { value: 100 }); - assert_eq!(state.try_take::().unwrap().value, 100); - assert!(state.try_take::().is_none()); - assert!(state.try_borrow_mut::().is_none()); - assert!(state.try_borrow::().is_none()); - assert!(state.try_take::().is_none()); - } - - #[test] - fn take() { - let mut state = GothamState::default(); - state.put(MyStruct { value: 110 }); - assert_eq!(state.take::().value, 110); - assert!(state.try_take::().is_none()); - assert!(state.try_borrow_mut::().is_none()); - assert!(state.try_borrow::().is_none()); - } - - #[test] - fn type_alias() { - let mut state = GothamState::default(); - state.put::("alias1".to_string()); - state.put::("alias2".to_string()); - assert_eq!(state.take::(), "alias2"); - assert!(state.try_take::().is_none()); - assert!(state.try_take::().is_none()); - } - - #[test] - #[should_panic( - expected = "required type deno_core::gotham_state::tests::MyStruct is not present in GothamState container" - )] - fn missing() { - let state = GothamState::default(); - let _ = state.borrow::(); - } -} diff --git a/core/inspector.rs b/core/inspector.rs deleted file mode 100644 index bd1080a949..0000000000 --- a/core/inspector.rs +++ /dev/null @@ -1,853 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -//! The documentation for the inspector API is sparse, but these are helpful: -//! -//! - -use crate::error::generic_error; -use crate::futures::channel::mpsc; -use crate::futures::channel::mpsc::UnboundedReceiver; -use crate::futures::channel::mpsc::UnboundedSender; -use crate::futures::channel::oneshot; -use crate::futures::future::select; -use crate::futures::future::Either; -use crate::futures::prelude::*; -use crate::futures::stream::SelectAll; -use crate::futures::stream::StreamExt; -use crate::futures::task; -use crate::futures::task::Context; -use crate::futures::task::Poll; -use crate::serde_json; -use crate::serde_json::json; -use crate::serde_json::Value; -use anyhow::Error; -use parking_lot::Mutex; -use std::cell::BorrowMutError; -use std::cell::RefCell; -use std::collections::HashMap; -use std::ffi::c_void; -use std::mem::take; -use std::mem::MaybeUninit; -use std::pin::Pin; -use std::ptr; -use std::ptr::NonNull; -use std::rc::Rc; -use std::sync::Arc; -use std::thread; -use v8::HandleScope; - -pub enum InspectorMsgKind { - Notification, - Message(i32), -} -pub struct InspectorMsg { - pub kind: InspectorMsgKind, - pub content: String, -} -pub type SessionProxySender = UnboundedSender; -pub type SessionProxyReceiver = UnboundedReceiver; - -/// Encapsulates an UnboundedSender/UnboundedReceiver pair that together form -/// a duplex channel for sending/receiving messages in V8 session. -pub struct InspectorSessionProxy { - pub tx: SessionProxySender, - pub rx: SessionProxyReceiver, -} - -#[derive(Clone, Copy)] -enum PollState { - Idle, - Woken, - Polling, - Parked, - Dropped, -} - -/// This structure is used responsible for providing inspector interface -/// to the `JsRuntime`. -/// -/// It stores an instance of `v8::inspector::V8Inspector` and additionally -/// implements `v8::inspector::V8InspectorClientImpl`. -/// -/// After creating this structure it's possible to connect multiple sessions -/// to the inspector, in case of Deno it's either: a "websocket session" that -/// provides integration with Chrome Devtools, or an "in-memory session" that -/// is used for REPL or coverage collection. -pub struct JsRuntimeInspector { - v8_inspector_client: v8::inspector::V8InspectorClientBase, - v8_inspector: Rc>>, - new_session_tx: UnboundedSender, - sessions: RefCell, - flags: RefCell, - waker: Arc, - deregister_tx: Option>, - is_dispatching_message: RefCell, -} - -impl Drop for JsRuntimeInspector { - fn drop(&mut self) { - // Since the waker is cloneable, it might outlive the inspector itself. - // Set the poll state to 'dropped' so it doesn't attempt to request an - // interrupt from the isolate. - self.waker.update(|w| w.poll_state = PollState::Dropped); - - // V8 automatically deletes all sessions when an `V8Inspector` instance is - // deleted, however InspectorSession also has a drop handler that cleans - // up after itself. To avoid a double free, make sure the inspector is - // dropped last. - self.sessions.borrow_mut().drop_sessions(); - - // Notify counterparty that this instance is being destroyed. Ignoring - // result because counterparty waiting for the signal might have already - // dropped the other end of channel. - if let Some(deregister_tx) = self.deregister_tx.take() { - let _ = deregister_tx.send(()); - } - } -} - -impl v8::inspector::V8InspectorClientImpl for JsRuntimeInspector { - fn base(&self) -> &v8::inspector::V8InspectorClientBase { - &self.v8_inspector_client - } - - unsafe fn base_ptr( - this: *const Self, - ) -> *const v8::inspector::V8InspectorClientBase - where - Self: Sized, - { - // SAFETY: this pointer is valid for the whole lifetime of inspector - unsafe { std::ptr::addr_of!((*this).v8_inspector_client) } - } - - fn base_mut(&mut self) -> &mut v8::inspector::V8InspectorClientBase { - &mut self.v8_inspector_client - } - - fn run_message_loop_on_pause(&mut self, context_group_id: i32) { - assert_eq!(context_group_id, JsRuntimeInspector::CONTEXT_GROUP_ID); - self.flags.borrow_mut().on_pause = true; - let _ = self.poll_sessions(None); - } - - fn quit_message_loop_on_pause(&mut self) { - self.flags.borrow_mut().on_pause = false; - } - - fn run_if_waiting_for_debugger(&mut self, context_group_id: i32) { - assert_eq!(context_group_id, JsRuntimeInspector::CONTEXT_GROUP_ID); - self.flags.borrow_mut().waiting_for_session = false; - } -} - -impl JsRuntimeInspector { - /// Currently Deno supports only a single context in `JsRuntime` - /// and thus it's id is provided as an associated constant. - const CONTEXT_GROUP_ID: i32 = 1; - - pub fn new( - scope: &mut v8::HandleScope, - context: v8::Local, - is_main: bool, - ) -> Rc> { - let (new_session_tx, new_session_rx) = - mpsc::unbounded::(); - - let v8_inspector_client = - v8::inspector::V8InspectorClientBase::new::(); - - let waker = InspectorWaker::new(scope.thread_safe_handle()); - - // Create JsRuntimeInspector instance. - let self__ = Rc::new(RefCell::new(Self { - v8_inspector_client, - v8_inspector: Default::default(), - sessions: RefCell::new(SessionContainer::temporary_placeholder()), - new_session_tx, - flags: Default::default(), - waker, - deregister_tx: None, - is_dispatching_message: Default::default(), - })); - let mut self_ = self__.borrow_mut(); - self_.v8_inspector = Rc::new(RefCell::new( - v8::inspector::V8Inspector::create(scope, &mut *self_).into(), - )); - self_.sessions = RefCell::new(SessionContainer::new( - self_.v8_inspector.clone(), - new_session_rx, - )); - - // Tell the inspector about the global context. - let context_name = v8::inspector::StringView::from(&b"global context"[..]); - // NOTE(bartlomieju): this is what Node.js does and it turns out some - // debuggers (like VSCode) rely on this information to disconnect after - // program completes - let aux_data = if is_main { - r#"{"isDefault": true}"# - } else { - r#"{"isDefault": false}"# - }; - let aux_data_view = v8::inspector::StringView::from(aux_data.as_bytes()); - self_ - .v8_inspector - .borrow_mut() - .as_mut() - .unwrap() - .context_created( - context, - Self::CONTEXT_GROUP_ID, - context_name, - aux_data_view, - ); - - // Poll the session handler so we will get notified whenever there is - // new incoming debugger activity. - let _ = self_.poll_sessions(None).unwrap(); - drop(self_); - - self__ - } - - pub fn is_dispatching_message(&self) -> bool { - *self.is_dispatching_message.borrow() - } - - pub fn context_destroyed( - &mut self, - scope: &mut HandleScope, - context: v8::Global, - ) { - let context = v8::Local::new(scope, context); - self - .v8_inspector - .borrow_mut() - .as_mut() - .unwrap() - .context_destroyed(context); - } - - pub fn exception_thrown( - &self, - scope: &mut HandleScope, - exception: v8::Local<'_, v8::Value>, - in_promise: bool, - ) { - let context = scope.get_current_context(); - let message = v8::Exception::create_message(scope, exception); - let stack_trace = message.get_stack_trace(scope).unwrap(); - let mut v8_inspector_ref = self.v8_inspector.borrow_mut(); - let v8_inspector = v8_inspector_ref.as_mut().unwrap(); - let stack_trace = v8_inspector.create_stack_trace(stack_trace); - v8_inspector.exception_thrown( - context, - if in_promise { - v8::inspector::StringView::from("Uncaught (in promise)".as_bytes()) - } else { - v8::inspector::StringView::from("Uncaught".as_bytes()) - }, - exception, - v8::inspector::StringView::from("".as_bytes()), - v8::inspector::StringView::from("".as_bytes()), - 0, - 0, - stack_trace, - 0, - ); - } - - pub fn has_active_sessions(&self) -> bool { - self.sessions.borrow().has_active_sessions() - } - - pub fn has_blocking_sessions(&self) -> bool { - self.sessions.borrow().has_blocking_sessions() - } - - pub fn poll_sessions( - &self, - mut invoker_cx: Option<&mut Context>, - ) -> Result, BorrowMutError> { - // The futures this function uses do not have re-entrant poll() functions. - // However it is can happen that poll_sessions() gets re-entered, e.g. - // when an interrupt request is honored while the inspector future is polled - // by the task executor. We let the caller know by returning some error. - let mut sessions = self.sessions.try_borrow_mut()?; - - self.waker.update(|w| { - match w.poll_state { - PollState::Idle | PollState::Woken => w.poll_state = PollState::Polling, - _ => unreachable!(), - }; - }); - - // Create a new Context object that will make downstream futures - // use the InspectorWaker when they are ready to be polled again. - let waker_ref = task::waker_ref(&self.waker); - let cx = &mut Context::from_waker(&waker_ref); - - loop { - loop { - // Do one "handshake" with a newly connected session at a time. - if let Some(mut session) = sessions.handshake.take() { - let poll_result = session.poll_next_unpin(cx); - match poll_result { - Poll::Pending => { - sessions.established.push(session); - continue; - } - Poll::Ready(Some(session_stream_item)) => { - let (v8_session_ptr, msg) = session_stream_item; - InspectorSession::dispatch_message(v8_session_ptr, msg); - sessions.established.push(session); - continue; - } - Poll::Ready(None) => {} - } - } - - // Accept new connections. - let poll_result = sessions.session_rx.poll_next_unpin(cx); - if let Poll::Ready(Some(session_proxy)) = poll_result { - let session = InspectorSession::new( - sessions.v8_inspector.clone(), - session_proxy, - false, - ); - let prev = sessions.handshake.replace(session); - assert!(prev.is_none()); - } - - // Poll established sessions. - match sessions.established.poll_next_unpin(cx) { - Poll::Ready(Some(session_stream_item)) => { - let (v8_session_ptr, msg) = session_stream_item; - *self.is_dispatching_message.borrow_mut() = true; - InspectorSession::dispatch_message(v8_session_ptr, msg); - *self.is_dispatching_message.borrow_mut() = false; - continue; - } - Poll::Ready(None) => break, - Poll::Pending => break, - }; - } - - let should_block = - self.flags.borrow().on_pause || self.flags.borrow().waiting_for_session; - - let new_state = self.waker.update(|w| { - match w.poll_state { - PollState::Woken => { - // The inspector was woken while the session handler was being - // polled, so we poll it another time. - w.poll_state = PollState::Polling; - } - PollState::Polling if !should_block => { - // The session handler doesn't need to be polled any longer, and - // there's no reason to block (execution is not paused), so this - // function is about to return. - w.poll_state = PollState::Idle; - // Register the task waker that can be used to wake the parent - // task that will poll the inspector future. - if let Some(cx) = invoker_cx.take() { - w.task_waker.replace(cx.waker().clone()); - } - // Register the address of the inspector, which allows the waker - // to request an interrupt from the isolate. - w.inspector_ptr = NonNull::new(self as *const _ as *mut Self); - } - PollState::Polling if should_block => { - // Isolate execution has been paused but there are no more - // events to process, so this thread will be parked. Therefore, - // store the current thread handle in the waker so it knows - // which thread to unpark when new events arrive. - w.poll_state = PollState::Parked; - w.parked_thread.replace(thread::current()); - } - _ => unreachable!(), - }; - w.poll_state - }); - match new_state { - PollState::Idle => break Ok(Poll::Pending), // Yield to task. - PollState::Polling => {} // Poll the session handler again. - PollState::Parked => thread::park(), // Park the thread. - _ => unreachable!(), - }; - } - } - - /// This function blocks the thread until at least one inspector client has - /// established a websocket connection. - pub fn wait_for_session(&mut self) { - loop { - match self.sessions.get_mut().established.iter_mut().next() { - Some(_session) => { - self.flags.get_mut().waiting_for_session = false; - break; - } - None => { - self.flags.get_mut().waiting_for_session = true; - let _ = self.poll_sessions(None).unwrap(); - } - }; - } - } - - /// This function blocks the thread until at least one inspector client has - /// established a websocket connection. - /// - /// After that, it instructs V8 to pause at the next statement. - /// Frontend must send "Runtime.runIfWaitingForDebugger" message to resume - /// execution. - pub fn wait_for_session_and_break_on_next_statement(&mut self) { - loop { - match self.sessions.get_mut().established.iter_mut().next() { - Some(session) => break session.break_on_next_statement(), - None => { - self.flags.get_mut().waiting_for_session = true; - let _ = self.poll_sessions(None).unwrap(); - } - }; - } - } - - /// Obtain a sender for proxy channels. - pub fn get_session_sender(&self) -> UnboundedSender { - self.new_session_tx.clone() - } - - /// Create a channel that notifies the frontend when inspector is dropped. - /// - /// NOTE: Only a single handler is currently available. - pub fn add_deregister_handler(&mut self) -> oneshot::Receiver<()> { - let (tx, rx) = oneshot::channel::<()>(); - let prev = self.deregister_tx.replace(tx); - assert!( - prev.is_none(), - "Only a single deregister handler is allowed" - ); - rx - } - - /// Create a local inspector session that can be used on - /// the same thread as the isolate. - pub fn create_local_session(&self) -> LocalInspectorSession { - // The 'outbound' channel carries messages sent to the session. - let (outbound_tx, outbound_rx) = mpsc::unbounded(); - - // The 'inbound' channel carries messages received from the session. - let (inbound_tx, inbound_rx) = mpsc::unbounded(); - - let proxy = InspectorSessionProxy { - tx: outbound_tx, - rx: inbound_rx, - }; - - // InspectorSessions for a local session is added directly to the "established" - // sessions, so it doesn't need to go through the session sender. - let inspector_session = - InspectorSession::new(self.v8_inspector.clone(), proxy, true); - self - .sessions - .borrow_mut() - .established - .push(inspector_session); - take(&mut self.flags.borrow_mut().waiting_for_session); - - LocalInspectorSession::new(inbound_tx, outbound_rx) - } -} - -#[derive(Default)] -struct InspectorFlags { - waiting_for_session: bool, - on_pause: bool, -} - -/// A helper structure that helps coordinate sessions during different -/// parts of their lifecycle. -struct SessionContainer { - v8_inspector: Rc>>, - session_rx: UnboundedReceiver, - handshake: Option>, - established: SelectAll>, -} - -impl SessionContainer { - fn new( - v8_inspector: Rc>>, - new_session_rx: UnboundedReceiver, - ) -> Self { - Self { - v8_inspector, - session_rx: new_session_rx, - handshake: None, - established: SelectAll::new(), - } - } - - /// V8 automatically deletes all sessions when an `V8Inspector` instance is - /// deleted, however InspectorSession also has a drop handler that cleans - /// up after itself. To avoid a double free, we need to manually drop - /// all sessions before dropping the inspector instance. - fn drop_sessions(&mut self) { - self.v8_inspector = Default::default(); - self.handshake.take(); - self.established.clear(); - } - - fn has_active_sessions(&self) -> bool { - !self.established.is_empty() || self.handshake.is_some() - } - - fn has_blocking_sessions(&self) -> bool { - self.established.iter().any(|s| s.blocking) - } - - /// A temporary placeholder that should be used before actual - /// instance of V8Inspector is created. It's used in favor - /// of `Default` implementation to signal that it's not meant - /// for actual use. - fn temporary_placeholder() -> Self { - let (_tx, rx) = mpsc::unbounded::(); - Self { - v8_inspector: Default::default(), - session_rx: rx, - handshake: None, - established: SelectAll::new(), - } - } -} - -struct InspectorWakerInner { - poll_state: PollState, - task_waker: Option, - parked_thread: Option, - inspector_ptr: Option>, - isolate_handle: v8::IsolateHandle, -} - -// SAFETY: unsafe trait must have unsafe implementation -unsafe impl Send for InspectorWakerInner {} - -struct InspectorWaker(Mutex); - -impl InspectorWaker { - fn new(isolate_handle: v8::IsolateHandle) -> Arc { - let inner = InspectorWakerInner { - poll_state: PollState::Idle, - task_waker: None, - parked_thread: None, - inspector_ptr: None, - isolate_handle, - }; - Arc::new(Self(Mutex::new(inner))) - } - - fn update(&self, update_fn: F) -> R - where - F: FnOnce(&mut InspectorWakerInner) -> R, - { - let mut g = self.0.lock(); - update_fn(&mut g) - } -} - -impl task::ArcWake for InspectorWaker { - fn wake_by_ref(arc_self: &Arc) { - arc_self.update(|w| { - match w.poll_state { - PollState::Idle => { - // Wake the task, if any, that has polled the Inspector future last. - if let Some(waker) = w.task_waker.take() { - waker.wake() - } - // Request an interrupt from the isolate if it's running and there's - // not unhandled interrupt request in flight. - if let Some(arg) = w - .inspector_ptr - .take() - .map(|ptr| ptr.as_ptr() as *mut c_void) - { - w.isolate_handle.request_interrupt(handle_interrupt, arg); - } - extern "C" fn handle_interrupt( - _isolate: &mut v8::Isolate, - arg: *mut c_void, - ) { - // SAFETY: `InspectorWaker` is owned by `JsRuntimeInspector`, so the - // pointer to the latter is valid as long as waker is alive. - let inspector = unsafe { &*(arg as *mut JsRuntimeInspector) }; - let _ = inspector.poll_sessions(None); - } - } - PollState::Parked => { - // Unpark the isolate thread. - let parked_thread = w.parked_thread.take().unwrap(); - assert_ne!(parked_thread.id(), thread::current().id()); - parked_thread.unpark(); - } - _ => {} - }; - w.poll_state = PollState::Woken; - }); - } -} - -/// An inspector session that proxies messages to concrete "transport layer", -/// eg. Websocket or another set of channels. -struct InspectorSession { - v8_channel: v8::inspector::ChannelBase, - v8_session: v8::UniqueRef, - proxy: InspectorSessionProxy, - // Describes if session should keep event loop alive, eg. a local REPL - // session should keep event loop alive, but a Websocket session shouldn't. - blocking: bool, -} - -impl InspectorSession { - const CONTEXT_GROUP_ID: i32 = 1; - - pub fn new( - v8_inspector_rc: Rc>>, - session_proxy: InspectorSessionProxy, - blocking: bool, - ) -> Box { - new_box_with(move |self_ptr| { - let v8_channel = v8::inspector::ChannelBase::new::(); - let mut v8_inspector = v8_inspector_rc.borrow_mut(); - let v8_inspector_ptr = v8_inspector.as_mut().unwrap(); - // TODO(piscisaureus): safety comment - #[allow(clippy::undocumented_unsafe_blocks)] - let v8_session = v8_inspector_ptr.connect( - Self::CONTEXT_GROUP_ID, - // Todo(piscisaureus): V8Inspector::connect() should require that - // the 'v8_channel' argument cannot move. - unsafe { &mut *self_ptr }, - v8::inspector::StringView::empty(), - v8::inspector::V8InspectorClientTrustLevel::FullyTrusted, - ); - - Self { - v8_channel, - v8_session, - proxy: session_proxy, - blocking, - } - }) - } - - // Dispatch message to V8 session - fn dispatch_message( - v8_session_ptr: *mut v8::inspector::V8InspectorSession, - msg: String, - ) { - let msg = v8::inspector::StringView::from(msg.as_bytes()); - // SAFETY: `InspectorSession` is the only owner of `v8_session_ptr`, so - // the pointer is valid for as long the struct. - unsafe { - (*v8_session_ptr).dispatch_protocol_message(msg); - }; - } - - fn send_message( - &self, - msg_kind: InspectorMsgKind, - msg: v8::UniquePtr, - ) { - let msg = msg.unwrap().string().to_string(); - let _ = self.proxy.tx.unbounded_send(InspectorMsg { - kind: msg_kind, - content: msg, - }); - } - - pub fn break_on_next_statement(&mut self) { - let reason = v8::inspector::StringView::from(&b"debugCommand"[..]); - let detail = v8::inspector::StringView::empty(); - // TODO(bartlomieju): use raw `*mut V8InspectorSession` pointer, as this - // reference may become aliased. - (*self.v8_session).schedule_pause_on_next_statement(reason, detail); - } -} - -impl v8::inspector::ChannelImpl for InspectorSession { - fn base(&self) -> &v8::inspector::ChannelBase { - &self.v8_channel - } - - unsafe fn base_ptr(this: *const Self) -> *const v8::inspector::ChannelBase - where - Self: Sized, - { - // SAFETY: this pointer is valid for the whole lifetime of inspector - unsafe { std::ptr::addr_of!((*this).v8_channel) } - } - - fn base_mut(&mut self) -> &mut v8::inspector::ChannelBase { - &mut self.v8_channel - } - - fn send_response( - &mut self, - call_id: i32, - message: v8::UniquePtr, - ) { - self.send_message(InspectorMsgKind::Message(call_id), message); - } - - fn send_notification( - &mut self, - message: v8::UniquePtr, - ) { - self.send_message(InspectorMsgKind::Notification, message); - } - - fn flush_protocol_notifications(&mut self) {} -} - -impl Stream for InspectorSession { - type Item = (*mut v8::inspector::V8InspectorSession, String); - - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context, - ) -> Poll> { - let inner = self.get_mut(); - if let Poll::Ready(maybe_msg) = inner.proxy.rx.poll_next_unpin(cx) { - if let Some(msg) = maybe_msg { - return Poll::Ready(Some((&mut *inner.v8_session, msg))); - } else { - return Poll::Ready(None); - } - } - - Poll::Pending - } -} - -/// A local inspector session that can be used to send and receive protocol messages directly on -/// the same thread as an isolate. -pub struct LocalInspectorSession { - v8_session_tx: UnboundedSender, - v8_session_rx: UnboundedReceiver, - response_tx_map: HashMap>, - next_message_id: i32, - notification_tx: UnboundedSender, - notification_rx: Option>, -} - -impl LocalInspectorSession { - pub fn new( - v8_session_tx: UnboundedSender, - v8_session_rx: UnboundedReceiver, - ) -> Self { - let response_tx_map = HashMap::new(); - let next_message_id = 0; - - let (notification_tx, notification_rx) = mpsc::unbounded::(); - - Self { - v8_session_tx, - v8_session_rx, - response_tx_map, - next_message_id, - notification_tx, - notification_rx: Some(notification_rx), - } - } - - pub fn take_notification_rx(&mut self) -> UnboundedReceiver { - self.notification_rx.take().unwrap() - } - - pub async fn post_message( - &mut self, - method: &str, - params: Option, - ) -> Result { - let id = self.next_message_id; - self.next_message_id += 1; - - let (response_tx, mut response_rx) = - oneshot::channel::(); - self.response_tx_map.insert(id, response_tx); - - let message = json!({ - "id": id, - "method": method, - "params": params, - }); - - let stringified_msg = serde_json::to_string(&message).unwrap(); - self.v8_session_tx.unbounded_send(stringified_msg).unwrap(); - - loop { - let receive_fut = self.receive_from_v8_session().boxed_local(); - match select(receive_fut, &mut response_rx).await { - Either::Left(_) => continue, - Either::Right((result, _)) => { - let response = result?; - if let Some(error) = response.get("error") { - return Err(generic_error(error.to_string())); - } - - let result = response.get("result").unwrap().clone(); - return Ok(result); - } - } - } - } - - async fn receive_from_v8_session(&mut self) { - let inspector_msg = self.v8_session_rx.next().await.unwrap(); - if let InspectorMsgKind::Message(msg_id) = inspector_msg.kind { - let message: serde_json::Value = - match serde_json::from_str(&inspector_msg.content) { - Ok(v) => v, - Err(error) => match error.classify() { - serde_json::error::Category::Syntax => json!({ - "id": msg_id, - "result": { - "result": { - "type": "error", - "description": "Unterminated string literal", - "value": "Unterminated string literal", - }, - "exceptionDetails": { - "exceptionId": 0, - "text": "Unterminated string literal", - "lineNumber": 0, - "columnNumber": 0 - }, - }, - }), - _ => panic!("Could not parse inspector message"), - }, - }; - - self - .response_tx_map - .remove(&msg_id) - .unwrap() - .send(message) - .unwrap(); - } else { - let message = serde_json::from_str(&inspector_msg.content).unwrap(); - // Ignore if the receiver has been dropped. - let _ = self.notification_tx.unbounded_send(message); - } - } -} - -fn new_box_with(new_fn: impl FnOnce(*mut T) -> T) -> Box { - let b = Box::new(MaybeUninit::::uninit()); - let p = Box::into_raw(b) as *mut T; - // SAFETY: memory layout for `T` is ensured on first line of this function - unsafe { - ptr::write(p, new_fn(p)); - Box::from_raw(p) - } -} diff --git a/core/internal.d.ts b/core/internal.d.ts deleted file mode 100644 index b09d188d8f..0000000000 --- a/core/internal.d.ts +++ /dev/null @@ -1,1079 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -// Based on https://github.com/nodejs/node/blob/889ad35d3d41e376870f785b0c1b669cb732013d/typings/primordials.d.ts -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. -// This file subclasses and stores the JS builtins that come from the VM -// so that Node.js's builtin modules do not need to later look these up from -// the global proxy, which can be mutated by users. - -/// -/// - -declare namespace __bootstrap { - /** - * Primordials are a way to safely use globals without fear of global mutation - * Generally, this means removing `this` parameter usage and instead using - * a regular parameter: - * - * @example - * - * ```js - * 'thing'.startsWith('hello'); - * ``` - * - * becomes - * - * ```js - * primordials.StringPrototypeStartsWith('thing', 'hello') - * ``` - */ - declare namespace primordials { - type UncurryThis unknown> = - (self: ThisParameterType, ...args: Parameters) => ReturnType; - type UncurryThisStaticApply< - T extends (this: unknown, ...args: unknown[]) => unknown, - > = (self: ThisParameterType, args: Parameters) => ReturnType; - type StaticApply unknown> = - (args: Parameters) => ReturnType; - - export function uncurryThis unknown>( - fn: T, - ): (self: ThisType, ...args: Parameters) => ReturnType; - export function applyBind unknown>( - fn: T, - ): (self: ThisType, args: Parameters) => ReturnType; - - // safe objects - export function makeSafe( - unsafe: NewableFunction, - safe: T, - ): T; - export const SafeMap: typeof globalThis.Map; - export const SafeWeakMap: typeof globalThis.WeakMap; - export const SafeSet: typeof globalThis.Set; - export const SafeWeakSet: typeof globalThis.WeakSet; - export const SafeFinalizationRegistry: - typeof globalThis.FinalizationRegistry; - export const SafeWeakRef: typeof globalThis.WeakRef; - export const SafePromiseAll: typeof Promise.all; - // NOTE: Uncomment the following functions when you need to use them - // export const SafePromiseAllSettled: typeof Promise.allSettled; - // export const SafePromiseAny: typeof Promise.any; - // export const SafePromiseRace: typeof Promise.race; - export const SafePromisePrototypeFinally: UncurryThis< - Promise.prototype.finally - >; - export const SafeRegExp: typeof RegExp; - - // safe iterators - export const SafeArrayIterator: new (array: T[]) => IterableIterator; - export const SafeSetIterator: new (set: Set) => IterableIterator; - export const SafeMapIterator: new ( - map: Map, - ) => IterableIterator<[K, V]>; - export const SafeStringIterator: new ( - str: string, - ) => IterableIterator; - - // intrinsic objects - export const indirectEval: typeof globalThis.eval; - export const isNaN: typeof globalThis.isNaN; - export const decodeURI: typeof globalThis.decodeURI; - export const decodeURIComponent: typeof globalThis.decodeURIComponent; - export const encodeURI: typeof globalThis.encodeURI; - export const encodeURIComponent: typeof globalThis.encodeURIComponent; - export const queueMicrotask: typeof globalThis.queueMicrotask; - export const setQueueMicrotask: ( - queueMicrotask: typeof globalThis.queueMicrotask, - ) => void; - export const JSONParse: typeof JSON.parse; - export const JSONStringify: typeof JSON.stringify; - export const MathAbs: typeof Math.abs; - export const MathAcos: typeof Math.acos; - export const MathAcosh: typeof Math.acosh; - export const MathAsin: typeof Math.asin; - export const MathAsinh: typeof Math.asinh; - export const MathAtan: typeof Math.atan; - export const MathAtanh: typeof Math.atanh; - export const MathAtan2: typeof Math.atan2; - export const MathCeil: typeof Math.ceil; - export const MathCbrt: typeof Math.cbrt; - export const MathExpm1: typeof Math.expm1; - export const MathClz32: typeof Math.clz32; - export const MathCos: typeof Math.cos; - export const MathCosh: typeof Math.cosh; - export const MathExp: typeof Math.exp; - export const MathFloor: typeof Math.floor; - export const MathFround: typeof Math.fround; - export const MathHypot: typeof Math.hypot; - export const MathHypotApply: StaticApply; - export const MathImul: typeof Math.imul; - export const MathLog: typeof Math.log; - export const MathLog1p: typeof Math.log1p; - export const MathLog2: typeof Math.log2; - export const MathLog10: typeof Math.log10; - export const MathMax: typeof Math.max; - export const MathMaxApply: StaticApply; - export const MathMin: typeof Math.min; - export const MathMinApply: StaticApply; - export const MathPow: typeof Math.pow; - export const MathRandom: typeof Math.random; - export const MathRound: typeof Math.round; - export const MathSign: typeof Math.sign; - export const MathSin: typeof Math.sin; - export const MathSinh: typeof Math.sinh; - export const MathSqrt: typeof Math.sqrt; - export const MathTan: typeof Math.tan; - export const MathTanh: typeof Math.tanh; - export const MathTrunc: typeof Math.trunc; - export const MathE: typeof Math.E; - export const MathLN10: typeof Math.LN10; - export const MathLN2: typeof Math.LN2; - export const MathLOG10E: typeof Math.LOG10E; - export const MathLOG2E: typeof Math.LOG2E; - export const MathPI: typeof Math.PI; - export const MathSQRT1_2: typeof Math.SQRT1_2; - export const MathSQRT2: typeof Math.SQRT2; - export const Proxy: typeof globalThis.Proxy; - export const ProxyLength: typeof Proxy.length; - export const ProxyName: typeof Proxy.name; - export const ProxyRevocable: typeof Proxy.revocable; - export const ReflectDefineProperty: typeof Reflect.defineProperty; - export const ReflectDeleteProperty: typeof Reflect.deleteProperty; - export const ReflectApply: typeof Reflect.apply; - export const ReflectConstruct: typeof Reflect.construct; - export const ReflectGet: typeof Reflect.get; - export const ReflectGetOwnPropertyDescriptor: - typeof Reflect.getOwnPropertyDescriptor; - export const ReflectGetPrototypeOf: typeof Reflect.getPrototypeOf; - export const ReflectHas: typeof Reflect.has; - export const ReflectIsExtensible: typeof Reflect.isExtensible; - export const ReflectOwnKeys: typeof Reflect.ownKeys; - export const ReflectPreventExtensions: typeof Reflect.preventExtensions; - export const ReflectSet: typeof Reflect.set; - export const ReflectSetPrototypeOf: typeof Reflect.setPrototypeOf; - export const AggregateError: typeof globalThis.AggregateError; - export const AggregateErrorLength: typeof AggregateError.length; - export const AggregateErrorName: typeof AggregateError.name; - export const AggregateErrorPrototype: typeof AggregateError.prototype; - export const Array: typeof globalThis.Array; - export const ArrayLength: typeof Array.length; - export const ArrayName: typeof Array.name; - export const ArrayPrototype: typeof Array.prototype; - export const ArrayIsArray: typeof Array.isArray; - export const ArrayFrom: typeof Array.from; - export const ArrayOf: typeof Array.of; - export const ArrayOfApply: StaticApply; - export const ArrayPrototypeAt: UncurryThis; - export const ArrayPrototypeConcat: UncurryThis< - typeof Array.prototype.concat - >; - export const ArrayPrototypeCopyWithin: UncurryThis< - typeof Array.prototype.copyWithin - >; - export const ArrayPrototypeFill: UncurryThis; - export const ArrayPrototypeFind: UncurryThis; - export const ArrayPrototypeFindIndex: UncurryThis< - typeof Array.prototype.findIndex - >; - export const ArrayPrototypeLastIndexOf: UncurryThis< - typeof Array.prototype.lastIndexOf - >; - export const ArrayPrototypePop: UncurryThis; - export const ArrayPrototypePush: UncurryThis; - export const ArrayPrototypePushApply: UncurryThisStaticApply< - typeof Array.prototype.push - >; - export const ArrayPrototypeReverse: UncurryThis< - typeof Array.prototype.reverse - >; - export const ArrayPrototypeShift: UncurryThis; - export const ArrayPrototypeUnshift: UncurryThis< - typeof Array.prototype.unshift - >; - export const ArrayPrototypeUnshiftApply: UncurryThisStaticApply< - typeof Array.prototype.unshift - >; - export const ArrayPrototypeSlice: UncurryThis; - export const ArrayPrototypeSort: UncurryThis; - export const ArrayPrototypeSplice: UncurryThis< - typeof Array.prototype.splice - >; - export const ArrayPrototypeIncludes: UncurryThis< - typeof Array.prototype.includes - >; - export const ArrayPrototypeIndexOf: UncurryThis< - typeof Array.prototype.indexOf - >; - export const ArrayPrototypeJoin: UncurryThis; - export const ArrayPrototypeKeys: UncurryThis; - export const ArrayPrototypeEntries: UncurryThis< - typeof Array.prototype.entries - >; - export const ArrayPrototypeValues: UncurryThis< - typeof Array.prototype.values - >; - export const ArrayPrototypeForEach: UncurryThis< - typeof Array.prototype.forEach - >; - export const ArrayPrototypeFilter: UncurryThis< - typeof Array.prototype.filter - >; - export const ArrayPrototypeFlat: UncurryThis; - export const ArrayPrototypeFlatMap: UncurryThis< - typeof Array.prototype.flatMap - >; - export const ArrayPrototypeMap: UncurryThis; - export const ArrayPrototypeEvery: UncurryThis; - export const ArrayPrototypeSome: UncurryThis; - export const ArrayPrototypeReduce: UncurryThis< - typeof Array.prototype.reduce - >; - export const ArrayPrototypeReduceRight: UncurryThis< - typeof Array.prototype.reduceRight - >; - export const ArrayPrototypeToLocaleString: UncurryThis< - typeof Array.prototype.toLocaleString - >; - export const ArrayPrototypeToString: UncurryThis< - typeof Array.prototype.toString - >; - export const ArrayBuffer: typeof globalThis.ArrayBuffer; - export const ArrayBufferLength: typeof ArrayBuffer.length; - export const ArrayBufferName: typeof ArrayBuffer.name; - export const ArrayBufferIsView: typeof ArrayBuffer.isView; - export const ArrayBufferPrototype: typeof ArrayBuffer.prototype; - export const ArrayBufferPrototypeGetByteLength: ( - buffer: ArrayBuffer, - ) => number; - export const ArrayBufferPrototypeSlice: UncurryThis< - typeof ArrayBuffer.prototype.slice - >; - export const BigInt: typeof globalThis.BigInt; - export const BigIntLength: typeof BigInt.length; - export const BigIntName: typeof BigInt.name; - export const BigIntPrototype: typeof BigInt.prototype; - export const BigIntAsUintN: typeof BigInt.asUintN; - export const BigIntAsIntN: typeof BigInt.asIntN; - export const BigIntPrototypeToLocaleString: UncurryThis< - typeof BigInt.prototype.toLocaleString - >; - export const BigIntPrototypeToString: UncurryThis< - typeof BigInt.prototype.toString - >; - export const BigIntPrototypeValueOf: UncurryThis< - typeof BigInt.prototype.valueOf - >; - export const BigInt64Array: typeof globalThis.BigInt64Array; - export const BigInt64ArrayLength: typeof BigInt64Array.length; - export const BigInt64ArrayName: typeof BigInt64Array.name; - export const BigInt64ArrayPrototype: typeof BigInt64Array.prototype; - export const BigInt64ArrayBYTES_PER_ELEMENT: - typeof BigInt64Array.BYTES_PER_ELEMENT; - export const BigUint64Array: typeof globalThis.BigUint64Array; - export const BigUint64ArrayLength: typeof BigUint64Array.length; - export const BigUint64ArrayName: typeof BigUint64Array.name; - export const BigUint64ArrayPrototype: typeof BigUint64Array.prototype; - export const BigUint64ArrayBYTES_PER_ELEMENT: - typeof BigUint64Array.BYTES_PER_ELEMENT; - export const Boolean: typeof globalThis.Boolean; - export const BooleanLength: typeof Boolean.length; - export const BooleanName: typeof Boolean.name; - export const BooleanPrototype: typeof Boolean.prototype; - export const BooleanPrototypeToString: UncurryThis< - typeof Boolean.prototype.toString - >; - export const BooleanPrototypeValueOf: UncurryThis< - typeof Boolean.prototype.valueOf - >; - export const DataView: typeof globalThis.DataView; - export const DataViewLength: typeof DataView.length; - export const DataViewName: typeof DataView.name; - export const DataViewPrototype: typeof DataView.prototype; - export const DataViewPrototypeGetBuffer: ( - view: DataView, - ) => ArrayBuffer | SharedArrayBuffer; - export const DataViewPrototypeGetByteLength: (view: DataView) => number; - export const DataViewPrototypeGetByteOffset: (view: DataView) => number; - export const DataViewPrototypeGetInt8: UncurryThis< - typeof DataView.prototype.getInt8 - >; - export const DataViewPrototypeSetInt8: UncurryThis< - typeof DataView.prototype.setInt8 - >; - export const DataViewPrototypeGetUint8: UncurryThis< - typeof DataView.prototype.getUint8 - >; - export const DataViewPrototypeSetUint8: UncurryThis< - typeof DataView.prototype.setUint8 - >; - export const DataViewPrototypeGetInt16: UncurryThis< - typeof DataView.prototype.getInt16 - >; - export const DataViewPrototypeSetInt16: UncurryThis< - typeof DataView.prototype.setInt16 - >; - export const DataViewPrototypeGetUint16: UncurryThis< - typeof DataView.prototype.getUint16 - >; - export const DataViewPrototypeSetUint16: UncurryThis< - typeof DataView.prototype.setUint16 - >; - export const DataViewPrototypeGetInt32: UncurryThis< - typeof DataView.prototype.getInt32 - >; - export const DataViewPrototypeSetInt32: UncurryThis< - typeof DataView.prototype.setInt32 - >; - export const DataViewPrototypeGetUint32: UncurryThis< - typeof DataView.prototype.getUint32 - >; - export const DataViewPrototypeSetUint32: UncurryThis< - typeof DataView.prototype.setUint32 - >; - export const DataViewPrototypeGetFloat32: UncurryThis< - typeof DataView.prototype.getFloat32 - >; - export const DataViewPrototypeSetFloat32: UncurryThis< - typeof DataView.prototype.setFloat32 - >; - export const DataViewPrototypeGetFloat64: UncurryThis< - typeof DataView.prototype.getFloat64 - >; - export const DataViewPrototypeSetFloat64: UncurryThis< - typeof DataView.prototype.setFloat64 - >; - export const DataViewPrototypeGetBigInt64: UncurryThis< - typeof DataView.prototype.getBigInt64 - >; - export const DataViewPrototypeSetBigInt64: UncurryThis< - typeof DataView.prototype.setBigInt64 - >; - export const DataViewPrototypeGetBigUint64: UncurryThis< - typeof DataView.prototype.getBigUint64 - >; - export const DataViewPrototypeSetBigUint64: UncurryThis< - typeof DataView.prototype.setBigUint64 - >; - export const Date: typeof globalThis.Date; - export const DateLength: typeof Date.length; - export const DateName: typeof Date.name; - export const DatePrototype: typeof Date.prototype; - export const DateNow: typeof Date.now; - export const DateParse: typeof Date.parse; - export const DateUTC: typeof Date.UTC; - export const DatePrototypeToString: UncurryThis< - typeof Date.prototype.toString - >; - export const DatePrototypeToDateString: UncurryThis< - typeof Date.prototype.toDateString - >; - export const DatePrototypeToTimeString: UncurryThis< - typeof Date.prototype.toTimeString - >; - export const DatePrototypeToISOString: UncurryThis< - typeof Date.prototype.toISOString - >; - export const DatePrototypeToUTCString: UncurryThis< - typeof Date.prototype.toUTCString - >; - export const DatePrototypeToGMTString: UncurryThis< - typeof Date.prototype.toGMTString - >; - export const DatePrototypeGetDate: UncurryThis< - typeof Date.prototype.getDate - >; - export const DatePrototypeSetDate: UncurryThis< - typeof Date.prototype.setDate - >; - export const DatePrototypeGetDay: UncurryThis; - export const DatePrototypeGetFullYear: UncurryThis< - typeof Date.prototype.getFullYear - >; - export const DatePrototypeSetFullYear: UncurryThis< - typeof Date.prototype.setFullYear - >; - export const DatePrototypeGetHours: UncurryThis< - typeof Date.prototype.getHours - >; - export const DatePrototypeSetHours: UncurryThis< - typeof Date.prototype.setHours - >; - export const DatePrototypeGetMilliseconds: UncurryThis< - typeof Date.prototype.getMilliseconds - >; - export const DatePrototypeSetMilliseconds: UncurryThis< - typeof Date.prototype.setMilliseconds - >; - export const DatePrototypeGetMinutes: UncurryThis< - typeof Date.prototype.getMinutes - >; - export const DatePrototypeSetMinutes: UncurryThis< - typeof Date.prototype.setMinutes - >; - export const DatePrototypeGetMonth: UncurryThis< - typeof Date.prototype.getMonth - >; - export const DatePrototypeSetMonth: UncurryThis< - typeof Date.prototype.setMonth - >; - export const DatePrototypeGetSeconds: UncurryThis< - typeof Date.prototype.getSeconds - >; - export const DatePrototypeSetSeconds: UncurryThis< - typeof Date.prototype.setSeconds - >; - export const DatePrototypeGetTime: UncurryThis< - typeof Date.prototype.getTime - >; - export const DatePrototypeSetTime: UncurryThis< - typeof Date.prototype.setTime - >; - export const DatePrototypeGetTimezoneOffset: UncurryThis< - typeof Date.prototype.getTimezoneOffset - >; - export const DatePrototypeGetUTCDate: UncurryThis< - typeof Date.prototype.getUTCDate - >; - export const DatePrototypeSetUTCDate: UncurryThis< - typeof Date.prototype.setUTCDate - >; - export const DatePrototypeGetUTCDay: UncurryThis< - typeof Date.prototype.getUTCDay - >; - export const DatePrototypeGetUTCFullYear: UncurryThis< - typeof Date.prototype.getUTCFullYear - >; - export const DatePrototypeSetUTCFullYear: UncurryThis< - typeof Date.prototype.setUTCFullYear - >; - export const DatePrototypeGetUTCHours: UncurryThis< - typeof Date.prototype.getUTCHours - >; - export const DatePrototypeSetUTCHours: UncurryThis< - typeof Date.prototype.setUTCHours - >; - export const DatePrototypeGetUTCMilliseconds: UncurryThis< - typeof Date.prototype.getUTCMilliseconds - >; - export const DatePrototypeSetUTCMilliseconds: UncurryThis< - typeof Date.prototype.setUTCMilliseconds - >; - export const DatePrototypeGetUTCMinutes: UncurryThis< - typeof Date.prototype.getUTCMinutes - >; - export const DatePrototypeSetUTCMinutes: UncurryThis< - typeof Date.prototype.setUTCMinutes - >; - export const DatePrototypeGetUTCMonth: UncurryThis< - typeof Date.prototype.getUTCMonth - >; - export const DatePrototypeSetUTCMonth: UncurryThis< - typeof Date.prototype.setUTCMonth - >; - export const DatePrototypeGetUTCSeconds: UncurryThis< - typeof Date.prototype.getUTCSeconds - >; - export const DatePrototypeSetUTCSeconds: UncurryThis< - typeof Date.prototype.setUTCSeconds - >; - export const DatePrototypeValueOf: UncurryThis< - typeof Date.prototype.valueOf - >; - export const DatePrototypeGetYear: UncurryThis< - typeof Date.prototype.getYear - >; - export const DatePrototypeSetYear: UncurryThis< - typeof Date.prototype.setYear - >; - export const DatePrototypeToJSON: UncurryThis; - export const DatePrototypeToLocaleString: UncurryThis< - typeof Date.prototype.toLocaleString - >; - export const DatePrototypeToLocaleDateString: UncurryThis< - typeof Date.prototype.toLocaleDateString - >; - export const DatePrototypeToLocaleTimeString: UncurryThis< - typeof Date.prototype.toLocaleTimeString - >; - export const Error: typeof globalThis.Error; - export const ErrorLength: typeof Error.length; - export const ErrorName: typeof Error.name; - export const ErrorPrototype: typeof Error.prototype; - export const ErrorCaptureStackTrace: typeof Error.captureStackTrace; - export const ErrorStackTraceLimit: typeof Error.stackTraceLimit; - export const ErrorPrototypeToString: UncurryThis< - typeof Error.prototype.toString - >; - export const EvalError: typeof globalThis.EvalError; - export const EvalErrorLength: typeof EvalError.length; - export const EvalErrorName: typeof EvalError.name; - export const EvalErrorPrototype: typeof EvalError.prototype; - export const FinalizationRegistry: typeof globalThis.FinalizationRegistry; - export const FinalizationRegistryLength: typeof FinalizationRegistry.length; - export const FinalizationRegistryName: typeof FinalizationRegistry.name; - export const FinalizationRegistryPrototype: - typeof FinalizationRegistry.prototype; - export const FinalizationRegistryPrototypeRegister: UncurryThis< - typeof FinalizationRegistry.prototype.register - >; - export const FinalizationRegistryPrototypeUnregister: UncurryThis< - typeof FinalizationRegistry.prototype.unregister - >; - export const Float32Array: typeof globalThis.Float32Array; - export const Float32ArrayLength: typeof Float32Array.length; - export const Float32ArrayName: typeof Float32Array.name; - export const Float32ArrayPrototype: typeof Float32Array.prototype; - export const Float32ArrayBYTES_PER_ELEMENT: - typeof Float32Array.BYTES_PER_ELEMENT; - export const Float64Array: typeof globalThis.Float64Array; - export const Float64ArrayLength: typeof Float64Array.length; - export const Float64ArrayName: typeof Float64Array.name; - export const Float64ArrayPrototype: typeof Float64Array.prototype; - export const Float64ArrayBYTES_PER_ELEMENT: - typeof Float64Array.BYTES_PER_ELEMENT; - export const Function: typeof globalThis.Function; - export const FunctionLength: typeof Function.length; - export const FunctionName: typeof Function.name; - export const FunctionPrototype: typeof Function.prototype; - export const FunctionPrototypeApply: UncurryThis< - typeof Function.prototype.apply - >; - export const FunctionPrototypeBind: UncurryThis< - typeof Function.prototype.bind - >; - export const FunctionPrototypeCall: UncurryThis< - typeof Function.prototype.call - >; - export const FunctionPrototypeToString: UncurryThis< - typeof Function.prototype.toString - >; - export const Int16Array: typeof globalThis.Int16Array; - export const Int16ArrayLength: typeof Int16Array.length; - export const Int16ArrayName: typeof Int16Array.name; - export const Int16ArrayPrototype: typeof Int16Array.prototype; - export const Int16ArrayBYTES_PER_ELEMENT: - typeof Int16Array.BYTES_PER_ELEMENT; - export const Int32Array: typeof globalThis.Int32Array; - export const Int32ArrayLength: typeof Int32Array.length; - export const Int32ArrayName: typeof Int32Array.name; - export const Int32ArrayPrototype: typeof Int32Array.prototype; - export const Int32ArrayBYTES_PER_ELEMENT: - typeof Int32Array.BYTES_PER_ELEMENT; - export const Int8Array: typeof globalThis.Int8Array; - export const Int8ArrayLength: typeof Int8Array.length; - export const Int8ArrayName: typeof Int8Array.name; - export const Int8ArrayPrototype: typeof Int8Array.prototype; - export const Int8ArrayBYTES_PER_ELEMENT: typeof Int8Array.BYTES_PER_ELEMENT; - export const Map: typeof globalThis.Map; - export const MapLength: typeof Map.length; - export const MapName: typeof Map.name; - export const MapPrototype: typeof Map.prototype; - export const MapPrototypeGetSize: (map: Map) => number; - export const MapPrototypeGet: UncurryThis; - export const MapPrototypeSet: UncurryThis; - export const MapPrototypeHas: UncurryThis; - export const MapPrototypeDelete: UncurryThis; - export const MapPrototypeClear: UncurryThis; - export const MapPrototypeEntries: UncurryThis; - export const MapPrototypeForEach: UncurryThis; - export const MapPrototypeKeys: UncurryThis; - export const MapPrototypeValues: UncurryThis; - export const Number: typeof globalThis.Number; - export const NumberLength: typeof Number.length; - export const NumberName: typeof Number.name; - export const NumberPrototype: typeof Number.prototype; - export const NumberIsFinite: typeof Number.isFinite; - export const NumberIsInteger: typeof Number.isInteger; - export const NumberIsNaN: typeof Number.isNaN; - export const NumberIsSafeInteger: typeof Number.isSafeInteger; - export const NumberParseFloat: typeof Number.parseFloat; - export const NumberParseInt: typeof Number.parseInt; - export const NumberMAX_VALUE: typeof Number.MAX_VALUE; - export const NumberMIN_VALUE: typeof Number.MIN_VALUE; - export const NumberNaN: typeof Number.NaN; - export const NumberNEGATIVE_INFINITY: typeof Number.NEGATIVE_INFINITY; - export const NumberPOSITIVE_INFINITY: typeof Number.POSITIVE_INFINITY; - export const NumberMAX_SAFE_INTEGER: typeof Number.MAX_SAFE_INTEGER; - export const NumberMIN_SAFE_INTEGER: typeof Number.MIN_SAFE_INTEGER; - export const NumberEPSILON: typeof Number.EPSILON; - export const NumberPrototypeToExponential: UncurryThis< - typeof Number.prototype.toExponential - >; - export const NumberPrototypeToFixed: UncurryThis< - typeof Number.prototype.toFixed - >; - export const NumberPrototypeToPrecision: UncurryThis< - typeof Number.prototype.toPrecision - >; - export const NumberPrototypeToString: UncurryThis< - typeof Number.prototype.toString - >; - export const NumberPrototypeValueOf: UncurryThis< - typeof Number.prototype.valueOf - >; - export const NumberPrototypeToLocaleString: UncurryThis< - typeof Number.prototype.toLocaleString - >; - export const Object: typeof globalThis.Object; - export const ObjectLength: typeof Object.length; - export const ObjectName: typeof Object.name; - export const ObjectAssign: typeof Object.assign; - export const ObjectGetOwnPropertyDescriptor: - typeof Object.getOwnPropertyDescriptor; - export const ObjectGetOwnPropertyDescriptors: - typeof Object.getOwnPropertyDescriptors; - export const ObjectGetOwnPropertyNames: typeof Object.getOwnPropertyNames; - export const ObjectGetOwnPropertySymbols: - typeof Object.getOwnPropertySymbols; - export const ObjectHasOwn: typeof Object.hasOwn; - export const ObjectIs: typeof Object.is; - export const ObjectPreventExtensions: typeof Object.preventExtensions; - export const ObjectSeal: typeof Object.seal; - export const ObjectCreate: typeof Object.create; - export const ObjectDefineProperties: typeof Object.defineProperties; - export const ObjectDefineProperty: typeof Object.defineProperty; - export const ObjectFreeze: typeof Object.freeze; - export const ObjectGetPrototypeOf: typeof Object.getPrototypeOf; - export const ObjectSetPrototypeOf: typeof Object.setPrototypeOf; - export const ObjectIsExtensible: typeof Object.isExtensible; - export const ObjectIsFrozen: typeof Object.isFrozen; - export const ObjectIsSealed: typeof Object.isSealed; - export const ObjectKeys: typeof Object.keys; - export const ObjectEntries: typeof Object.entries; - export const ObjectFromEntries: typeof Object.fromEntries; - export const ObjectValues: typeof Object.values; - export const ObjectPrototype: typeof Object.prototype; - export const ObjectPrototype__defineGetter__: UncurryThis< - typeof Object.prototype.__defineGetter__ - >; - export const ObjectPrototype__defineSetter__: UncurryThis< - typeof Object.prototype.__defineSetter__ - >; - export const ObjectPrototypeHasOwnProperty: UncurryThis< - typeof Object.prototype.hasOwnProperty - >; - export const ObjectPrototype__lookupGetter__: UncurryThis< - typeof Object.prototype.__lookupGetter__ - >; - export const ObjectPrototype__lookupSetter__: UncurryThis< - typeof Object.prototype.__lookupSetter__ - >; - export const ObjectPrototypeIsPrototypeOf: UncurryThis< - typeof Object.prototype.isPrototypeOf - >; - export const ObjectPrototypePropertyIsEnumerable: UncurryThis< - typeof Object.prototype.propertyIsEnumerable - >; - export const ObjectPrototypeToString: UncurryThis< - typeof Object.prototype.toString - >; - export const ObjectPrototypeValueOf: UncurryThis< - typeof Object.prototype.valueOf - >; - export const ObjectPrototypeToLocaleString: UncurryThis< - typeof Object.prototype.toLocaleString - >; - export const RangeError: typeof globalThis.RangeError; - export const RangeErrorLength: typeof RangeError.length; - export const RangeErrorName: typeof RangeError.name; - export const RangeErrorPrototype: typeof RangeError.prototype; - export const ReferenceError: typeof globalThis.ReferenceError; - export const ReferenceErrorLength: typeof ReferenceError.length; - export const ReferenceErrorName: typeof ReferenceError.name; - export const ReferenceErrorPrototype: typeof ReferenceError.prototype; - export const RegExp: typeof globalThis.RegExp; - export const RegExpLength: typeof RegExp.length; - export const RegExpName: typeof RegExp.name; - export const RegExpPrototype: typeof RegExp.prototype; - export const RegExpPrototypeExec: UncurryThis; - export const RegExpPrototypeCompile: UncurryThis< - typeof RegExp.prototype.compile - >; - export const RegExpPrototypeToString: UncurryThis< - typeof RegExp.prototype.toString - >; - export const RegExpPrototypeTest: UncurryThis; - export const Set: typeof globalThis.Set; - export const SetLength: typeof Set.length; - export const SetName: typeof Set.name; - export const SetPrototype: typeof Set.prototype; - export const SetPrototypeGetSize: (set: Set) => number; - export const SetPrototypeHas: UncurryThis; - export const SetPrototypeAdd: UncurryThis; - export const SetPrototypeDelete: UncurryThis; - export const SetPrototypeClear: UncurryThis; - export const SetPrototypeEntries: UncurryThis; - export const SetPrototypeForEach: UncurryThis; - export const SetPrototypeValues: UncurryThis; - export const SetPrototypeKeys: UncurryThis; - export const String: typeof globalThis.String; - export const StringLength: typeof String.length; - export const StringName: typeof String.name; - export const StringPrototype: typeof String.prototype; - export const StringFromCharCode: typeof String.fromCharCode; - export const StringFromCodePoint: typeof String.fromCodePoint; - export const StringRaw: typeof String.raw; - export const StringPrototypeAnchor: UncurryThis< - typeof String.prototype.anchor - >; - export const StringPrototypeBig: UncurryThis; - export const StringPrototypeBlink: UncurryThis< - typeof String.prototype.blink - >; - export const StringPrototypeBold: UncurryThis; - export const StringPrototypeCharAt: UncurryThis< - typeof String.prototype.charAt - >; - export const StringPrototypeCharCodeAt: UncurryThis< - typeof String.prototype.charCodeAt - >; - export const StringPrototypeCodePointAt: UncurryThis< - typeof String.prototype.codePointAt - >; - export const StringPrototypeConcat: UncurryThis< - typeof String.prototype.concat - >; - export const StringPrototypeConcatApply: UncurryThisStaticApply< - typeof String.prototype.concat - >; - export const StringPrototypeEndsWith: UncurryThis< - typeof String.prototype.endsWith - >; - export const StringPrototypeFontcolor: UncurryThis< - typeof String.prototype.fontcolor - >; - export const StringPrototypeFontsize: UncurryThis< - typeof String.prototype.fontsize - >; - export const StringPrototypeFixed: UncurryThis< - typeof String.prototype.fixed - >; - export const StringPrototypeIncludes: UncurryThis< - typeof String.prototype.includes - >; - export const StringPrototypeIndexOf: UncurryThis< - typeof String.prototype.indexOf - >; - export const StringPrototypeItalics: UncurryThis< - typeof String.prototype.italics - >; - export const StringPrototypeLastIndexOf: UncurryThis< - typeof String.prototype.lastIndexOf - >; - export const StringPrototypeLink: UncurryThis; - export const StringPrototypeLocaleCompare: UncurryThis< - typeof String.prototype.localeCompare - >; - export const StringPrototypeMatch: UncurryThis< - typeof String.prototype.match - >; - export const StringPrototypeMatchAll: UncurryThis< - typeof String.prototype.matchAll - >; - export const StringPrototypeNormalize: UncurryThis< - typeof String.prototype.normalize - >; - export const StringPrototypePadEnd: UncurryThis< - typeof String.prototype.padEnd - >; - export const StringPrototypePadStart: UncurryThis< - typeof String.prototype.padStart - >; - export const StringPrototypeRepeat: UncurryThis< - typeof String.prototype.repeat - >; - export const StringPrototypeReplace: UncurryThis< - typeof String.prototype.replace - >; - export const StringPrototypeSearch: UncurryThis< - typeof String.prototype.search - >; - export const StringPrototypeSlice: UncurryThis< - typeof String.prototype.slice - >; - export const StringPrototypeSmall: UncurryThis< - typeof String.prototype.small - >; - export const StringPrototypeSplit: UncurryThis< - typeof String.prototype.split - >; - export const StringPrototypeStrike: UncurryThis< - typeof String.prototype.strike - >; - export const StringPrototypeSub: UncurryThis; - export const StringPrototypeSubstr: UncurryThis< - typeof String.prototype.substr - >; - export const StringPrototypeSubstring: UncurryThis< - typeof String.prototype.substring - >; - export const StringPrototypeSup: UncurryThis; - export const StringPrototypeStartsWith: UncurryThis< - typeof String.prototype.startsWith - >; - export const StringPrototypeToString: UncurryThis< - typeof String.prototype.toString - >; - export const StringPrototypeTrim: UncurryThis; - export const StringPrototypeTrimStart: UncurryThis< - typeof String.prototype.trimStart - >; - export const StringPrototypeTrimLeft: UncurryThis< - typeof String.prototype.trimLeft - >; - export const StringPrototypeTrimEnd: UncurryThis< - typeof String.prototype.trimEnd - >; - export const StringPrototypeTrimRight: UncurryThis< - typeof String.prototype.trimRight - >; - export const StringPrototypeToLocaleLowerCase: UncurryThis< - typeof String.prototype.toLocaleLowerCase - >; - export const StringPrototypeToLocaleUpperCase: UncurryThis< - typeof String.prototype.toLocaleUpperCase - >; - export const StringPrototypeToLowerCase: UncurryThis< - typeof String.prototype.toLowerCase - >; - export const StringPrototypeToUpperCase: UncurryThis< - typeof String.prototype.toUpperCase - >; - export const StringPrototypeValueOf: UncurryThis< - typeof String.prototype.valueOf - >; - export const StringPrototypeReplaceAll: UncurryThis< - typeof String.prototype.replaceAll - >; - export const Symbol: typeof globalThis.Symbol; - export const SymbolLength: typeof Symbol.length; - export const SymbolName: typeof Symbol.name; - export const SymbolPrototype: typeof Symbol.prototype; - export const SymbolPrototypeGetDescription: (symbol: symbol) => string; - export const SymbolFor: typeof Symbol.for; - export const SymbolKeyFor: typeof Symbol.keyFor; - export const SymbolAsyncIterator: typeof Symbol.asyncIterator; - export const SymbolHasInstance: typeof Symbol.hasInstance; - export const SymbolIsConcatSpreadable: typeof Symbol.isConcatSpreadable; - export const SymbolIterator: typeof Symbol.iterator; - export const SymbolMatch: typeof Symbol.match; - export const SymbolMatchAll: typeof Symbol.matchAll; - export const SymbolReplace: typeof Symbol.replace; - export const SymbolSearch: typeof Symbol.search; - export const SymbolSpecies: typeof Symbol.species; - export const SymbolSplit: typeof Symbol.split; - export const SymbolToPrimitive: typeof Symbol.toPrimitive; - export const SymbolToStringTag: typeof Symbol.toStringTag; - export const SymbolUnscopables: typeof Symbol.unscopables; - export const SymbolPrototypeToString: UncurryThis< - typeof Symbol.prototype.toString - >; - export const SymbolPrototypeValueOf: UncurryThis< - typeof Symbol.prototype.valueOf - >; - export const SyntaxError: typeof globalThis.SyntaxError; - export const SyntaxErrorLength: typeof SyntaxError.length; - export const SyntaxErrorName: typeof SyntaxError.name; - export const SyntaxErrorPrototype: typeof SyntaxError.prototype; - export const TypeError: typeof globalThis.TypeError; - export const TypeErrorLength: typeof TypeError.length; - export const TypeErrorName: typeof TypeError.name; - export const TypeErrorPrototype: typeof TypeError.prototype; - export const URIError: typeof globalThis.URIError; - export const URIErrorLength: typeof URIError.length; - export const URIErrorName: typeof URIError.name; - export const URIErrorPrototype: typeof URIError.prototype; - export const Uint16Array: typeof globalThis.Uint16Array; - export const Uint16ArrayLength: typeof Uint16Array.length; - export const Uint16ArrayName: typeof Uint16Array.name; - export const Uint16ArrayPrototype: typeof Uint16Array.prototype; - export const Uint16ArrayBYTES_PER_ELEMENT: - typeof Uint16Array.BYTES_PER_ELEMENT; - export const Uint32Array: typeof globalThis.Uint32Array; - export const Uint32ArrayLength: typeof Uint32Array.length; - export const Uint32ArrayName: typeof Uint32Array.name; - export const Uint32ArrayPrototype: typeof Uint32Array.prototype; - export const Uint32ArrayBYTES_PER_ELEMENT: - typeof Uint32Array.BYTES_PER_ELEMENT; - export const Uint8Array: typeof globalThis.Uint8Array; - export const Uint8ArrayLength: typeof Uint8Array.length; - export const Uint8ArrayName: typeof Uint8Array.name; - export const Uint8ArrayPrototype: typeof Uint8Array.prototype; - export const Uint8ArrayBYTES_PER_ELEMENT: - typeof Uint8Array.BYTES_PER_ELEMENT; - export const Uint8ClampedArray: typeof globalThis.Uint8ClampedArray; - export const Uint8ClampedArrayLength: typeof Uint8ClampedArray.length; - export const Uint8ClampedArrayName: typeof Uint8ClampedArray.name; - export const Uint8ClampedArrayPrototype: typeof Uint8ClampedArray.prototype; - export const Uint8ClampedArrayBYTES_PER_ELEMENT: - typeof Uint8ClampedArray.BYTES_PER_ELEMENT; - export const WeakMap: typeof globalThis.WeakMap; - export const WeakMapLength: typeof WeakMap.length; - export const WeakMapName: typeof WeakMap.name; - export const WeakMapPrototype: typeof WeakMap.prototype; - export const WeakMapPrototypeDelete: UncurryThis< - typeof WeakMap.prototype.delete - >; - export const WeakMapPrototypeGet: UncurryThis; - export const WeakMapPrototypeSet: UncurryThis; - export const WeakMapPrototypeHas: UncurryThis; - export const WeakRef: typeof globalThis.WeakRef; - export const WeakRefLength: typeof WeakRef.length; - export const WeakRefName: typeof WeakRef.name; - export const WeakRefPrototype: typeof WeakRef.prototype; - export const WeakRefPrototypeDeref: UncurryThis< - typeof WeakRef.prototype.deref - >; - export const WeakSet: typeof globalThis.WeakSet; - export const WeakSetLength: typeof WeakSet.length; - export const WeakSetName: typeof WeakSet.name; - export const WeakSetPrototype: typeof WeakSet.prototype; - export const WeakSetPrototypeDelete: UncurryThis< - typeof WeakSet.prototype.delete - >; - export const WeakSetPrototypeHas: UncurryThis; - export const WeakSetPrototypeAdd: UncurryThis; - export const Promise: typeof globalThis.Promise; - export const PromiseLength: typeof Promise.length; - export const PromiseName: typeof Promise.name; - export const PromisePrototype: typeof Promise.prototype; - export const PromiseAll: typeof Promise.all; - export const PromiseRace: typeof Promise.race; - export const PromiseResolve: typeof Promise.resolve; - export const PromiseReject: typeof Promise.reject; - export const PromiseAllSettled: typeof Promise.allSettled; - export const PromiseAny: typeof Promise.any; - export const PromisePrototypeThen: UncurryThis< - typeof Promise.prototype.then - >; - export const PromisePrototypeCatch: UncurryThis< - typeof Promise.prototype.catch - >; - export const PromisePrototypeFinally: UncurryThis< - typeof Promise.prototype.finally - >; - - // abstract intrinsic objects - export const ArrayIteratorPrototypeNext: ( - iterator: IterableIterator, - ) => IteratorResult; - export const SetIteratorPrototypeNext: ( - iterator: IterableIterator, - ) => IteratorResult; - export const MapIteratorPrototypeNext: ( - iterator: IterableIterator, - ) => IteratorResult; - export const StringIteratorPrototypeNext: ( - iterator: IterableIterator, - ) => IteratorResult; - export const GeneratorPrototypeNext: ( - generator: Generator, - ) => IteratorResult; - export const AsyncGeneratorPrototypeNext: ( - asyncGenerator: AsyncGenerator, - ) => Promise>; - export const TypedArrayFrom: ( - constructor: Uint8ArrayConstructor, - arrayLike: ArrayLike, - ) => Uint8Array; - export const TypedArrayPrototypeGetBuffer: ( - array: Uint8Array, - ) => ArrayBuffer | SharedArrayBuffer; - export const TypedArrayPrototypeGetByteLength: ( - array: Uint8Array, - ) => number; - export const TypedArrayPrototypeGetByteOffset: ( - array: Uint8Array, - ) => number; - export const TypedArrayPrototypeGetLength: (array: Uint8Array) => number; - export const TypedArrayPrototypeGetSymbolToStringTag: ( - v: unknown, - ) => string | undefined; - export const TypedArrayPrototypeCopyWithin: UncurryThis< - typeof Uint8Array.prototype.copyWithin - >; - export const TypedArrayPrototypeEvery: UncurryThis< - typeof Uint8Array.prototype.every - >; - export const TypedArrayPrototypeFill: UncurryThis< - typeof Uint8Array.prototype.fill - >; - export const TypedArrayPrototypeFilter: UncurryThis< - typeof Uint8Array.prototype.filter - >; - export const TypedArrayPrototypeFind: UncurryThis< - typeof Uint8Array.prototype.find - >; - export const TypedArrayPrototypeFindIndex: UncurryThis< - typeof Uint8Array.prototype.findIndex - >; - export const TypedArrayPrototypeForEach: UncurryThis< - typeof Uint8Array.prototype.forEach - >; - export const TypedArrayPrototypeIndexOf: UncurryThis< - typeof Uint8Array.prototype.indexOf - >; - export const TypedArrayPrototypeJoin: UncurryThis< - typeof Uint8Array.prototype.join - >; - export const TypedArrayPrototypeLastIndexOf: UncurryThis< - typeof Uint8Array.prototype.lastIndexOf - >; - export const TypedArrayPrototypeMap: UncurryThis< - typeof Uint8Array.prototype.map - >; - export const TypedArrayPrototypeReduce: UncurryThis< - typeof Uint8Array.prototype.reduce - >; - export const TypedArrayPrototypeReduceRight: UncurryThis< - typeof Uint8Array.prototype.reduceRight - >; - export const TypedArrayPrototypeReverse: UncurryThis< - typeof Uint8Array.prototype.reverse - >; - export const TypedArrayPrototypeSet: UncurryThis< - typeof Uint8Array.prototype.set - >; - export const TypedArrayPrototypeSlice: UncurryThis< - typeof Uint8Array.prototype.slice - >; - export const TypedArrayPrototypeSome: UncurryThis< - typeof Uint8Array.prototype.some - >; - export const TypedArrayPrototypeSort: UncurryThis< - typeof Uint8Array.prototype.sort - >; - export const TypedArrayPrototypeSubarray: UncurryThis< - typeof Uint8Array.prototype.subarray - >; - export const TypedArrayPrototypeToLocaleString: UncurryThis< - typeof Uint8Array.prototype.toLocaleString - >; - export const TypedArrayPrototypeToString: UncurryThis< - typeof Uint8Array.prototype.toString - >; - export const TypedArrayPrototypeValueOf: UncurryThis< - typeof Uint8Array.prototype.valueOf - >; - } -} diff --git a/core/io.rs b/core/io.rs deleted file mode 100644 index c7a56d942d..0000000000 --- a/core/io.rs +++ /dev/null @@ -1,300 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use std::ops::Deref; -use std::ops::DerefMut; - -use bytes::Buf; -use serde_v8::JsBuffer; - -/// BufView is a wrapper around an underlying contiguous chunk of bytes. It can -/// be created from a [JsBuffer], [bytes::Bytes], or [Vec] and implements -/// `Deref<[u8]>` and `AsRef<[u8]>`. -/// -/// The wrapper has the ability to constrain the exposed view to a sub-region of -/// the underlying buffer. This is useful for write operations, because they may -/// have to be called multiple times, with different views onto the buffer to be -/// able to write it entirely. -pub struct BufView { - inner: BufViewInner, - cursor: usize, -} - -enum BufViewInner { - Empty, - Bytes(bytes::Bytes), - JsBuffer(JsBuffer), - Vec(Vec), -} - -impl BufView { - const fn from_inner(inner: BufViewInner) -> Self { - Self { inner, cursor: 0 } - } - - pub const fn empty() -> Self { - Self::from_inner(BufViewInner::Empty) - } - - /// Get the length of the buffer view. This is the length of the underlying - /// buffer minus the cursor position. - pub fn len(&self) -> usize { - match &self.inner { - BufViewInner::Empty => 0, - BufViewInner::Bytes(bytes) => bytes.len() - self.cursor, - BufViewInner::JsBuffer(js_buf) => js_buf.len() - self.cursor, - BufViewInner::Vec(vec) => vec.len() - self.cursor, - } - } - - /// Is the buffer view empty? - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Advance the internal cursor of the buffer view by `n` bytes. - pub fn advance_cursor(&mut self, n: usize) { - assert!(self.len() >= n); - self.cursor += n; - } - - /// Reset the internal cursor of the buffer view to the beginning of the - /// buffer. Returns the old cursor position. - pub fn reset_cursor(&mut self) -> usize { - let old = self.cursor; - self.cursor = 0; - old - } -} - -impl Buf for BufView { - fn remaining(&self) -> usize { - self.len() - } - - fn chunk(&self) -> &[u8] { - self.deref() - } - - fn advance(&mut self, cnt: usize) { - self.advance_cursor(cnt) - } -} - -impl Deref for BufView { - type Target = [u8]; - - fn deref(&self) -> &[u8] { - let buf = match &self.inner { - BufViewInner::Empty => &[], - BufViewInner::Bytes(bytes) => bytes.deref(), - BufViewInner::JsBuffer(js_buf) => js_buf.deref(), - BufViewInner::Vec(vec) => vec.deref(), - }; - &buf[self.cursor..] - } -} - -impl AsRef<[u8]> for BufView { - fn as_ref(&self) -> &[u8] { - self.deref() - } -} - -impl From for BufView { - fn from(buf: JsBuffer) -> Self { - Self::from_inner(BufViewInner::JsBuffer(buf)) - } -} - -impl From> for BufView { - fn from(vec: Vec) -> Self { - Self::from_inner(BufViewInner::Vec(vec)) - } -} - -impl From for BufView { - fn from(buf: bytes::Bytes) -> Self { - Self::from_inner(BufViewInner::Bytes(buf)) - } -} - -impl From for bytes::Bytes { - fn from(buf: BufView) -> Self { - match buf.inner { - BufViewInner::Empty => bytes::Bytes::new(), - BufViewInner::Bytes(bytes) => bytes, - BufViewInner::JsBuffer(js_buf) => js_buf.into(), - BufViewInner::Vec(vec) => vec.into(), - } - } -} - -/// BufMutView is a wrapper around an underlying contiguous chunk of writable -/// bytes. It can be created from a `JsBuffer` or a `Vec` and implements -/// `DerefMut<[u8]>` and `AsMut<[u8]>`. -/// -/// The wrapper has the ability to constrain the exposed view to a sub-region of -/// the underlying buffer. This is useful for write operations, because they may -/// have to be called multiple times, with different views onto the buffer to be -/// able to write it entirely. -/// -/// A `BufMutView` can be turned into a `BufView` by calling `BufMutView::into_view`. -pub struct BufMutView { - inner: BufMutViewInner, - cursor: usize, -} - -enum BufMutViewInner { - JsBuffer(JsBuffer), - Vec(Vec), -} - -impl BufMutView { - fn from_inner(inner: BufMutViewInner) -> Self { - Self { inner, cursor: 0 } - } - - pub fn new(len: usize) -> Self { - Self::from_inner(BufMutViewInner::Vec(vec![0; len])) - } - - /// Get the length of the buffer view. This is the length of the underlying - /// buffer minus the cursor position. - pub fn len(&self) -> usize { - match &self.inner { - BufMutViewInner::JsBuffer(js_buf) => js_buf.len() - self.cursor, - BufMutViewInner::Vec(vec) => vec.len() - self.cursor, - } - } - - /// Is the buffer view empty? - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Advance the internal cursor of the buffer view by `n` bytes. - pub fn advance_cursor(&mut self, n: usize) { - assert!(self.len() >= n); - self.cursor += n; - } - - /// Reset the internal cursor of the buffer view to the beginning of the - /// buffer. Returns the old cursor position. - pub fn reset_cursor(&mut self) -> usize { - let old = self.cursor; - self.cursor = 0; - old - } - - /// Turn this `BufMutView` into a `BufView`. - pub fn into_view(self) -> BufView { - let inner = match self.inner { - BufMutViewInner::JsBuffer(js_buf) => BufViewInner::JsBuffer(js_buf), - BufMutViewInner::Vec(vec) => BufViewInner::Vec(vec), - }; - BufView { - inner, - cursor: self.cursor, - } - } - - /// Unwrap the underlying buffer into a `Vec`, consuming the `BufMutView`. - /// - /// This method panics when called on a `BufMutView` that was created from a - /// `JsBuffer`. - pub fn unwrap_vec(self) -> Vec { - match self.inner { - BufMutViewInner::JsBuffer(_) => { - panic!("Cannot unwrap a JsBuffer backed BufMutView into a Vec"); - } - BufMutViewInner::Vec(vec) => vec, - } - } - - /// Get a mutable reference to an underlying `Vec`. - /// - /// This method panics when called on a `BufMutView` that was created from a - /// `JsBuffer`. - pub fn get_mut_vec(&mut self) -> &mut Vec { - match &mut self.inner { - BufMutViewInner::JsBuffer(_) => { - panic!("Cannot unwrap a JsBuffer backed BufMutView into a Vec"); - } - BufMutViewInner::Vec(vec) => vec, - } - } -} - -impl Buf for BufMutView { - fn remaining(&self) -> usize { - self.len() - } - - fn chunk(&self) -> &[u8] { - self.deref() - } - - fn advance(&mut self, cnt: usize) { - self.advance_cursor(cnt) - } -} - -impl Deref for BufMutView { - type Target = [u8]; - - fn deref(&self) -> &[u8] { - let buf = match &self.inner { - BufMutViewInner::JsBuffer(js_buf) => js_buf.deref(), - BufMutViewInner::Vec(vec) => vec.deref(), - }; - &buf[self.cursor..] - } -} - -impl DerefMut for BufMutView { - fn deref_mut(&mut self) -> &mut [u8] { - let buf = match &mut self.inner { - BufMutViewInner::JsBuffer(js_buf) => js_buf.deref_mut(), - BufMutViewInner::Vec(vec) => vec.deref_mut(), - }; - &mut buf[self.cursor..] - } -} - -impl AsRef<[u8]> for BufMutView { - fn as_ref(&self) -> &[u8] { - self.deref() - } -} - -impl AsMut<[u8]> for BufMutView { - fn as_mut(&mut self) -> &mut [u8] { - self.deref_mut() - } -} - -impl From for BufMutView { - fn from(buf: JsBuffer) -> Self { - Self::from_inner(BufMutViewInner::JsBuffer(buf)) - } -} - -impl From> for BufMutView { - fn from(buf: Vec) -> Self { - Self::from_inner(BufMutViewInner::Vec(buf)) - } -} - -pub enum WriteOutcome { - Partial { nwritten: usize, view: BufView }, - Full { nwritten: usize }, -} - -impl WriteOutcome { - pub fn nwritten(&self) -> usize { - match self { - WriteOutcome::Partial { nwritten, .. } => *nwritten, - WriteOutcome::Full { nwritten } => *nwritten, - } - } -} diff --git a/core/joinset.rs b/core/joinset.rs deleted file mode 100644 index f80c95712c..0000000000 --- a/core/joinset.rs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -// Some code and comments under MIT license where adapted from Tokio code -// Copyright (c) 2023 Tokio Contributors - -use std::task::Context; -use std::task::Poll; -use std::task::Waker; - -use futures::Future; -use tokio::task::AbortHandle; -use tokio::task::JoinError; - -use crate::task::MaskFutureAsSend; -use crate::task::MaskResultAsSend; - -/// Wraps the tokio [`JoinSet`] to make it !Send-friendly and to make it easier and safer for us to -/// poll while empty. -pub(crate) struct JoinSet { - joinset: tokio::task::JoinSet>, - /// If join_next returns Ready(None), we stash the waker - waker: Option, -} - -impl Default for JoinSet { - fn default() -> Self { - Self { - joinset: Default::default(), - waker: None, - } - } -} - -impl JoinSet { - /// Spawn the provided task on the `JoinSet`, returning an [`AbortHandle`] - /// that can be used to remotely cancel the task. - /// - /// The provided future will start running in the background immediately - /// when this method is called, even if you don't await anything on this - /// `JoinSet`. - /// - /// # Panics - /// - /// This method panics if called outside of a Tokio runtime. - /// - /// [`AbortHandle`]: tokio::task::AbortHandle - #[track_caller] - pub fn spawn(&mut self, task: F) -> AbortHandle - where - F: Future, - F: 'static, - T: 'static, - { - // SAFETY: We only use this with the single-thread executor - let handle = self.joinset.spawn(unsafe { MaskFutureAsSend::new(task) }); - - // If someone had called poll_join_next while we were empty, ask them to poll again - // so we can properly register the waker with the underlying JoinSet. - if let Some(waker) = self.waker.take() { - waker.wake(); - } - handle - } - - /// Returns the number of tasks currently in the `JoinSet`. - pub fn len(&self) -> usize { - self.joinset.len() - } - - /// Waits until one of the tasks in the set completes and returns its output. - /// - /// # Cancel Safety - /// - /// This method is cancel safe. If `join_next` is used as the event in a `tokio::select!` - /// statement and some other branch completes first, it is guaranteed that no tasks were - /// removed from this `JoinSet`. - pub fn poll_join_next( - &mut self, - cx: &mut Context, - ) -> Poll> { - // TODO(mmastrac): Use poll_join_next from Tokio - let next = std::pin::pin!(self.joinset.join_next()); - match next.poll(cx) { - Poll::Ready(Some(res)) => Poll::Ready(res.map(|res| res.into_inner())), - Poll::Ready(None) => { - // Stash waker - self.waker = Some(cx.waker().clone()); - Poll::Pending - } - Poll::Pending => Poll::Pending, - } - } -} diff --git a/core/lib.deno_core.d.ts b/core/lib.deno_core.d.ts deleted file mode 100644 index fc78658294..0000000000 --- a/core/lib.deno_core.d.ts +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -// deno-lint-ignore-file no-explicit-any - -/// -/// - -declare namespace Deno { - namespace core { - /** Call an op in Rust, and asynchronously receive the result. */ - function opAsync( - opName: string, - ...args: any[] - ): Promise; - - /** Mark following promise as "ref", ie. event loop won't exit - * until all "ref" promises are resolved. All async ops are "ref" by default. */ - function refOp(promiseId: number): void; - - /** Mark following promise as "unref", ie. event loop will exit - * if there are only "unref" promises left. */ - function unrefOp(promiseId: number): void; - - /** - * List of all registered ops, in the form of a map that maps op - * name to function. - */ - const ops: Record any>; - - /** - * List of all registered async ops, in the form of a map that maps op - * name to function. - */ - const asyncOps: Record any>; - - /** - * Retrieve a list of all open resources, in the form of a map that maps - * resource id to the resource name. - */ - function resources(): Record; - - /** - * Close the resource with the specified op id. Throws `BadResource` error - * if resource doesn't exist in resource table. - */ - function close(rid: number): void; - - /** - * Try close the resource with the specified op id; if resource with given - * id doesn't exist do nothing. - */ - function tryClose(rid: number): void; - - /** - * Read from a (stream) resource that implements read() - */ - function read(rid: number, buf: Uint8Array): Promise; - - /** - * Write to a (stream) resource that implements write() - */ - function write(rid: number, buf: Uint8Array): Promise; - - /** - * Write to a (stream) resource that implements write() - */ - function writeAll(rid: number, buf: Uint8Array): Promise; - - /** - * Synchronously read from a (stream) resource that implements readSync(). - */ - function readSync(rid: number, buf: Uint8Array): number; - - /** - * Synchronously write to a (stream) resource that implements writeSync(). - */ - function writeSync(rid: number, buf: Uint8Array): number; - - /** - * Print a message to stdout or stderr - */ - function print(message: string, is_err?: boolean): void; - - /** - * Shutdown a resource - */ - function shutdown(rid: number): Promise; - - /** Encode a string to its Uint8Array representation. */ - function encode(input: string): Uint8Array; - - /** - * Set a callback that will be called when the WebAssembly streaming APIs - * (`WebAssembly.compileStreaming` and `WebAssembly.instantiateStreaming`) - * are called in order to feed the source's bytes to the wasm compiler. - * The callback is called with the source argument passed to the streaming - * APIs and an rid to use with the wasm streaming ops. - * - * The callback should eventually invoke the following ops: - * - `op_wasm_streaming_feed`. Feeds bytes from the wasm resource to the - * compiler. Takes the rid and a `Uint8Array`. - * - `op_wasm_streaming_abort`. Aborts the wasm compilation. Takes the rid - * and an exception. Invalidates the resource. - * - `op_wasm_streaming_set_url`. Sets a source URL for the wasm module. - * Takes the rid and a string. - * - To indicate the end of the resource, use `Deno.core.close()` with the - * rid. - */ - function setWasmStreamingCallback( - cb: (source: any, rid: number) => void, - ): void; - - /** - * Set a callback that will be called after resolving ops and before resolving - * macrotasks. - */ - function setNextTickCallback( - cb: () => void, - ): void; - - /** Check if there's a scheduled "next tick". */ - function hasNextTickScheduled(): boolean; - - /** Set a value telling the runtime if there are "next ticks" scheduled */ - function setHasNextTickScheduled(value: boolean): void; - - /** - * Set a callback that will be called after resolving ops and "next ticks". - */ - function setMacrotaskCallback( - cb: () => boolean, - ): void; - - /** - * Set a callback that will be called when a promise without a .catch - * handler is rejected. Returns the old handler or undefined. - */ - function setPromiseRejectCallback( - cb: PromiseRejectCallback, - ): undefined | PromiseRejectCallback; - - export type PromiseRejectCallback = ( - type: number, - promise: Promise, - reason: any, - ) => void; - - /** - * Set a callback that will be called when an exception isn't caught - * by any try/catch handlers. Currently only invoked when the callback - * to setPromiseRejectCallback() throws an exception but that is expected - * to change in the future. Returns the old handler or undefined. - */ - function setUncaughtExceptionCallback( - cb: UncaughtExceptionCallback, - ): undefined | UncaughtExceptionCallback; - - export type UncaughtExceptionCallback = (err: any) => void; - - /** - * Enables collection of stack traces of all async ops. This allows for - * debugging of where a given async op was started. Deno CLI uses this for - * improving error message in op sanitizer errors for `deno test`. - * - * **NOTE:** enabling tracing has a significant negative performance impact. - * To get high level metrics on async ops with no added performance cost, - * use `Deno.core.metrics()`. - */ - function enableOpCallTracing(): void; - - export interface OpCallTrace { - opName: string; - stack: string; - } - - /** - * A map containing traces for all ongoing async ops. The key is the op id. - * Tracing only occurs when `Deno.core.enableOpCallTracing()` was previously - * enabled. - */ - const opCallTraces: Map; - - /** - * Adds a callback for the given Promise event. If this function is called - * multiple times, the callbacks are called in the order they were added. - * - `init_hook` is called when a new promise is created. When a new promise - * is created as part of the chain in the case of `Promise.then` or in the - * intermediate promises created by `Promise.{race, all}`/`AsyncFunctionAwait`, - * we pass the parent promise otherwise we pass undefined. - * - `before_hook` is called at the beginning of the promise reaction. - * - `after_hook` is called at the end of the promise reaction. - * - `resolve_hook` is called at the beginning of resolve or reject function. - */ - function setPromiseHooks( - init_hook?: ( - promise: Promise, - parentPromise?: Promise, - ) => void, - before_hook?: (promise: Promise) => void, - after_hook?: (promise: Promise) => void, - resolve_hook?: (promise: Promise) => void, - ): void; - - const build: { - target: string; - arch: string; - os: string; - vendor: string; - env: string | undefined; - }; - } -} diff --git a/core/lib.rs b/core/lib.rs deleted file mode 100644 index 2f8d9142cf..0000000000 --- a/core/lib.rs +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -mod async_cancel; -mod async_cell; -pub mod error; -mod error_codes; -mod extensions; -mod fast_string; -mod flags; -mod gotham_state; -mod inspector; -mod io; -mod joinset; -mod module_specifier; -mod modules; -mod normalize_path; -mod ops; -mod ops_builtin; -mod ops_builtin_v8; -mod ops_metrics; -mod path; -mod resources; -mod runtime; -mod source_map; -pub mod task; -mod task_queue; - -// Re-exports -pub use anyhow; -pub use futures; -pub use parking_lot; -pub use serde; -pub use serde_json; -pub use serde_v8; -pub use serde_v8::ByteString; -pub use serde_v8::DetachedBuffer; -pub use serde_v8::JsBuffer; -pub use serde_v8::StringOrBuffer; -pub use serde_v8::ToJsBuffer; -pub use serde_v8::U16String; -pub use sourcemap; -pub use url; -pub use v8; - -pub use deno_ops::op; -pub use deno_ops::op2; - -pub use crate::async_cancel::CancelFuture; -pub use crate::async_cancel::CancelHandle; -pub use crate::async_cancel::CancelTryFuture; -pub use crate::async_cancel::Cancelable; -pub use crate::async_cancel::Canceled; -pub use crate::async_cancel::TryCancelable; -pub use crate::async_cell::AsyncMut; -pub use crate::async_cell::AsyncMutFuture; -pub use crate::async_cell::AsyncRef; -pub use crate::async_cell::AsyncRefCell; -pub use crate::async_cell::AsyncRefFuture; -pub use crate::async_cell::RcLike; -pub use crate::async_cell::RcRef; -pub use crate::error::GetErrorClassFn; -pub use crate::error::JsErrorCreateFn; -pub use crate::extensions::Extension; -pub use crate::extensions::ExtensionBuilder; -pub use crate::extensions::ExtensionFileSource; -pub use crate::extensions::ExtensionFileSourceCode; -pub use crate::extensions::OpDecl; -pub use crate::extensions::OpMiddlewareFn; -pub use crate::fast_string::FastString; -pub use crate::flags::v8_set_flags; -pub use crate::inspector::InspectorMsg; -pub use crate::inspector::InspectorMsgKind; -pub use crate::inspector::InspectorSessionProxy; -pub use crate::inspector::JsRuntimeInspector; -pub use crate::inspector::LocalInspectorSession; -pub use crate::io::BufMutView; -pub use crate::io::BufView; -pub use crate::io::WriteOutcome; -pub use crate::module_specifier::resolve_import; -pub use crate::module_specifier::resolve_path; -pub use crate::module_specifier::resolve_url; -pub use crate::module_specifier::resolve_url_or_path; -pub use crate::module_specifier::ModuleResolutionError; -pub use crate::module_specifier::ModuleSpecifier; -pub use crate::modules::ExtModuleLoaderCb; -pub use crate::modules::FsModuleLoader; -pub use crate::modules::ModuleCode; -pub use crate::modules::ModuleId; -pub use crate::modules::ModuleLoader; -pub use crate::modules::ModuleSource; -pub use crate::modules::ModuleSourceFuture; -pub use crate::modules::ModuleType; -pub use crate::modules::NoopModuleLoader; -pub use crate::modules::ResolutionKind; -pub use crate::normalize_path::normalize_path; -pub use crate::ops::OpCall; -pub use crate::ops::OpError; -pub use crate::ops::OpId; -pub use crate::ops::OpResult; -pub use crate::ops::OpState; -pub use crate::ops::PromiseId; -pub use crate::ops_builtin::op_close; -pub use crate::ops_builtin::op_print; -pub use crate::ops_builtin::op_resources; -pub use crate::ops_builtin::op_void_async; -pub use crate::ops_builtin::op_void_sync; -pub use crate::ops_metrics::OpsTracker; -pub use crate::path::strip_unc_prefix; -pub use crate::resources::AsyncResult; -pub use crate::resources::Resource; -pub use crate::resources::ResourceId; -pub use crate::resources::ResourceTable; -pub use crate::runtime::CompiledWasmModuleStore; -pub use crate::runtime::CrossIsolateStore; -pub use crate::runtime::JsRealm; -pub use crate::runtime::JsRuntime; -pub use crate::runtime::JsRuntimeForSnapshot; -pub use crate::runtime::RuntimeOptions; -pub use crate::runtime::SharedArrayBufferStore; -pub use crate::runtime::Snapshot; -pub use crate::runtime::V8_WRAPPER_OBJECT_INDEX; -pub use crate::runtime::V8_WRAPPER_TYPE_INDEX; -pub use crate::source_map::SourceMapGetter; -pub use crate::task_queue::TaskQueue; -pub use crate::task_queue::TaskQueuePermit; - -pub fn v8_version() -> &'static str { - v8::V8::get_version() -} - -/// An internal module re-exporting functions used by the #[op] (`deno_ops`) macro -#[doc(hidden)] -pub mod _ops { - pub use super::error::throw_type_error; - pub use super::error_codes::get_error_code; - pub use super::extensions::Op; - pub use super::extensions::OpDecl; - pub use super::ops::to_op_result; - pub use super::ops::OpCtx; - pub use super::ops::OpResult; - pub use super::runtime::ops::map_async_op1; - pub use super::runtime::ops::map_async_op2; - pub use super::runtime::ops::map_async_op3; - pub use super::runtime::ops::map_async_op4; - pub use super::runtime::ops::queue_async_op; - pub use super::runtime::ops::queue_fast_async_op; - pub use super::runtime::ops::to_i32; - pub use super::runtime::ops::to_str; - pub use super::runtime::ops::to_str_ptr; - pub use super::runtime::ops::to_string_ptr; - pub use super::runtime::ops::to_u32; - pub use super::runtime::V8_WRAPPER_OBJECT_INDEX; - pub use super::runtime::V8_WRAPPER_TYPE_INDEX; -} - -// TODO(mmastrac): Temporary while we move code around -pub mod snapshot_util { - pub use crate::runtime::create_snapshot; - pub use crate::runtime::get_js_files; - pub use crate::runtime::CreateSnapshotOptions; - pub use crate::runtime::CreateSnapshotOutput; - pub use crate::runtime::FilterFn; -} - -/// A helper macro that will return a call site in Rust code. Should be -/// used when executing internal one-line scripts for JsRuntime lifecycle. -/// -/// Returns a string in form of: "`[ext:::]`" -#[macro_export] -macro_rules! located_script_name { - () => { - concat!( - "[ext:", - std::file!(), - ":", - std::line!(), - ":", - std::column!(), - "]" - ) - }; -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn located_script_name() { - // Note that this test will fail if this file is moved. We don't - // test line locations because that's just too brittle. - let name = located_script_name!(); - let expected = if cfg!(windows) { - "[ext:core\\lib.rs:" - } else { - "[ext:core/lib.rs:" - }; - assert_eq!(&name[..expected.len()], expected); - } - - #[test] - fn test_v8_version() { - assert!(v8_version().len() > 3); - } -} diff --git a/core/module_specifier.rs b/core/module_specifier.rs deleted file mode 100644 index 20358e79c8..0000000000 --- a/core/module_specifier.rs +++ /dev/null @@ -1,500 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use crate::normalize_path; -use std::error::Error; -use std::fmt; -use std::path::Path; -use std::path::PathBuf; -use url::ParseError; -use url::Url; - -/// Error indicating the reason resolving a module specifier failed. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ModuleResolutionError { - InvalidUrl(ParseError), - InvalidBaseUrl(ParseError), - InvalidPath(PathBuf), - ImportPrefixMissing(String, Option), -} -use ModuleResolutionError::*; - -impl Error for ModuleResolutionError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - InvalidUrl(ref err) | InvalidBaseUrl(ref err) => Some(err), - _ => None, - } - } -} - -impl fmt::Display for ModuleResolutionError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - InvalidUrl(ref err) => write!(f, "invalid URL: {err}"), - InvalidBaseUrl(ref err) => { - write!(f, "invalid base URL for relative import: {err}") - } - InvalidPath(ref path) => write!(f, "invalid module path: {path:?}"), - ImportPrefixMissing(ref specifier, ref maybe_referrer) => write!( - f, - "Relative import path \"{}\" not prefixed with / or ./ or ../{}", - specifier, - match maybe_referrer { - Some(referrer) => format!(" from \"{referrer}\""), - None => String::new(), - } - ), - } - } -} - -/// Resolved module specifier -pub type ModuleSpecifier = Url; - -/// Resolves module using this algorithm: -/// -pub fn resolve_import( - specifier: &str, - base: &str, -) -> Result { - let url = match Url::parse(specifier) { - // 1. Apply the URL parser to specifier. - // If the result is not failure, return he result. - Ok(url) => url, - - // 2. If specifier does not start with the character U+002F SOLIDUS (/), - // the two-character sequence U+002E FULL STOP, U+002F SOLIDUS (./), - // or the three-character sequence U+002E FULL STOP, U+002E FULL STOP, - // U+002F SOLIDUS (../), return failure. - Err(ParseError::RelativeUrlWithoutBase) - if !(specifier.starts_with('/') - || specifier.starts_with("./") - || specifier.starts_with("../")) => - { - let maybe_referrer = if base.is_empty() { - None - } else { - Some(base.to_string()) - }; - return Err(ImportPrefixMissing(specifier.to_string(), maybe_referrer)); - } - - // 3. Return the result of applying the URL parser to specifier with base - // URL as the base URL. - Err(ParseError::RelativeUrlWithoutBase) => { - let base = Url::parse(base).map_err(InvalidBaseUrl)?; - base.join(specifier).map_err(InvalidUrl)? - } - - // If parsing the specifier as a URL failed for a different reason than - // it being relative, always return the original error. We don't want to - // return `ImportPrefixMissing` or `InvalidBaseUrl` if the real - // problem lies somewhere else. - Err(err) => return Err(InvalidUrl(err)), - }; - - Ok(url) -} - -/// Converts a string representing an absolute URL into a ModuleSpecifier. -pub fn resolve_url( - url_str: &str, -) -> Result { - Url::parse(url_str).map_err(ModuleResolutionError::InvalidUrl) -} - -/// Takes a string representing either an absolute URL or a file path, -/// as it may be passed to deno as a command line argument. -/// The string is interpreted as a URL if it starts with a valid URI scheme, -/// e.g. 'http:' or 'file:' or 'git+ssh:'. If not, it's interpreted as a -/// file path; if it is a relative path it's resolved relative to passed -/// `current_dir`. -pub fn resolve_url_or_path( - specifier: &str, - current_dir: &Path, -) -> Result { - if specifier_has_uri_scheme(specifier) { - resolve_url(specifier) - } else { - resolve_path(specifier, current_dir) - } -} - -/// Converts a string representing a relative or absolute path into a -/// ModuleSpecifier. A relative path is considered relative to the passed -/// `current_dir`. -pub fn resolve_path( - path_str: &str, - current_dir: &Path, -) -> Result { - let path = current_dir.join(path_str); - let path = normalize_path(path); - Url::from_file_path(&path) - .map_err(|()| ModuleResolutionError::InvalidPath(path)) -} - -/// Returns true if the input string starts with a sequence of characters -/// that could be a valid URI scheme, like 'https:', 'git+ssh:' or 'data:'. -/// -/// According to RFC 3986 (https://tools.ietf.org/html/rfc3986#section-3.1), -/// a valid scheme has the following format: -/// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) -/// -/// We additionally require the scheme to be at least 2 characters long, -/// because otherwise a windows path like c:/foo would be treated as a URL, -/// while no schemes with a one-letter name actually exist. -fn specifier_has_uri_scheme(specifier: &str) -> bool { - let mut chars = specifier.chars(); - let mut len = 0usize; - // THe first character must be a letter. - match chars.next() { - Some(c) if c.is_ascii_alphabetic() => len += 1, - _ => return false, - } - // Second and following characters must be either a letter, number, - // plus sign, minus sign, or dot. - loop { - match chars.next() { - Some(c) if c.is_ascii_alphanumeric() || "+-.".contains(c) => len += 1, - Some(':') if len >= 2 => return true, - _ => return false, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::serde_json::from_value; - use crate::serde_json::json; - use std::env::current_dir; - use std::path::Path; - - #[test] - fn test_resolve_import() { - let tests = vec![ - ( - "./005_more_imports.ts", - "http://deno.land/core/tests/006_url_imports.ts", - "http://deno.land/core/tests/005_more_imports.ts", - ), - ( - "../005_more_imports.ts", - "http://deno.land/core/tests/006_url_imports.ts", - "http://deno.land/core/005_more_imports.ts", - ), - ( - "http://deno.land/core/tests/005_more_imports.ts", - "http://deno.land/core/tests/006_url_imports.ts", - "http://deno.land/core/tests/005_more_imports.ts", - ), - ( - "data:text/javascript,export default 'grapes';", - "http://deno.land/core/tests/006_url_imports.ts", - "data:text/javascript,export default 'grapes';", - ), - ( - "blob:https://whatwg.org/d0360e2f-caee-469f-9a2f-87d5b0456f6f", - "http://deno.land/core/tests/006_url_imports.ts", - "blob:https://whatwg.org/d0360e2f-caee-469f-9a2f-87d5b0456f6f", - ), - ( - "javascript:export default 'artichokes';", - "http://deno.land/core/tests/006_url_imports.ts", - "javascript:export default 'artichokes';", - ), - ( - "data:text/plain,export default 'kale';", - "http://deno.land/core/tests/006_url_imports.ts", - "data:text/plain,export default 'kale';", - ), - ( - "/dev/core/tests/005_more_imports.ts", - "file:///home/yeti", - "file:///dev/core/tests/005_more_imports.ts", - ), - ( - "//zombo.com/1999.ts", - "https://cherry.dev/its/a/thing", - "https://zombo.com/1999.ts", - ), - ( - "http://deno.land/this/url/is/valid", - "base is clearly not a valid url", - "http://deno.land/this/url/is/valid", - ), - ( - "//server/some/dir/file", - "file:///home/yeti/deno", - "file://server/some/dir/file", - ), - // This test is disabled because the url crate does not follow the spec, - // dropping the server part from the final result. - // ( - // "/another/path/at/the/same/server", - // "file://server/some/dir/file", - // "file://server/another/path/at/the/same/server", - // ), - ]; - - for (specifier, base, expected_url) in tests { - let url = resolve_import(specifier, base).unwrap().to_string(); - assert_eq!(url, expected_url); - } - } - - #[test] - fn test_resolve_import_error() { - use url::ParseError::*; - use ModuleResolutionError::*; - - let tests = vec![ - ( - "awesome.ts", - "", - ImportPrefixMissing( - "awesome.ts".to_string(), - Some("".to_string()), - ), - ), - ( - "005_more_imports.ts", - "http://deno.land/core/tests/006_url_imports.ts", - ImportPrefixMissing( - "005_more_imports.ts".to_string(), - Some("http://deno.land/core/tests/006_url_imports.ts".to_string()), - ), - ), - ( - ".tomato", - "http://deno.land/core/tests/006_url_imports.ts", - ImportPrefixMissing( - ".tomato".to_string(), - Some("http://deno.land/core/tests/006_url_imports.ts".to_string()), - ), - ), - ( - "..zucchini.mjs", - "http://deno.land/core/tests/006_url_imports.ts", - ImportPrefixMissing( - "..zucchini.mjs".to_string(), - Some("http://deno.land/core/tests/006_url_imports.ts".to_string()), - ), - ), - ( - r".\yam.es", - "http://deno.land/core/tests/006_url_imports.ts", - ImportPrefixMissing( - r".\yam.es".to_string(), - Some("http://deno.land/core/tests/006_url_imports.ts".to_string()), - ), - ), - ( - r"..\yam.es", - "http://deno.land/core/tests/006_url_imports.ts", - ImportPrefixMissing( - r"..\yam.es".to_string(), - Some("http://deno.land/core/tests/006_url_imports.ts".to_string()), - ), - ), - ( - "https://eggplant:b/c", - "http://deno.land/core/tests/006_url_imports.ts", - InvalidUrl(InvalidPort), - ), - ( - "https://eggplant@/c", - "http://deno.land/core/tests/006_url_imports.ts", - InvalidUrl(EmptyHost), - ), - ( - "./foo.ts", - "/relative/base/url", - InvalidBaseUrl(RelativeUrlWithoutBase), - ), - ]; - - for (specifier, base, expected_err) in tests { - let err = resolve_import(specifier, base).unwrap_err(); - assert_eq!(err, expected_err); - } - } - - #[test] - fn test_resolve_url_or_path() { - // Absolute URL. - let mut tests: Vec<(&str, String)> = vec![ - ( - "http://deno.land/core/tests/006_url_imports.ts", - "http://deno.land/core/tests/006_url_imports.ts".to_string(), - ), - ( - "https://deno.land/core/tests/006_url_imports.ts", - "https://deno.land/core/tests/006_url_imports.ts".to_string(), - ), - ]; - - // The local path tests assume that the cwd is the deno repo root. - let cwd = current_dir().unwrap(); - let cwd_str = cwd.to_str().unwrap(); - - if cfg!(target_os = "windows") { - // Absolute local path. - let expected_url = "file:///C:/deno/tests/006_url_imports.ts"; - tests.extend(vec![ - ( - r"C:/deno/tests/006_url_imports.ts", - expected_url.to_string(), - ), - ( - r"C:\deno\tests\006_url_imports.ts", - expected_url.to_string(), - ), - ( - r"\\?\C:\deno\tests\006_url_imports.ts", - expected_url.to_string(), - ), - // Not supported: `Url::from_file_path()` fails. - // (r"\\.\C:\deno\tests\006_url_imports.ts", expected_url.to_string()), - // Not supported: `Url::from_file_path()` performs the wrong conversion. - // (r"//./C:/deno/tests/006_url_imports.ts", expected_url.to_string()), - ]); - - // Rooted local path without drive letter. - let expected_url = format!( - "file:///{}:/deno/tests/006_url_imports.ts", - cwd_str.get(..1).unwrap(), - ); - tests.extend(vec![ - (r"/deno/tests/006_url_imports.ts", expected_url.to_string()), - (r"\deno\tests\006_url_imports.ts", expected_url.to_string()), - ( - r"\deno\..\deno\tests\006_url_imports.ts", - expected_url.to_string(), - ), - (r"\deno\.\tests\006_url_imports.ts", expected_url), - ]); - - // Relative local path. - let expected_url = format!( - "file:///{}/tests/006_url_imports.ts", - cwd_str.replace('\\', "/") - ); - tests.extend(vec![ - (r"tests/006_url_imports.ts", expected_url.to_string()), - (r"tests\006_url_imports.ts", expected_url.to_string()), - (r"./tests/006_url_imports.ts", (*expected_url).to_string()), - (r".\tests\006_url_imports.ts", (*expected_url).to_string()), - ]); - - // UNC network path. - let expected_url = "file://server/share/deno/cool"; - tests.extend(vec![ - (r"\\server\share\deno\cool", expected_url.to_string()), - (r"\\server/share/deno/cool", expected_url.to_string()), - // Not supported: `Url::from_file_path()` performs the wrong conversion. - // (r"//server/share/deno/cool", expected_url.to_string()), - ]); - } else { - // Absolute local path. - let expected_url = "file:///deno/tests/006_url_imports.ts"; - tests.extend(vec![ - ("/deno/tests/006_url_imports.ts", expected_url.to_string()), - ("//deno/tests/006_url_imports.ts", expected_url.to_string()), - ]); - - // Relative local path. - let expected_url = format!("file://{cwd_str}/tests/006_url_imports.ts"); - tests.extend(vec![ - ("tests/006_url_imports.ts", expected_url.to_string()), - ("./tests/006_url_imports.ts", expected_url.to_string()), - ( - "tests/../tests/006_url_imports.ts", - expected_url.to_string(), - ), - ("tests/./006_url_imports.ts", expected_url), - ]); - } - - for (specifier, expected_url) in tests { - let url = resolve_url_or_path(specifier, &cwd).unwrap().to_string(); - assert_eq!(url, expected_url); - } - } - - #[test] - fn test_resolve_url_or_path_deprecated_error() { - use url::ParseError::*; - use ModuleResolutionError::*; - - let mut tests = vec![ - ("https://eggplant:b/c", InvalidUrl(InvalidPort)), - ("https://:8080/a/b/c", InvalidUrl(EmptyHost)), - ]; - if cfg!(target_os = "windows") { - let p = r"\\.\c:/stuff/deno/script.ts"; - tests.push((p, InvalidPath(PathBuf::from(p)))); - } - - for (specifier, expected_err) in tests { - let err = - resolve_url_or_path(specifier, &PathBuf::from("/")).unwrap_err(); - assert_eq!(err, expected_err); - } - } - - #[test] - fn test_specifier_has_uri_scheme() { - let tests = vec![ - ("http://foo.bar/etc", true), - ("HTTP://foo.bar/etc", true), - ("http:ftp:", true), - ("http:", true), - ("hTtP:", true), - ("ftp:", true), - ("mailto:spam@please.me", true), - ("git+ssh://git@github.com/denoland/deno", true), - ("blob:https://whatwg.org/mumbojumbo", true), - ("abc.123+DEF-ghi:", true), - ("abc.123+def-ghi:@", true), - ("", false), - (":not", false), - ("http", false), - ("c:dir", false), - ("X:", false), - ("./http://not", false), - ("1abc://kinda/but/no", false), - ("schluẞ://no/more", false), - ]; - - for (specifier, expected) in tests { - let result = specifier_has_uri_scheme(specifier); - assert_eq!(result, expected); - } - } - - #[test] - fn test_normalize_path() { - assert_eq!(normalize_path(Path::new("a/../b")), PathBuf::from("b")); - assert_eq!(normalize_path(Path::new("a/./b/")), PathBuf::from("a/b/")); - assert_eq!( - normalize_path(Path::new("a/./b/../c")), - PathBuf::from("a/c") - ); - - if cfg!(windows) { - assert_eq!( - normalize_path(Path::new("C:\\a\\.\\b\\..\\c")), - PathBuf::from("C:\\a\\c") - ); - } - } - - #[test] - fn test_deserialize_module_specifier() { - let actual: ModuleSpecifier = - from_value(json!("http://deno.land/x/mod.ts")).unwrap(); - let expected = resolve_url("http://deno.land/x/mod.ts").unwrap(); - assert_eq!(actual, expected); - } -} diff --git a/core/modules/loaders.rs b/core/modules/loaders.rs deleted file mode 100644 index bc645567e8..0000000000 --- a/core/modules/loaders.rs +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use crate::error::generic_error; -use crate::error::AnyError; -use crate::extensions::ExtensionFileSource; -use crate::module_specifier::ModuleSpecifier; -use crate::modules::ModuleCode; -use crate::modules::ModuleSource; -use crate::modules::ModuleSourceFuture; -use crate::modules::ModuleType; -use crate::modules::ResolutionKind; -use crate::resolve_import; -use crate::Extension; -use anyhow::anyhow; -use anyhow::Error; -use futures::future::FutureExt; -use std::cell::RefCell; -use std::collections::HashMap; -use std::collections::HashSet; -use std::future::Future; -use std::pin::Pin; -use std::rc::Rc; - -pub trait ModuleLoader { - /// Returns an absolute URL. - /// When implementing an spec-complaint VM, this should be exactly the - /// algorithm described here: - /// - /// - /// `is_main` can be used to resolve from current working directory or - /// apply import map for child imports. - /// - /// `is_dyn_import` can be used to check permissions or deny - /// dynamic imports altogether. - fn resolve( - &self, - specifier: &str, - referrer: &str, - kind: ResolutionKind, - ) -> Result; - - /// Given ModuleSpecifier, load its source code. - /// - /// `is_dyn_import` can be used to check permissions or deny - /// dynamic imports altogether. - fn load( - &self, - module_specifier: &ModuleSpecifier, - maybe_referrer: Option<&ModuleSpecifier>, - is_dyn_import: bool, - ) -> Pin>; - - /// This hook can be used by implementors to do some preparation - /// work before starting loading of modules. - /// - /// For example implementor might download multiple modules in - /// parallel and transpile them to final JS sources before - /// yielding control back to the runtime. - /// - /// It's not required to implement this method. - fn prepare_load( - &self, - _module_specifier: &ModuleSpecifier, - _maybe_referrer: Option, - _is_dyn_import: bool, - ) -> Pin>>> { - async { Ok(()) }.boxed_local() - } -} - -/// Placeholder structure used when creating -/// a runtime that doesn't support module loading. -pub struct NoopModuleLoader; - -impl ModuleLoader for NoopModuleLoader { - fn resolve( - &self, - specifier: &str, - referrer: &str, - _kind: ResolutionKind, - ) -> Result { - Err(generic_error( - format!("Module loading is not supported; attempted to resolve: \"{specifier}\" from \"{referrer}\"") - )) - } - - fn load( - &self, - module_specifier: &ModuleSpecifier, - maybe_referrer: Option<&ModuleSpecifier>, - _is_dyn_import: bool, - ) -> Pin> { - let err = generic_error( - format!( - "Module loading is not supported; attempted to load: \"{module_specifier}\" from \"{maybe_referrer:?}\"", - ) - ); - async move { Err(err) }.boxed_local() - } -} - -/// Function that can be passed to the `ExtModuleLoader` that allows to -/// transpile sources before passing to V8. -pub type ExtModuleLoaderCb = - Box Result>; - -pub(crate) struct ExtModuleLoader { - maybe_load_callback: Option>, - sources: RefCell>, - used_specifiers: RefCell>, -} - -impl ExtModuleLoader { - pub fn new( - extensions: &[Extension], - maybe_load_callback: Option>, - ) -> Self { - let mut sources = HashMap::new(); - sources.extend( - extensions - .iter() - .flat_map(|e| e.get_esm_sources()) - .map(|s| (s.specifier.to_string(), s.clone())), - ); - ExtModuleLoader { - maybe_load_callback, - sources: RefCell::new(sources), - used_specifiers: Default::default(), - } - } -} - -impl ModuleLoader for ExtModuleLoader { - fn resolve( - &self, - specifier: &str, - referrer: &str, - _kind: ResolutionKind, - ) -> Result { - Ok(resolve_import(specifier, referrer)?) - } - - fn load( - &self, - specifier: &ModuleSpecifier, - _maybe_referrer: Option<&ModuleSpecifier>, - _is_dyn_import: bool, - ) -> Pin> { - let sources = self.sources.borrow(); - let source = match sources.get(specifier.as_str()) { - Some(source) => source, - None => return futures::future::err(anyhow!("Specifier \"{}\" was not passed as an extension module and was not included in the snapshot.", specifier)).boxed_local(), - }; - self - .used_specifiers - .borrow_mut() - .insert(specifier.to_string()); - let result = if let Some(load_callback) = &self.maybe_load_callback { - load_callback(source) - } else { - source.load() - }; - match result { - Ok(code) => { - let res = ModuleSource::new(ModuleType::JavaScript, code, specifier); - return futures::future::ok(res).boxed_local(); - } - Err(err) => return futures::future::err(err).boxed_local(), - } - } - - fn prepare_load( - &self, - _specifier: &ModuleSpecifier, - _maybe_referrer: Option, - _is_dyn_import: bool, - ) -> Pin>>> { - async { Ok(()) }.boxed_local() - } -} - -impl Drop for ExtModuleLoader { - fn drop(&mut self) { - let sources = self.sources.get_mut(); - let used_specifiers = self.used_specifiers.get_mut(); - let unused_modules: Vec<_> = sources - .iter() - .filter(|(k, _)| !used_specifiers.contains(k.as_str())) - .collect(); - - if !unused_modules.is_empty() { - let mut msg = - "Following modules were passed to ExtModuleLoader but never used:\n" - .to_string(); - for m in unused_modules { - msg.push_str(" - "); - msg.push_str(m.0); - msg.push('\n'); - } - panic!("{}", msg); - } - } -} - -/// Basic file system module loader. -/// -/// Note that this loader will **block** event loop -/// when loading file as it uses synchronous FS API -/// from standard library. -pub struct FsModuleLoader; - -impl ModuleLoader for FsModuleLoader { - fn resolve( - &self, - specifier: &str, - referrer: &str, - _kind: ResolutionKind, - ) -> Result { - Ok(resolve_import(specifier, referrer)?) - } - - fn load( - &self, - module_specifier: &ModuleSpecifier, - _maybe_referrer: Option<&ModuleSpecifier>, - _is_dynamic: bool, - ) -> Pin> { - fn load( - module_specifier: &ModuleSpecifier, - ) -> Result { - let path = module_specifier.to_file_path().map_err(|_| { - generic_error(format!( - "Provided module specifier \"{module_specifier}\" is not a file URL." - )) - })?; - let module_type = if let Some(extension) = path.extension() { - let ext = extension.to_string_lossy().to_lowercase(); - if ext == "json" { - ModuleType::Json - } else { - ModuleType::JavaScript - } - } else { - ModuleType::JavaScript - }; - - let code = std::fs::read_to_string(path)?.into(); - let module = ModuleSource::new(module_type, code, module_specifier); - Ok(module) - } - - futures::future::ready(load(module_specifier)).boxed_local() - } -} diff --git a/core/modules/map.rs b/core/modules/map.rs deleted file mode 100644 index 786772e5be..0000000000 --- a/core/modules/map.rs +++ /dev/null @@ -1,1014 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use crate::error::exception_to_err_result; -use crate::error::generic_error; -use crate::error::throw_type_error; -use crate::fast_string::FastString; -use crate::modules::get_asserted_module_type_from_assertions; -use crate::modules::parse_import_assertions; -use crate::modules::validate_import_assertions; -use crate::modules::ImportAssertionsKind; -use crate::modules::ModuleCode; -use crate::modules::ModuleError; -use crate::modules::ModuleId; -use crate::modules::ModuleInfo; -use crate::modules::ModuleLoadId; -use crate::modules::ModuleLoader; -use crate::modules::ModuleName; -use crate::modules::ModuleRequest; -use crate::modules::ModuleType; -use crate::modules::NoopModuleLoader; -use crate::modules::PrepareLoadFuture; -use crate::modules::RecursiveModuleLoad; -use crate::modules::ResolutionKind; -use crate::runtime::JsRuntime; -use crate::runtime::SnapshottedData; -use anyhow::Error; -use futures::future::FutureExt; -use futures::stream::FuturesUnordered; -use futures::stream::StreamFuture; -use std::cell::RefCell; -use std::collections::HashMap; -use std::pin::Pin; -use std::rc::Rc; - -use super::AssertedModuleType; - -pub const BOM_CHAR: &[u8] = &[0xef, 0xbb, 0xbf]; - -/// Strips the byte order mark from the provided text if it exists. -fn strip_bom(source_code: &[u8]) -> &[u8] { - if source_code.starts_with(BOM_CHAR) { - &source_code[BOM_CHAR.len()..] - } else { - source_code - } -} - -/// A symbolic module entity. -#[derive(Debug, PartialEq)] -pub(crate) enum SymbolicModule { - /// This module is an alias to another module. - /// This is useful such that multiple names could point to - /// the same underlying module (particularly due to redirects). - Alias(ModuleName), - /// This module associates with a V8 module by id. - Mod(ModuleId), -} - -/// A collection of JS modules. -pub(crate) struct ModuleMap { - // Handling of specifiers and v8 objects - pub handles: Vec>, - pub info: Vec, - pub(crate) by_name_js: HashMap, - pub(crate) by_name_json: HashMap, - pub(crate) next_load_id: ModuleLoadId, - - // Handling of futures for loading module sources - pub loader: Rc, - pub(crate) dynamic_import_map: - HashMap>, - pub(crate) preparing_dynamic_imports: - FuturesUnordered>>, - pub(crate) pending_dynamic_imports: - FuturesUnordered>, - - // This store is used temporarily, to forward parsed JSON - // value from `new_json_module` to `json_module_evaluation_steps` - json_value_store: HashMap, v8::Global>, -} - -impl ModuleMap { - pub fn collect_modules( - &self, - ) -> Vec<(AssertedModuleType, &ModuleName, &SymbolicModule)> { - let mut output = vec![]; - for module_type in [ - AssertedModuleType::JavaScriptOrWasm, - AssertedModuleType::Json, - ] { - output.extend( - self - .by_name(module_type) - .iter() - .map(|x| (module_type, x.0, x.1)), - ) - } - output - } - - #[cfg(debug_assertions)] - pub(crate) fn assert_all_modules_evaluated( - &self, - scope: &mut v8::HandleScope, - ) { - let mut not_evaluated = vec![]; - - for (i, handle) in self.handles.iter().enumerate() { - let module = v8::Local::new(scope, handle); - if !matches!(module.get_status(), v8::ModuleStatus::Evaluated) { - not_evaluated.push(self.info[i].name.as_str().to_string()); - } - } - - if !not_evaluated.is_empty() { - let mut msg = "Following modules were not evaluated; make sure they are imported from other code:\n".to_string(); - for m in not_evaluated { - msg.push_str(&format!(" - {}\n", m)); - } - panic!("{}", msg); - } - } - - pub fn serialize_for_snapshotting( - &self, - scope: &mut v8::HandleScope, - ) -> SnapshottedData { - let array = v8::Array::new(scope, 3); - - let next_load_id = v8::Integer::new(scope, self.next_load_id); - array.set_index(scope, 0, next_load_id.into()); - - let info_arr = v8::Array::new(scope, self.info.len() as i32); - for (i, info) in self.info.iter().enumerate() { - let module_info_arr = v8::Array::new(scope, 5); - - let id = v8::Integer::new(scope, info.id as i32); - module_info_arr.set_index(scope, 0, id.into()); - - let main = v8::Boolean::new(scope, info.main); - module_info_arr.set_index(scope, 1, main.into()); - - let name = info.name.v8(scope); - module_info_arr.set_index(scope, 2, name.into()); - - let array_len = 2 * info.requests.len() as i32; - let requests_arr = v8::Array::new(scope, array_len); - for (i, request) in info.requests.iter().enumerate() { - let specifier = v8::String::new_from_one_byte( - scope, - request.specifier.as_bytes(), - v8::NewStringType::Normal, - ) - .unwrap(); - requests_arr.set_index(scope, 2 * i as u32, specifier.into()); - - let asserted_module_type = - v8::Integer::new(scope, request.asserted_module_type as i32); - requests_arr.set_index( - scope, - (2 * i) as u32 + 1, - asserted_module_type.into(), - ); - } - module_info_arr.set_index(scope, 3, requests_arr.into()); - - let module_type = v8::Integer::new(scope, info.module_type as i32); - module_info_arr.set_index(scope, 4, module_type.into()); - - info_arr.set_index(scope, i as u32, module_info_arr.into()); - } - array.set_index(scope, 1, info_arr.into()); - - let by_name = self.collect_modules(); - let by_name_array = v8::Array::new(scope, by_name.len() as i32); - { - for (i, (module_type, name, module)) in by_name.into_iter().enumerate() { - let arr = v8::Array::new(scope, 3); - - let specifier = name.v8(scope); - arr.set_index(scope, 0, specifier.into()); - - let asserted_module_type = v8::Integer::new(scope, module_type as i32); - arr.set_index(scope, 1, asserted_module_type.into()); - - let symbolic_module: v8::Local = match module { - SymbolicModule::Alias(alias) => { - let alias = v8::String::new_from_one_byte( - scope, - alias.as_bytes(), - v8::NewStringType::Normal, - ) - .unwrap(); - alias.into() - } - SymbolicModule::Mod(id) => { - let id = v8::Integer::new(scope, *id as i32); - id.into() - } - }; - arr.set_index(scope, 2, symbolic_module); - - by_name_array.set_index(scope, i as u32, arr.into()); - } - } - array.set_index(scope, 2, by_name_array.into()); - - let array_global = v8::Global::new(scope, array); - - let handles = self.handles.clone(); - SnapshottedData { - module_map_data: array_global, - module_handles: handles, - } - } - - pub fn update_with_snapshotted_data( - &mut self, - scope: &mut v8::HandleScope, - snapshotted_data: SnapshottedData, - ) { - let local_data: v8::Local = - v8::Local::new(scope, snapshotted_data.module_map_data); - - { - let next_load_id = local_data.get_index(scope, 0).unwrap(); - assert!(next_load_id.is_int32()); - let integer = next_load_id.to_integer(scope).unwrap(); - let val = integer.int32_value(scope).unwrap(); - self.next_load_id = val; - } - - { - let info_val = local_data.get_index(scope, 1).unwrap(); - - let info_arr: v8::Local = info_val.try_into().unwrap(); - let len = info_arr.length() as usize; - // Over allocate so executing a few scripts doesn't have to resize this vec. - let mut info = Vec::with_capacity(len + 16); - - for i in 0..len { - let module_info_arr: v8::Local = info_arr - .get_index(scope, i as u32) - .unwrap() - .try_into() - .unwrap(); - let id = module_info_arr - .get_index(scope, 0) - .unwrap() - .to_integer(scope) - .unwrap() - .value() as ModuleId; - - let main = module_info_arr - .get_index(scope, 1) - .unwrap() - .to_boolean(scope) - .is_true(); - - let name = module_info_arr - .get_index(scope, 2) - .unwrap() - .to_rust_string_lossy(scope) - .into(); - - let requests_arr: v8::Local = module_info_arr - .get_index(scope, 3) - .unwrap() - .try_into() - .unwrap(); - let len = (requests_arr.length() as usize) / 2; - let mut requests = Vec::with_capacity(len); - for i in 0..len { - let specifier = requests_arr - .get_index(scope, (2 * i) as u32) - .unwrap() - .to_rust_string_lossy(scope); - let asserted_module_type_no = requests_arr - .get_index(scope, (2 * i + 1) as u32) - .unwrap() - .to_integer(scope) - .unwrap() - .value(); - let asserted_module_type = match asserted_module_type_no { - 0 => AssertedModuleType::JavaScriptOrWasm, - 1 => AssertedModuleType::Json, - _ => unreachable!(), - }; - requests.push(ModuleRequest { - specifier, - asserted_module_type, - }); - } - - let module_type_no = module_info_arr - .get_index(scope, 4) - .unwrap() - .to_integer(scope) - .unwrap() - .value(); - let module_type = match module_type_no { - 0 => ModuleType::JavaScript, - 1 => ModuleType::Json, - _ => unreachable!(), - }; - - let module_info = ModuleInfo { - id, - main, - name, - requests, - module_type, - }; - info.push(module_info); - } - - self.info = info; - } - - self - .by_name_mut(AssertedModuleType::JavaScriptOrWasm) - .clear(); - self.by_name_mut(AssertedModuleType::Json).clear(); - - { - let by_name_arr: v8::Local = - local_data.get_index(scope, 2).unwrap().try_into().unwrap(); - let len = by_name_arr.length() as usize; - - for i in 0..len { - let arr: v8::Local = by_name_arr - .get_index(scope, i as u32) - .unwrap() - .try_into() - .unwrap(); - - let specifier = - arr.get_index(scope, 0).unwrap().to_rust_string_lossy(scope); - let asserted_module_type = match arr - .get_index(scope, 1) - .unwrap() - .to_integer(scope) - .unwrap() - .value() - { - 0 => AssertedModuleType::JavaScriptOrWasm, - 1 => AssertedModuleType::Json, - _ => unreachable!(), - }; - - let symbolic_module_val = arr.get_index(scope, 2).unwrap(); - let val = if symbolic_module_val.is_number() { - SymbolicModule::Mod( - symbolic_module_val - .to_integer(scope) - .unwrap() - .value() - .try_into() - .unwrap(), - ) - } else { - SymbolicModule::Alias( - symbolic_module_val.to_rust_string_lossy(scope).into(), - ) - }; - - self - .by_name_mut(asserted_module_type) - .insert(specifier.into(), val); - } - } - - self.handles = snapshotted_data.module_handles; - } - - pub(crate) fn new(loader: Rc) -> ModuleMap { - Self { - handles: vec![], - info: vec![], - by_name_js: HashMap::new(), - by_name_json: HashMap::new(), - next_load_id: 1, - loader, - dynamic_import_map: HashMap::new(), - preparing_dynamic_imports: FuturesUnordered::new(), - pending_dynamic_imports: FuturesUnordered::new(), - json_value_store: HashMap::new(), - } - } - - /// Get module id, following all aliases in case of module specifier - /// that had been redirected. - pub(crate) fn get_id( - &self, - name: impl AsRef, - asserted_module_type: AssertedModuleType, - ) -> Option { - let map = self.by_name(asserted_module_type); - let first_symbolic_module = map.get(name.as_ref())?; - let mut mod_name = match first_symbolic_module { - SymbolicModule::Mod(mod_id) => return Some(*mod_id), - SymbolicModule::Alias(target) => target, - }; - loop { - let symbolic_module = map.get(mod_name.as_ref())?; - match symbolic_module { - SymbolicModule::Alias(target) => { - debug_assert!(mod_name != target); - mod_name = target; - } - SymbolicModule::Mod(mod_id) => return Some(*mod_id), - } - } - } - - pub(crate) fn new_json_module( - &mut self, - scope: &mut v8::HandleScope, - name: ModuleName, - source: ModuleCode, - ) -> Result { - let name_str = name.v8(scope); - let source_str = v8::String::new_from_utf8( - scope, - strip_bom(source.as_bytes()), - v8::NewStringType::Normal, - ) - .unwrap(); - - let tc_scope = &mut v8::TryCatch::new(scope); - - let parsed_json = match v8::json::parse(tc_scope, source_str) { - Some(parsed_json) => parsed_json, - None => { - assert!(tc_scope.has_caught()); - let exception = tc_scope.exception().unwrap(); - let exception = v8::Global::new(tc_scope, exception); - return Err(ModuleError::Exception(exception)); - } - }; - - let export_names = [v8::String::new(tc_scope, "default").unwrap()]; - let module = v8::Module::create_synthetic_module( - tc_scope, - name_str, - &export_names, - json_module_evaluation_steps, - ); - - let handle = v8::Global::::new(tc_scope, module); - let value_handle = v8::Global::::new(tc_scope, parsed_json); - self.json_value_store.insert(handle.clone(), value_handle); - - let id = - self.create_module_info(name, ModuleType::Json, handle, false, vec![]); - - Ok(id) - } - - /// Create and compile an ES module. - pub(crate) fn new_es_module( - &mut self, - scope: &mut v8::HandleScope, - main: bool, - name: ModuleName, - source: ModuleCode, - is_dynamic_import: bool, - ) -> Result { - let name_str = name.v8(scope); - let source_str = source.v8(scope); - - let origin = module_origin(scope, name_str); - let source = v8::script_compiler::Source::new(source_str, Some(&origin)); - - let tc_scope = &mut v8::TryCatch::new(scope); - - let maybe_module = v8::script_compiler::compile_module(tc_scope, source); - - if tc_scope.has_caught() { - assert!(maybe_module.is_none()); - let exception = tc_scope.exception().unwrap(); - let exception = v8::Global::new(tc_scope, exception); - return Err(ModuleError::Exception(exception)); - } - - let module = maybe_module.unwrap(); - - let mut requests: Vec = vec![]; - let module_requests = module.get_module_requests(); - for i in 0..module_requests.length() { - let module_request = v8::Local::::try_from( - module_requests.get(tc_scope, i).unwrap(), - ) - .unwrap(); - let import_specifier = module_request - .get_specifier() - .to_rust_string_lossy(tc_scope); - - let import_assertions = module_request.get_import_assertions(); - - let assertions = parse_import_assertions( - tc_scope, - import_assertions, - ImportAssertionsKind::StaticImport, - ); - - // FIXME(bartomieju): there are no stack frames if exception - // is thrown here - validate_import_assertions(tc_scope, &assertions); - if tc_scope.has_caught() { - let exception = tc_scope.exception().unwrap(); - let exception = v8::Global::new(tc_scope, exception); - return Err(ModuleError::Exception(exception)); - } - - let module_specifier = match self.loader.resolve( - &import_specifier, - name.as_ref(), - if is_dynamic_import { - ResolutionKind::DynamicImport - } else { - ResolutionKind::Import - }, - ) { - Ok(s) => s, - Err(e) => return Err(ModuleError::Other(e)), - }; - let asserted_module_type = - get_asserted_module_type_from_assertions(&assertions); - let request = ModuleRequest { - specifier: module_specifier.to_string(), - asserted_module_type, - }; - requests.push(request); - } - - if main { - let maybe_main_module = self.info.iter().find(|module| module.main); - if let Some(main_module) = maybe_main_module { - return Err(ModuleError::Other(generic_error( - format!("Trying to create \"main\" module ({:?}), when one already exists ({:?})", - name.as_ref(), - main_module.name, - )))); - } - } - - let handle = v8::Global::::new(tc_scope, module); - let id = self.create_module_info( - name, - ModuleType::JavaScript, - handle, - main, - requests, - ); - - Ok(id) - } - - pub(crate) fn instantiate_module( - &mut self, - scope: &mut v8::HandleScope, - id: ModuleId, - ) -> Result<(), v8::Global> { - let tc_scope = &mut v8::TryCatch::new(scope); - - let module = self - .get_handle(id) - .map(|handle| v8::Local::new(tc_scope, handle)) - .expect("ModuleInfo not found"); - - if module.get_status() == v8::ModuleStatus::Errored { - return Err(v8::Global::new(tc_scope, module.get_exception())); - } - - tc_scope.set_slot(self as *const _); - let instantiate_result = - module.instantiate_module(tc_scope, Self::module_resolve_callback); - tc_scope.remove_slot::<*const Self>(); - if instantiate_result.is_none() { - let exception = tc_scope.exception().unwrap(); - return Err(v8::Global::new(tc_scope, exception)); - } - - Ok(()) - } - - /// Called by V8 during `JsRuntime::instantiate_module`. This is only used internally, so we use the Isolate's annex - /// to propagate a &Self. - fn module_resolve_callback<'s>( - context: v8::Local<'s, v8::Context>, - specifier: v8::Local<'s, v8::String>, - import_assertions: v8::Local<'s, v8::FixedArray>, - referrer: v8::Local<'s, v8::Module>, - ) -> Option> { - // SAFETY: `CallbackScope` can be safely constructed from `Local` - let scope = &mut unsafe { v8::CallbackScope::new(context) }; - - let module_map = - // SAFETY: We retrieve the pointer from the slot, having just set it a few stack frames up - unsafe { scope.get_slot::<*const Self>().unwrap().as_ref().unwrap() }; - - let referrer_global = v8::Global::new(scope, referrer); - - let referrer_info = module_map - .get_info(&referrer_global) - .expect("ModuleInfo not found"); - let referrer_name = referrer_info.name.as_str(); - - let specifier_str = specifier.to_rust_string_lossy(scope); - - let assertions = parse_import_assertions( - scope, - import_assertions, - ImportAssertionsKind::StaticImport, - ); - let maybe_module = module_map.resolve_callback( - scope, - &specifier_str, - referrer_name, - assertions, - ); - if let Some(module) = maybe_module { - return Some(module); - } - - let msg = format!( - r#"Cannot resolve module "{specifier_str}" from "{referrer_name}""# - ); - throw_type_error(scope, msg); - None - } - - /// Called by `module_resolve_callback` during module instantiation. - fn resolve_callback<'s>( - &self, - scope: &mut v8::HandleScope<'s>, - specifier: &str, - referrer: &str, - import_assertions: HashMap, - ) -> Option> { - let resolved_specifier = self - .loader - .resolve(specifier, referrer, ResolutionKind::Import) - .expect("Module should have been already resolved"); - - let module_type = - get_asserted_module_type_from_assertions(&import_assertions); - - if let Some(id) = self.get_id(resolved_specifier.as_str(), module_type) { - if let Some(handle) = self.get_handle(id) { - return Some(v8::Local::new(scope, handle)); - } - } - - None - } - - pub(crate) fn clear(&mut self) { - *self = Self::new(self.loader.clone()) - } - - pub(crate) fn get_handle_by_name( - &self, - name: impl AsRef, - ) -> Option> { - let id = self - .get_id(name.as_ref(), AssertedModuleType::JavaScriptOrWasm) - .or_else(|| self.get_id(name.as_ref(), AssertedModuleType::Json))?; - self.get_handle(id) - } - - pub(crate) fn inject_handle( - &mut self, - name: ModuleName, - module_type: ModuleType, - handle: v8::Global, - ) { - self.create_module_info(name, module_type, handle, false, vec![]); - } - - fn create_module_info( - &mut self, - name: FastString, - module_type: ModuleType, - handle: v8::Global, - main: bool, - requests: Vec, - ) -> ModuleId { - let id = self.handles.len(); - let (name1, name2) = name.into_cheap_copy(); - self - .by_name_mut(module_type.into()) - .insert(name1, SymbolicModule::Mod(id)); - self.handles.push(handle); - self.info.push(ModuleInfo { - id, - main, - name: name2, - requests, - module_type, - }); - - id - } - - pub(crate) fn get_requested_modules( - &self, - id: ModuleId, - ) -> Option<&Vec> { - self.info.get(id).map(|i| &i.requests) - } - - fn is_registered( - &self, - specifier: impl AsRef, - asserted_module_type: AssertedModuleType, - ) -> bool { - if let Some(id) = self.get_id(specifier.as_ref(), asserted_module_type) { - let info = self.get_info_by_id(id).unwrap(); - return asserted_module_type == info.module_type.into(); - } - - false - } - - pub(crate) fn by_name( - &self, - asserted_module_type: AssertedModuleType, - ) -> &HashMap { - match asserted_module_type { - AssertedModuleType::Json => &self.by_name_json, - AssertedModuleType::JavaScriptOrWasm => &self.by_name_js, - } - } - - pub(crate) fn by_name_mut( - &mut self, - asserted_module_type: AssertedModuleType, - ) -> &mut HashMap { - match asserted_module_type { - AssertedModuleType::Json => &mut self.by_name_json, - AssertedModuleType::JavaScriptOrWasm => &mut self.by_name_js, - } - } - - pub(crate) fn alias( - &mut self, - name: FastString, - asserted_module_type: AssertedModuleType, - target: FastString, - ) { - debug_assert_ne!(name, target); - self - .by_name_mut(asserted_module_type) - .insert(name, SymbolicModule::Alias(target)); - } - - #[cfg(test)] - pub(crate) fn is_alias( - &self, - name: &str, - asserted_module_type: AssertedModuleType, - ) -> bool { - let cond = self.by_name(asserted_module_type).get(name); - matches!(cond, Some(SymbolicModule::Alias(_))) - } - - pub(crate) fn get_handle( - &self, - id: ModuleId, - ) -> Option> { - self.handles.get(id).cloned() - } - - pub(crate) fn get_info( - &self, - global: &v8::Global, - ) -> Option<&ModuleInfo> { - if let Some(id) = self.handles.iter().position(|module| module == global) { - return self.info.get(id); - } - - None - } - - pub(crate) fn get_info_by_id(&self, id: ModuleId) -> Option<&ModuleInfo> { - self.info.get(id) - } - - pub(crate) async fn load_main( - module_map_rc: Rc>, - specifier: impl AsRef, - ) -> Result { - let load = - RecursiveModuleLoad::main(specifier.as_ref(), module_map_rc.clone()); - load.prepare().await?; - Ok(load) - } - - pub(crate) async fn load_side( - module_map_rc: Rc>, - specifier: impl AsRef, - ) -> Result { - let load = - RecursiveModuleLoad::side(specifier.as_ref(), module_map_rc.clone()); - load.prepare().await?; - Ok(load) - } - - // Initiate loading of a module graph imported using `import()`. - pub(crate) fn load_dynamic_import( - module_map_rc: Rc>, - specifier: &str, - referrer: &str, - asserted_module_type: AssertedModuleType, - resolver_handle: v8::Global, - ) { - let load = RecursiveModuleLoad::dynamic_import( - specifier, - referrer, - asserted_module_type, - module_map_rc.clone(), - ); - module_map_rc - .borrow_mut() - .dynamic_import_map - .insert(load.id, resolver_handle); - - let loader = module_map_rc.borrow().loader.clone(); - let resolve_result = - loader.resolve(specifier, referrer, ResolutionKind::DynamicImport); - let fut = match resolve_result { - Ok(module_specifier) => { - if module_map_rc - .borrow() - .is_registered(module_specifier, asserted_module_type) - { - async move { (load.id, Ok(load)) }.boxed_local() - } else { - async move { (load.id, load.prepare().await.map(|()| load)) } - .boxed_local() - } - } - Err(error) => async move { (load.id, Err(error)) }.boxed_local(), - }; - module_map_rc - .borrow_mut() - .preparing_dynamic_imports - .push(fut); - } - - pub(crate) fn has_pending_dynamic_imports(&self) -> bool { - !(self.preparing_dynamic_imports.is_empty() - && self.pending_dynamic_imports.is_empty()) - } - - /// Returns the namespace object of a module. - /// - /// This is only available after module evaluation has completed. - /// This function panics if module has not been instantiated. - pub fn get_module_namespace( - &self, - scope: &mut v8::HandleScope, - module_id: ModuleId, - ) -> Result, Error> { - let module_handle = - self.get_handle(module_id).expect("ModuleInfo not found"); - - let module = module_handle.open(scope); - - if module.get_status() == v8::ModuleStatus::Errored { - let exception = module.get_exception(); - return exception_to_err_result(scope, exception, false); - } - - assert!(matches!( - module.get_status(), - v8::ModuleStatus::Instantiated | v8::ModuleStatus::Evaluated - )); - - let module_namespace: v8::Local = - v8::Local::try_from(module.get_module_namespace()) - .map_err(|err: v8::DataError| generic_error(err.to_string()))?; - - Ok(v8::Global::new(scope, module_namespace)) - } - - /// Clear the module map, meant to be used after initializing extensions. - /// Optionally pass a list of exceptions `(old_name, new_name)` representing - /// specifiers which will be renamed and preserved in the module map. - pub fn clear_module_map( - &mut self, - exceptions: impl Iterator, - ) { - let handles = exceptions - .map(|(old_name, new_name)| { - (self.get_handle_by_name(old_name).unwrap(), new_name) - }) - .collect::>(); - self.clear(); - for (handle, new_name) in handles { - self.inject_handle( - ModuleName::from_static(new_name), - ModuleType::JavaScript, - handle, - ) - } - } - - fn get_stalled_top_level_await_message_for_module( - &self, - scope: &mut v8::HandleScope, - module_id: ModuleId, - ) -> Vec> { - let module_handle = self.handles.get(module_id).unwrap(); - - let module = v8::Local::new(scope, module_handle); - let stalled = module.get_stalled_top_level_await_message(scope); - let mut messages = vec![]; - for (_, message) in stalled { - messages.push(v8::Global::new(scope, message)); - } - messages - } - - pub(crate) fn find_stalled_top_level_await( - &self, - scope: &mut v8::HandleScope, - ) -> Vec> { - // First check if that's root module - let root_module_id = - self.info.iter().filter(|m| m.main).map(|m| m.id).next(); - - if let Some(root_module_id) = root_module_id { - let messages = self - .get_stalled_top_level_await_message_for_module(scope, root_module_id); - if !messages.is_empty() { - return messages; - } - } - - // It wasn't a top module, so iterate over all modules and try to find - // any with stalled top level await - for module_id in 0..self.handles.len() { - let messages = - self.get_stalled_top_level_await_message_for_module(scope, module_id); - if !messages.is_empty() { - return messages; - } - } - - unreachable!() - } -} - -impl Default for ModuleMap { - fn default() -> Self { - Self::new(Rc::new(NoopModuleLoader)) - } -} - -// Clippy thinks the return value doesn't need to be an Option, it's unaware -// of the mapping that MapFnFrom does for ResolveModuleCallback. -#[allow(clippy::unnecessary_wraps)] -fn json_module_evaluation_steps<'a>( - context: v8::Local<'a, v8::Context>, - module: v8::Local, -) -> Option> { - // SAFETY: `CallbackScope` can be safely constructed from `Local` - let scope = &mut unsafe { v8::CallbackScope::new(context) }; - let tc_scope = &mut v8::TryCatch::new(scope); - let module_map = JsRuntime::module_map_from(tc_scope); - - let handle = v8::Global::::new(tc_scope, module); - let value_handle = module_map - .borrow_mut() - .json_value_store - .remove(&handle) - .unwrap(); - let value_local = v8::Local::new(tc_scope, value_handle); - - let name = v8::String::new(tc_scope, "default").unwrap(); - // This should never fail - assert!( - module.set_synthetic_module_export(tc_scope, name, value_local) - == Some(true) - ); - assert!(!tc_scope.has_caught()); - - // Since TLA is active we need to return a promise. - let resolver = v8::PromiseResolver::new(tc_scope).unwrap(); - let undefined = v8::undefined(tc_scope); - resolver.resolve(tc_scope, undefined.into()); - Some(resolver.get_promise(tc_scope).into()) -} - -pub fn module_origin<'a>( - s: &mut v8::HandleScope<'a>, - resource_name: v8::Local<'a, v8::String>, -) -> v8::ScriptOrigin<'a> { - let source_map_url = v8::String::empty(s); - v8::ScriptOrigin::new( - s, - resource_name.into(), - 0, - 0, - false, - 123, - source_map_url.into(), - true, - false, - true, - ) -} diff --git a/core/modules/mod.rs b/core/modules/mod.rs deleted file mode 100644 index 83f10bc727..0000000000 --- a/core/modules/mod.rs +++ /dev/null @@ -1,689 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use crate::error::generic_error; -use crate::fast_string::FastString; -use crate::module_specifier::ModuleSpecifier; -use crate::resolve_url; -use anyhow::Error; -use futures::future::FutureExt; -use futures::stream::FuturesUnordered; -use futures::stream::Stream; -use futures::stream::TryStreamExt; -use log::debug; -use serde::Deserialize; -use serde::Serialize; -use std::cell::RefCell; -use std::collections::HashMap; -use std::collections::HashSet; -use std::collections::VecDeque; -use std::future::Future; -use std::pin::Pin; -use std::rc::Rc; -use std::task::Context; -use std::task::Poll; - -mod loaders; -mod map; - -#[cfg(test)] -mod tests; - -pub(crate) use loaders::ExtModuleLoader; -pub use loaders::ExtModuleLoaderCb; -pub use loaders::FsModuleLoader; -pub use loaders::ModuleLoader; -pub use loaders::NoopModuleLoader; -pub(crate) use map::ModuleMap; -#[cfg(test)] -pub(crate) use map::SymbolicModule; - -pub type ModuleId = usize; -pub(crate) type ModuleLoadId = i32; -pub type ModuleCode = FastString; -pub type ModuleName = FastString; - -const SUPPORTED_TYPE_ASSERTIONS: &[&str] = &["json"]; - -/// Throws V8 exception if assertions are invalid -pub(crate) fn validate_import_assertions( - scope: &mut v8::HandleScope, - assertions: &HashMap, -) { - for (key, value) in assertions { - if key == "type" && !SUPPORTED_TYPE_ASSERTIONS.contains(&value.as_str()) { - let message = v8::String::new( - scope, - &format!("\"{value}\" is not a valid module type."), - ) - .unwrap(); - let exception = v8::Exception::type_error(scope, message); - scope.throw_exception(exception); - return; - } - } -} - -#[derive(Debug)] -pub(crate) enum ImportAssertionsKind { - StaticImport, - DynamicImport, -} - -pub(crate) fn parse_import_assertions( - scope: &mut v8::HandleScope, - import_assertions: v8::Local, - kind: ImportAssertionsKind, -) -> HashMap { - let mut assertions: HashMap = HashMap::default(); - - let assertions_per_line = match kind { - // For static imports, assertions are triples of (keyword, value and source offset) - // Also used in `module_resolve_callback`. - ImportAssertionsKind::StaticImport => 3, - // For dynamic imports, assertions are tuples of (keyword, value) - ImportAssertionsKind::DynamicImport => 2, - }; - assert_eq!(import_assertions.length() % assertions_per_line, 0); - let no_of_assertions = import_assertions.length() / assertions_per_line; - - for i in 0..no_of_assertions { - let assert_key = import_assertions - .get(scope, assertions_per_line * i) - .unwrap(); - let assert_key_val = v8::Local::::try_from(assert_key).unwrap(); - let assert_value = import_assertions - .get(scope, (assertions_per_line * i) + 1) - .unwrap(); - let assert_value_val = - v8::Local::::try_from(assert_value).unwrap(); - assertions.insert( - assert_key_val.to_rust_string_lossy(scope), - assert_value_val.to_rust_string_lossy(scope), - ); - } - - assertions -} - -pub(crate) fn get_asserted_module_type_from_assertions( - assertions: &HashMap, -) -> AssertedModuleType { - assertions - .get("type") - .map(|ty| { - if ty == "json" { - AssertedModuleType::Json - } else { - AssertedModuleType::JavaScriptOrWasm - } - }) - .unwrap_or(AssertedModuleType::JavaScriptOrWasm) -} - -/// A type of module to be executed. -/// -/// For non-`JavaScript` modules, this value doesn't tell -/// how to interpret the module; it is only used to validate -/// the module against an import assertion (if one is present -/// in the import statement). -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -#[repr(u32)] -pub enum ModuleType { - JavaScript, - Json, -} - -impl std::fmt::Display for ModuleType { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Self::JavaScript => write!(f, "JavaScript"), - Self::Json => write!(f, "JSON"), - } - } -} - -/// EsModule source code that will be loaded into V8. -/// -/// Users can implement `Into` for different file types that -/// can be transpiled to valid EsModule. -/// -/// Found module URL might be different from specified URL -/// used for loading due to redirections (like HTTP 303). -/// Eg. Both "`https://example.com/a.ts`" and -/// "`https://example.com/b.ts`" may point to "`https://example.com/c.ts`" -/// By keeping track of specified and found URL we can alias modules and avoid -/// recompiling the same code 3 times. -// TODO(bartlomieju): I have a strong opinion we should store all redirects -// that happened; not only first and final target. It would simplify a lot -// of things throughout the codebase otherwise we may end up requesting -// intermediate redirects from file loader. -// NOTE: This should _not_ be made #[derive(Clone)] unless we take some precautions to avoid excessive string copying. -#[derive(Debug)] -pub struct ModuleSource { - pub code: ModuleCode, - pub module_type: ModuleType, - module_url_specified: ModuleName, - /// If the module was found somewhere other than the specified address, this will be [`Some`]. - module_url_found: Option, -} - -impl ModuleSource { - /// Create a [`ModuleSource`] without a redirect. - pub fn new( - module_type: impl Into, - code: ModuleCode, - specifier: &ModuleSpecifier, - ) -> Self { - let module_url_specified = specifier.as_ref().to_owned().into(); - Self { - code, - module_type: module_type.into(), - module_url_specified, - module_url_found: None, - } - } - - /// Create a [`ModuleSource`] with a potential redirect. If the `specifier_found` parameter is the same as the - /// specifier, the code behaves the same was as `ModuleSource::new`. - pub fn new_with_redirect( - module_type: impl Into, - code: ModuleCode, - specifier: &ModuleSpecifier, - specifier_found: &ModuleSpecifier, - ) -> Self { - let module_url_found = if specifier == specifier_found { - None - } else { - Some(specifier_found.as_ref().to_owned().into()) - }; - let module_url_specified = specifier.as_ref().to_owned().into(); - Self { - code, - module_type: module_type.into(), - module_url_specified, - module_url_found, - } - } - - #[cfg(test)] - pub fn for_test(code: &'static str, file: impl AsRef) -> Self { - Self { - code: ModuleCode::from_static(code), - module_type: ModuleType::JavaScript, - module_url_specified: file.as_ref().to_owned().into(), - module_url_found: None, - } - } - - /// If the `found` parameter is the same as the `specified` parameter, the code behaves the same was as `ModuleSource::for_test`. - #[cfg(test)] - pub fn for_test_with_redirect( - code: &'static str, - specified: impl AsRef, - found: impl AsRef, - ) -> Self { - let specified = specified.as_ref().to_string(); - let found = found.as_ref().to_string(); - let found = if found == specified { - None - } else { - Some(found.into()) - }; - Self { - code: ModuleCode::from_static(code), - module_type: ModuleType::JavaScript, - module_url_specified: specified.into(), - module_url_found: found, - } - } -} - -pub(crate) type PrepareLoadFuture = - dyn Future)>; -pub type ModuleSourceFuture = dyn Future>; - -type ModuleLoadFuture = - dyn Future>; - -#[derive(Debug, PartialEq, Eq)] -pub enum ResolutionKind { - /// This kind is used in only one situation: when a module is loaded via - /// `JsRuntime::load_main_module` and is the top-level module, ie. the one - /// passed as an argument to `JsRuntime::load_main_module`. - MainModule, - /// This kind is returned for all other modules during module load, that are - /// static imports. - Import, - /// This kind is returned for all modules that are loaded as a result of a - /// call to `import()` API (ie. top-level module as well as all its - /// dependencies, and any other `import()` calls from that load). - DynamicImport, -} - -/// Describes the entrypoint of a recursive module load. -#[derive(Debug)] -enum LoadInit { - /// Main module specifier. - Main(String), - /// Module specifier for side module. - Side(String), - /// Dynamic import specifier with referrer and expected - /// module type (which is determined by import assertion). - DynamicImport(String, String, AssertedModuleType), -} - -#[derive(Debug, Eq, PartialEq)] -pub enum LoadState { - Init, - LoadingRoot, - LoadingImports, - Done, -} - -/// This future is used to implement parallel async module loading. -pub(crate) struct RecursiveModuleLoad { - pub id: ModuleLoadId, - pub root_module_id: Option, - init: LoadInit, - root_asserted_module_type: Option, - root_module_type: Option, - state: LoadState, - module_map_rc: Rc>, - pending: FuturesUnordered>>, - visited: HashSet, - // The loader is copied from `module_map_rc`, but its reference is cloned - // ahead of time to avoid already-borrowed errors. - loader: Rc, -} - -impl RecursiveModuleLoad { - /// Starts a new asynchronous load of the module graph for given specifier. - /// - /// The module corresponding for the given `specifier` will be marked as - // "the main module" (`import.meta.main` will return `true` for this module). - fn main(specifier: &str, module_map_rc: Rc>) -> Self { - Self::new(LoadInit::Main(specifier.to_string()), module_map_rc) - } - - /// Starts a new asynchronous load of the module graph for given specifier. - fn side(specifier: &str, module_map_rc: Rc>) -> Self { - Self::new(LoadInit::Side(specifier.to_string()), module_map_rc) - } - - /// Starts a new asynchronous load of the module graph for given specifier - /// that was imported using `import()`. - fn dynamic_import( - specifier: &str, - referrer: &str, - asserted_module_type: AssertedModuleType, - module_map_rc: Rc>, - ) -> Self { - Self::new( - LoadInit::DynamicImport( - specifier.to_string(), - referrer.to_string(), - asserted_module_type, - ), - module_map_rc, - ) - } - - fn new(init: LoadInit, module_map_rc: Rc>) -> Self { - let id = { - let mut module_map = module_map_rc.borrow_mut(); - let id = module_map.next_load_id; - module_map.next_load_id += 1; - id - }; - let loader = module_map_rc.borrow().loader.clone(); - let asserted_module_type = match init { - LoadInit::DynamicImport(_, _, module_type) => module_type, - _ => AssertedModuleType::JavaScriptOrWasm, - }; - let mut load = Self { - id, - root_module_id: None, - root_asserted_module_type: None, - root_module_type: None, - init, - state: LoadState::Init, - module_map_rc: module_map_rc.clone(), - loader, - pending: FuturesUnordered::new(), - visited: HashSet::new(), - }; - // FIXME(bartlomieju): this seems fishy - // Ignore the error here, let it be hit in `Stream::poll_next()`. - if let Ok(root_specifier) = load.resolve_root() { - if let Some(module_id) = module_map_rc - .borrow() - .get_id(root_specifier, asserted_module_type) - { - load.root_module_id = Some(module_id); - load.root_asserted_module_type = Some(asserted_module_type); - load.root_module_type = Some( - module_map_rc - .borrow() - .get_info_by_id(module_id) - .unwrap() - .module_type, - ); - } - } - load - } - - fn resolve_root(&self) -> Result { - match self.init { - LoadInit::Main(ref specifier) => { - self - .loader - .resolve(specifier, ".", ResolutionKind::MainModule) - } - LoadInit::Side(ref specifier) => { - self.loader.resolve(specifier, ".", ResolutionKind::Import) - } - LoadInit::DynamicImport(ref specifier, ref referrer, _) => self - .loader - .resolve(specifier, referrer, ResolutionKind::DynamicImport), - } - } - - async fn prepare(&self) -> Result<(), Error> { - let (module_specifier, maybe_referrer) = match self.init { - LoadInit::Main(ref specifier) => { - let spec = - self - .loader - .resolve(specifier, ".", ResolutionKind::MainModule)?; - (spec, None) - } - LoadInit::Side(ref specifier) => { - let spec = - self - .loader - .resolve(specifier, ".", ResolutionKind::Import)?; - (spec, None) - } - LoadInit::DynamicImport(ref specifier, ref referrer, _) => { - let spec = self.loader.resolve( - specifier, - referrer, - ResolutionKind::DynamicImport, - )?; - (spec, Some(referrer.to_string())) - } - }; - - self - .loader - .prepare_load(&module_specifier, maybe_referrer, self.is_dynamic_import()) - .await - } - - fn is_currently_loading_main_module(&self) -> bool { - !self.is_dynamic_import() - && matches!(self.init, LoadInit::Main(..)) - && self.state == LoadState::LoadingRoot - } - - fn is_dynamic_import(&self) -> bool { - matches!(self.init, LoadInit::DynamicImport(..)) - } - - pub(crate) fn register_and_recurse( - &mut self, - scope: &mut v8::HandleScope, - module_request: &ModuleRequest, - module_source: ModuleSource, - ) -> Result<(), ModuleError> { - let expected_asserted_module_type = module_source.module_type.into(); - let module_url_found = module_source.module_url_found; - let module_url_specified = module_source.module_url_specified; - - if module_request.asserted_module_type != expected_asserted_module_type { - return Err(ModuleError::Other(generic_error(format!( - "Expected a \"{}\" module but loaded a \"{}\" module.", - module_request.asserted_module_type, module_source.module_type, - )))); - } - - // Register the module in the module map unless it's already there. If the - // specified URL and the "true" URL are different, register the alias. - let module_url_found = if let Some(module_url_found) = module_url_found { - let (module_url_found1, module_url_found2) = - module_url_found.into_cheap_copy(); - self.module_map_rc.borrow_mut().alias( - module_url_specified, - expected_asserted_module_type, - module_url_found1, - ); - module_url_found2 - } else { - module_url_specified - }; - - let maybe_module_id = self - .module_map_rc - .borrow() - .get_id(&module_url_found, expected_asserted_module_type); - let module_id = match maybe_module_id { - Some(id) => { - debug!( - "Already-registered module fetched again: {:?}", - module_url_found - ); - id - } - None => match module_source.module_type { - ModuleType::JavaScript => { - self.module_map_rc.borrow_mut().new_es_module( - scope, - self.is_currently_loading_main_module(), - module_url_found, - module_source.code, - self.is_dynamic_import(), - )? - } - ModuleType::Json => self.module_map_rc.borrow_mut().new_json_module( - scope, - module_url_found, - module_source.code, - )?, - }, - }; - - // Recurse the module's imports. There are two cases for each import: - // 1. If the module is not in the module map, start a new load for it in - // `self.pending`. The result of that load should eventually be passed to - // this function for recursion. - // 2. If the module is already in the module map, queue it up to be - // recursed synchronously here. - // This robustly ensures that the whole graph is in the module map before - // `LoadState::Done` is set. - let mut already_registered = VecDeque::new(); - already_registered.push_back((module_id, module_request.clone())); - self.visited.insert(module_request.clone()); - while let Some((module_id, module_request)) = already_registered.pop_front() - { - let referrer = ModuleSpecifier::parse(&module_request.specifier).unwrap(); - let imports = self - .module_map_rc - .borrow() - .get_requested_modules(module_id) - .unwrap() - .clone(); - for module_request in imports { - if !self.visited.contains(&module_request) { - if let Some(module_id) = self.module_map_rc.borrow().get_id( - module_request.specifier.as_str(), - module_request.asserted_module_type, - ) { - already_registered.push_back((module_id, module_request.clone())); - } else { - let request = module_request.clone(); - let specifier = - ModuleSpecifier::parse(&module_request.specifier).unwrap(); - let referrer = referrer.clone(); - let loader = self.loader.clone(); - let is_dynamic_import = self.is_dynamic_import(); - let fut = async move { - let load_result = loader - .load(&specifier, Some(&referrer), is_dynamic_import) - .await; - load_result.map(|s| (request, s)) - }; - self.pending.push(fut.boxed_local()); - } - self.visited.insert(module_request); - } - } - } - - // Update `self.state` however applicable. - if self.state == LoadState::LoadingRoot { - self.root_module_id = Some(module_id); - self.root_asserted_module_type = Some(module_source.module_type.into()); - self.state = LoadState::LoadingImports; - } - if self.pending.is_empty() { - self.state = LoadState::Done; - } - - Ok(()) - } -} - -impl Stream for RecursiveModuleLoad { - type Item = Result<(ModuleRequest, ModuleSource), Error>; - - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context, - ) -> Poll> { - let inner = self.get_mut(); - // IMPORTANT: Do not borrow `inner.module_map_rc` here. It may not be - // available. - match inner.state { - LoadState::Init => { - let module_specifier = match inner.resolve_root() { - Ok(url) => url, - Err(error) => return Poll::Ready(Some(Err(error))), - }; - let load_fut = if let Some(_module_id) = inner.root_module_id { - // FIXME(bartlomieju): this is very bad - // The root module is already in the module map. - // TODO(nayeemrmn): In this case we would ideally skip to - // `LoadState::LoadingImports` and synchronously recurse the imports - // like the bottom of `RecursiveModuleLoad::register_and_recurse()`. - // But the module map cannot be borrowed here. Instead fake a load - // event so it gets passed to that function and recursed eventually. - let asserted_module_type = inner.root_asserted_module_type.unwrap(); - let module_type = inner.root_module_type.unwrap(); - let module_request = ModuleRequest { - specifier: module_specifier.to_string(), - asserted_module_type, - }; - // The code will be discarded, since this module is already in the - // module map. - let module_source = ModuleSource::new( - module_type, - Default::default(), - &module_specifier, - ); - futures::future::ok((module_request, module_source)).boxed() - } else { - let maybe_referrer = match inner.init { - LoadInit::DynamicImport(_, ref referrer, _) => { - resolve_url(referrer).ok() - } - _ => None, - }; - let asserted_module_type = match inner.init { - LoadInit::DynamicImport(_, _, module_type) => module_type, - _ => AssertedModuleType::JavaScriptOrWasm, - }; - let module_request = ModuleRequest { - specifier: module_specifier.to_string(), - asserted_module_type, - }; - let loader = inner.loader.clone(); - let is_dynamic_import = inner.is_dynamic_import(); - async move { - let result = loader - .load( - &module_specifier, - maybe_referrer.as_ref(), - is_dynamic_import, - ) - .await; - result.map(|s| (module_request, s)) - } - .boxed_local() - }; - inner.pending.push(load_fut); - inner.state = LoadState::LoadingRoot; - inner.try_poll_next_unpin(cx) - } - LoadState::LoadingRoot | LoadState::LoadingImports => { - match inner.pending.try_poll_next_unpin(cx)? { - Poll::Ready(None) => unreachable!(), - Poll::Ready(Some(info)) => Poll::Ready(Some(Ok(info))), - Poll::Pending => Poll::Pending, - } - } - LoadState::Done => Poll::Ready(None), - } - } -} - -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -#[repr(u32)] -pub(crate) enum AssertedModuleType { - JavaScriptOrWasm, - Json, -} - -impl From for AssertedModuleType { - fn from(module_type: ModuleType) -> AssertedModuleType { - match module_type { - ModuleType::JavaScript => AssertedModuleType::JavaScriptOrWasm, - ModuleType::Json => AssertedModuleType::Json, - } - } -} - -impl std::fmt::Display for AssertedModuleType { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Self::JavaScriptOrWasm => write!(f, "JavaScriptOrWasm"), - Self::Json => write!(f, "JSON"), - } - } -} - -/// Describes a request for a module as parsed from the source code. -/// Usually executable (`JavaScriptOrWasm`) is used, except when an -/// import assertions explicitly constrains an import to JSON, in -/// which case this will have a `AssertedModuleType::Json`. -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub(crate) struct ModuleRequest { - pub specifier: String, - pub asserted_module_type: AssertedModuleType, -} - -#[derive(Debug, PartialEq)] -pub(crate) struct ModuleInfo { - #[allow(unused)] - pub id: ModuleId, - // Used in "bindings.rs" for "import.meta.main" property value. - pub main: bool, - pub name: ModuleName, - pub requests: Vec, - pub module_type: ModuleType, -} - -#[derive(Debug)] -pub(crate) enum ModuleError { - Exception(v8::Global), - Other(Error), -} diff --git a/core/modules/tests.rs b/core/modules/tests.rs deleted file mode 100644 index 0eb7ce5149..0000000000 --- a/core/modules/tests.rs +++ /dev/null @@ -1,1252 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use crate::ascii_str; -use crate::resolve_import; -use crate::runtime::JsRuntime; -use crate::runtime::JsRuntimeForSnapshot; -use crate::RuntimeOptions; -use crate::Snapshot; -use deno_ops::op; -use futures::future::poll_fn; -use futures::future::FutureExt; -use parking_lot::Mutex; -use std::fmt; -use std::future::Future; -use std::io; -use std::path::PathBuf; -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering; -use std::sync::Arc; - -use super::*; - -// deno_ops macros generate code assuming deno_core in scope. -mod deno_core { - pub use crate::*; -} - -#[derive(Default)] -struct MockLoader { - pub loads: Arc>>, -} - -impl MockLoader { - fn new() -> Rc { - Default::default() - } -} - -fn mock_source_code(url: &str) -> Option<(&'static str, &'static str)> { - const A_SRC: &str = r#" -import { b } from "/b.js"; -import { c } from "/c.js"; -if (b() != 'b') throw Error(); -if (c() != 'c') throw Error(); -if (!import.meta.main) throw Error(); -if (import.meta.url != 'file:///a.js') throw Error(); -"#; - - const B_SRC: &str = r#" -import { c } from "/c.js"; -if (c() != 'c') throw Error(); -export function b() { return 'b'; } -if (import.meta.main) throw Error(); -if (import.meta.url != 'file:///b.js') throw Error(); -"#; - - const C_SRC: &str = r#" -import { d } from "/d.js"; -export function c() { return 'c'; } -if (d() != 'd') throw Error(); -if (import.meta.main) throw Error(); -if (import.meta.url != 'file:///c.js') throw Error(); -"#; - - const D_SRC: &str = r#" -export function d() { return 'd'; } -if (import.meta.main) throw Error(); -if (import.meta.url != 'file:///d.js') throw Error(); -"#; - - const CIRCULAR1_SRC: &str = r#" -import "/circular2.js"; -Deno.core.print("circular1"); -"#; - - const CIRCULAR2_SRC: &str = r#" -import "/circular3.js"; -Deno.core.print("circular2"); -"#; - - const CIRCULAR3_SRC: &str = r#" -import "/circular1.js"; -import "/circular2.js"; -Deno.core.print("circular3"); -"#; - - const REDIRECT1_SRC: &str = r#" -import "./redirect2.js"; -Deno.core.print("redirect1"); -"#; - - const REDIRECT2_SRC: &str = r#" -import "./redirect3.js"; -Deno.core.print("redirect2"); -"#; - - const REDIRECT3_SRC: &str = r#"Deno.core.print("redirect3");"#; - - const MAIN_SRC: &str = r#" -// never_ready.js never loads. -import "/never_ready.js"; -// slow.js resolves after one tick. -import "/slow.js"; -"#; - - const SLOW_SRC: &str = r#" -// Circular import of never_ready.js -// Does this trigger two ModuleLoader calls? It shouldn't. -import "/never_ready.js"; -import "/a.js"; -"#; - - const BAD_IMPORT_SRC: &str = r#"import "foo";"#; - - // (code, real_module_name) - let spec: Vec<&str> = url.split("file://").collect(); - match spec[1] { - "/a.js" => Some((A_SRC, "file:///a.js")), - "/b.js" => Some((B_SRC, "file:///b.js")), - "/c.js" => Some((C_SRC, "file:///c.js")), - "/d.js" => Some((D_SRC, "file:///d.js")), - "/circular1.js" => Some((CIRCULAR1_SRC, "file:///circular1.js")), - "/circular2.js" => Some((CIRCULAR2_SRC, "file:///circular2.js")), - "/circular3.js" => Some((CIRCULAR3_SRC, "file:///circular3.js")), - "/redirect1.js" => Some((REDIRECT1_SRC, "file:///redirect1.js")), - // pretend redirect - real module name is different than one requested - "/redirect2.js" => Some((REDIRECT2_SRC, "file:///dir/redirect2.js")), - "/dir/redirect3.js" => Some((REDIRECT3_SRC, "file:///redirect3.js")), - "/slow.js" => Some((SLOW_SRC, "file:///slow.js")), - "/never_ready.js" => { - Some(("should never be Ready", "file:///never_ready.js")) - } - "/main.js" => Some((MAIN_SRC, "file:///main.js")), - "/bad_import.js" => Some((BAD_IMPORT_SRC, "file:///bad_import.js")), - // deliberately empty code. - "/main_with_code.js" => Some(("", "file:///main_with_code.js")), - _ => None, - } -} - -#[derive(Debug, PartialEq)] -enum MockError { - ResolveErr, - LoadErr, -} - -impl fmt::Display for MockError { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { - unimplemented!() - } -} - -impl std::error::Error for MockError { - fn cause(&self) -> Option<&dyn std::error::Error> { - unimplemented!() - } -} - -struct DelayedSourceCodeFuture { - url: String, - counter: u32, -} - -impl Future for DelayedSourceCodeFuture { - type Output = Result; - - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - let inner = self.get_mut(); - inner.counter += 1; - if inner.url == "file:///never_ready.js" { - return Poll::Pending; - } - if inner.url == "file:///slow.js" && inner.counter < 2 { - // TODO(ry) Hopefully in the future we can remove current task - // notification. - cx.waker().wake_by_ref(); - return Poll::Pending; - } - match mock_source_code(&inner.url) { - Some(src) => Poll::Ready(Ok(ModuleSource::for_test_with_redirect( - src.0, - inner.url.as_str(), - src.1, - ))), - None => Poll::Ready(Err(MockError::LoadErr.into())), - } - } -} - -impl ModuleLoader for MockLoader { - fn resolve( - &self, - specifier: &str, - referrer: &str, - _kind: ResolutionKind, - ) -> Result { - let referrer = if referrer == "." { - "file:///" - } else { - referrer - }; - - let output_specifier = match resolve_import(specifier, referrer) { - Ok(specifier) => specifier, - Err(..) => return Err(MockError::ResolveErr.into()), - }; - - if mock_source_code(output_specifier.as_ref()).is_some() { - Ok(output_specifier) - } else { - Err(MockError::ResolveErr.into()) - } - } - - fn load( - &self, - module_specifier: &ModuleSpecifier, - _maybe_referrer: Option<&ModuleSpecifier>, - _is_dyn_import: bool, - ) -> Pin> { - let mut loads = self.loads.lock(); - loads.push(module_specifier.to_string()); - let url = module_specifier.to_string(); - DelayedSourceCodeFuture { url, counter: 0 }.boxed() - } -} - -#[test] -fn test_recursive_load() { - let loader = MockLoader::new(); - let loads = loader.loads.clone(); - let mut runtime = JsRuntime::new(RuntimeOptions { - module_loader: Some(loader), - ..Default::default() - }); - let spec = resolve_url("file:///a.js").unwrap(); - let a_id_fut = runtime.load_main_module(&spec, None); - let a_id = futures::executor::block_on(a_id_fut).unwrap(); - - #[allow(clippy::let_underscore_future)] - let _ = runtime.mod_evaluate(a_id); - futures::executor::block_on(runtime.run_event_loop(false)).unwrap(); - let l = loads.lock(); - assert_eq!( - l.to_vec(), - vec![ - "file:///a.js", - "file:///b.js", - "file:///c.js", - "file:///d.js" - ] - ); - - let module_map_rc = runtime.module_map(); - let modules = module_map_rc.borrow(); - - assert_eq!( - modules.get_id("file:///a.js", AssertedModuleType::JavaScriptOrWasm), - Some(a_id) - ); - let b_id = modules - .get_id("file:///b.js", AssertedModuleType::JavaScriptOrWasm) - .unwrap(); - let c_id = modules - .get_id("file:///c.js", AssertedModuleType::JavaScriptOrWasm) - .unwrap(); - let d_id = modules - .get_id("file:///d.js", AssertedModuleType::JavaScriptOrWasm) - .unwrap(); - assert_eq!( - modules.get_requested_modules(a_id), - Some(&vec![ - ModuleRequest { - specifier: "file:///b.js".to_string(), - asserted_module_type: AssertedModuleType::JavaScriptOrWasm, - }, - ModuleRequest { - specifier: "file:///c.js".to_string(), - asserted_module_type: AssertedModuleType::JavaScriptOrWasm, - }, - ]) - ); - assert_eq!( - modules.get_requested_modules(b_id), - Some(&vec![ModuleRequest { - specifier: "file:///c.js".to_string(), - asserted_module_type: AssertedModuleType::JavaScriptOrWasm, - },]) - ); - assert_eq!( - modules.get_requested_modules(c_id), - Some(&vec![ModuleRequest { - specifier: "file:///d.js".to_string(), - asserted_module_type: AssertedModuleType::JavaScriptOrWasm, - },]) - ); - assert_eq!(modules.get_requested_modules(d_id), Some(&vec![])); -} - -#[test] -fn test_mods() { - #[derive(Default)] - struct ModsLoader { - pub count: Arc, - } - - impl ModuleLoader for ModsLoader { - fn resolve( - &self, - specifier: &str, - referrer: &str, - _kind: ResolutionKind, - ) -> Result { - self.count.fetch_add(1, Ordering::Relaxed); - assert_eq!(specifier, "./b.js"); - assert_eq!(referrer, "file:///a.js"); - let s = resolve_import(specifier, referrer).unwrap(); - Ok(s) - } - - fn load( - &self, - _module_specifier: &ModuleSpecifier, - _maybe_referrer: Option<&ModuleSpecifier>, - _is_dyn_import: bool, - ) -> Pin> { - unreachable!() - } - } - - let loader = Rc::new(ModsLoader::default()); - - let resolve_count = loader.count.clone(); - static DISPATCH_COUNT: AtomicUsize = AtomicUsize::new(0); - - #[op] - fn op_test(control: u8) -> u8 { - DISPATCH_COUNT.fetch_add(1, Ordering::Relaxed); - assert_eq!(control, 42); - 43 - } - - deno_core::extension!(test_ext, ops = [op_test]); - - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![test_ext::init_ops()], - module_loader: Some(loader), - ..Default::default() - }); - - runtime - .execute_script_static( - "setup.js", - r#" - function assert(cond) { - if (!cond) { - throw Error("assert"); - } - } - "#, - ) - .unwrap(); - - assert_eq!(DISPATCH_COUNT.load(Ordering::Relaxed), 0); - - let module_map_rc = runtime.module_map().clone(); - - let (mod_a, mod_b) = { - let scope = &mut runtime.handle_scope(); - let mut module_map = module_map_rc.borrow_mut(); - let specifier_a = ascii_str!("file:///a.js"); - let mod_a = module_map - .new_es_module( - scope, - true, - specifier_a, - ascii_str!( - r#" - import { b } from './b.js' - if (b() != 'b') throw Error(); - let control = 42; - Deno.core.ops.op_test(control); - "# - ), - false, - ) - .unwrap(); - - assert_eq!(DISPATCH_COUNT.load(Ordering::Relaxed), 0); - let imports = module_map.get_requested_modules(mod_a); - assert_eq!( - imports, - Some(&vec![ModuleRequest { - specifier: "file:///b.js".to_string(), - asserted_module_type: AssertedModuleType::JavaScriptOrWasm, - },]) - ); - - let mod_b = module_map - .new_es_module( - scope, - false, - ascii_str!("file:///b.js"), - ascii_str!("export function b() { return 'b' }"), - false, - ) - .unwrap(); - let imports = module_map.get_requested_modules(mod_b).unwrap(); - assert_eq!(imports.len(), 0); - (mod_a, mod_b) - }; - - runtime.instantiate_module(mod_b).unwrap(); - assert_eq!(DISPATCH_COUNT.load(Ordering::Relaxed), 0); - assert_eq!(resolve_count.load(Ordering::SeqCst), 1); - - runtime.instantiate_module(mod_a).unwrap(); - assert_eq!(DISPATCH_COUNT.load(Ordering::Relaxed), 0); - - #[allow(clippy::let_underscore_future)] - let _ = runtime.mod_evaluate(mod_a); - assert_eq!(DISPATCH_COUNT.load(Ordering::Relaxed), 1); -} - -#[test] -fn test_json_module() { - #[derive(Default)] - struct ModsLoader { - pub count: Arc, - } - - impl ModuleLoader for ModsLoader { - fn resolve( - &self, - specifier: &str, - referrer: &str, - _kind: ResolutionKind, - ) -> Result { - self.count.fetch_add(1, Ordering::Relaxed); - assert_eq!(specifier, "./b.json"); - assert_eq!(referrer, "file:///a.js"); - let s = resolve_import(specifier, referrer).unwrap(); - Ok(s) - } - - fn load( - &self, - _module_specifier: &ModuleSpecifier, - _maybe_referrer: Option<&ModuleSpecifier>, - _is_dyn_import: bool, - ) -> Pin> { - unreachable!() - } - } - - let loader = Rc::new(ModsLoader::default()); - - let resolve_count = loader.count.clone(); - - let mut runtime = JsRuntime::new(RuntimeOptions { - module_loader: Some(loader), - ..Default::default() - }); - - runtime - .execute_script_static( - "setup.js", - r#" - function assert(cond) { - if (!cond) { - throw Error("assert"); - } - } - "#, - ) - .unwrap(); - - let module_map_rc = runtime.module_map().clone(); - - let (mod_a, mod_b) = { - let scope = &mut runtime.handle_scope(); - let mut module_map = module_map_rc.borrow_mut(); - let specifier_a = ascii_str!("file:///a.js"); - let mod_a = module_map - .new_es_module( - scope, - true, - specifier_a, - ascii_str!( - r#" - import jsonData from './b.json' assert {type: "json"}; - assert(jsonData.a == "b"); - assert(jsonData.c.d == 10); - "# - ), - false, - ) - .unwrap(); - - let imports = module_map.get_requested_modules(mod_a); - assert_eq!( - imports, - Some(&vec![ModuleRequest { - specifier: "file:///b.json".to_string(), - asserted_module_type: AssertedModuleType::Json, - },]) - ); - - let mod_b = module_map - .new_json_module( - scope, - ascii_str!("file:///b.json"), - ascii_str!("{\"a\": \"b\", \"c\": {\"d\": 10}}"), - ) - .unwrap(); - let imports = module_map.get_requested_modules(mod_b).unwrap(); - assert_eq!(imports.len(), 0); - (mod_a, mod_b) - }; - - runtime.instantiate_module(mod_b).unwrap(); - assert_eq!(resolve_count.load(Ordering::SeqCst), 1); - - runtime.instantiate_module(mod_a).unwrap(); - - let receiver = runtime.mod_evaluate(mod_a); - futures::executor::block_on(runtime.run_event_loop(false)).unwrap(); - futures::executor::block_on(receiver).unwrap().unwrap(); -} - -#[tokio::test] -async fn dyn_import_err() { - #[derive(Clone, Default)] - struct DynImportErrLoader { - pub count: Arc, - } - - impl ModuleLoader for DynImportErrLoader { - fn resolve( - &self, - specifier: &str, - referrer: &str, - _kind: ResolutionKind, - ) -> Result { - self.count.fetch_add(1, Ordering::Relaxed); - assert_eq!(specifier, "/foo.js"); - assert_eq!(referrer, "file:///dyn_import2.js"); - let s = resolve_import(specifier, referrer).unwrap(); - Ok(s) - } - - fn load( - &self, - _module_specifier: &ModuleSpecifier, - _maybe_referrer: Option<&ModuleSpecifier>, - _is_dyn_import: bool, - ) -> Pin> { - async { Err(io::Error::from(io::ErrorKind::NotFound).into()) }.boxed() - } - } - - let loader = Rc::new(DynImportErrLoader::default()); - let count = loader.count.clone(); - let mut runtime = JsRuntime::new(RuntimeOptions { - module_loader: Some(loader), - ..Default::default() - }); - - // Test an erroneous dynamic import where the specified module isn't found. - poll_fn(move |cx| { - runtime - .execute_script_static( - "file:///dyn_import2.js", - r#" - (async () => { - await import("/foo.js"); - })(); - "#, - ) - .unwrap(); - - // We should get an error here. - let result = runtime.poll_event_loop(cx, false); - if let Poll::Ready(Ok(_)) = result { - unreachable!(); - } - assert_eq!(count.load(Ordering::Relaxed), 4); - Poll::Ready(()) - }) - .await; -} - -#[derive(Clone, Default)] -struct DynImportOkLoader { - pub prepare_load_count: Arc, - pub resolve_count: Arc, - pub load_count: Arc, -} - -impl ModuleLoader for DynImportOkLoader { - fn resolve( - &self, - specifier: &str, - referrer: &str, - _kind: ResolutionKind, - ) -> Result { - let c = self.resolve_count.fetch_add(1, Ordering::Relaxed); - assert!(c < 7); - assert_eq!(specifier, "./b.js"); - assert_eq!(referrer, "file:///dyn_import3.js"); - let s = resolve_import(specifier, referrer).unwrap(); - Ok(s) - } - - fn load( - &self, - specifier: &ModuleSpecifier, - _maybe_referrer: Option<&ModuleSpecifier>, - _is_dyn_import: bool, - ) -> Pin> { - self.load_count.fetch_add(1, Ordering::Relaxed); - let info = - ModuleSource::for_test("export function b() { return 'b' }", specifier); - async move { Ok(info) }.boxed() - } - - fn prepare_load( - &self, - _module_specifier: &ModuleSpecifier, - _maybe_referrer: Option, - _is_dyn_import: bool, - ) -> Pin>>> { - self.prepare_load_count.fetch_add(1, Ordering::Relaxed); - async { Ok(()) }.boxed_local() - } -} - -#[tokio::test] -async fn dyn_import_ok() { - let loader = Rc::new(DynImportOkLoader::default()); - let prepare_load_count = loader.prepare_load_count.clone(); - let resolve_count = loader.resolve_count.clone(); - let load_count = loader.load_count.clone(); - let mut runtime = JsRuntime::new(RuntimeOptions { - module_loader: Some(loader), - ..Default::default() - }); - poll_fn(move |cx| { - // Dynamically import mod_b - runtime - .execute_script_static( - "file:///dyn_import3.js", - r#" - (async () => { - let mod = await import("./b.js"); - if (mod.b() !== 'b') { - throw Error("bad1"); - } - // And again! - mod = await import("./b.js"); - if (mod.b() !== 'b') { - throw Error("bad2"); - } - })(); - "#, - ) - .unwrap(); - - assert!(matches!( - runtime.poll_event_loop(cx, false), - Poll::Ready(Ok(_)) - )); - assert_eq!(prepare_load_count.load(Ordering::Relaxed), 1); - assert_eq!(resolve_count.load(Ordering::Relaxed), 7); - assert_eq!(load_count.load(Ordering::Relaxed), 1); - assert!(matches!( - runtime.poll_event_loop(cx, false), - Poll::Ready(Ok(_)) - )); - assert_eq!(resolve_count.load(Ordering::Relaxed), 7); - assert_eq!(load_count.load(Ordering::Relaxed), 1); - Poll::Ready(()) - }) - .await; -} - -#[tokio::test] -async fn dyn_import_borrow_mut_error() { - // https://github.com/denoland/deno/issues/6054 - let loader = Rc::new(DynImportOkLoader::default()); - let prepare_load_count = loader.prepare_load_count.clone(); - let mut runtime = JsRuntime::new(RuntimeOptions { - module_loader: Some(loader), - ..Default::default() - }); - - poll_fn(move |cx| { - runtime - .execute_script_static( - "file:///dyn_import3.js", - r#" - (async () => { - let mod = await import("./b.js"); - if (mod.b() !== 'b') { - throw Error("bad"); - } - })(); - "#, - ) - .unwrap(); - // First poll runs `prepare_load` hook. - let _ = runtime.poll_event_loop(cx, false); - assert_eq!(prepare_load_count.load(Ordering::Relaxed), 1); - // Second poll triggers error - let _ = runtime.poll_event_loop(cx, false); - Poll::Ready(()) - }) - .await; -} - -// Regression test for https://github.com/denoland/deno/issues/3736. -#[test] -fn dyn_concurrent_circular_import() { - #[derive(Clone, Default)] - struct DynImportCircularLoader { - pub resolve_count: Arc, - pub load_count: Arc, - } - - impl ModuleLoader for DynImportCircularLoader { - fn resolve( - &self, - specifier: &str, - referrer: &str, - _kind: ResolutionKind, - ) -> Result { - self.resolve_count.fetch_add(1, Ordering::Relaxed); - let s = resolve_import(specifier, referrer).unwrap(); - Ok(s) - } - - fn load( - &self, - specifier: &ModuleSpecifier, - _maybe_referrer: Option<&ModuleSpecifier>, - _is_dyn_import: bool, - ) -> Pin> { - self.load_count.fetch_add(1, Ordering::Relaxed); - let filename = PathBuf::from(specifier.to_string()) - .file_name() - .unwrap() - .to_string_lossy() - .to_string(); - let code = match filename.as_str() { - "a.js" => "import './b.js';", - "b.js" => "import './c.js';\nimport './a.js';", - "c.js" => "import './d.js';", - "d.js" => "// pass", - _ => unreachable!(), - }; - let info = ModuleSource::for_test(code, specifier); - async move { Ok(info) }.boxed() - } - } - - let loader = Rc::new(DynImportCircularLoader::default()); - let mut runtime = JsRuntime::new(RuntimeOptions { - module_loader: Some(loader), - ..Default::default() - }); - - runtime - .execute_script_static( - "file:///entry.js", - "import('./b.js');\nimport('./a.js');", - ) - .unwrap(); - - let result = futures::executor::block_on(runtime.run_event_loop(false)); - assert!(result.is_ok()); -} - -#[test] -fn test_circular_load() { - let loader = MockLoader::new(); - let loads = loader.loads.clone(); - let mut runtime = JsRuntime::new(RuntimeOptions { - module_loader: Some(loader), - ..Default::default() - }); - - let fut = async move { - let spec = resolve_url("file:///circular1.js").unwrap(); - let result = runtime.load_main_module(&spec, None).await; - assert!(result.is_ok()); - let circular1_id = result.unwrap(); - #[allow(clippy::let_underscore_future)] - let _ = runtime.mod_evaluate(circular1_id); - runtime.run_event_loop(false).await.unwrap(); - - let l = loads.lock(); - assert_eq!( - l.to_vec(), - vec![ - "file:///circular1.js", - "file:///circular2.js", - "file:///circular3.js" - ] - ); - - let module_map_rc = runtime.module_map(); - let modules = module_map_rc.borrow(); - - assert_eq!( - modules - .get_id("file:///circular1.js", AssertedModuleType::JavaScriptOrWasm), - Some(circular1_id) - ); - let circular2_id = modules - .get_id("file:///circular2.js", AssertedModuleType::JavaScriptOrWasm) - .unwrap(); - - assert_eq!( - modules.get_requested_modules(circular1_id), - Some(&vec![ModuleRequest { - specifier: "file:///circular2.js".to_string(), - asserted_module_type: AssertedModuleType::JavaScriptOrWasm, - }]) - ); - - assert_eq!( - modules.get_requested_modules(circular2_id), - Some(&vec![ModuleRequest { - specifier: "file:///circular3.js".to_string(), - asserted_module_type: AssertedModuleType::JavaScriptOrWasm, - }]) - ); - - assert!(modules - .get_id("file:///circular3.js", AssertedModuleType::JavaScriptOrWasm) - .is_some()); - let circular3_id = modules - .get_id("file:///circular3.js", AssertedModuleType::JavaScriptOrWasm) - .unwrap(); - assert_eq!( - modules.get_requested_modules(circular3_id), - Some(&vec![ - ModuleRequest { - specifier: "file:///circular1.js".to_string(), - asserted_module_type: AssertedModuleType::JavaScriptOrWasm, - }, - ModuleRequest { - specifier: "file:///circular2.js".to_string(), - asserted_module_type: AssertedModuleType::JavaScriptOrWasm, - } - ]) - ); - } - .boxed_local(); - - futures::executor::block_on(fut); -} - -#[test] -fn test_redirect_load() { - let loader = MockLoader::new(); - let loads = loader.loads.clone(); - let mut runtime = JsRuntime::new(RuntimeOptions { - module_loader: Some(loader), - ..Default::default() - }); - - let fut = async move { - let spec = resolve_url("file:///redirect1.js").unwrap(); - let result = runtime.load_main_module(&spec, None).await; - assert!(result.is_ok()); - let redirect1_id = result.unwrap(); - #[allow(clippy::let_underscore_future)] - let _ = runtime.mod_evaluate(redirect1_id); - runtime.run_event_loop(false).await.unwrap(); - let l = loads.lock(); - assert_eq!( - l.to_vec(), - vec![ - "file:///redirect1.js", - "file:///redirect2.js", - "file:///dir/redirect3.js" - ] - ); - - let module_map_rc = runtime.module_map(); - let modules = module_map_rc.borrow(); - - assert_eq!( - modules - .get_id("file:///redirect1.js", AssertedModuleType::JavaScriptOrWasm), - Some(redirect1_id) - ); - - let redirect2_id = modules - .get_id( - "file:///dir/redirect2.js", - AssertedModuleType::JavaScriptOrWasm, - ) - .unwrap(); - assert!(modules - .is_alias("file:///redirect2.js", AssertedModuleType::JavaScriptOrWasm)); - assert!(!modules.is_alias( - "file:///dir/redirect2.js", - AssertedModuleType::JavaScriptOrWasm - )); - assert_eq!( - modules - .get_id("file:///redirect2.js", AssertedModuleType::JavaScriptOrWasm), - Some(redirect2_id) - ); - - let redirect3_id = modules - .get_id("file:///redirect3.js", AssertedModuleType::JavaScriptOrWasm) - .unwrap(); - assert!(modules.is_alias( - "file:///dir/redirect3.js", - AssertedModuleType::JavaScriptOrWasm - )); - assert!(!modules - .is_alias("file:///redirect3.js", AssertedModuleType::JavaScriptOrWasm)); - assert_eq!( - modules.get_id( - "file:///dir/redirect3.js", - AssertedModuleType::JavaScriptOrWasm - ), - Some(redirect3_id) - ); - } - .boxed_local(); - - futures::executor::block_on(fut); -} - -#[tokio::test] -async fn slow_never_ready_modules() { - let loader = MockLoader::new(); - let loads = loader.loads.clone(); - let mut runtime = JsRuntime::new(RuntimeOptions { - module_loader: Some(loader), - ..Default::default() - }); - - poll_fn(move |cx| { - let spec = resolve_url("file:///main.js").unwrap(); - let mut recursive_load = - runtime.load_main_module(&spec, None).boxed_local(); - - let result = recursive_load.poll_unpin(cx); - assert!(result.is_pending()); - - // TODO(ry) Arguably the first time we poll only the following modules - // should be loaded: - // "file:///main.js", - // "file:///never_ready.js", - // "file:///slow.js" - // But due to current task notification in DelayedSourceCodeFuture they - // all get loaded in a single poll. - - for _ in 0..10 { - let result = recursive_load.poll_unpin(cx); - assert!(result.is_pending()); - let l = loads.lock(); - assert_eq!( - l.to_vec(), - vec![ - "file:///main.js", - "file:///never_ready.js", - "file:///slow.js", - "file:///a.js", - "file:///b.js", - "file:///c.js", - "file:///d.js" - ] - ); - } - Poll::Ready(()) - }) - .await; -} - -#[tokio::test] -async fn loader_disappears_after_error() { - let loader = MockLoader::new(); - let mut runtime = JsRuntime::new(RuntimeOptions { - module_loader: Some(loader), - ..Default::default() - }); - - let spec = resolve_url("file:///bad_import.js").unwrap(); - let result = runtime.load_main_module(&spec, None).await; - let err = result.unwrap_err(); - assert_eq!( - err.downcast_ref::().unwrap(), - &MockError::ResolveErr - ); -} - -#[test] -fn recursive_load_main_with_code() { - const MAIN_WITH_CODE_SRC: FastString = ascii_str!( - r#" -import { b } from "/b.js"; -import { c } from "/c.js"; -if (b() != 'b') throw Error(); -if (c() != 'c') throw Error(); -if (!import.meta.main) throw Error(); -if (import.meta.url != 'file:///main_with_code.js') throw Error(); -"# - ); - - let loader = MockLoader::new(); - let loads = loader.loads.clone(); - let mut runtime = JsRuntime::new(RuntimeOptions { - module_loader: Some(loader), - ..Default::default() - }); - // In default resolution code should be empty. - // Instead we explicitly pass in our own code. - // The behavior should be very similar to /a.js. - let spec = resolve_url("file:///main_with_code.js").unwrap(); - let main_id_fut = runtime - .load_main_module(&spec, Some(MAIN_WITH_CODE_SRC)) - .boxed_local(); - let main_id = futures::executor::block_on(main_id_fut).unwrap(); - - #[allow(clippy::let_underscore_future)] - let _ = runtime.mod_evaluate(main_id); - futures::executor::block_on(runtime.run_event_loop(false)).unwrap(); - - let l = loads.lock(); - assert_eq!( - l.to_vec(), - vec!["file:///b.js", "file:///c.js", "file:///d.js"] - ); - - let module_map_rc = runtime.module_map(); - let modules = module_map_rc.borrow(); - - assert_eq!( - modules.get_id( - "file:///main_with_code.js", - AssertedModuleType::JavaScriptOrWasm - ), - Some(main_id) - ); - let b_id = modules - .get_id("file:///b.js", AssertedModuleType::JavaScriptOrWasm) - .unwrap(); - let c_id = modules - .get_id("file:///c.js", AssertedModuleType::JavaScriptOrWasm) - .unwrap(); - let d_id = modules - .get_id("file:///d.js", AssertedModuleType::JavaScriptOrWasm) - .unwrap(); - - assert_eq!( - modules.get_requested_modules(main_id), - Some(&vec![ - ModuleRequest { - specifier: "file:///b.js".to_string(), - asserted_module_type: AssertedModuleType::JavaScriptOrWasm, - }, - ModuleRequest { - specifier: "file:///c.js".to_string(), - asserted_module_type: AssertedModuleType::JavaScriptOrWasm, - } - ]) - ); - assert_eq!( - modules.get_requested_modules(b_id), - Some(&vec![ModuleRequest { - specifier: "file:///c.js".to_string(), - asserted_module_type: AssertedModuleType::JavaScriptOrWasm, - }]) - ); - assert_eq!( - modules.get_requested_modules(c_id), - Some(&vec![ModuleRequest { - specifier: "file:///d.js".to_string(), - asserted_module_type: AssertedModuleType::JavaScriptOrWasm, - }]) - ); - assert_eq!(modules.get_requested_modules(d_id), Some(&vec![])); -} - -#[test] -fn main_and_side_module() { - struct ModsLoader {} - - let main_specifier = resolve_url("file:///main_module.js").unwrap(); - let side_specifier = resolve_url("file:///side_module.js").unwrap(); - - impl ModuleLoader for ModsLoader { - fn resolve( - &self, - specifier: &str, - referrer: &str, - _kind: ResolutionKind, - ) -> Result { - let s = resolve_import(specifier, referrer).unwrap(); - Ok(s) - } - - fn load( - &self, - module_specifier: &ModuleSpecifier, - _maybe_referrer: Option<&ModuleSpecifier>, - _is_dyn_import: bool, - ) -> Pin> { - let module_source = match module_specifier.as_str() { - "file:///main_module.js" => ModuleSource::for_test( - "if (!import.meta.main) throw Error();", - "file:///main_module.js", - ), - "file:///side_module.js" => ModuleSource::for_test( - "if (import.meta.main) throw Error();", - "file:///side_module.js", - ), - _ => unreachable!(), - }; - async move { Ok(module_source) }.boxed() - } - } - - let loader = Rc::new(ModsLoader {}); - let mut runtime = JsRuntime::new(RuntimeOptions { - module_loader: Some(loader), - ..Default::default() - }); - - let main_id_fut = runtime - .load_main_module(&main_specifier, None) - .boxed_local(); - let main_id = futures::executor::block_on(main_id_fut).unwrap(); - - #[allow(clippy::let_underscore_future)] - let _ = runtime.mod_evaluate(main_id); - futures::executor::block_on(runtime.run_event_loop(false)).unwrap(); - - // Try to add another main module - it should error. - let side_id_fut = runtime - .load_main_module(&side_specifier, None) - .boxed_local(); - futures::executor::block_on(side_id_fut).unwrap_err(); - - // And now try to load it as a side module - let side_id_fut = runtime - .load_side_module(&side_specifier, None) - .boxed_local(); - let side_id = futures::executor::block_on(side_id_fut).unwrap(); - - #[allow(clippy::let_underscore_future)] - let _ = runtime.mod_evaluate(side_id); - futures::executor::block_on(runtime.run_event_loop(false)).unwrap(); -} - -#[test] -fn dynamic_imports_snapshot() { - //TODO: Once the issue with the ModuleNamespaceEntryGetter is fixed, we can maintain a reference to the module - // and use it when loading the snapshot - let snapshot = { - const MAIN_WITH_CODE_SRC: FastString = ascii_str!( - r#" - await import("./b.js"); - "# - ); - - let loader = MockLoader::new(); - let mut runtime = JsRuntimeForSnapshot::new( - RuntimeOptions { - module_loader: Some(loader), - ..Default::default() - }, - Default::default(), - ); - // In default resolution code should be empty. - // Instead we explicitly pass in our own code. - // The behavior should be very similar to /a.js. - let spec = resolve_url("file:///main_with_code.js").unwrap(); - let main_id_fut = runtime - .load_main_module(&spec, Some(MAIN_WITH_CODE_SRC)) - .boxed_local(); - let main_id = futures::executor::block_on(main_id_fut).unwrap(); - - #[allow(clippy::let_underscore_future)] - let _ = runtime.mod_evaluate(main_id); - futures::executor::block_on(runtime.run_event_loop(false)).unwrap(); - runtime.snapshot() - }; - - let snapshot = Snapshot::JustCreated(snapshot); - let mut runtime2 = JsRuntime::new(RuntimeOptions { - startup_snapshot: Some(snapshot), - ..Default::default() - }); - - //Evaluate the snapshot with an empty function - runtime2.execute_script_static("check.js", "true").unwrap(); -} - -#[test] -fn import_meta_snapshot() { - let snapshot = { - const MAIN_WITH_CODE_SRC: ModuleCode = ascii_str!( - r#" - if (import.meta.url != 'file:///main_with_code.js') throw Error(); - globalThis.meta = import.meta; - globalThis.url = import.meta.url; - "# - ); - - let loader = MockLoader::new(); - let mut runtime = JsRuntimeForSnapshot::new( - RuntimeOptions { - module_loader: Some(loader), - ..Default::default() - }, - Default::default(), - ); - // In default resolution code should be empty. - // Instead we explicitly pass in our own code. - // The behavior should be very similar to /a.js. - let spec = resolve_url("file:///main_with_code.js").unwrap(); - let main_id_fut = runtime - .load_main_module(&spec, Some(MAIN_WITH_CODE_SRC)) - .boxed_local(); - let main_id = futures::executor::block_on(main_id_fut).unwrap(); - - #[allow(clippy::let_underscore_future)] - let _ = runtime.mod_evaluate(main_id); - futures::executor::block_on(runtime.run_event_loop(false)).unwrap(); - runtime.snapshot() - }; - - let snapshot = Snapshot::JustCreated(snapshot); - let mut runtime2 = JsRuntime::new(RuntimeOptions { - startup_snapshot: Some(snapshot), - ..Default::default() - }); - - runtime2 - .execute_script_static( - "check.js", - "if (globalThis.url !== 'file:///main_with_code.js') throw Error('x')", - ) - .unwrap(); -} diff --git a/core/normalize_path.rs b/core/normalize_path.rs deleted file mode 100644 index 43af6fea6f..0000000000 --- a/core/normalize_path.rs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use std::path::Component; -use std::path::Path; -use std::path::PathBuf; - -/// Normalize all intermediate components of the path (ie. remove "./" and "../" components). -/// Similar to `fs::canonicalize()` but doesn't resolve symlinks. -/// -/// Taken from Cargo -/// -#[inline] -pub fn normalize_path>(path: P) -> PathBuf { - let mut components = path.as_ref().components().peekable(); - let mut ret = - if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { - components.next(); - PathBuf::from(c.as_os_str()) - } else { - PathBuf::new() - }; - - for component in components { - match component { - Component::Prefix(..) => unreachable!(), - Component::RootDir => { - ret.push(component.as_os_str()); - } - Component::CurDir => {} - Component::ParentDir => { - ret.pop(); - } - Component::Normal(c) => { - ret.push(c); - } - } - } - ret -} diff --git a/core/ops.rs b/core/ops.rs deleted file mode 100644 index 7d4bc1e7db..0000000000 --- a/core/ops.rs +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use crate::error::AnyError; -use crate::error::GetErrorClassFn; -use crate::gotham_state::GothamState; -use crate::resources::ResourceTable; -use crate::runtime::ContextState; -use crate::runtime::JsRuntimeState; -use crate::OpDecl; -use crate::OpsTracker; -use anyhow::Error; -use futures::task::AtomicWaker; -use futures::Future; -use pin_project::pin_project; -use serde::Serialize; -use std::cell::RefCell; -use std::cell::UnsafeCell; -use std::ops::Deref; -use std::ops::DerefMut; -use std::ptr::NonNull; -use std::rc::Rc; -use std::rc::Weak; -use std::sync::Arc; -use v8::fast_api::CFunctionInfo; -use v8::fast_api::CTypeInfo; -use v8::fast_api::Int64Representation; - -pub type PromiseId = i32; -pub type OpId = u16; - -#[pin_project] -pub struct OpCall> { - promise_id: PromiseId, - op_id: OpId, - /// Future is not necessarily Unpin, so we need to pin_project. - #[pin] - fut: F, -} - -impl> OpCall { - /// Wraps a future; the inner future is polled the usual way (lazily). - pub fn new(op_ctx: &OpCtx, promise_id: PromiseId, fut: F) -> Self { - Self { - op_id: op_ctx.id, - promise_id, - fut, - } - } -} - -impl> Future for OpCall { - type Output = (PromiseId, OpId, OpResult); - - fn poll( - self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - let promise_id = self.promise_id; - let op_id = self.op_id; - let fut = self.project().fut; - fut.poll(cx).map(move |res| (promise_id, op_id, res)) - } -} - -pub enum OpResult { - Ok(serde_v8::SerializablePkg), - Err(OpError), -} - -impl OpResult { - pub fn to_v8<'a>( - &mut self, - scope: &mut v8::HandleScope<'a>, - ) -> Result, serde_v8::Error> { - match self { - Self::Ok(x) => x.to_v8(scope), - Self::Err(err) => serde_v8::to_v8(scope, err), - } - } -} - -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct OpError { - #[serde(rename = "$err_class_name")] - class_name: &'static str, - message: String, - code: Option<&'static str>, -} - -impl OpError { - pub fn new(get_class: GetErrorClassFn, err: Error) -> Self { - Self { - class_name: (get_class)(&err), - message: format!("{err:#}"), - code: crate::error_codes::get_error_code(&err), - } - } -} - -pub fn to_op_result( - get_class: GetErrorClassFn, - result: Result, -) -> OpResult { - match result { - Ok(v) => OpResult::Ok(v.into()), - Err(err) => OpResult::Err(OpError::new(get_class, err)), - } -} - -/// Per-op context. -/// -// Note: We don't worry too much about the size of this struct because it's allocated once per realm, and is -// stored in a contiguous array. -pub struct OpCtx { - pub id: OpId, - pub state: Rc>, - pub decl: Rc, - pub fast_fn_c_info: Option>, - pub runtime_state: Weak>, - pub(crate) context_state: Rc>, - /// If the last fast op failed, stores the error to be picked up by the slow op. - pub(crate) last_fast_error: UnsafeCell>, -} - -impl OpCtx { - pub(crate) fn new( - id: OpId, - context_state: Rc>, - decl: Rc, - state: Rc>, - runtime_state: Weak>, - ) -> Self { - let mut fast_fn_c_info = None; - - if let Some(fast_fn) = &decl.fast_fn { - let args = CTypeInfo::new_from_slice(fast_fn.args); - let ret = CTypeInfo::new(fast_fn.return_type); - - // SAFETY: all arguments are coming from the trait and they have - // static lifetime - let c_fn = unsafe { - CFunctionInfo::new( - args.as_ptr(), - fast_fn.args.len(), - ret.as_ptr(), - // TODO(bartlomieju): in the future we might want to change it - // to use BigInt representation. - Int64Representation::Number, - ) - }; - fast_fn_c_info = Some(c_fn); - } - - OpCtx { - id, - state, - runtime_state, - decl, - context_state, - fast_fn_c_info, - last_fast_error: UnsafeCell::new(None), - } - } - - /// This takes the last error from an [`OpCtx`], assuming that no other code anywhere - /// can hold a `&mut` to the last_fast_error field. - /// - /// # Safety - /// - /// Must only be called from op implementations. - #[inline(always)] - pub unsafe fn unsafely_take_last_error_for_ops_only( - &self, - ) -> Option { - let opt_mut = &mut *self.last_fast_error.get(); - opt_mut.take() - } - - /// This set the last error for an [`OpCtx`], assuming that no other code anywhere - /// can hold a `&mut` to the last_fast_error field. - /// - /// # Safety - /// - /// Must only be called from op implementations. - #[inline(always)] - pub unsafe fn unsafely_set_last_error_for_ops_only(&self, error: AnyError) { - let opt_mut = &mut *self.last_fast_error.get(); - *opt_mut = Some(error); - } -} - -/// Maintains the resources and ops inside a JS runtime. -pub struct OpState { - pub resource_table: ResourceTable, - pub get_error_class_fn: GetErrorClassFn, - pub tracker: OpsTracker, - pub last_fast_op_error: Option, - pub(crate) gotham_state: GothamState, - pub waker: Arc, -} - -impl OpState { - pub fn new(ops_count: usize) -> OpState { - OpState { - resource_table: Default::default(), - get_error_class_fn: &|_| "Error", - gotham_state: Default::default(), - last_fast_op_error: None, - tracker: OpsTracker::new(ops_count), - waker: Arc::new(AtomicWaker::new()), - } - } - - /// Clear all user-provided resources and state. - pub(crate) fn clear(&mut self) { - std::mem::take(&mut self.gotham_state); - std::mem::take(&mut self.resource_table); - } -} - -impl Deref for OpState { - type Target = GothamState; - - fn deref(&self) -> &Self::Target { - &self.gotham_state - } -} - -impl DerefMut for OpState { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.gotham_state - } -} diff --git a/core/ops_builtin.rs b/core/ops_builtin.rs deleted file mode 100644 index eeb753d5a9..0000000000 --- a/core/ops_builtin.rs +++ /dev/null @@ -1,369 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use crate::error::format_file_name; -use crate::error::type_error; -use crate::io::BufMutView; -use crate::io::BufView; -use crate::ops_builtin_v8; -use crate::ops_metrics::OpMetrics; -use crate::resources::ResourceId; -use crate::JsBuffer; -use crate::OpState; -use crate::Resource; -use anyhow::Error; -use deno_ops::op; -use deno_ops::op2; -use serde_v8::ToJsBuffer; -use std::cell::RefCell; -use std::io::stderr; -use std::io::stdout; -use std::io::Write; -use std::rc::Rc; - -crate::extension!( - core, - ops = [ - op_close, - op_try_close, - op_print, - op_resources, - op_wasm_streaming_feed, - op_wasm_streaming_set_url, - op_void_sync, - op_error_async, - op_error_async_deferred, - op_void_async, - op_void_async_deferred, - op_add, - op_add_async, - // TODO(@AaronO): track IO metrics for builtin streams - op_read, - op_read_all, - op_write, - op_read_sync, - op_write_sync, - op_write_all, - op_shutdown, - op_metrics, - op_format_file_name, - op_is_proxy, - op_str_byte_length, - ops_builtin_v8::op_ref_op, - ops_builtin_v8::op_unref_op, - ops_builtin_v8::op_set_promise_reject_callback, - ops_builtin_v8::op_run_microtasks, - ops_builtin_v8::op_has_tick_scheduled, - ops_builtin_v8::op_set_has_tick_scheduled, - ops_builtin_v8::op_eval_context, - ops_builtin_v8::op_queue_microtask, - ops_builtin_v8::op_create_host_object, - ops_builtin_v8::op_encode, - ops_builtin_v8::op_decode, - ops_builtin_v8::op_serialize, - ops_builtin_v8::op_deserialize, - ops_builtin_v8::op_set_promise_hooks, - ops_builtin_v8::op_get_promise_details, - ops_builtin_v8::op_get_proxy_details, - ops_builtin_v8::op_get_non_index_property_names, - ops_builtin_v8::op_get_constructor_name, - ops_builtin_v8::op_memory_usage, - ops_builtin_v8::op_set_wasm_streaming_callback, - ops_builtin_v8::op_abort_wasm_streaming, - ops_builtin_v8::op_destructure_error, - ops_builtin_v8::op_dispatch_exception, - ops_builtin_v8::op_op_names, - ops_builtin_v8::op_apply_source_map, - ops_builtin_v8::op_set_format_exception_callback, - ops_builtin_v8::op_event_loop_has_more_work, - ops_builtin_v8::op_store_pending_promise_rejection, - ops_builtin_v8::op_remove_pending_promise_rejection, - ops_builtin_v8::op_has_pending_promise_rejection, - ops_builtin_v8::op_arraybuffer_was_detached, - ], -); - -/// Return map of resources with id as key -/// and string representation as value. -#[op] -pub fn op_resources(state: &mut OpState) -> Vec<(ResourceId, String)> { - state - .resource_table - .names() - .map(|(rid, name)| (rid, name.to_string())) - .collect() -} - -#[op2(core, fast)] -fn op_add(a: i32, b: i32) -> i32 { - a + b -} - -#[op] -pub async fn op_add_async(a: i32, b: i32) -> i32 { - a + b -} - -#[op(fast)] -pub fn op_void_sync() {} - -#[op] -pub async fn op_void_async() {} - -#[op] -pub async fn op_error_async() -> Result<(), Error> { - Err(Error::msg("error")) -} - -#[op(deferred)] -pub async fn op_error_async_deferred() -> Result<(), Error> { - Err(Error::msg("error")) -} - -#[op(deferred)] -pub async fn op_void_async_deferred() {} - -/// Remove a resource from the resource table. -#[op] -pub fn op_close( - state: &mut OpState, - rid: Option, -) -> Result<(), Error> { - // TODO(@AaronO): drop Option after improving type-strictness balance in - // serde_v8 - let rid = rid.ok_or_else(|| type_error("missing or invalid `rid`"))?; - state.resource_table.close(rid)?; - Ok(()) -} - -/// Try to remove a resource from the resource table. If there is no resource -/// with the specified `rid`, this is a no-op. -#[op] -pub fn op_try_close( - state: &mut OpState, - rid: Option, -) -> Result<(), Error> { - // TODO(@AaronO): drop Option after improving type-strictness balance in - // serde_v8. - let rid = rid.ok_or_else(|| type_error("missing or invalid `rid`"))?; - let _ = state.resource_table.close(rid); - Ok(()) -} - -#[op] -pub fn op_metrics(state: &mut OpState) -> (OpMetrics, Vec) { - let aggregate = state.tracker.aggregate(); - let per_op = state.tracker.per_op(); - (aggregate, per_op) -} - -/// Builtin utility to print to stdout/stderr -#[op] -pub fn op_print(msg: &str, is_err: bool) -> Result<(), Error> { - if is_err { - stderr().write_all(msg.as_bytes())?; - stderr().flush().unwrap(); - } else { - stdout().write_all(msg.as_bytes())?; - stdout().flush().unwrap(); - } - Ok(()) -} - -pub struct WasmStreamingResource(pub(crate) RefCell); - -impl Resource for WasmStreamingResource { - fn close(self: Rc) { - // At this point there are no clones of Rc on the - // resource table, and no one should own a reference outside of the stack. - // Therefore, we can be sure `self` is the only reference. - if let Ok(wsr) = Rc::try_unwrap(self) { - wsr.0.into_inner().finish(); - } else { - panic!("Couldn't consume WasmStreamingResource."); - } - } -} - -/// Feed bytes to WasmStreamingResource. -#[op] -pub fn op_wasm_streaming_feed( - state: &mut OpState, - rid: ResourceId, - bytes: &[u8], -) -> Result<(), Error> { - let wasm_streaming = - state.resource_table.get::(rid)?; - - wasm_streaming.0.borrow_mut().on_bytes_received(bytes); - - Ok(()) -} - -#[op] -pub fn op_wasm_streaming_set_url( - state: &mut OpState, - rid: ResourceId, - url: &str, -) -> Result<(), Error> { - let wasm_streaming = - state.resource_table.get::(rid)?; - - wasm_streaming.0.borrow_mut().set_url(url); - - Ok(()) -} - -#[op] -async fn op_read( - state: Rc>, - rid: ResourceId, - buf: JsBuffer, -) -> Result { - let resource = state.borrow().resource_table.get_any(rid)?; - let view = BufMutView::from(buf); - resource.read_byob(view).await.map(|(n, _)| n as u32) -} - -#[op] -async fn op_read_all( - state: Rc>, - rid: ResourceId, -) -> Result { - let resource = state.borrow().resource_table.get_any(rid)?; - - // The number of bytes we attempt to grow the buffer by each time it fills - // up and we have more data to read. We start at 64 KB. The grow_len is - // doubled if the nread returned from a single read is equal or greater than - // the grow_len. This allows us to reduce allocations for resources that can - // read large chunks of data at a time. - let mut grow_len: usize = 64 * 1024; - - let (min, maybe_max) = resource.size_hint(); - // Try to determine an optimal starting buffer size for this resource based - // on the size hint. - let initial_size = match (min, maybe_max) { - (min, Some(max)) if min == max => min as usize, - (_min, Some(max)) if (max as usize) < grow_len => max as usize, - (min, _) if (min as usize) < grow_len => grow_len, - (min, _) => min as usize, - }; - - let mut buf = BufMutView::new(initial_size); - loop { - // if the buffer does not have much remaining space, we may have to grow it. - if buf.len() < grow_len { - let vec = buf.get_mut_vec(); - match maybe_max { - Some(max) if vec.len() >= max as usize => { - // no need to resize the vec, because the vec is already large enough - // to accommodate the maximum size of the read data. - } - Some(max) if (max as usize) < vec.len() + grow_len => { - // grow the vec to the maximum size of the read data - vec.resize(max as usize, 0); - } - _ => { - // grow the vec by grow_len - vec.resize(vec.len() + grow_len, 0); - } - } - } - let (n, new_buf) = resource.clone().read_byob(buf).await?; - buf = new_buf; - buf.advance_cursor(n); - if n == 0 { - break; - } - if n >= grow_len { - // we managed to read more or equal data than fits in a single grow_len in - // a single go, so let's attempt to read even more next time. this reduces - // allocations for resources that can read large chunks of data at a time. - grow_len *= 2; - } - } - - let nread = buf.reset_cursor(); - let mut vec = buf.unwrap_vec(); - // If the buffer is larger than the amount of data read, shrink it to the - // amount of data read. - if nread < vec.len() { - vec.truncate(nread); - } - - Ok(ToJsBuffer::from(vec)) -} - -#[op] -async fn op_write( - state: Rc>, - rid: ResourceId, - buf: JsBuffer, -) -> Result { - let resource = state.borrow().resource_table.get_any(rid)?; - let view = BufView::from(buf); - let resp = resource.write(view).await?; - Ok(resp.nwritten() as u32) -} - -#[op(fast)] -fn op_read_sync( - state: &mut OpState, - rid: ResourceId, - data: &mut [u8], -) -> Result { - let resource = state.resource_table.get_any(rid)?; - resource.read_byob_sync(data).map(|n| n as u32) -} - -#[op] -fn op_write_sync( - state: &mut OpState, - rid: ResourceId, - data: &[u8], -) -> Result { - let resource = state.resource_table.get_any(rid)?; - let nwritten = resource.write_sync(data)?; - Ok(nwritten as u32) -} - -#[op] -async fn op_write_all( - state: Rc>, - rid: ResourceId, - buf: JsBuffer, -) -> Result<(), Error> { - let resource = state.borrow().resource_table.get_any(rid)?; - let view = BufView::from(buf); - resource.write_all(view).await?; - Ok(()) -} - -#[op] -async fn op_shutdown( - state: Rc>, - rid: ResourceId, -) -> Result<(), Error> { - let resource = state.borrow().resource_table.get_any(rid)?; - resource.shutdown().await -} - -#[op] -fn op_format_file_name(file_name: String) -> String { - format_file_name(&file_name) -} - -#[op(fast)] -fn op_is_proxy(value: serde_v8::Value) -> bool { - value.v8_value.is_proxy() -} - -#[op(v8)] -fn op_str_byte_length( - scope: &mut v8::HandleScope, - value: serde_v8::Value, -) -> u32 { - if let Ok(string) = v8::Local::::try_from(value.v8_value) { - string.utf8_length(scope) as u32 - } else { - 0 - } -} diff --git a/core/ops_builtin_v8.rs b/core/ops_builtin_v8.rs deleted file mode 100644 index 034a3810d6..0000000000 --- a/core/ops_builtin_v8.rs +++ /dev/null @@ -1,939 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use crate::error::custom_error; -use crate::error::is_instance_of_error; -use crate::error::range_error; -use crate::error::type_error; -use crate::error::JsError; -use crate::ops_builtin::WasmStreamingResource; -use crate::resolve_url; -use crate::runtime::script_origin; -use crate::serde_v8::from_v8; -use crate::source_map::apply_source_map; -use crate::JsBuffer; -use crate::JsRealm; -use crate::JsRuntime; -use crate::ToJsBuffer; -use anyhow::Error; -use deno_ops::op; -use serde::Deserialize; -use serde::Serialize; -use std::cell::RefCell; -use std::rc::Rc; -use v8::ValueDeserializerHelper; -use v8::ValueSerializerHelper; - -fn to_v8_fn( - scope: &mut v8::HandleScope, - value: serde_v8::Value, -) -> Result, Error> { - v8::Local::::try_from(value.v8_value) - .map(|cb| v8::Global::new(scope, cb)) - .map_err(|err| type_error(err.to_string())) -} - -#[inline] -fn to_v8_local_fn( - value: serde_v8::Value, -) -> Result, Error> { - v8::Local::::try_from(value.v8_value) - .map_err(|err| type_error(err.to_string())) -} - -#[op(v8)] -fn op_ref_op(scope: &mut v8::HandleScope, promise_id: i32) { - let context_state = JsRealm::state_from_scope(scope); - context_state.borrow_mut().unrefed_ops.remove(&promise_id); -} - -#[op(v8)] -fn op_unref_op(scope: &mut v8::HandleScope, promise_id: i32) { - let context_state = JsRealm::state_from_scope(scope); - context_state.borrow_mut().unrefed_ops.insert(promise_id); -} - -#[op(v8)] -fn op_set_promise_reject_callback<'a>( - scope: &mut v8::HandleScope<'a>, - cb: serde_v8::Value, -) -> Result>, Error> { - let cb = to_v8_fn(scope, cb)?; - let context_state_rc = JsRealm::state_from_scope(scope); - let old = context_state_rc - .borrow_mut() - .js_promise_reject_cb - .replace(Rc::new(cb)); - let old = old.map(|v| v8::Local::new(scope, &*v)); - Ok(old.map(|v| from_v8(scope, v.into()).unwrap())) -} - -#[op(v8)] -fn op_run_microtasks(scope: &mut v8::HandleScope) { - scope.perform_microtask_checkpoint(); -} - -#[op(v8)] -fn op_has_tick_scheduled(scope: &mut v8::HandleScope) -> bool { - let state_rc = JsRuntime::state_from(scope); - let state = state_rc.borrow(); - state.has_tick_scheduled -} - -#[op(v8)] -fn op_set_has_tick_scheduled(scope: &mut v8::HandleScope, v: bool) { - let state_rc = JsRuntime::state_from(scope); - state_rc.borrow_mut().has_tick_scheduled = v; -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct EvalContextError<'s> { - thrown: serde_v8::Value<'s>, - is_native_error: bool, - is_compile_error: bool, -} - -#[derive(Serialize)] -struct EvalContextResult<'s>( - Option>, - Option>, -); - -#[op(v8)] -fn op_eval_context<'a>( - scope: &mut v8::HandleScope<'a>, - source: serde_v8::Value<'a>, - specifier: String, -) -> Result, Error> { - let tc_scope = &mut v8::TryCatch::new(scope); - let source = v8::Local::::try_from(source.v8_value) - .map_err(|_| type_error("Invalid source"))?; - let specifier = resolve_url(&specifier)?.to_string(); - let specifier = v8::String::new(tc_scope, &specifier).unwrap(); - let origin = script_origin(tc_scope, specifier); - - let script = match v8::Script::compile(tc_scope, source, Some(&origin)) { - Some(s) => s, - None => { - assert!(tc_scope.has_caught()); - let exception = tc_scope.exception().unwrap(); - return Ok(EvalContextResult( - None, - Some(EvalContextError { - thrown: exception.into(), - is_native_error: is_instance_of_error(tc_scope, exception), - is_compile_error: true, - }), - )); - } - }; - - match script.run(tc_scope) { - Some(result) => Ok(EvalContextResult(Some(result.into()), None)), - None => { - assert!(tc_scope.has_caught()); - let exception = tc_scope.exception().unwrap(); - Ok(EvalContextResult( - None, - Some(EvalContextError { - thrown: exception.into(), - is_native_error: is_instance_of_error(tc_scope, exception), - is_compile_error: false, - }), - )) - } - } -} - -#[op(v8)] -fn op_queue_microtask( - scope: &mut v8::HandleScope, - cb: serde_v8::Value, -) -> Result<(), Error> { - scope.enqueue_microtask(to_v8_local_fn(cb)?); - Ok(()) -} - -#[op(v8)] -fn op_create_host_object<'a>( - scope: &mut v8::HandleScope<'a>, -) -> serde_v8::Value<'a> { - let template = v8::ObjectTemplate::new(scope); - template.set_internal_field_count(1); - let object = template.new_instance(scope).unwrap(); - from_v8(scope, object.into()).unwrap() -} - -#[op(v8)] -fn op_encode<'a>( - scope: &mut v8::HandleScope<'a>, - text: serde_v8::Value<'a>, -) -> Result, Error> { - let text = v8::Local::::try_from(text.v8_value) - .map_err(|_| type_error("Invalid argument"))?; - let text_str = serde_v8::to_utf8(text, scope); - let bytes = text_str.into_bytes(); - let len = bytes.len(); - let backing_store = - v8::ArrayBuffer::new_backing_store_from_vec(bytes).make_shared(); - let buffer = v8::ArrayBuffer::with_backing_store(scope, &backing_store); - let u8array = v8::Uint8Array::new(scope, buffer, 0, len).unwrap(); - Ok((from_v8(scope, u8array.into()))?) -} - -#[op(v8)] -fn op_decode<'a>( - scope: &mut v8::HandleScope<'a>, - zero_copy: &[u8], -) -> Result, Error> { - let buf = &zero_copy; - - // Strip BOM - let buf = - if buf.len() >= 3 && buf[0] == 0xef && buf[1] == 0xbb && buf[2] == 0xbf { - &buf[3..] - } else { - buf - }; - - // If `String::new_from_utf8()` returns `None`, this means that the - // length of the decoded string would be longer than what V8 can - // handle. In this case we return `RangeError`. - // - // For more details see: - // - https://encoding.spec.whatwg.org/#dom-textdecoder-decode - // - https://github.com/denoland/deno/issues/6649 - // - https://github.com/v8/v8/blob/d68fb4733e39525f9ff0a9222107c02c28096e2a/include/v8.h#L3277-L3278 - match v8::String::new_from_utf8(scope, buf, v8::NewStringType::Normal) { - Some(text) => Ok(from_v8(scope, text.into())?), - None => Err(range_error("string too long")), - } -} - -struct SerializeDeserialize<'a> { - host_objects: Option>, - error_callback: Option>, - for_storage: bool, -} - -impl<'a> v8::ValueSerializerImpl for SerializeDeserialize<'a> { - #[allow(unused_variables)] - fn throw_data_clone_error<'s>( - &mut self, - scope: &mut v8::HandleScope<'s>, - message: v8::Local<'s, v8::String>, - ) { - if let Some(cb) = self.error_callback { - let scope = &mut v8::TryCatch::new(scope); - let undefined = v8::undefined(scope).into(); - cb.call(scope, undefined, &[message.into()]); - if scope.has_caught() || scope.has_terminated() { - scope.rethrow(); - return; - }; - } - let error = v8::Exception::type_error(scope, message); - scope.throw_exception(error); - } - - fn get_shared_array_buffer_id<'s>( - &mut self, - scope: &mut v8::HandleScope<'s>, - shared_array_buffer: v8::Local<'s, v8::SharedArrayBuffer>, - ) -> Option { - if self.for_storage { - return None; - } - let state_rc = JsRuntime::state_from(scope); - let state = state_rc.borrow_mut(); - if let Some(shared_array_buffer_store) = &state.shared_array_buffer_store { - let backing_store = shared_array_buffer.get_backing_store(); - let id = shared_array_buffer_store.insert(backing_store); - Some(id) - } else { - None - } - } - - fn get_wasm_module_transfer_id( - &mut self, - scope: &mut v8::HandleScope<'_>, - module: v8::Local, - ) -> Option { - if self.for_storage { - let message = v8::String::new(scope, "Wasm modules cannot be stored")?; - self.throw_data_clone_error(scope, message); - return None; - } - let state_rc = JsRuntime::state_from(scope); - let state = state_rc.borrow_mut(); - if let Some(compiled_wasm_module_store) = &state.compiled_wasm_module_store - { - let compiled_wasm_module = module.get_compiled_module(); - let id = compiled_wasm_module_store.insert(compiled_wasm_module); - Some(id) - } else { - None - } - } - - fn write_host_object<'s>( - &mut self, - scope: &mut v8::HandleScope<'s>, - object: v8::Local<'s, v8::Object>, - value_serializer: &mut dyn v8::ValueSerializerHelper, - ) -> Option { - if let Some(host_objects) = self.host_objects { - for i in 0..host_objects.length() { - let value = host_objects.get_index(scope, i).unwrap(); - if value == object { - value_serializer.write_uint32(i); - return Some(true); - } - } - } - let message = v8::String::new(scope, "Unsupported object type").unwrap(); - self.throw_data_clone_error(scope, message); - None - } -} - -impl<'a> v8::ValueDeserializerImpl for SerializeDeserialize<'a> { - fn get_shared_array_buffer_from_id<'s>( - &mut self, - scope: &mut v8::HandleScope<'s>, - transfer_id: u32, - ) -> Option> { - if self.for_storage { - return None; - } - let state_rc = JsRuntime::state_from(scope); - let state = state_rc.borrow_mut(); - if let Some(shared_array_buffer_store) = &state.shared_array_buffer_store { - let backing_store = shared_array_buffer_store.take(transfer_id)?; - let shared_array_buffer = - v8::SharedArrayBuffer::with_backing_store(scope, &backing_store); - Some(shared_array_buffer) - } else { - None - } - } - - fn get_wasm_module_from_id<'s>( - &mut self, - scope: &mut v8::HandleScope<'s>, - clone_id: u32, - ) -> Option> { - if self.for_storage { - return None; - } - let state_rc = JsRuntime::state_from(scope); - let state = state_rc.borrow_mut(); - if let Some(compiled_wasm_module_store) = &state.compiled_wasm_module_store - { - let compiled_module = compiled_wasm_module_store.take(clone_id)?; - v8::WasmModuleObject::from_compiled_module(scope, &compiled_module) - } else { - None - } - } - - fn read_host_object<'s>( - &mut self, - scope: &mut v8::HandleScope<'s>, - value_deserializer: &mut dyn v8::ValueDeserializerHelper, - ) -> Option> { - if let Some(host_objects) = self.host_objects { - let mut i = 0; - if !value_deserializer.read_uint32(&mut i) { - return None; - } - let maybe_value = host_objects.get_index(scope, i); - if let Some(value) = maybe_value { - return value.to_object(scope); - } - } - - let message: v8::Local = - v8::String::new(scope, "Failed to deserialize host object").unwrap(); - let error = v8::Exception::error(scope, message); - scope.throw_exception(error); - None - } -} - -#[derive(Default, Deserialize)] -#[serde(rename_all = "camelCase")] -struct SerializeDeserializeOptions<'a> { - host_objects: Option>, - transferred_array_buffers: Option>, - #[serde(default)] - for_storage: bool, -} - -#[op(v8)] -fn op_serialize( - scope: &mut v8::HandleScope, - value: serde_v8::Value, - options: Option, - error_callback: Option, -) -> Result { - let options = options.unwrap_or_default(); - let error_callback = match error_callback { - Some(cb) => Some( - v8::Local::::try_from(cb.v8_value) - .map_err(|_| type_error("Invalid error callback"))?, - ), - None => None, - }; - let host_objects = match options.host_objects { - Some(value) => Some( - v8::Local::::try_from(value.v8_value) - .map_err(|_| type_error("hostObjects not an array"))?, - ), - None => None, - }; - let transferred_array_buffers = match options.transferred_array_buffers { - Some(value) => Some( - v8::Local::::try_from(value.v8_value) - .map_err(|_| type_error("transferredArrayBuffers not an array"))?, - ), - None => None, - }; - - let serialize_deserialize = Box::new(SerializeDeserialize { - host_objects, - error_callback, - for_storage: options.for_storage, - }); - let mut value_serializer = - v8::ValueSerializer::new(scope, serialize_deserialize); - value_serializer.write_header(); - - if let Some(transferred_array_buffers) = transferred_array_buffers { - let state_rc = JsRuntime::state_from(scope); - let state = state_rc.borrow_mut(); - for index in 0..transferred_array_buffers.length() { - let i = v8::Number::new(scope, index as f64).into(); - let buf = transferred_array_buffers.get(scope, i).unwrap(); - let buf = v8::Local::::try_from(buf).map_err(|_| { - type_error("item in transferredArrayBuffers not an ArrayBuffer") - })?; - if let Some(shared_array_buffer_store) = &state.shared_array_buffer_store - { - if !buf.is_detachable() { - return Err(type_error( - "item in transferredArrayBuffers is not transferable", - )); - } - - if buf.was_detached() { - return Err(custom_error( - "DOMExceptionOperationError", - format!("ArrayBuffer at index {index} is already detached"), - )); - } - - let backing_store = buf.get_backing_store(); - buf.detach(None); - let id = shared_array_buffer_store.insert(backing_store); - value_serializer.transfer_array_buffer(id, buf); - let id = v8::Number::new(scope, id as f64).into(); - transferred_array_buffers.set(scope, i, id); - } - } - } - - let scope = &mut v8::TryCatch::new(scope); - let ret = - value_serializer.write_value(scope.get_current_context(), value.v8_value); - if scope.has_caught() || scope.has_terminated() { - scope.rethrow(); - // Dummy value, this result will be discarded because an error was thrown. - Ok(ToJsBuffer::empty()) - } else if let Some(true) = ret { - let vector = value_serializer.release(); - Ok(vector.into()) - } else { - Err(type_error("Failed to serialize response")) - } -} - -#[op(v8)] -fn op_deserialize<'a>( - scope: &mut v8::HandleScope<'a>, - zero_copy: JsBuffer, - options: Option, -) -> Result, Error> { - let options = options.unwrap_or_default(); - let host_objects = match options.host_objects { - Some(value) => Some( - v8::Local::::try_from(value.v8_value) - .map_err(|_| type_error("hostObjects not an array"))?, - ), - None => None, - }; - let transferred_array_buffers = match options.transferred_array_buffers { - Some(value) => Some( - v8::Local::::try_from(value.v8_value) - .map_err(|_| type_error("transferredArrayBuffers not an array"))?, - ), - None => None, - }; - - let serialize_deserialize = Box::new(SerializeDeserialize { - host_objects, - error_callback: None, - for_storage: options.for_storage, - }); - let mut value_deserializer = - v8::ValueDeserializer::new(scope, serialize_deserialize, &zero_copy); - let parsed_header = value_deserializer - .read_header(scope.get_current_context()) - .unwrap_or_default(); - if !parsed_header { - return Err(range_error("could not deserialize value")); - } - - if let Some(transferred_array_buffers) = transferred_array_buffers { - let state_rc = JsRuntime::state_from(scope); - let state = state_rc.borrow_mut(); - if let Some(shared_array_buffer_store) = &state.shared_array_buffer_store { - for i in 0..transferred_array_buffers.length() { - let i = v8::Number::new(scope, i as f64).into(); - let id_val = transferred_array_buffers.get(scope, i).unwrap(); - let id = match id_val.number_value(scope) { - Some(id) => id as u32, - None => { - return Err(type_error( - "item in transferredArrayBuffers not number", - )) - } - }; - if let Some(backing_store) = shared_array_buffer_store.take(id) { - let array_buffer = - v8::ArrayBuffer::with_backing_store(scope, &backing_store); - value_deserializer.transfer_array_buffer(id, array_buffer); - transferred_array_buffers.set(scope, id_val, array_buffer.into()); - } else { - return Err(type_error( - "transferred array buffer not present in shared_array_buffer_store", - )); - } - } - } - } - - let value = value_deserializer.read_value(scope.get_current_context()); - match value { - Some(deserialized) => Ok(deserialized.into()), - None => Err(range_error("could not deserialize value")), - } -} - -#[derive(Serialize)] -struct PromiseDetails<'s>(u32, Option>); - -#[op(v8)] -fn op_get_promise_details<'a>( - scope: &mut v8::HandleScope<'a>, - promise: serde_v8::Value<'a>, -) -> Result, Error> { - let promise = v8::Local::::try_from(promise.v8_value) - .map_err(|_| type_error("Invalid argument"))?; - match promise.state() { - v8::PromiseState::Pending => Ok(PromiseDetails(0, None)), - v8::PromiseState::Fulfilled => { - Ok(PromiseDetails(1, Some(promise.result(scope).into()))) - } - v8::PromiseState::Rejected => { - Ok(PromiseDetails(2, Some(promise.result(scope).into()))) - } - } -} - -#[op(v8)] -fn op_set_promise_hooks( - scope: &mut v8::HandleScope, - init_hook: serde_v8::Value, - before_hook: serde_v8::Value, - after_hook: serde_v8::Value, - resolve_hook: serde_v8::Value, -) -> Result<(), Error> { - let v8_fns = [init_hook, before_hook, after_hook, resolve_hook] - .into_iter() - .enumerate() - .filter(|(_, hook)| !hook.v8_value.is_undefined()) - .try_fold([None; 4], |mut v8_fns, (i, hook)| { - let v8_fn = v8::Local::::try_from(hook.v8_value) - .map_err(|err| type_error(err.to_string()))?; - v8_fns[i] = Some(v8_fn); - Ok::<_, Error>(v8_fns) - })?; - - scope.set_promise_hooks( - v8_fns[0], // init - v8_fns[1], // before - v8_fns[2], // after - v8_fns[3], // resolve - ); - - Ok(()) -} - -// Based on https://github.com/nodejs/node/blob/1e470510ff74391d7d4ec382909ea8960d2d2fbc/src/node_util.cc -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. -#[op(v8)] -fn op_get_proxy_details<'a>( - scope: &mut v8::HandleScope<'a>, - proxy: serde_v8::Value<'a>, -) -> Option<(serde_v8::Value<'a>, serde_v8::Value<'a>)> { - let proxy = match v8::Local::::try_from(proxy.v8_value) { - Ok(proxy) => proxy, - Err(_) => return None, - }; - let target = proxy.get_target(scope); - let handler = proxy.get_handler(scope); - Some((target.into(), handler.into())) -} - -#[op(v8)] -fn op_get_non_index_property_names<'a>( - scope: &mut v8::HandleScope<'a>, - obj: serde_v8::Value<'a>, - filter: u32, -) -> Option> { - let obj = match v8::Local::::try_from(obj.v8_value) { - Ok(proxy) => proxy, - Err(_) => return None, - }; - - let mut property_filter = v8::PropertyFilter::ALL_PROPERTIES; - if filter & 1 == 1 { - property_filter = property_filter | v8::PropertyFilter::ONLY_WRITABLE - } - if filter & 2 == 2 { - property_filter = property_filter | v8::PropertyFilter::ONLY_ENUMERABLE - } - if filter & 4 == 4 { - property_filter = property_filter | v8::PropertyFilter::ONLY_CONFIGURABLE - } - if filter & 8 == 8 { - property_filter = property_filter | v8::PropertyFilter::SKIP_STRINGS - } - if filter & 16 == 16 { - property_filter = property_filter | v8::PropertyFilter::SKIP_SYMBOLS - } - - let maybe_names = obj.get_property_names( - scope, - v8::GetPropertyNamesArgs { - mode: v8::KeyCollectionMode::OwnOnly, - property_filter, - index_filter: v8::IndexFilter::SkipIndices, - ..Default::default() - }, - ); - - if let Some(names) = maybe_names { - let names_val: v8::Local = names.into(); - Some(names_val.into()) - } else { - None - } -} - -#[op(v8)] -fn op_get_constructor_name<'a>( - scope: &mut v8::HandleScope<'a>, - obj: serde_v8::Value<'a>, -) -> Option { - let obj = match v8::Local::::try_from(obj.v8_value) { - Ok(proxy) => proxy, - Err(_) => return None, - }; - - let name = obj.get_constructor_name().to_rust_string_lossy(scope); - Some(name) -} - -// HeapStats stores values from a isolate.get_heap_statistics() call -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct MemoryUsage { - physical_total: usize, - heap_total: usize, - heap_used: usize, - external: usize, - // TODO: track ArrayBuffers, would require using a custom allocator to track - // but it's otherwise a subset of external so can be indirectly tracked - // array_buffers: usize, -} - -#[op(v8)] -fn op_memory_usage(scope: &mut v8::HandleScope) -> MemoryUsage { - let mut s = v8::HeapStatistics::default(); - scope.get_heap_statistics(&mut s); - MemoryUsage { - physical_total: s.total_physical_size(), - heap_total: s.total_heap_size(), - heap_used: s.used_heap_size(), - external: s.external_memory(), - } -} - -#[op(v8)] -fn op_set_wasm_streaming_callback( - scope: &mut v8::HandleScope, - cb: serde_v8::Value, -) -> Result<(), Error> { - let cb = to_v8_fn(scope, cb)?; - let context_state_rc = JsRealm::state_from_scope(scope); - let mut context_state = context_state_rc.borrow_mut(); - // The callback to pass to the v8 API has to be a unit type, so it can't - // borrow or move any local variables. Therefore, we're storing the JS - // callback in a JsRuntimeState slot. - if context_state.js_wasm_streaming_cb.is_some() { - return Err(type_error("op_set_wasm_streaming_callback already called")); - } - context_state.js_wasm_streaming_cb = Some(Rc::new(cb)); - - scope.set_wasm_streaming_callback(|scope, arg, wasm_streaming| { - let (cb_handle, streaming_rid) = { - let context_state_rc = JsRealm::state_from_scope(scope); - let cb_handle = context_state_rc - .borrow() - .js_wasm_streaming_cb - .as_ref() - .unwrap() - .clone(); - let state_rc = JsRuntime::state_from(scope); - let streaming_rid = state_rc - .borrow() - .op_state - .borrow_mut() - .resource_table - .add(WasmStreamingResource(RefCell::new(wasm_streaming))); - (cb_handle, streaming_rid) - }; - - let undefined = v8::undefined(scope); - let rid = serde_v8::to_v8(scope, streaming_rid).unwrap(); - cb_handle - .open(scope) - .call(scope, undefined.into(), &[arg, rid]); - }); - Ok(()) -} - -#[allow(clippy::let_and_return)] -#[op(v8)] -fn op_abort_wasm_streaming( - scope: &mut v8::HandleScope, - rid: u32, - error: serde_v8::Value, -) -> Result<(), Error> { - let wasm_streaming = { - let state_rc = JsRuntime::state_from(scope); - let state = state_rc.borrow(); - let wsr = state - .op_state - .borrow_mut() - .resource_table - .take::(rid)?; - wsr - }; - - // At this point there are no clones of Rc on the - // resource table, and no one should own a reference because we're never - // cloning them. So we can be sure `wasm_streaming` is the only reference. - if let Ok(wsr) = std::rc::Rc::try_unwrap(wasm_streaming) { - // NOTE: v8::WasmStreaming::abort can't be called while `state` is borrowed; - // see https://github.com/denoland/deno/issues/13917 - wsr.0.into_inner().abort(Some(error.v8_value)); - } else { - panic!("Couldn't consume WasmStreamingResource."); - } - Ok(()) -} - -#[op(v8)] -fn op_destructure_error( - scope: &mut v8::HandleScope, - error: serde_v8::Value, -) -> JsError { - JsError::from_v8_exception(scope, error.v8_value) -} - -/// Effectively throw an uncatchable error. This will terminate runtime -/// execution before any more JS code can run, except in the REPL where it -/// should just output the error to the console. -#[op(v8)] -fn op_dispatch_exception( - scope: &mut v8::HandleScope, - exception: serde_v8::Value, -) { - let state_rc = JsRuntime::state_from(scope); - let mut state = state_rc.borrow_mut(); - if let Some(inspector) = &state.inspector { - let inspector = inspector.borrow(); - inspector.exception_thrown(scope, exception.v8_value, false); - // This indicates that the op is being called from a REPL. Skip termination. - if inspector.is_dispatching_message() { - return; - } - } - state.dispatched_exception = Some(v8::Global::new(scope, exception.v8_value)); - scope.terminate_execution(); -} - -#[op(v8)] -fn op_op_names(scope: &mut v8::HandleScope) -> Vec { - let state_rc = JsRealm::state_from_scope(scope); - let state = state_rc.borrow(); - state - .op_ctxs - .iter() - .map(|o| o.decl.name.to_string()) - .collect() -} - -#[derive(Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -struct Location { - file_name: String, - line_number: u32, - column_number: u32, -} - -#[op(v8)] -fn op_apply_source_map( - scope: &mut v8::HandleScope, - location: Location, -) -> Result { - let state_rc = JsRuntime::state_from(scope); - let (getter, cache) = { - let state = state_rc.borrow(); - ( - state.source_map_getter.clone(), - state.source_map_cache.clone(), - ) - }; - - if let Some(source_map_getter) = getter { - let mut cache = cache.borrow_mut(); - let mut location = location; - let (f, l, c) = apply_source_map( - location.file_name, - location.line_number.into(), - location.column_number.into(), - &mut cache, - &**source_map_getter, - ); - location.file_name = f; - location.line_number = l as u32; - location.column_number = c as u32; - Ok(location) - } else { - Ok(location) - } -} - -/// Set a callback which formats exception messages as stored in -/// `JsError::exception_message`. The callback is passed the error value and -/// should return a string or `null`. If no callback is set or the callback -/// returns `null`, the built-in default formatting will be used. -#[op(v8)] -fn op_set_format_exception_callback<'a>( - scope: &mut v8::HandleScope<'a>, - cb: serde_v8::Value<'a>, -) -> Result>, Error> { - let cb = to_v8_fn(scope, cb)?; - let context_state_rc = JsRealm::state_from_scope(scope); - let old = context_state_rc - .borrow_mut() - .js_format_exception_cb - .replace(Rc::new(cb)); - let old = old.map(|v| v8::Local::new(scope, &*v)); - Ok(old.map(|v| from_v8(scope, v.into()).unwrap())) -} - -#[op(v8)] -fn op_event_loop_has_more_work(scope: &mut v8::HandleScope) -> bool { - JsRuntime::event_loop_pending_state_from_scope(scope).is_pending() -} - -#[op(v8)] -fn op_store_pending_promise_rejection<'a>( - scope: &mut v8::HandleScope<'a>, - promise: serde_v8::Value<'a>, - reason: serde_v8::Value<'a>, -) { - let context_state_rc = JsRealm::state_from_scope(scope); - let mut context_state = context_state_rc.borrow_mut(); - let promise_value = - v8::Local::::try_from(promise.v8_value).unwrap(); - let promise_global = v8::Global::new(scope, promise_value); - let error_global = v8::Global::new(scope, reason.v8_value); - context_state - .pending_promise_rejections - .push_back((promise_global, error_global)); -} - -#[op(v8)] -fn op_remove_pending_promise_rejection<'a>( - scope: &mut v8::HandleScope<'a>, - promise: serde_v8::Value<'a>, -) { - let context_state_rc = JsRealm::state_from_scope(scope); - let mut context_state = context_state_rc.borrow_mut(); - let promise_value = - v8::Local::::try_from(promise.v8_value).unwrap(); - let promise_global = v8::Global::new(scope, promise_value); - context_state - .pending_promise_rejections - .retain(|(key, _)| key != &promise_global); -} - -#[op(v8)] -fn op_has_pending_promise_rejection<'a>( - scope: &mut v8::HandleScope<'a>, - promise: serde_v8::Value<'a>, -) -> bool { - let context_state_rc = JsRealm::state_from_scope(scope); - let context_state = context_state_rc.borrow(); - let promise_value = - v8::Local::::try_from(promise.v8_value).unwrap(); - let promise_global = v8::Global::new(scope, promise_value); - context_state - .pending_promise_rejections - .iter() - .any(|(key, _)| key == &promise_global) -} - -#[op(v8)] -fn op_arraybuffer_was_detached<'a>( - _scope: &mut v8::HandleScope<'a>, - input: serde_v8::Value<'a>, -) -> Result { - let ab = v8::Local::::try_from(input.v8_value)?; - Ok(ab.was_detached()) -} diff --git a/core/ops_metrics.rs b/core/ops_metrics.rs deleted file mode 100644 index b25368bd01..0000000000 --- a/core/ops_metrics.rs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use crate::serde::Serialize; -use crate::OpId; -use std::cell::RefCell; -use std::cell::RefMut; - -// TODO(@AaronO): split into AggregateMetrics & PerOpMetrics -#[derive(Clone, Default, Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct OpMetrics { - pub ops_dispatched: u64, - pub ops_dispatched_sync: u64, - pub ops_dispatched_async: u64, - // TODO(bartlomieju): this field is never updated - pub ops_dispatched_async_unref: u64, - pub ops_completed: u64, - pub ops_completed_sync: u64, - pub ops_completed_async: u64, - // TODO(bartlomieju): this field is never updated - pub ops_completed_async_unref: u64, - pub bytes_sent_control: u64, - pub bytes_sent_data: u64, - pub bytes_received: u64, -} - -// TODO(@AaronO): track errors -#[derive(Default, Debug)] -pub struct OpsTracker { - ops: RefCell>, -} - -impl OpsTracker { - pub fn new(ops_count: usize) -> Self { - Self { - ops: RefCell::new(vec![Default::default(); ops_count]), - } - } - - pub fn per_op(&self) -> Vec { - self.ops.borrow().clone() - } - - pub fn aggregate(&self) -> OpMetrics { - let mut sum = OpMetrics::default(); - - for metrics in self.ops.borrow().iter() { - sum.ops_dispatched += metrics.ops_dispatched; - sum.ops_dispatched_sync += metrics.ops_dispatched_sync; - sum.ops_dispatched_async += metrics.ops_dispatched_async; - sum.ops_dispatched_async_unref += metrics.ops_dispatched_async_unref; - sum.ops_completed += metrics.ops_completed; - sum.ops_completed_sync += metrics.ops_completed_sync; - sum.ops_completed_async += metrics.ops_completed_async; - sum.ops_completed_async_unref += metrics.ops_completed_async_unref; - sum.bytes_sent_control += metrics.bytes_sent_control; - sum.bytes_sent_data += metrics.bytes_sent_data; - sum.bytes_received += metrics.bytes_received; - } - - sum - } - - #[inline] - fn metrics_mut(&self, id: OpId) -> RefMut { - RefMut::map(self.ops.borrow_mut(), |ops| &mut ops[id as usize]) - } - - #[inline] - pub fn track_sync(&self, id: OpId) { - let mut metrics = self.metrics_mut(id); - metrics.ops_dispatched += 1; - metrics.ops_completed += 1; - metrics.ops_dispatched_sync += 1; - metrics.ops_completed_sync += 1; - } - - #[inline] - pub fn track_async(&self, id: OpId) { - let mut metrics = self.metrics_mut(id); - metrics.ops_dispatched += 1; - metrics.ops_dispatched_async += 1; - } - - #[inline] - pub fn track_async_completed(&self, id: OpId) { - let mut metrics = self.metrics_mut(id); - metrics.ops_completed += 1; - metrics.ops_completed_async += 1; - } -} diff --git a/core/path.rs b/core/path.rs deleted file mode 100644 index fd8b1a9b64..0000000000 --- a/core/path.rs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use std::path::PathBuf; - -#[cfg(not(windows))] -#[inline] -pub fn strip_unc_prefix(path: PathBuf) -> PathBuf { - path -} - -/// Strips the unc prefix (ex. \\?\) from Windows paths. -#[cfg(windows)] -pub fn strip_unc_prefix(path: PathBuf) -> PathBuf { - use std::path::Component; - use std::path::Prefix; - - let mut components = path.components(); - match components.next() { - Some(Component::Prefix(prefix)) => { - match prefix.kind() { - // \\?\device - Prefix::Verbatim(device) => { - let mut path = PathBuf::new(); - path.push(format!(r"\\{}\", device.to_string_lossy())); - path.extend(components.filter(|c| !matches!(c, Component::RootDir))); - path - } - // \\?\c:\path - Prefix::VerbatimDisk(_) => { - let mut path = PathBuf::new(); - path.push(prefix.as_os_str().to_string_lossy().replace(r"\\?\", "")); - path.extend(components); - path - } - // \\?\UNC\hostname\share_name\path - Prefix::VerbatimUNC(hostname, share_name) => { - let mut path = PathBuf::new(); - path.push(format!( - r"\\{}\{}\", - hostname.to_string_lossy(), - share_name.to_string_lossy() - )); - path.extend(components.filter(|c| !matches!(c, Component::RootDir))); - path - } - _ => path, - } - } - _ => path, - } -} - -#[cfg(test)] -mod test { - #[cfg(windows)] - #[test] - fn test_strip_unc_prefix() { - use std::path::PathBuf; - - run_test(r"C:\", r"C:\"); - run_test(r"C:\test\file.txt", r"C:\test\file.txt"); - - run_test(r"\\?\C:\", r"C:\"); - run_test(r"\\?\C:\test\file.txt", r"C:\test\file.txt"); - - run_test(r"\\.\C:\", r"\\.\C:\"); - run_test(r"\\.\C:\Test\file.txt", r"\\.\C:\Test\file.txt"); - - run_test(r"\\?\UNC\localhost\", r"\\localhost"); - run_test(r"\\?\UNC\localhost\c$\", r"\\localhost\c$"); - run_test( - r"\\?\UNC\localhost\c$\Windows\file.txt", - r"\\localhost\c$\Windows\file.txt", - ); - run_test(r"\\?\UNC\wsl$\deno.json", r"\\wsl$\deno.json"); - - run_test(r"\\?\server1", r"\\server1"); - run_test(r"\\?\server1\e$\", r"\\server1\e$\"); - run_test( - r"\\?\server1\e$\test\file.txt", - r"\\server1\e$\test\file.txt", - ); - - fn run_test(input: &str, expected: &str) { - assert_eq!( - super::strip_unc_prefix(PathBuf::from(input)), - PathBuf::from(expected) - ); - } - } -} diff --git a/core/resources.rs b/core/resources.rs deleted file mode 100644 index 94d2a2306a..0000000000 --- a/core/resources.rs +++ /dev/null @@ -1,429 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -// Think of Resources as File Descriptors. They are integers that are allocated -// by the privileged side of Deno which refer to various rust objects that need -// to be persisted between various ops. For example, network sockets are -// resources. Resources may or may not correspond to a real operating system -// file descriptor (hence the different name). - -use crate::error::bad_resource_id; -use crate::error::not_supported; -use crate::io::BufMutView; -use crate::io::BufView; -use crate::io::WriteOutcome; -use anyhow::Error; -use futures::Future; -use std::any::type_name; -use std::any::Any; -use std::any::TypeId; -use std::borrow::Cow; -use std::collections::BTreeMap; -use std::iter::Iterator; -use std::pin::Pin; -use std::rc::Rc; - -/// Returned by resource read/write/shutdown methods -pub type AsyncResult = Pin>>>; - -/// Resources are Rust objects that are attached to a [deno_core::JsRuntime]. -/// They are identified in JS by a numeric ID (the resource ID, or rid). -/// Resources can be created in ops. Resources can also be retrieved in ops by -/// their rid. Resources are not thread-safe - they can only be accessed from -/// the thread that the JsRuntime lives on. -/// -/// Resources are reference counted in Rust. This means that they can be -/// cloned and passed around. When the last reference is dropped, the resource -/// is automatically closed. As long as the resource exists in the resource -/// table, the reference count is at least 1. -/// -/// ### Readable -/// -/// Readable resources are resources that can have data read from. Examples of -/// this are files, sockets, or HTTP streams. -/// -/// Readables can be read from from either JS or Rust. In JS one can use -/// `Deno.core.read()` to read from a single chunk of data from a readable. In -/// Rust one can directly call `read()` or `read_byob()`. The Rust side code is -/// used to implement ops like `op_slice`. -/// -/// A distinction can be made between readables that produce chunks of data -/// themselves (they allocate the chunks), and readables that fill up -/// bring-your-own-buffers (BYOBs). The former is often the case for framed -/// protocols like HTTP, while the latter is often the case for kernel backed -/// resources like files and sockets. -/// -/// All readables must implement `read()`. If resources can support an optimized -/// path for BYOBs, they should also implement `read_byob()`. For kernel backed -/// resources it often makes sense to implement `read_byob()` first, and then -/// implement `read()` as an operation that allocates a new chunk with -/// `len == limit`, then calls `read_byob()`, and then returns a chunk sliced to -/// the number of bytes read. Kernel backed resources can use the -/// [deno_core::impl_readable_byob] macro to implement optimized `read_byob()` -/// and `read()` implementations from a single `Self::read()` method. -/// -/// ### Writable -/// -/// Writable resources are resources that can have data written to. Examples of -/// this are files, sockets, or HTTP streams. -/// -/// Writables can be written to from either JS or Rust. In JS one can use -/// `Deno.core.write()` to write to a single chunk of data to a writable. In -/// Rust one can directly call `write()`. The latter is used to implement ops -/// like `op_slice`. -pub trait Resource: Any + 'static { - /// Returns a string representation of the resource which is made available - /// to JavaScript code through `op_resources`. The default implementation - /// returns the Rust type name, but specific resource types may override this - /// trait method. - fn name(&self) -> Cow { - type_name::().into() - } - - /// Read a single chunk of data from the resource. This operation returns a - /// `BufView` that represents the data that was read. If a zero length buffer - /// is returned, it indicates that the resource has reached EOF. - /// - /// If this method is not implemented, the default implementation will error - /// with a "not supported" error. - /// - /// If a readable can provide an optimized path for BYOBs, it should also - /// implement `read_byob()`. - fn read(self: Rc, limit: usize) -> AsyncResult { - _ = limit; - Box::pin(futures::future::err(not_supported())) - } - - /// Read a single chunk of data from the resource into the provided `BufMutView`. - /// - /// This operation returns the number of bytes read. If zero bytes are read, - /// it indicates that the resource has reached EOF. - /// - /// If this method is not implemented explicitly, the default implementation - /// will call `read()` and then copy the data into the provided buffer. For - /// readable resources that can provide an optimized path for BYOBs, it is - /// strongly recommended to override this method. - fn read_byob( - self: Rc, - mut buf: BufMutView, - ) -> AsyncResult<(usize, BufMutView)> { - Box::pin(async move { - let read = self.read(buf.len()).await?; - let nread = read.len(); - buf[..nread].copy_from_slice(&read); - Ok((nread, buf)) - }) - } - - /// Write a single chunk of data to the resource. The operation may not be - /// able to write the entire chunk, in which case it should return the number - /// of bytes written. Additionally it should return the `BufView` that was - /// passed in. - /// - /// If this method is not implemented, the default implementation will error - /// with a "not supported" error. - fn write(self: Rc, buf: BufView) -> AsyncResult { - _ = buf; - Box::pin(futures::future::err(not_supported())) - } - - /// Write an entire chunk of data to the resource. Unlike `write()`, this will - /// ensure the entire chunk is written. If the operation is not able to write - /// the entire chunk, an error is to be returned. - /// - /// By default this method will call `write()` repeatedly until the entire - /// chunk is written. Resources that can write the entire chunk in a single - /// operation using an optimized path should override this method. - fn write_all(self: Rc, view: BufView) -> AsyncResult<()> { - Box::pin(async move { - let mut view = view; - let this = self; - while !view.is_empty() { - let resp = this.clone().write(view).await?; - match resp { - WriteOutcome::Partial { - nwritten, - view: new_view, - } => { - view = new_view; - view.advance_cursor(nwritten); - } - WriteOutcome::Full { .. } => break, - } - } - Ok(()) - }) - } - - /// The same as [`read_byob()`][Resource::read_byob], but synchronous. - fn read_byob_sync(self: Rc, data: &mut [u8]) -> Result { - _ = data; - Err(not_supported()) - } - - /// The same as [`write()`][Resource::write], but synchronous. - fn write_sync(self: Rc, data: &[u8]) -> Result { - _ = data; - Err(not_supported()) - } - - /// The shutdown method can be used to asynchronously close the resource. It - /// is not automatically called when the resource is dropped or closed. - /// - /// If this method is not implemented, the default implementation will error - /// with a "not supported" error. - fn shutdown(self: Rc) -> AsyncResult<()> { - Box::pin(futures::future::err(not_supported())) - } - - /// Resources may implement the `close()` trait method if they need to do - /// resource specific clean-ups, such as cancelling pending futures, after a - /// resource has been removed from the resource table. - fn close(self: Rc) {} - - /// Resources backed by a file descriptor can let ops know to allow for - /// low-level optimizations. - #[cfg(unix)] - fn backing_fd(self: Rc) -> Option { - None - } - - /// Resources backed by a file descriptor can let ops know to allow for - /// low-level optimizations. - #[cfg(windows)] - fn backing_fd(self: Rc) -> Option { - None - } - - fn size_hint(&self) -> (u64, Option) { - (0, None) - } -} - -impl dyn Resource { - #[inline(always)] - fn is(&self) -> bool { - self.type_id() == TypeId::of::() - } - - #[inline(always)] - #[allow(clippy::needless_lifetimes)] - pub fn downcast_rc<'a, T: Resource>(self: &'a Rc) -> Option<&'a Rc> { - if self.is::() { - let ptr = self as *const Rc<_> as *const Rc; - // TODO(piscisaureus): safety comment - #[allow(clippy::undocumented_unsafe_blocks)] - Some(unsafe { &*ptr }) - } else { - None - } - } -} - -/// A `ResourceId` is an integer value referencing a resource. It could be -/// considered to be the Deno equivalent of a `file descriptor` in POSIX like -/// operating systems. Elsewhere in the code base it is commonly abbreviated -/// to `rid`. -// TODO: use `u64` instead? -pub type ResourceId = u32; - -/// Map-like data structure storing Deno's resources (equivalent to file -/// descriptors). -/// -/// Provides basic methods for element access. A resource can be of any type. -/// Different types of resources can be stored in the same map, and provided -/// with a name for description. -/// -/// Each resource is identified through a _resource ID (rid)_, which acts as -/// the key in the map. -#[derive(Default)] -pub struct ResourceTable { - index: BTreeMap>, - next_rid: ResourceId, -} - -impl ResourceTable { - /// Inserts resource into the resource table, which takes ownership of it. - /// - /// The resource type is erased at runtime and must be statically known - /// when retrieving it through `get()`. - /// - /// Returns a unique resource ID, which acts as a key for this resource. - pub fn add(&mut self, resource: T) -> ResourceId { - self.add_rc(Rc::new(resource)) - } - - /// Inserts a `Rc`-wrapped resource into the resource table. - /// - /// The resource type is erased at runtime and must be statically known - /// when retrieving it through `get()`. - /// - /// Returns a unique resource ID, which acts as a key for this resource. - pub fn add_rc(&mut self, resource: Rc) -> ResourceId { - let resource = resource as Rc; - self.add_rc_dyn(resource) - } - - pub fn add_rc_dyn(&mut self, resource: Rc) -> ResourceId { - let rid = self.next_rid; - let removed_resource = self.index.insert(rid, resource); - assert!(removed_resource.is_none()); - self.next_rid += 1; - rid - } - - /// Returns true if any resource with the given `rid` exists. - pub fn has(&self, rid: ResourceId) -> bool { - self.index.contains_key(&rid) - } - - /// Returns a reference counted pointer to the resource of type `T` with the - /// given `rid`. If `rid` is not present or has a type different than `T`, - /// this function returns `None`. - pub fn get(&self, rid: ResourceId) -> Result, Error> { - self - .index - .get(&rid) - .and_then(|rc| rc.downcast_rc::()) - .map(Clone::clone) - .ok_or_else(bad_resource_id) - } - - pub fn get_any(&self, rid: ResourceId) -> Result, Error> { - self - .index - .get(&rid) - .map(Clone::clone) - .ok_or_else(bad_resource_id) - } - - /// Replaces a resource with a new resource. - /// - /// Panics if the resource does not exist. - pub fn replace(&mut self, rid: ResourceId, resource: T) { - let result = self - .index - .insert(rid, Rc::new(resource) as Rc); - assert!(result.is_some()); - } - - /// Removes a resource of type `T` from the resource table and returns it. - /// If a resource with the given `rid` exists but its type does not match `T`, - /// it is not removed from the resource table. Note that the resource's - /// `close()` method is *not* called. - /// - /// Also note that there might be a case where - /// the returned `Rc` is referenced by other variables. That is, we cannot - /// assume that `Rc::strong_count(&returned_rc)` is always equal to 1 on success. - /// In particular, be really careful when you want to extract the inner value of - /// type `T` from `Rc`. - pub fn take(&mut self, rid: ResourceId) -> Result, Error> { - let resource = self.get::(rid)?; - self.index.remove(&rid); - Ok(resource) - } - - /// Removes a resource from the resource table and returns it. Note that the - /// resource's `close()` method is *not* called. - /// - /// Also note that there might be a - /// case where the returned `Rc` is referenced by other variables. That is, - /// we cannot assume that `Rc::strong_count(&returned_rc)` is always equal to 1 - /// on success. In particular, be really careful when you want to extract the - /// inner value of type `T` from `Rc`. - pub fn take_any( - &mut self, - rid: ResourceId, - ) -> Result, Error> { - self.index.remove(&rid).ok_or_else(bad_resource_id) - } - - /// Removes the resource with the given `rid` from the resource table. If the - /// only reference to this resource existed in the resource table, this will - /// cause the resource to be dropped. However, since resources are reference - /// counted, therefore pending ops are not automatically cancelled. A resource - /// may implement the `close()` method to perform clean-ups such as canceling - /// ops. - pub fn close(&mut self, rid: ResourceId) -> Result<(), Error> { - self - .index - .remove(&rid) - .ok_or_else(bad_resource_id) - .map(|resource| resource.close()) - } - - /// Returns an iterator that yields a `(id, name)` pair for every resource - /// that's currently in the resource table. This can be used for debugging - /// purposes or to implement the `op_resources` op. Note that the order in - /// which items appear is not specified. - /// - /// # Example - /// - /// ``` - /// # use deno_core::ResourceTable; - /// # let resource_table = ResourceTable::default(); - /// let resource_names = resource_table.names().collect::>(); - /// ``` - pub fn names(&self) -> impl Iterator)> { - self - .index - .iter() - .map(|(&id, resource)| (id, resource.name())) - } -} - -#[macro_export] -macro_rules! impl_readable_byob { - () => { - fn read(self: Rc, limit: usize) -> AsyncResult<$crate::BufView> { - Box::pin(async move { - let mut vec = vec![0; limit]; - let nread = self.read(&mut vec).await?; - if nread != vec.len() { - vec.truncate(nread); - } - let view = $crate::BufView::from(vec); - Ok(view) - }) - } - - fn read_byob( - self: Rc, - mut buf: $crate::BufMutView, - ) -> AsyncResult<(usize, $crate::BufMutView)> { - Box::pin(async move { - let nread = self.read(buf.as_mut()).await?; - Ok((nread, buf)) - }) - } - }; -} - -#[macro_export] -macro_rules! impl_writable { - (__write) => { - fn write( - self: Rc, - view: $crate::BufView, - ) -> AsyncResult<$crate::WriteOutcome> { - Box::pin(async move { - let nwritten = self.write(&view).await?; - Ok($crate::WriteOutcome::Partial { nwritten, view }) - }) - } - }; - (__write_all) => { - fn write_all(self: Rc, view: $crate::BufView) -> AsyncResult<()> { - Box::pin(async move { - self.write_all(&view).await?; - Ok(()) - }) - } - }; - () => { - $crate::impl_writable!(__write); - }; - (with_all) => { - $crate::impl_writable!(__write); - $crate::impl_writable!(__write_all); - }; -} diff --git a/core/runtime/bindings.js b/core/runtime/bindings.js deleted file mode 100644 index 21d27a2c32..0000000000 --- a/core/runtime/bindings.js +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -if (!globalThis.Deno) { - globalThis.Deno = { - core: { - ops: {}, - asyncOps: {}, - }, - }; -} - -Deno.__op__console = function (callConsole, console) { - Deno.core.callConsole = callConsole; - Deno.core.console = console; -}; - -Deno.__op__registerOp = function (isAsync, op, opName) { - const core = Deno.core; - if (isAsync) { - if (core.ops[opName] !== undefined) { - return; - } - core.asyncOps[opName] = op; - const fn = function (...args) { - if (this !== core.ops) { - // deno-lint-ignore prefer-primordials - throw new Error( - "An async stub cannot be separated from Deno.core.ops. Use ???", - ); - } - return core.asyncStub(opName, args); - }; - fn.name = opName; - core.ops[opName] = fn; - } else { - core.ops[opName] = op; - } -}; - -Deno.__op__unregisterOp = function (isAsync, opName) { - if (isAsync) { - delete Deno.core.asyncOps[opName]; - } - delete Deno.core.ops[opName]; -}; - -Deno.__op__cleanup = function () { - delete Deno.__op__console; - delete Deno.__op__registerOp; - delete Deno.__op__unregisterOp; - delete Deno.__op__cleanup; -}; diff --git a/core/runtime/bindings.rs b/core/runtime/bindings.rs deleted file mode 100644 index 55911825f1..0000000000 --- a/core/runtime/bindings.rs +++ /dev/null @@ -1,545 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use log::debug; -use std::fmt::Write; -use std::option::Option; -use std::os::raw::c_void; -use v8::MapFnTo; - -use crate::error::is_instance_of_error; -use crate::error::throw_type_error; -use crate::error::JsStackFrame; -use crate::modules::get_asserted_module_type_from_assertions; -use crate::modules::parse_import_assertions; -use crate::modules::validate_import_assertions; -use crate::modules::ImportAssertionsKind; -use crate::modules::ModuleMap; -use crate::modules::ResolutionKind; -use crate::ops::OpCtx; -use crate::runtime::InitMode; -use crate::JsRealm; -use crate::JsRuntime; - -pub(crate) fn external_references(ops: &[OpCtx]) -> v8::ExternalReferences { - // Overallocate a bit, it's better than having to resize the vector. - let mut references = Vec::with_capacity(4 + ops.len() * 4); - - references.push(v8::ExternalReference { - function: call_console.map_fn_to(), - }); - references.push(v8::ExternalReference { - function: import_meta_resolve.map_fn_to(), - }); - references.push(v8::ExternalReference { - function: catch_dynamic_import_promise_error.map_fn_to(), - }); - references.push(v8::ExternalReference { - function: empty_fn.map_fn_to(), - }); - - for ctx in ops { - let ctx_ptr = ctx as *const OpCtx as _; - references.push(v8::ExternalReference { pointer: ctx_ptr }); - references.push(v8::ExternalReference { - function: ctx.decl.v8_fn_ptr, - }); - if let Some(fast_fn) = &ctx.decl.fast_fn { - references.push(v8::ExternalReference { - pointer: fast_fn.function as _, - }); - references.push(v8::ExternalReference { - pointer: ctx.fast_fn_c_info.unwrap().as_ptr() as _, - }); - } - } - - let refs = v8::ExternalReferences::new(&references); - // Leak, V8 takes ownership of the references. - std::mem::forget(references); - refs -} - -// TODO(nayeemrmn): Move to runtime and/or make `pub(crate)`. -pub fn script_origin<'a>( - s: &mut v8::HandleScope<'a>, - resource_name: v8::Local<'a, v8::String>, -) -> v8::ScriptOrigin<'a> { - let source_map_url = v8::String::empty(s); - v8::ScriptOrigin::new( - s, - resource_name.into(), - 0, - 0, - false, - 123, - source_map_url.into(), - true, - false, - false, - ) -} - -fn get<'s, T>( - scope: &mut v8::HandleScope<'s>, - from: v8::Local, - key: &'static [u8], - path: &'static str, -) -> T -where - v8::Local<'s, v8::Value>: TryInto, -{ - let key = v8::String::new_external_onebyte_static(scope, key).unwrap(); - from - .get(scope, key.into()) - .unwrap_or_else(|| panic!("{path} exists")) - .try_into() - .unwrap_or_else(|_| panic!("unable to convert")) -} - -pub(crate) fn initialize_context<'s>( - scope: &mut v8::HandleScope<'s>, - context: v8::Local<'s, v8::Context>, - op_ctxs: &[OpCtx], - init_mode: InitMode, -) -> v8::Local<'s, v8::Context> { - let global = context.global(scope); - - let mut codegen = String::with_capacity(op_ctxs.len() * 200); - codegen.push_str(include_str!("bindings.js")); - _ = writeln!( - codegen, - "Deno.__op__ = function(opFns, callConsole, console) {{" - ); - if init_mode == InitMode::New { - _ = writeln!(codegen, "Deno.__op__console(callConsole, console);"); - } - for op_ctx in op_ctxs { - if op_ctx.decl.enabled { - _ = writeln!( - codegen, - "Deno.__op__registerOp({}, opFns[{}], \"{}\");", - op_ctx.decl.is_async, op_ctx.id, op_ctx.decl.name - ); - } else { - _ = writeln!( - codegen, - "Deno.__op__unregisterOp({}, \"{}\");", - op_ctx.decl.is_async, op_ctx.decl.name - ); - } - } - codegen.push_str("Deno.__op__cleanup();"); - _ = writeln!(codegen, "}}"); - - let script = v8::String::new_from_one_byte( - scope, - codegen.as_bytes(), - v8::NewStringType::Normal, - ) - .unwrap(); - let script = v8::Script::compile(scope, script, None).unwrap(); - script.run(scope); - - let deno = get(scope, global, b"Deno", "Deno"); - let op_fn: v8::Local = - get(scope, deno, b"__op__", "Deno.__op__"); - let recv = v8::undefined(scope); - let op_fns = v8::Array::new(scope, op_ctxs.len() as i32); - for op_ctx in op_ctxs { - let op_fn = op_ctx_function(scope, op_ctx); - op_fns.set_index(scope, op_ctx.id as u32, op_fn.into()); - } - if init_mode == InitMode::FromSnapshot { - op_fn.call(scope, recv.into(), &[op_fns.into()]); - } else { - // Bind functions to Deno.core.* - let call_console_fn = v8::Function::new(scope, call_console).unwrap(); - - // Bind v8 console object to Deno.core.console - let extra_binding_obj = context.get_extras_binding_object(scope); - let console_obj: v8::Local = get( - scope, - extra_binding_obj, - b"console", - "ExtrasBindingObject.console", - ); - - op_fn.call( - scope, - recv.into(), - &[op_fns.into(), call_console_fn.into(), console_obj.into()], - ); - } - - context -} - -fn op_ctx_function<'s>( - scope: &mut v8::HandleScope<'s>, - op_ctx: &OpCtx, -) -> v8::Local<'s, v8::Function> { - let op_ctx_ptr = op_ctx as *const OpCtx as *const c_void; - let external = v8::External::new(scope, op_ctx_ptr as *mut c_void); - let v8name = - v8::String::new_external_onebyte_static(scope, op_ctx.decl.name.as_bytes()) - .unwrap(); - let builder: v8::FunctionBuilder = - v8::FunctionTemplate::builder_raw(op_ctx.decl.v8_fn_ptr) - .data(external.into()) - .length(op_ctx.decl.arg_count as i32); - - let template = if let Some(fast_function) = &op_ctx.decl.fast_fn { - builder.build_fast( - scope, - fast_function, - Some(op_ctx.fast_fn_c_info.unwrap().as_ptr()), - None, - None, - ) - } else { - builder.build(scope) - }; - - let v8fn = template.get_function(scope).unwrap(); - v8fn.set_name(v8name); - v8fn -} - -pub extern "C" fn wasm_async_resolve_promise_callback( - _isolate: *mut v8::Isolate, - context: v8::Local, - resolver: v8::Local, - compilation_result: v8::Local, - success: v8::WasmAsyncSuccess, -) { - // SAFETY: `CallbackScope` can be safely constructed from `Local` - let scope = &mut unsafe { v8::CallbackScope::new(context) }; - if success == v8::WasmAsyncSuccess::Success { - resolver.resolve(scope, compilation_result).unwrap(); - } else { - resolver.reject(scope, compilation_result).unwrap(); - } -} - -pub fn host_import_module_dynamically_callback<'s>( - scope: &mut v8::HandleScope<'s>, - _host_defined_options: v8::Local<'s, v8::Data>, - resource_name: v8::Local<'s, v8::Value>, - specifier: v8::Local<'s, v8::String>, - import_assertions: v8::Local<'s, v8::FixedArray>, -) -> Option> { - // NOTE(bartlomieju): will crash for non-UTF-8 specifier - let specifier_str = specifier - .to_string(scope) - .unwrap() - .to_rust_string_lossy(scope); - let referrer_name_str = resource_name - .to_string(scope) - .unwrap() - .to_rust_string_lossy(scope); - - let resolver = v8::PromiseResolver::new(scope).unwrap(); - let promise = resolver.get_promise(scope); - - let assertions = parse_import_assertions( - scope, - import_assertions, - ImportAssertionsKind::DynamicImport, - ); - - { - let tc_scope = &mut v8::TryCatch::new(scope); - validate_import_assertions(tc_scope, &assertions); - if tc_scope.has_caught() { - let e = tc_scope.exception().unwrap(); - resolver.reject(tc_scope, e); - } - } - let asserted_module_type = - get_asserted_module_type_from_assertions(&assertions); - - let resolver_handle = v8::Global::new(scope, resolver); - { - let state_rc = JsRuntime::state_from(scope); - let module_map_rc = JsRuntime::module_map_from(scope); - - debug!( - "dyn_import specifier {} referrer {} ", - specifier_str, referrer_name_str - ); - ModuleMap::load_dynamic_import( - module_map_rc, - &specifier_str, - &referrer_name_str, - asserted_module_type, - resolver_handle, - ); - state_rc.borrow_mut().notify_new_dynamic_import(); - } - // Map errors from module resolution (not JS errors from module execution) to - // ones rethrown from this scope, so they include the call stack of the - // dynamic import site. Error objects without any stack frames are assumed to - // be module resolution errors, other exception values are left as they are. - let builder = v8::FunctionBuilder::new(catch_dynamic_import_promise_error); - - let map_err = - v8::FunctionBuilder::::build(builder, scope).unwrap(); - - let promise = promise.catch(scope, map_err).unwrap(); - - Some(promise) -} - -pub extern "C" fn host_initialize_import_meta_object_callback( - context: v8::Local, - module: v8::Local, - meta: v8::Local, -) { - // SAFETY: `CallbackScope` can be safely constructed from `Local` - let scope = &mut unsafe { v8::CallbackScope::new(context) }; - let module_map_rc = JsRuntime::module_map_from(scope); - let module_map = module_map_rc.borrow(); - - let module_global = v8::Global::new(scope, module); - let info = module_map - .get_info(&module_global) - .expect("Module not found"); - - let url_key = v8::String::new_external_onebyte_static(scope, b"url").unwrap(); - let url_val = info.name.v8(scope); - meta.create_data_property(scope, url_key.into(), url_val.into()); - - let main_key = - v8::String::new_external_onebyte_static(scope, b"main").unwrap(); - let main_val = v8::Boolean::new(scope, info.main); - meta.create_data_property(scope, main_key.into(), main_val.into()); - - let builder = - v8::FunctionBuilder::new(import_meta_resolve).data(url_val.into()); - let val = v8::FunctionBuilder::::build(builder, scope).unwrap(); - let resolve_key = - v8::String::new_external_onebyte_static(scope, b"resolve").unwrap(); - meta.set(scope, resolve_key.into(), val.into()); -} - -fn import_meta_resolve( - scope: &mut v8::HandleScope, - args: v8::FunctionCallbackArguments, - mut rv: v8::ReturnValue, -) { - if args.length() > 1 { - return throw_type_error(scope, "Invalid arguments"); - } - - let maybe_arg_str = args.get(0).to_string(scope); - if maybe_arg_str.is_none() { - return throw_type_error(scope, "Invalid arguments"); - } - let specifier = maybe_arg_str.unwrap(); - let referrer = { - let url_prop = args.data(); - url_prop.to_rust_string_lossy(scope) - }; - let module_map_rc = JsRuntime::module_map_from(scope); - let loader = module_map_rc.borrow().loader.clone(); - let specifier_str = specifier.to_rust_string_lossy(scope); - - if specifier_str.starts_with("npm:") { - throw_type_error(scope, "\"npm:\" specifiers are currently not supported in import.meta.resolve()"); - return; - } - - match loader.resolve(&specifier_str, &referrer, ResolutionKind::DynamicImport) - { - Ok(resolved) => { - let resolved_val = serde_v8::to_v8(scope, resolved.as_str()).unwrap(); - rv.set(resolved_val); - } - Err(err) => { - throw_type_error(scope, &err.to_string()); - } - }; -} - -fn empty_fn( - _scope: &mut v8::HandleScope, - _args: v8::FunctionCallbackArguments, - _rv: v8::ReturnValue, -) { - //Do Nothing -} - -//It creates a reference to an empty function which can be maintained after the snapshots -pub fn create_empty_fn<'s>( - scope: &mut v8::HandleScope<'s>, -) -> Option> { - let empty_fn = v8::FunctionTemplate::new(scope, empty_fn); - empty_fn.get_function(scope) -} - -fn catch_dynamic_import_promise_error( - scope: &mut v8::HandleScope, - args: v8::FunctionCallbackArguments, - _rv: v8::ReturnValue, -) { - let arg = args.get(0); - if is_instance_of_error(scope, arg) { - let e: crate::error::NativeJsError = serde_v8::from_v8(scope, arg).unwrap(); - let name = e.name.unwrap_or_else(|| "Error".to_string()); - let msg = v8::Exception::create_message(scope, arg); - if msg.get_stack_trace(scope).unwrap().get_frame_count() == 0 { - let arg: v8::Local = arg.try_into().unwrap(); - let message_key = - v8::String::new_external_onebyte_static(scope, b"message").unwrap(); - let message = arg.get(scope, message_key.into()).unwrap(); - let mut message: v8::Local = message.try_into().unwrap(); - if let Some(stack_frame) = JsStackFrame::from_v8_message(scope, msg) { - if let Some(location) = stack_frame.maybe_format_location() { - let str = - format!("{} at {location}", message.to_rust_string_lossy(scope)); - message = v8::String::new(scope, &str).unwrap(); - } - } - let exception = match name.as_str() { - "RangeError" => v8::Exception::range_error(scope, message), - "TypeError" => v8::Exception::type_error(scope, message), - "SyntaxError" => v8::Exception::syntax_error(scope, message), - "ReferenceError" => v8::Exception::reference_error(scope, message), - _ => v8::Exception::error(scope, message), - }; - let code_key = - v8::String::new_external_onebyte_static(scope, b"code").unwrap(); - let code_value = - v8::String::new_external_onebyte_static(scope, b"ERR_MODULE_NOT_FOUND") - .unwrap(); - let exception_obj = exception.to_object(scope).unwrap(); - exception_obj.set(scope, code_key.into(), code_value.into()); - scope.throw_exception(exception); - return; - } - } - scope.throw_exception(arg); -} - -pub extern "C" fn promise_reject_callback(message: v8::PromiseRejectMessage) { - use v8::PromiseRejectEvent::*; - - // SAFETY: `CallbackScope` can be safely constructed from `&PromiseRejectMessage` - let scope = &mut unsafe { v8::CallbackScope::new(&message) }; - - let context_state_rc = JsRealm::state_from_scope(scope); - let mut context_state = context_state_rc.borrow_mut(); - - if let Some(js_promise_reject_cb) = context_state.js_promise_reject_cb.clone() - { - drop(context_state); - - let tc_scope = &mut v8::TryCatch::new(scope); - let undefined: v8::Local = v8::undefined(tc_scope).into(); - let type_ = v8::Integer::new(tc_scope, message.get_event() as i32); - let promise = message.get_promise(); - - let reason = match message.get_event() { - PromiseRejectWithNoHandler - | PromiseRejectAfterResolved - | PromiseResolveAfterResolved => message.get_value().unwrap_or(undefined), - PromiseHandlerAddedAfterReject => undefined, - }; - - let promise_global = v8::Global::new(tc_scope, promise); - let args = &[type_.into(), promise.into(), reason]; - let maybe_has_unhandled_rejection_handler = js_promise_reject_cb - .open(tc_scope) - .call(tc_scope, undefined, args); - - let has_unhandled_rejection_handler = - if let Some(value) = maybe_has_unhandled_rejection_handler { - value.is_true() - } else { - false - }; - - if has_unhandled_rejection_handler { - let state_rc = JsRuntime::state_from(tc_scope); - let mut state = state_rc.borrow_mut(); - if let Some(pending_mod_evaluate) = state.pending_mod_evaluate.as_mut() { - if !pending_mod_evaluate.has_evaluated { - pending_mod_evaluate - .handled_promise_rejections - .push(promise_global); - } - } - } - } else { - let promise = message.get_promise(); - let promise_global = v8::Global::new(scope, promise); - match message.get_event() { - PromiseRejectWithNoHandler => { - let error = message.get_value().unwrap(); - let error_global = v8::Global::new(scope, error); - context_state - .pending_promise_rejections - .push_back((promise_global, error_global)); - } - PromiseHandlerAddedAfterReject => { - context_state - .pending_promise_rejections - .retain(|(key, _)| key != &promise_global); - } - PromiseRejectAfterResolved => {} - PromiseResolveAfterResolved => { - // Should not warn. See #1272 - } - } - } -} - -/// This binding should be used if there's a custom console implementation -/// available. Using it will make sure that proper stack frames are displayed -/// in the inspector console. -/// -/// Each method on console object should be bound to this function, eg: -/// ```ignore -/// function wrapConsole(consoleFromDeno, consoleFromV8) { -/// const callConsole = core.callConsole; -/// -/// for (const key of Object.keys(consoleFromV8)) { -/// if (consoleFromDeno.hasOwnProperty(key)) { -/// consoleFromDeno[key] = callConsole.bind( -/// consoleFromDeno, -/// consoleFromV8[key], -/// consoleFromDeno[key], -/// ); -/// } -/// } -/// } -/// ``` -/// -/// Inspired by: -/// https://github.com/nodejs/node/blob/1317252dfe8824fd9cfee125d2aaa94004db2f3b/src/inspector_js_api.cc#L194-L222 -fn call_console( - scope: &mut v8::HandleScope, - args: v8::FunctionCallbackArguments, - _rv: v8::ReturnValue, -) { - if args.length() < 2 - || !args.get(0).is_function() - || !args.get(1).is_function() - { - return throw_type_error(scope, "Invalid arguments"); - } - - let mut call_args = vec![]; - for i in 2..args.length() { - call_args.push(args.get(i)); - } - - let receiver = args.this(); - let inspector_console_method = - v8::Local::::try_from(args.get(0)).unwrap(); - let deno_console_method = - v8::Local::::try_from(args.get(1)).unwrap(); - - inspector_console_method.call(scope, receiver.into(), &call_args); - deno_console_method.call(scope, receiver.into(), &call_args); -} diff --git a/core/runtime/encode_decode_test.js b/core/runtime/encode_decode_test.js deleted file mode 100644 index 0f46687657..0000000000 --- a/core/runtime/encode_decode_test.js +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -"use strict"; -function assert(cond) { - if (!cond) { - throw Error("assert"); - } -} - -function assertArrayEquals(a1, a2) { - if (a1.length !== a2.length) throw Error("assert"); - - for (const index in a1) { - if (a1[index] !== a2[index]) { - throw Error("assert"); - } - } -} - -function main() { - // deno-fmt-ignore - const fixture1 = [ - 0xf0, 0x9d, 0x93, 0xbd, - 0xf0, 0x9d, 0x93, 0xae, - 0xf0, 0x9d, 0x94, 0x81, - 0xf0, 0x9d, 0x93, 0xbd - ]; - // deno-fmt-ignore - const fixture2 = [ - 72, 101, 108, 108, - 111, 32, 239, 191, - 189, 239, 191, 189, - 32, 87, 111, 114, - 108, 100 - ]; - - const empty = Deno.core.ops.op_encode(""); - if (empty.length !== 0) throw new Error("assert"); - - assertArrayEquals( - Array.from(Deno.core.ops.op_encode("𝓽𝓮𝔁𝓽")), - fixture1, - ); - assertArrayEquals( - Array.from(Deno.core.ops.op_encode("Hello \udc12\ud834 World")), - fixture2, - ); - - const emptyBuf = Deno.core.ops.op_decode(new Uint8Array(0)); - if (emptyBuf !== "") throw new Error("assert"); - - assert(Deno.core.ops.op_decode(new Uint8Array(fixture1)) === "𝓽𝓮𝔁𝓽"); - assert( - Deno.core.ops.op_decode(new Uint8Array(fixture2)) === - "Hello �� World", - ); - - // See https://github.com/denoland/deno/issues/6649 - let thrown = false; - try { - Deno.core.ops.op_decode(new Uint8Array(2 ** 29)); - } catch (e) { - thrown = true; - assert(e instanceof RangeError); - assert(e.message === "string too long"); - } - assert(thrown); -} - -main(); diff --git a/core/runtime/error_builder_test.js b/core/runtime/error_builder_test.js deleted file mode 100644 index f442819cbc..0000000000 --- a/core/runtime/error_builder_test.js +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -const { core } = Deno; -const { ops } = core; - -class DOMException { - constructor(message, code) { - this.msg = message; - this.code = code; - } -} - -core.registerErrorBuilder( - "DOMExceptionOperationError", - function DOMExceptionOperationError(msg) { - return new DOMException(msg, "OperationError"); - }, -); - -try { - ops.op_err(); - throw new Error("op_err didn't throw!"); -} catch (err) { - if (!(err instanceof DOMException)) { - throw new Error("err not DOMException"); - } - if (err.msg !== "abc") { - throw new Error("err.message is incorrect"); - } - if (err.code !== "OperationError") { - throw new Error("err.code is incorrect"); - } -} diff --git a/core/runtime/icudtl.dat b/core/runtime/icudtl.dat deleted file mode 100644 index d1f10917ab..0000000000 Binary files a/core/runtime/icudtl.dat and /dev/null differ diff --git a/core/runtime/jsrealm.rs b/core/runtime/jsrealm.rs deleted file mode 100644 index 72818eebe4..0000000000 --- a/core/runtime/jsrealm.rs +++ /dev/null @@ -1,365 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use super::bindings; -use crate::error::exception_to_err_result; -use crate::joinset::JoinSet; -use crate::modules::ModuleCode; -use crate::ops::OpCtx; -use crate::runtime::JsRuntimeState; -use crate::JsRuntime; -use crate::OpId; -use crate::OpResult; -use crate::PromiseId; -use anyhow::Error; -use std::cell::RefCell; -use std::collections::HashSet; -use std::collections::VecDeque; -use std::hash::BuildHasherDefault; -use std::hash::Hasher; -use std::option::Option; -use std::rc::Rc; -use v8::HandleScope; -use v8::Local; - -// Hasher used for `unrefed_ops`. Since these are rolling i32, there's no -// need to actually hash them. -#[derive(Default)] -pub(crate) struct IdentityHasher(u64); - -impl Hasher for IdentityHasher { - fn write_i32(&mut self, i: i32) { - self.0 = i as u64; - } - - fn finish(&self) -> u64 { - self.0 - } - - fn write(&mut self, _bytes: &[u8]) { - unreachable!() - } -} - -#[derive(Default)] -pub(crate) struct ContextState { - pub(crate) js_event_loop_tick_cb: Option>>, - pub(crate) js_build_custom_error_cb: Option>>, - pub(crate) js_promise_reject_cb: Option>>, - pub(crate) js_format_exception_cb: Option>>, - pub(crate) js_wasm_streaming_cb: Option>>, - pub(crate) pending_promise_rejections: - VecDeque<(v8::Global, v8::Global)>, - pub(crate) unrefed_ops: HashSet>, - pub(crate) pending_ops: JoinSet<(PromiseId, OpId, OpResult)>, - // We don't explicitly re-read this prop but need the slice to live alongside - // the context - pub(crate) op_ctxs: Box<[OpCtx]>, - pub(crate) isolate: Option<*mut v8::OwnedIsolate>, -} - -/// A representation of a JavaScript realm tied to a [`JsRuntime`], that allows -/// execution in the realm's context. -/// -/// A [`JsRealm`] instance is a reference to an already existing realm, which -/// does not hold ownership of it, so instances can be created and dropped as -/// needed. As such, calling [`JsRealm::new`] doesn't create a new realm, and -/// cloning a [`JsRealm`] only creates a new reference. See -/// [`JsRuntime::create_realm`] to create new realms instead. -/// -/// Despite [`JsRealm`] instances being references, multiple instances that -/// point to the same realm won't overlap because every operation requires -/// passing a mutable reference to the [`v8::Isolate`]. Therefore, no operation -/// on two [`JsRealm`] instances tied to the same isolate can be run at the same -/// time, regardless of whether they point to the same realm. -/// -/// # Panics -/// -/// Every method of [`JsRealm`] will panic if you call it with a reference to a -/// [`v8::Isolate`] other than the one that corresponds to the current context. -/// -/// In other words, the [`v8::Isolate`] parameter for all the related [`JsRealm`] methods -/// must be extracted from the pre-existing [`JsRuntime`]. -/// -/// Example usage with the [`JsRealm::execute_script`] method: -/// ``` -/// use deno_core::JsRuntime; -/// use deno_core::RuntimeOptions; -/// -/// let mut runtime = JsRuntime::new(RuntimeOptions::default()); -/// let new_realm = runtime -/// .create_realm() -/// .expect("Handle the error properly"); -/// let source_code = "var a = 0; a + 1"; -/// let result = new_realm -/// .execute_script_static(runtime.v8_isolate(), "", source_code) -/// .expect("Handle the error properly"); -/// # drop(result); -/// ``` -/// -/// # Lifetime of the realm -/// -/// As long as the corresponding isolate is alive, a [`JsRealm`] instance will -/// keep the underlying V8 context alive even if it would have otherwise been -/// garbage collected. -#[derive(Clone)] -#[repr(transparent)] -pub struct JsRealm(pub(crate) JsRealmInner); - -#[derive(Clone)] -pub(crate) struct JsRealmInner { - context_state: Rc>, - context: Rc>, - runtime_state: Rc>, - is_global: bool, -} - -impl JsRealmInner { - pub(crate) fn new( - context_state: Rc>, - context: v8::Global, - runtime_state: Rc>, - is_global: bool, - ) -> Self { - Self { - context_state, - context: context.into(), - runtime_state, - is_global, - } - } - - pub fn num_pending_ops(&self) -> usize { - self.context_state.borrow().pending_ops.len() - } - - pub fn num_unrefed_ops(&self) -> usize { - self.context_state.borrow().unrefed_ops.len() - } - - #[inline(always)] - pub fn context(&self) -> &v8::Global { - &self.context - } - - #[inline(always)] - pub(crate) fn state(&self) -> Rc> { - self.context_state.clone() - } - - /// For info on the [`v8::Isolate`] parameter, check [`JsRealm#panics`]. - #[inline(always)] - pub fn handle_scope<'s>( - &self, - isolate: &'s mut v8::Isolate, - ) -> v8::HandleScope<'s> { - v8::HandleScope::with_context(isolate, &*self.context) - } - - pub(crate) fn check_promise_rejections( - &self, - scope: &mut v8::HandleScope, - ) -> Result<(), Error> { - let Some((_, handle)) = self.context_state.borrow_mut().pending_promise_rejections.pop_front() else { - return Ok(()); - }; - - let exception = v8::Local::new(scope, handle); - let state_rc = JsRuntime::state_from(scope); - let state = state_rc.borrow(); - if let Some(inspector) = &state.inspector { - let inspector = inspector.borrow(); - inspector.exception_thrown(scope, exception, true); - if inspector.has_blocking_sessions() { - return Ok(()); - } - } - exception_to_err_result(scope, exception, true) - } - - pub(crate) fn is_same(&self, other: &Rc>) -> bool { - Rc::ptr_eq(&self.context, other) - } - - pub fn destroy(self) { - let state = self.state(); - let raw_ptr = self.state().borrow().isolate.unwrap(); - // SAFETY: We know the isolate outlives the realm - let isolate = unsafe { raw_ptr.as_mut().unwrap() }; - let mut realm_state = state.borrow_mut(); - // These globals will prevent snapshots from completing, take them - std::mem::take(&mut realm_state.js_event_loop_tick_cb); - std::mem::take(&mut realm_state.js_build_custom_error_cb); - std::mem::take(&mut realm_state.js_promise_reject_cb); - std::mem::take(&mut realm_state.js_format_exception_cb); - std::mem::take(&mut realm_state.js_wasm_streaming_cb); - // The OpCtx slice may contain a circular reference - std::mem::take(&mut realm_state.op_ctxs); - - self.context().open(isolate).clear_all_slots(isolate); - - // Expect that this context is dead (we only check this in debug mode) - // TODO(mmastrac): This check fails for some tests, will need to fix this - // debug_assert_eq!(Rc::strong_count(&self.context), 1, "Realm was still alive when we wanted to destroy it. Not dropped?"); - } -} - -impl JsRealm { - pub(crate) fn new(inner: JsRealmInner) -> Self { - Self(inner) - } - - #[inline(always)] - pub(crate) fn state_from_scope( - scope: &mut v8::HandleScope, - ) -> Rc> { - let context = scope.get_current_context(); - context - .get_slot::>>(scope) - .unwrap() - .clone() - } - - #[inline(always)] - pub fn num_pending_ops(&self) -> usize { - self.0.num_pending_ops() - } - - #[inline(always)] - pub fn num_unrefed_ops(&self) -> usize { - self.0.num_unrefed_ops() - } - - /// For info on the [`v8::Isolate`] parameter, check [`JsRealm#panics`]. - #[inline(always)] - pub fn handle_scope<'s>( - &self, - isolate: &'s mut v8::Isolate, - ) -> v8::HandleScope<'s> { - self.0.handle_scope(isolate) - } - - #[inline(always)] - pub fn context(&self) -> &v8::Global { - self.0.context() - } - - /// For info on the [`v8::Isolate`] parameter, check [`JsRealm#panics`]. - pub fn global_object<'s>( - &self, - isolate: &'s mut v8::Isolate, - ) -> v8::Local<'s, v8::Object> { - let scope = &mut self.0.handle_scope(isolate); - self.0.context.open(scope).global(scope) - } - - fn string_from_code<'a>( - scope: &mut HandleScope<'a>, - code: &ModuleCode, - ) -> Option> { - if let Some(code) = code.try_static_ascii() { - v8::String::new_external_onebyte_static(scope, code) - } else { - v8::String::new_from_utf8( - scope, - code.as_bytes(), - v8::NewStringType::Normal, - ) - } - } - - /// Executes traditional JavaScript code (traditional = not ES modules) in the - /// realm's context. - /// - /// For info on the [`v8::Isolate`] parameter, check [`JsRealm#panics`]. - /// - /// The `name` parameter can be a filepath or any other string. E.g.: - /// - /// - "/some/file/path.js" - /// - "" - /// - "[native code]" - /// - /// The same `name` value can be used for multiple executions. - /// - /// `Error` can usually be downcast to `JsError`. - pub fn execute_script_static( - &self, - isolate: &mut v8::Isolate, - name: &'static str, - source_code: &'static str, - ) -> Result, Error> { - self.execute_script(isolate, name, ModuleCode::from_static(source_code)) - } - - /// Executes traditional JavaScript code (traditional = not ES modules) in the - /// realm's context. - /// - /// For info on the [`v8::Isolate`] parameter, check [`JsRealm#panics`]. - /// - /// The `name` parameter can be a filepath or any other string. E.g.: - /// - /// - "/some/file/path.js" - /// - "" - /// - "[native code]" - /// - /// The same `name` value can be used for multiple executions. - /// - /// `Error` can usually be downcast to `JsError`. - pub fn execute_script( - &self, - isolate: &mut v8::Isolate, - name: &'static str, - source_code: ModuleCode, - ) -> Result, Error> { - let scope = &mut self.0.handle_scope(isolate); - - let source = Self::string_from_code(scope, &source_code).unwrap(); - debug_assert!(name.is_ascii()); - let name = - v8::String::new_external_onebyte_static(scope, name.as_bytes()).unwrap(); - let origin = bindings::script_origin(scope, name); - - let tc_scope = &mut v8::TryCatch::new(scope); - - let script = match v8::Script::compile(tc_scope, source, Some(&origin)) { - Some(script) => script, - None => { - let exception = tc_scope.exception().unwrap(); - return exception_to_err_result(tc_scope, exception, false); - } - }; - - match script.run(tc_scope) { - Some(value) => { - let value_handle = v8::Global::new(tc_scope, value); - Ok(value_handle) - } - None => { - assert!(tc_scope.has_caught()); - let exception = tc_scope.exception().unwrap(); - exception_to_err_result(tc_scope, exception, false) - } - } - } - - // TODO(andreubotella): `mod_evaluate`, `load_main_module`, `load_side_module` -} - -impl Drop for JsRealm { - fn drop(&mut self) { - // Don't do anything special with the global realm - if self.0.is_global { - return; - } - - // There's us and there's the runtime - if Rc::strong_count(&self.0.context) == 2 { - self - .0 - .runtime_state - .borrow_mut() - .remove_realm(&self.0.context); - assert_eq!(Rc::strong_count(&self.0.context), 1); - self.0.clone().destroy(); - assert_eq!(Rc::strong_count(&self.0.context_state), 1); - } - } -} diff --git a/core/runtime/jsruntime.rs b/core/runtime/jsruntime.rs deleted file mode 100644 index cea8fcb1ff..0000000000 --- a/core/runtime/jsruntime.rs +++ /dev/null @@ -1,2316 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use super::bindings; -use super::jsrealm::JsRealmInner; -use super::snapshot_util; -use crate::error::exception_to_err_result; -use crate::error::generic_error; -use crate::error::to_v8_type_error; -use crate::error::GetErrorClassFn; -use crate::error::JsError; -use crate::extensions::OpDecl; -use crate::extensions::OpEventLoopFn; -use crate::include_js_files; -use crate::inspector::JsRuntimeInspector; -use crate::module_specifier::ModuleSpecifier; -use crate::modules::AssertedModuleType; -use crate::modules::ExtModuleLoader; -use crate::modules::ExtModuleLoaderCb; -use crate::modules::ModuleCode; -use crate::modules::ModuleError; -use crate::modules::ModuleId; -use crate::modules::ModuleLoadId; -use crate::modules::ModuleLoader; -use crate::modules::ModuleMap; -use crate::ops::*; -use crate::runtime::ContextState; -use crate::runtime::JsRealm; -use crate::source_map::SourceMapCache; -use crate::source_map::SourceMapGetter; -use crate::Extension; -use crate::ExtensionFileSource; -use crate::NoopModuleLoader; -use crate::OpMiddlewareFn; -use crate::OpResult; -use crate::OpState; -use crate::V8_WRAPPER_OBJECT_INDEX; -use crate::V8_WRAPPER_TYPE_INDEX; -use anyhow::Context as AnyhowContext; -use anyhow::Error; -use futures::channel::oneshot; -use futures::future::poll_fn; -use futures::stream::StreamExt; -use once_cell::sync::Lazy; -use smallvec::SmallVec; -use std::any::Any; -use std::cell::RefCell; -use std::collections::HashMap; -use std::ffi::c_void; -use std::mem::ManuallyDrop; -use std::ops::Deref; -use std::ops::DerefMut; -use std::option::Option; -use std::rc::Rc; -use std::sync::atomic::AtomicBool; -use std::sync::atomic::Ordering; -use std::sync::Arc; -use std::sync::Mutex; -use std::sync::Once; -use std::task::Context; -use std::task::Poll; - -const STATE_DATA_OFFSET: u32 = 0; -const MODULE_MAP_DATA_OFFSET: u32 = 1; - -pub enum Snapshot { - Static(&'static [u8]), - JustCreated(v8::StartupData), - Boxed(Box<[u8]>), -} - -/// Objects that need to live as long as the isolate -#[derive(Default)] -pub(crate) struct IsolateAllocations { - pub(crate) near_heap_limit_callback_data: - Option<(Box>, v8::NearHeapLimitCallback)>, -} - -/// ManuallyDrop> is clone, but it returns a ManuallyDrop> which is a massive -/// memory-leak footgun. -pub(crate) struct ManuallyDropRc(ManuallyDrop>); - -impl ManuallyDropRc { - pub fn clone(&self) -> Rc { - self.0.deref().clone() - } -} - -impl Deref for ManuallyDropRc { - type Target = Rc; - fn deref(&self) -> &Self::Target { - self.0.deref() - } -} - -impl DerefMut for ManuallyDropRc { - fn deref_mut(&mut self) -> &mut Self::Target { - self.0.deref_mut() - } -} - -/// This struct contains the [`JsRuntimeState`] and [`v8::OwnedIsolate`] that are required -/// to do an orderly shutdown of V8. We keep these in a separate struct to allow us to control -/// the destruction more closely, as snapshots require the isolate to be destroyed by the -/// snapshot process, not the destructor. -/// -/// The way rusty_v8 works w/snapshots is that the [`v8::OwnedIsolate`] gets consumed by a -/// [`v8::snapshot::SnapshotCreator`] that is stored in its annex. It's a bit awkward, because this -/// means we cannot let it drop (because we don't have it after a snapshot). On top of that, we have -/// to consume it in the snapshot creator because otherwise it panics. -/// -/// This inner struct allows us to let the outer JsRuntime drop normally without a Drop impl, while we -/// control dropping more closely here using ManuallyDrop. -pub(crate) struct InnerIsolateState { - will_snapshot: bool, - pub(crate) state: ManuallyDropRc>, - v8_isolate: ManuallyDrop, -} - -impl InnerIsolateState { - /// Clean out the opstate and take the inspector to prevent the inspector from getting destroyed - /// after we've torn down the contexts. If the inspector is not correctly torn down, random crashes - /// happen in tests (and possibly for users using the inspector). - pub fn prepare_for_cleanup(&mut self) { - let mut state = self.state.borrow_mut(); - let inspector = state.inspector.take(); - state.op_state.borrow_mut().clear(); - if let Some(inspector) = inspector { - assert_eq!( - Rc::strong_count(&inspector), - 1, - "The inspector must be dropped before the runtime" - ); - } - } - - pub fn cleanup(&mut self) { - self.prepare_for_cleanup(); - - let state_ptr = self.v8_isolate.get_data(STATE_DATA_OFFSET); - // SAFETY: We are sure that it's a valid pointer for whole lifetime of - // the runtime. - _ = unsafe { Rc::from_raw(state_ptr as *const RefCell) }; - - let module_map_ptr = self.v8_isolate.get_data(MODULE_MAP_DATA_OFFSET); - // SAFETY: We are sure that it's a valid pointer for whole lifetime of - // the runtime. - _ = unsafe { Rc::from_raw(module_map_ptr as *const RefCell) }; - - self.state.borrow_mut().destroy_all_realms(); - - debug_assert_eq!(Rc::strong_count(&self.state), 1); - } - - pub fn prepare_for_snapshot(mut self) -> v8::OwnedIsolate { - self.cleanup(); - // SAFETY: We're copying out of self and then immediately forgetting self - let (state, isolate) = unsafe { - ( - ManuallyDrop::take(&mut self.state.0), - ManuallyDrop::take(&mut self.v8_isolate), - ) - }; - std::mem::forget(self); - drop(state); - isolate - } -} - -impl Drop for InnerIsolateState { - fn drop(&mut self) { - self.cleanup(); - // SAFETY: We gotta drop these - unsafe { - ManuallyDrop::drop(&mut self.state.0); - if self.will_snapshot { - // Create the snapshot and just drop it. - eprintln!("WARNING: v8::OwnedIsolate for snapshot was leaked"); - } else { - ManuallyDrop::drop(&mut self.v8_isolate); - } - } - } -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub(crate) enum InitMode { - /// We have no snapshot -- this is a pristine context. - New, - /// We are using a snapshot, thus certain initialization steps are skipped. - FromSnapshot, -} - -impl InitMode { - fn from_options(options: &RuntimeOptions) -> Self { - match options.startup_snapshot { - None => Self::New, - Some(_) => Self::FromSnapshot, - } - } -} - -pub(crate) static BUILTIN_SOURCES: Lazy> = - Lazy::new(|| { - include_js_files!( - core - "00_primordials.js", - "01_core.js", - "02_error.js", - ) - }); - -/// A single execution context of JavaScript. Corresponds roughly to the "Web -/// Worker" concept in the DOM. -//// -/// The JsRuntime future completes when there is an error or when all -/// pending ops have completed. -/// -/// Use [`JsRuntimeForSnapshot`] to be able to create a snapshot. -pub struct JsRuntime { - pub(crate) inner: InnerIsolateState, - pub(crate) module_map: Rc>, - pub(crate) allocations: IsolateAllocations, - extensions: Vec, - event_loop_middlewares: Vec>, - init_mode: InitMode, - // Marks if this is considered the top-level runtime. Used only be inspector. - is_main: bool, -} - -/// The runtime type used for snapshot creation. -pub struct JsRuntimeForSnapshot(JsRuntime); - -impl Deref for JsRuntimeForSnapshot { - type Target = JsRuntime; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for JsRuntimeForSnapshot { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -pub(crate) struct DynImportModEvaluate { - load_id: ModuleLoadId, - module_id: ModuleId, - promise: v8::Global, - module: v8::Global, -} - -pub(crate) struct ModEvaluate { - pub(crate) promise: Option>, - pub(crate) has_evaluated: bool, - pub(crate) handled_promise_rejections: Vec>, - sender: oneshot::Sender>, -} - -pub struct CrossIsolateStore(Arc>>); - -struct CrossIsolateStoreInner { - map: HashMap, - last_id: u32, -} - -impl CrossIsolateStore { - pub(crate) fn insert(&self, value: T) -> u32 { - let mut store = self.0.lock().unwrap(); - let last_id = store.last_id; - store.map.insert(last_id, value); - store.last_id += 1; - last_id - } - - pub(crate) fn take(&self, id: u32) -> Option { - let mut store = self.0.lock().unwrap(); - store.map.remove(&id) - } -} - -impl Default for CrossIsolateStore { - fn default() -> Self { - CrossIsolateStore(Arc::new(Mutex::new(CrossIsolateStoreInner { - map: Default::default(), - last_id: 0, - }))) - } -} - -impl Clone for CrossIsolateStore { - fn clone(&self) -> Self { - Self(self.0.clone()) - } -} - -pub type SharedArrayBufferStore = - CrossIsolateStore>; - -pub type CompiledWasmModuleStore = CrossIsolateStore; - -/// Internal state for JsRuntime which is stored in one of v8::Isolate's -/// embedder slots. -pub struct JsRuntimeState { - global_realm: Option, - known_realms: Vec, - pub(crate) has_tick_scheduled: bool, - pub(crate) pending_dyn_mod_evaluate: Vec, - pub(crate) pending_mod_evaluate: Option, - /// A counter used to delay our dynamic import deadlock detection by one spin - /// of the event loop. - dyn_module_evaluate_idle_counter: u32, - pub(crate) source_map_getter: Option>>, - pub(crate) source_map_cache: Rc>, - pub(crate) op_state: Rc>, - pub(crate) shared_array_buffer_store: Option, - pub(crate) compiled_wasm_module_store: Option, - /// The error that was passed to an `op_dispatch_exception` call. - /// It will be retrieved by `exception_to_err_result` and used as an error - /// instead of any other exceptions. - // TODO(nayeemrmn): This is polled in `exception_to_err_result()` which is - // flimsy. Try to poll it similarly to `pending_promise_rejections`. - pub(crate) dispatched_exception: Option>, - pub(crate) inspector: Option>>, -} - -impl JsRuntimeState { - pub(crate) fn destroy_all_realms(&mut self) { - self.global_realm.take(); - for realm in self.known_realms.drain(..) { - realm.destroy() - } - } - - pub(crate) fn remove_realm( - &mut self, - realm_context: &Rc>, - ) { - self - .known_realms - .retain(|realm| !realm.is_same(realm_context)); - } -} - -fn v8_init( - v8_platform: Option>, - predictable: bool, -) { - // Include 10MB ICU data file. - #[repr(C, align(16))] - struct IcuData([u8; 10541264]); - static ICU_DATA: IcuData = IcuData(*include_bytes!("icudtl.dat")); - v8::icu::set_common_data_72(&ICU_DATA.0).unwrap(); - - let flags = concat!( - " --wasm-test-streaming", - " --harmony-import-assertions", - " --no-validate-asm", - " --turbo_fast_api_calls", - " --harmony-change-array-by-copy", - ); - - if predictable { - v8::V8::set_flags_from_string(&format!( - "{}{}", - flags, " --predictable --random-seed=42" - )); - } else { - v8::V8::set_flags_from_string(flags); - } - - let v8_platform = v8_platform - .unwrap_or_else(|| v8::new_default_platform(0, false).make_shared()); - v8::V8::initialize_platform(v8_platform); - v8::V8::initialize(); -} - -#[derive(Default)] -pub struct RuntimeOptions { - /// Source map reference for errors. - pub source_map_getter: Option>, - - /// Allows to map error type to a string "class" used to represent - /// error in JavaScript. - pub get_error_class_fn: Option, - - /// Implementation of `ModuleLoader` which will be - /// called when V8 requests to load ES modules. - /// - /// If not provided runtime will error if code being - /// executed tries to load modules. - pub module_loader: Option>, - - /// JsRuntime extensions, not to be confused with ES modules. - /// Only ops registered by extensions will be initialized. If you need - /// to execute JS code from extensions, pass source files in `js` or `esm` - /// option on `ExtensionBuilder`. - /// - /// If you are creating a runtime from a snapshot take care not to include - /// JavaScript sources in the extensions. - pub extensions: Vec, - - /// If provided, the module map will be cleared and left only with the specifiers - /// in this list, with the new names provided. If not provided, the module map is - /// left intact. - pub rename_modules: Option>, - - /// V8 snapshot that should be loaded on startup. - pub startup_snapshot: Option, - - /// Isolate creation parameters. - pub create_params: Option, - - /// V8 platform instance to use. Used when Deno initializes V8 - /// (which it only does once), otherwise it's silently dropped. - pub v8_platform: Option>, - - /// The store to use for transferring SharedArrayBuffers between isolates. - /// If multiple isolates should have the possibility of sharing - /// SharedArrayBuffers, they should use the same [SharedArrayBufferStore]. If - /// no [SharedArrayBufferStore] is specified, SharedArrayBuffer can not be - /// serialized. - pub shared_array_buffer_store: Option, - - /// The store to use for transferring `WebAssembly.Module` objects between - /// isolates. - /// If multiple isolates should have the possibility of sharing - /// `WebAssembly.Module` objects, they should use the same - /// [CompiledWasmModuleStore]. If no [CompiledWasmModuleStore] is specified, - /// `WebAssembly.Module` objects cannot be serialized. - pub compiled_wasm_module_store: Option, - - /// Start inspector instance to allow debuggers to connect. - pub inspector: bool, - - /// Describe if this is the main runtime instance, used by debuggers in some - /// situation - like disconnecting when program finishes running. - pub is_main: bool, -} - -#[derive(Default)] -pub struct RuntimeSnapshotOptions { - /// An optional callback that will be called for each module that is loaded - /// during snapshotting. This callback can be used to transpile source on the - /// fly, during snapshotting, eg. to transpile TypeScript to JavaScript. - pub snapshot_module_load_cb: Option, -} - -impl JsRuntime { - /// Only constructor, configuration is done through `options`. - pub fn new(mut options: RuntimeOptions) -> JsRuntime { - JsRuntime::init_v8(options.v8_platform.take(), cfg!(test)); - JsRuntime::new_inner(options, false, None) - } - - pub(crate) fn state_from( - isolate: &v8::Isolate, - ) -> Rc> { - let state_ptr = isolate.get_data(STATE_DATA_OFFSET); - let state_rc = - // SAFETY: We are sure that it's a valid pointer for whole lifetime of - // the runtime. - unsafe { Rc::from_raw(state_ptr as *const RefCell) }; - let state = state_rc.clone(); - std::mem::forget(state_rc); - state - } - - pub(crate) fn module_map_from( - isolate: &v8::Isolate, - ) -> Rc> { - let module_map_ptr = isolate.get_data(MODULE_MAP_DATA_OFFSET); - let module_map_rc = - // SAFETY: We are sure that it's a valid pointer for whole lifetime of - // the runtime. - unsafe { Rc::from_raw(module_map_ptr as *const RefCell) }; - let module_map = module_map_rc.clone(); - std::mem::forget(module_map_rc); - module_map - } - - pub(crate) fn event_loop_pending_state_from_scope( - scope: &mut v8::HandleScope, - ) -> EventLoopPendingState { - let state = JsRuntime::state_from(scope); - let module_map = JsRuntime::module_map_from(scope); - let state = EventLoopPendingState::new( - scope, - &mut state.borrow_mut(), - &module_map.borrow(), - ); - state - } - - fn init_v8( - v8_platform: Option>, - predictable: bool, - ) { - static DENO_INIT: Once = Once::new(); - static DENO_PREDICTABLE: AtomicBool = AtomicBool::new(false); - static DENO_PREDICTABLE_SET: AtomicBool = AtomicBool::new(false); - - if DENO_PREDICTABLE_SET.load(Ordering::SeqCst) { - let current = DENO_PREDICTABLE.load(Ordering::SeqCst); - assert_eq!(current, predictable, "V8 may only be initialized once in either snapshotting or non-snapshotting mode. Either snapshotting or non-snapshotting mode may be used in a single process, not both."); - DENO_PREDICTABLE_SET.store(true, Ordering::SeqCst); - DENO_PREDICTABLE.store(predictable, Ordering::SeqCst); - } - - DENO_INIT.call_once(move || v8_init(v8_platform, predictable)); - } - - fn new_inner( - mut options: RuntimeOptions, - will_snapshot: bool, - maybe_load_callback: Option, - ) -> JsRuntime { - let init_mode = InitMode::from_options(&options); - let (op_state, ops) = Self::create_opstate(&mut options); - let op_state = Rc::new(RefCell::new(op_state)); - - // Collect event-loop middleware - let mut event_loop_middlewares = - Vec::with_capacity(options.extensions.len()); - for extension in &mut options.extensions { - if let Some(middleware) = extension.init_event_loop_middleware() { - event_loop_middlewares.push(middleware); - } - } - - let align = std::mem::align_of::(); - let layout = std::alloc::Layout::from_size_align( - std::mem::size_of::<*mut v8::OwnedIsolate>(), - align, - ) - .unwrap(); - assert!(layout.size() > 0); - let isolate_ptr: *mut v8::OwnedIsolate = - // SAFETY: we just asserted that layout has non-0 size. - unsafe { std::alloc::alloc(layout) as *mut _ }; - - let state_rc = Rc::new(RefCell::new(JsRuntimeState { - pending_dyn_mod_evaluate: vec![], - pending_mod_evaluate: None, - dyn_module_evaluate_idle_counter: 0, - has_tick_scheduled: false, - source_map_getter: options.source_map_getter.map(Rc::new), - source_map_cache: Default::default(), - shared_array_buffer_store: options.shared_array_buffer_store, - compiled_wasm_module_store: options.compiled_wasm_module_store, - op_state: op_state.clone(), - dispatched_exception: None, - // Some fields are initialized later after isolate is created - inspector: None, - global_realm: None, - known_realms: Vec::with_capacity(1), - })); - - let weak = Rc::downgrade(&state_rc); - let context_state = Rc::new(RefCell::new(ContextState::default())); - let op_ctxs = ops - .into_iter() - .enumerate() - .map(|(id, decl)| { - OpCtx::new( - id as u16, - context_state.clone(), - Rc::new(decl), - op_state.clone(), - weak.clone(), - ) - }) - .collect::>() - .into_boxed_slice(); - context_state.borrow_mut().op_ctxs = op_ctxs; - context_state.borrow_mut().isolate = Some(isolate_ptr); - - let refs = bindings::external_references(&context_state.borrow().op_ctxs); - // V8 takes ownership of external_references. - let refs: &'static v8::ExternalReferences = Box::leak(Box::new(refs)); - - let mut isolate = if will_snapshot { - snapshot_util::create_snapshot_creator( - refs, - options.startup_snapshot.take(), - ) - } else { - let mut params = options - .create_params - .take() - .unwrap_or_default() - .embedder_wrapper_type_info_offsets( - V8_WRAPPER_TYPE_INDEX, - V8_WRAPPER_OBJECT_INDEX, - ) - .external_references(&**refs); - if let Some(snapshot) = options.startup_snapshot.take() { - params = match snapshot { - Snapshot::Static(data) => params.snapshot_blob(data), - Snapshot::JustCreated(data) => params.snapshot_blob(data), - Snapshot::Boxed(data) => params.snapshot_blob(data), - }; - } - v8::Isolate::new(params) - }; - isolate.set_capture_stack_trace_for_uncaught_exceptions(true, 10); - isolate.set_promise_reject_callback(bindings::promise_reject_callback); - isolate.set_host_initialize_import_meta_object_callback( - bindings::host_initialize_import_meta_object_callback, - ); - isolate.set_host_import_module_dynamically_callback( - bindings::host_import_module_dynamically_callback, - ); - isolate.set_wasm_async_resolve_promise_callback( - bindings::wasm_async_resolve_promise_callback, - ); - - let (global_context, snapshotted_data) = { - let scope = &mut v8::HandleScope::new(&mut isolate); - let context = v8::Context::new(scope); - - // Get module map data from the snapshot - let snapshotted_data = if init_mode == InitMode::FromSnapshot { - Some(snapshot_util::get_snapshotted_data(scope, context)) - } else { - None - }; - - (v8::Global::new(scope, context), snapshotted_data) - }; - - // SAFETY: this is first use of `isolate_ptr` so we are sure we're - // not overwriting an existing pointer. - isolate = unsafe { - isolate_ptr.write(isolate); - isolate_ptr.read() - }; - - let mut context_scope: v8::HandleScope = - v8::HandleScope::with_context(&mut isolate, global_context.clone()); - let scope = &mut context_scope; - let context = v8::Local::new(scope, global_context.clone()); - - bindings::initialize_context( - scope, - context, - &context_state.borrow().op_ctxs, - init_mode, - ); - - context.set_slot(scope, context_state.clone()); - - op_state.borrow_mut().put(isolate_ptr); - let inspector = if options.inspector { - Some(JsRuntimeInspector::new(scope, context, options.is_main)) - } else { - None - }; - - let loader = options - .module_loader - .unwrap_or_else(|| Rc::new(NoopModuleLoader)); - - { - let global_realm = JsRealmInner::new( - context_state, - global_context, - state_rc.clone(), - true, - ); - let mut state = state_rc.borrow_mut(); - state.global_realm = Some(JsRealm::new(global_realm.clone())); - state.inspector = inspector; - state.known_realms.push(global_realm); - } - scope.set_data( - STATE_DATA_OFFSET, - Rc::into_raw(state_rc.clone()) as *mut c_void, - ); - let module_map_rc = Rc::new(RefCell::new(ModuleMap::new(loader))); - if let Some(snapshotted_data) = snapshotted_data { - let mut module_map = module_map_rc.borrow_mut(); - module_map.update_with_snapshotted_data(scope, snapshotted_data); - } - scope.set_data( - MODULE_MAP_DATA_OFFSET, - Rc::into_raw(module_map_rc.clone()) as *mut c_void, - ); - - drop(context_scope); - - let mut js_runtime = JsRuntime { - inner: InnerIsolateState { - will_snapshot, - state: ManuallyDropRc(ManuallyDrop::new(state_rc)), - v8_isolate: ManuallyDrop::new(isolate), - }, - init_mode, - allocations: IsolateAllocations::default(), - event_loop_middlewares, - extensions: options.extensions, - module_map: module_map_rc, - is_main: options.is_main, - }; - - let realm = js_runtime.global_realm(); - // TODO(mmastrac): We should thread errors back out of the runtime - js_runtime - .init_extension_js(&realm, maybe_load_callback) - .unwrap(); - - // If the user has requested that we rename modules - if let Some(rename_modules) = options.rename_modules { - js_runtime - .module_map - .borrow_mut() - .clear_module_map(rename_modules.into_iter()); - } - - js_runtime - } - - #[cfg(test)] - #[inline] - pub(crate) fn module_map(&self) -> &Rc> { - &self.module_map - } - - #[inline] - pub fn global_context(&self) -> v8::Global { - self - .inner - .state - .borrow() - .known_realms - .get(0) - .unwrap() - .context() - .clone() - } - - #[inline] - pub fn v8_isolate(&mut self) -> &mut v8::OwnedIsolate { - &mut self.inner.v8_isolate - } - - #[inline] - pub fn inspector(&mut self) -> Rc> { - self.inner.state.borrow().inspector() - } - - #[inline] - pub fn global_realm(&mut self) -> JsRealm { - let state = self.inner.state.borrow(); - state.global_realm.clone().unwrap() - } - - /// Returns the extensions that this runtime is using (including internal ones). - pub fn extensions(&self) -> &Vec { - &self.extensions - } - - /// Creates a new realm (V8 context) in this JS execution context, - /// pre-initialized with all of the extensions that were passed in - /// [`RuntimeOptions::extensions`] when the [`JsRuntime`] was - /// constructed. - pub fn create_realm(&mut self) -> Result { - let realm = { - let context_state = Rc::new(RefCell::new(ContextState::default())); - let op_ctxs: Box<[OpCtx]> = self - .global_realm() - .0 - .state() - .borrow() - .op_ctxs - .iter() - .map(|op_ctx| { - OpCtx::new( - op_ctx.id, - context_state.clone(), - op_ctx.decl.clone(), - op_ctx.state.clone(), - op_ctx.runtime_state.clone(), - ) - }) - .collect(); - context_state.borrow_mut().op_ctxs = op_ctxs; - context_state.borrow_mut().isolate = Some(self.v8_isolate() as _); - - let raw_ptr = self.v8_isolate() as *mut v8::OwnedIsolate; - // SAFETY: Having the scope tied to self's lifetime makes it impossible to - // reference JsRuntimeState::op_ctxs while the scope is alive. Here we - // turn it into an unbound lifetime, which is sound because 1. it only - // lives until the end of this block, and 2. the HandleScope only has - // access to the isolate, and nothing else we're accessing from self does. - let isolate = unsafe { raw_ptr.as_mut() }.unwrap(); - let scope = &mut v8::HandleScope::new(isolate); - let context = v8::Context::new(scope); - let scope = &mut v8::ContextScope::new(scope, context); - - let context = bindings::initialize_context( - scope, - context, - &context_state.borrow().op_ctxs, - self.init_mode, - ); - context.set_slot(scope, context_state.clone()); - let realm = JsRealmInner::new( - context_state, - v8::Global::new(scope, context), - self.inner.state.clone(), - false, - ); - let mut state = self.inner.state.borrow_mut(); - state.known_realms.push(realm.clone()); - JsRealm::new(realm) - }; - - self.init_extension_js(&realm, None)?; - Ok(realm) - } - - #[inline] - pub fn handle_scope(&mut self) -> v8::HandleScope { - self.global_realm().handle_scope(self.v8_isolate()) - } - - /// Initializes JS of provided Extensions in the given realm. - fn init_extension_js( - &mut self, - realm: &JsRealm, - maybe_load_callback: Option, - ) -> Result<(), Error> { - // Initialization of JS happens in phases: - // 1. Iterate through all extensions: - // a. Execute all extension "script" JS files - // b. Load all extension "module" JS files (but do not execute them yet) - // 2. Iterate through all extensions: - // a. If an extension has a `esm_entry_point`, execute it. - - // Take extensions temporarily so we can avoid have a mutable reference to self - let extensions = std::mem::take(&mut self.extensions); - - // TODO(nayeemrmn): Module maps should be per-realm. - let loader = self.module_map.borrow().loader.clone(); - let ext_loader = Rc::new(ExtModuleLoader::new( - &extensions, - maybe_load_callback.map(Rc::new), - )); - self.module_map.borrow_mut().loader = ext_loader; - - let mut esm_entrypoints = vec![]; - - futures::executor::block_on(async { - if self.init_mode == InitMode::New { - for file_source in &*BUILTIN_SOURCES { - realm.execute_script( - self.v8_isolate(), - file_source.specifier, - file_source.load()?, - )?; - } - } - self.init_cbs(realm); - - for extension in &extensions { - let maybe_esm_entry_point = extension.get_esm_entry_point(); - - for file_source in extension.get_esm_sources() { - self - .load_side_module( - &ModuleSpecifier::parse(file_source.specifier)?, - None, - ) - .await?; - } - - if let Some(entry_point) = maybe_esm_entry_point { - esm_entrypoints.push(entry_point); - } - - for file_source in extension.get_js_sources() { - realm.execute_script( - self.v8_isolate(), - file_source.specifier, - file_source.load()?, - )?; - } - } - - for specifier in esm_entrypoints { - let mod_id = { - self - .module_map - .borrow() - .get_id(specifier, AssertedModuleType::JavaScriptOrWasm) - .unwrap_or_else(|| { - panic!("{} not present in the module map", specifier) - }) - }; - let receiver = self.mod_evaluate(mod_id); - self.run_event_loop(false).await?; - receiver - .await? - .with_context(|| format!("Couldn't execute '{specifier}'"))?; - } - - #[cfg(debug_assertions)] - { - let module_map_rc = self.module_map.clone(); - let mut scope = realm.handle_scope(self.v8_isolate()); - let module_map = module_map_rc.borrow(); - module_map.assert_all_modules_evaluated(&mut scope); - } - - Ok::<_, anyhow::Error>(()) - })?; - - self.extensions = extensions; - self.module_map.borrow_mut().loader = loader; - Ok(()) - } - - /// Collects ops from extensions & applies middleware - fn collect_ops(exts: &mut [Extension]) -> Vec { - for (ext, previous_exts) in - exts.iter().enumerate().map(|(i, ext)| (ext, &exts[..i])) - { - ext.check_dependencies(previous_exts); - } - - // Middleware - let middleware: Vec> = exts - .iter_mut() - .filter_map(|e| e.init_middleware()) - .collect(); - - // macroware wraps an opfn in all the middleware - let macroware = move |d| middleware.iter().fold(d, |d, m| m(d)); - - // Flatten ops, apply middleware & override disabled ops - let ops: Vec<_> = exts - .iter_mut() - .filter_map(|e| e.init_ops()) - .flatten() - .map(|d| OpDecl { - name: d.name, - ..macroware(d) - }) - .collect(); - - // In debug build verify there are no duplicate ops. - #[cfg(debug_assertions)] - { - let mut count_by_name = HashMap::new(); - - for op in ops.iter() { - count_by_name - .entry(&op.name) - .or_insert(vec![]) - .push(op.name.to_string()); - } - - let mut duplicate_ops = vec![]; - for (op_name, _count) in - count_by_name.iter().filter(|(_k, v)| v.len() > 1) - { - duplicate_ops.push(op_name.to_string()); - } - if !duplicate_ops.is_empty() { - let mut msg = "Found ops with duplicate names:\n".to_string(); - for op_name in duplicate_ops { - msg.push_str(&format!(" - {}\n", op_name)); - } - msg.push_str("Op names need to be unique."); - panic!("{}", msg); - } - } - - ops - } - - /// Initializes ops of provided Extensions - fn create_opstate(options: &mut RuntimeOptions) -> (OpState, Vec) { - // Add built-in extension - options - .extensions - .insert(0, crate::ops_builtin::core::init_ops()); - - let ops = Self::collect_ops(&mut options.extensions); - - let mut op_state = OpState::new(ops.len()); - - if let Some(get_error_class_fn) = options.get_error_class_fn { - op_state.get_error_class_fn = get_error_class_fn; - } - - // Setup state - for e in &mut options.extensions { - // ops are already registered during in bindings::initialize_context(); - e.init_state(&mut op_state); - } - - (op_state, ops) - } - - pub fn eval<'s, T>( - scope: &mut v8::HandleScope<'s>, - code: &str, - ) -> Option> - where - v8::Local<'s, T>: TryFrom, Error = v8::DataError>, - { - let scope = &mut v8::EscapableHandleScope::new(scope); - let source = v8::String::new(scope, code).unwrap(); - let script = v8::Script::compile(scope, source, None).unwrap(); - let v = script.run(scope)?; - scope.escape(v).try_into().ok() - } - - /// Grabs a reference to core.js' eventLoopTick & buildCustomError - fn init_cbs(&mut self, realm: &JsRealm) { - let (event_loop_tick_cb, build_custom_error_cb) = { - let scope = &mut realm.handle_scope(self.v8_isolate()); - let context = realm.context(); - let context_local = v8::Local::new(scope, context); - let global = context_local.global(scope); - let deno_str = - v8::String::new_external_onebyte_static(scope, b"Deno").unwrap(); - let core_str = - v8::String::new_external_onebyte_static(scope, b"core").unwrap(); - let event_loop_tick_str = - v8::String::new_external_onebyte_static(scope, b"eventLoopTick") - .unwrap(); - let build_custom_error_str = - v8::String::new_external_onebyte_static(scope, b"buildCustomError") - .unwrap(); - - let deno_obj: v8::Local = global - .get(scope, deno_str.into()) - .unwrap() - .try_into() - .unwrap(); - let core_obj: v8::Local = deno_obj - .get(scope, core_str.into()) - .unwrap() - .try_into() - .unwrap(); - - let event_loop_tick_cb: v8::Local = core_obj - .get(scope, event_loop_tick_str.into()) - .unwrap() - .try_into() - .unwrap(); - let build_custom_error_cb: v8::Local = core_obj - .get(scope, build_custom_error_str.into()) - .unwrap() - .try_into() - .unwrap(); - ( - v8::Global::new(scope, event_loop_tick_cb), - v8::Global::new(scope, build_custom_error_cb), - ) - }; - - // Put global handles in the realm's ContextState - let state_rc = realm.0.state(); - let mut state = state_rc.borrow_mut(); - state - .js_event_loop_tick_cb - .replace(Rc::new(event_loop_tick_cb)); - state - .js_build_custom_error_cb - .replace(Rc::new(build_custom_error_cb)); - } - - /// Returns the runtime's op state, which can be used to maintain ops - /// and access resources between op calls. - pub fn op_state(&mut self) -> Rc> { - let state = self.inner.state.borrow(); - state.op_state.clone() - } - - /// Executes traditional JavaScript code (traditional = not ES modules). - /// - /// The execution takes place on the current global context, so it is possible - /// to maintain local JS state and invoke this method multiple times. - /// - /// `name` can be a filepath or any other string, but it is required to be 7-bit ASCII, eg. - /// - /// - "/some/file/path.js" - /// - "" - /// - "[native code]" - /// - /// The same `name` value can be used for multiple executions. - /// - /// `Error` can usually be downcast to `JsError`. - pub fn execute_script( - &mut self, - name: &'static str, - source_code: ModuleCode, - ) -> Result, Error> { - self - .global_realm() - .execute_script(self.v8_isolate(), name, source_code) - } - - /// Executes traditional JavaScript code (traditional = not ES modules). - /// - /// The execution takes place on the current global context, so it is possible - /// to maintain local JS state and invoke this method multiple times. - /// - /// `name` can be a filepath or any other string, but it is required to be 7-bit ASCII, eg. - /// - /// - "/some/file/path.js" - /// - "" - /// - "[native code]" - /// - /// The same `name` value can be used for multiple executions. - /// - /// `Error` can usually be downcast to `JsError`. - pub fn execute_script_static( - &mut self, - name: &'static str, - source_code: &'static str, - ) -> Result, Error> { - self.global_realm().execute_script( - self.v8_isolate(), - name, - ModuleCode::from_static(source_code), - ) - } - - /// Call a function. If it returns a promise, run the event loop until that - /// promise is settled. If the promise rejects or there is an uncaught error - /// in the event loop, return `Err(error)`. Or return `Ok()`. - pub async fn call_and_await( - &mut self, - function: &v8::Global, - ) -> Result, Error> { - let promise = { - let scope = &mut self.handle_scope(); - let cb = function.open(scope); - let this = v8::undefined(scope).into(); - let promise = cb.call(scope, this, &[]); - if promise.is_none() || scope.is_execution_terminating() { - let undefined = v8::undefined(scope).into(); - return exception_to_err_result(scope, undefined, false); - } - v8::Global::new(scope, promise.unwrap()) - }; - self.resolve_value(promise).await - } - - /// Returns the namespace object of a module. - /// - /// This is only available after module evaluation has completed. - /// This function panics if module has not been instantiated. - pub fn get_module_namespace( - &mut self, - module_id: ModuleId, - ) -> Result, Error> { - self - .module_map - .clone() - .borrow() - .get_module_namespace(&mut self.handle_scope(), module_id) - } - - /// Registers a callback on the isolate when the memory limits are approached. - /// Use this to prevent V8 from crashing the process when reaching the limit. - /// - /// Calls the closure with the current heap limit and the initial heap limit. - /// The return value of the closure is set as the new limit. - pub fn add_near_heap_limit_callback(&mut self, cb: C) - where - C: FnMut(usize, usize) -> usize + 'static, - { - let boxed_cb = Box::new(RefCell::new(cb)); - let data = boxed_cb.as_ptr() as *mut c_void; - - let prev = self - .allocations - .near_heap_limit_callback_data - .replace((boxed_cb, near_heap_limit_callback::)); - if let Some((_, prev_cb)) = prev { - self - .v8_isolate() - .remove_near_heap_limit_callback(prev_cb, 0); - } - - self - .v8_isolate() - .add_near_heap_limit_callback(near_heap_limit_callback::, data); - } - - pub fn remove_near_heap_limit_callback(&mut self, heap_limit: usize) { - if let Some((_, cb)) = self.allocations.near_heap_limit_callback_data.take() - { - self - .v8_isolate() - .remove_near_heap_limit_callback(cb, heap_limit); - } - } - - fn pump_v8_message_loop(&mut self) -> Result<(), Error> { - let scope = &mut self.handle_scope(); - while v8::Platform::pump_message_loop( - &v8::V8::get_current_platform(), - scope, - false, // don't block if there are no tasks - ) { - // do nothing - } - - let tc_scope = &mut v8::TryCatch::new(scope); - tc_scope.perform_microtask_checkpoint(); - match tc_scope.exception() { - None => Ok(()), - Some(exception) => exception_to_err_result(tc_scope, exception, false), - } - } - - pub fn maybe_init_inspector(&mut self) { - if self.inner.state.borrow().inspector.is_some() { - return; - } - - let context = self.global_context(); - let scope = &mut v8::HandleScope::with_context( - self.inner.v8_isolate.as_mut(), - context.clone(), - ); - let context = v8::Local::new(scope, context); - - let mut state = self.inner.state.borrow_mut(); - state.inspector = - Some(JsRuntimeInspector::new(scope, context, self.is_main)); - } - - pub fn poll_value( - &mut self, - global: &v8::Global, - cx: &mut Context, - ) -> Poll, Error>> { - let state = self.poll_event_loop(cx, false); - - let mut scope = self.handle_scope(); - let local = v8::Local::::new(&mut scope, global); - - if let Ok(promise) = v8::Local::::try_from(local) { - match promise.state() { - v8::PromiseState::Pending => match state { - Poll::Ready(Ok(_)) => { - let msg = "Promise resolution is still pending but the event loop has already resolved."; - Poll::Ready(Err(generic_error(msg))) - } - Poll::Ready(Err(e)) => Poll::Ready(Err(e)), - Poll::Pending => Poll::Pending, - }, - v8::PromiseState::Fulfilled => { - let value = promise.result(&mut scope); - let value_handle = v8::Global::new(&mut scope, value); - Poll::Ready(Ok(value_handle)) - } - v8::PromiseState::Rejected => { - let exception = promise.result(&mut scope); - Poll::Ready(exception_to_err_result(&mut scope, exception, false)) - } - } - } else { - let value_handle = v8::Global::new(&mut scope, local); - Poll::Ready(Ok(value_handle)) - } - } - - /// Waits for the given value to resolve while polling the event loop. - /// - /// This future resolves when either the value is resolved or the event loop runs to - /// completion. - pub async fn resolve_value( - &mut self, - global: v8::Global, - ) -> Result, Error> { - poll_fn(|cx| self.poll_value(&global, cx)).await - } - - /// Runs event loop to completion - /// - /// This future resolves when: - /// - there are no more pending dynamic imports - /// - there are no more pending ops - /// - there are no more active inspector sessions (only if `wait_for_inspector` is set to true) - pub async fn run_event_loop( - &mut self, - wait_for_inspector: bool, - ) -> Result<(), Error> { - poll_fn(|cx| self.poll_event_loop(cx, wait_for_inspector)).await - } - - /// Runs a single tick of event loop - /// - /// If `wait_for_inspector` is set to true event loop - /// will return `Poll::Pending` if there are active inspector sessions. - pub fn poll_event_loop( - &mut self, - cx: &mut Context, - wait_for_inspector: bool, - ) -> Poll> { - let has_inspector: bool; - - { - let state = self.inner.state.borrow(); - has_inspector = state.inspector.is_some(); - state.op_state.borrow().waker.register(cx.waker()); - } - - if has_inspector { - // We poll the inspector first. - let _ = self.inspector().borrow().poll_sessions(Some(cx)).unwrap(); - } - - let module_map = self.module_map.clone(); - self.pump_v8_message_loop()?; - - // Dynamic module loading - ie. modules loaded using "import()" - { - // Run in a loop so that dynamic imports that only depend on another - // dynamic import can be resolved in this event loop iteration. - // - // For example, a dynamically imported module like the following can be - // immediately resolved after `dependency.ts` is fully evaluated, but it - // wouldn't if not for this loop. - // - // await delay(1000); - // await import("./dependency.ts"); - // console.log("test") - // - loop { - let poll_imports = self.prepare_dyn_imports(cx)?; - assert!(poll_imports.is_ready()); - - let poll_imports = self.poll_dyn_imports(cx)?; - assert!(poll_imports.is_ready()); - - if !self.evaluate_dyn_imports() { - break; - } - } - } - - // Resolve async ops, run all next tick callbacks and macrotasks callbacks - // and only then check for any promise exceptions (`unhandledrejection` - // handlers are run in macrotasks callbacks so we need to let them run - // first). - self.do_js_event_loop_tick(cx)?; - self.check_promise_rejections()?; - - // Event loop middlewares - let mut maybe_scheduling = false; - { - let op_state = self.inner.state.borrow().op_state.clone(); - for f in &self.event_loop_middlewares { - if f(op_state.clone(), cx) { - maybe_scheduling = true; - } - } - } - - // Top level module - self.evaluate_pending_module(); - - let pending_state = self.event_loop_pending_state(); - if !pending_state.is_pending() && !maybe_scheduling { - if has_inspector { - let inspector = self.inspector(); - let has_active_sessions = inspector.borrow().has_active_sessions(); - let has_blocking_sessions = inspector.borrow().has_blocking_sessions(); - - if wait_for_inspector && has_active_sessions { - // If there are no blocking sessions (eg. REPL) we can now notify - // debugger that the program has finished running and we're ready - // to exit the process once debugger disconnects. - if !has_blocking_sessions { - let context = self.global_context(); - let scope = &mut self.handle_scope(); - inspector.borrow_mut().context_destroyed(scope, context); - println!("Program finished. Waiting for inspector to disconnect to exit the process..."); - } - - return Poll::Pending; - } - } - - return Poll::Ready(Ok(())); - } - - let state = self.inner.state.borrow(); - - // Check if more async ops have been dispatched - // during this turn of event loop. - // If there are any pending background tasks, we also wake the runtime to - // make sure we don't miss them. - // TODO(andreubotella) The event loop will spin as long as there are pending - // background tasks. We should look into having V8 notify us when a - // background task is done. - if pending_state.has_pending_background_tasks - || pending_state.has_tick_scheduled - || maybe_scheduling - { - state.op_state.borrow().waker.wake(); - } - - drop(state); - - if pending_state.has_pending_module_evaluation { - if pending_state.has_pending_refed_ops - || pending_state.has_pending_dyn_imports - || pending_state.has_pending_dyn_module_evaluation - || pending_state.has_pending_background_tasks - || pending_state.has_tick_scheduled - || maybe_scheduling - { - // pass, will be polled again - } else { - let scope = &mut self.handle_scope(); - let messages = module_map.borrow().find_stalled_top_level_await(scope); - // We are gonna print only a single message to provide a nice formatting - // with source line of offending promise shown. Once user fixed it, then - // they will get another error message for the next promise (but this - // situation is gonna be very rare, if ever happening). - assert!(!messages.is_empty()); - let msg = v8::Local::new(scope, messages[0].clone()); - let js_error = JsError::from_v8_message(scope, msg); - return Poll::Ready(Err(js_error.into())); - } - } - - if pending_state.has_pending_dyn_module_evaluation { - if pending_state.has_pending_refed_ops - || pending_state.has_pending_dyn_imports - || pending_state.has_pending_background_tasks - || pending_state.has_tick_scheduled - { - // pass, will be polled again - } else if self.inner.state.borrow().dyn_module_evaluate_idle_counter >= 1 - { - let scope = &mut self.handle_scope(); - let messages = module_map.borrow().find_stalled_top_level_await(scope); - // We are gonna print only a single message to provide a nice formatting - // with source line of offending promise shown. Once user fixed it, then - // they will get another error message for the next promise (but this - // situation is gonna be very rare, if ever happening). - assert!(!messages.is_empty()); - let msg = v8::Local::new(scope, messages[0].clone()); - let js_error = JsError::from_v8_message(scope, msg); - return Poll::Ready(Err(js_error.into())); - } else { - let mut state = self.inner.state.borrow_mut(); - // Delay the above error by one spin of the event loop. A dynamic import - // evaluation may complete during this, in which case the counter will - // reset. - state.dyn_module_evaluate_idle_counter += 1; - state.op_state.borrow().waker.wake(); - } - } - - Poll::Pending - } - - fn event_loop_pending_state(&mut self) -> EventLoopPendingState { - let mut scope = v8::HandleScope::new(self.inner.v8_isolate.as_mut()); - EventLoopPendingState::new( - &mut scope, - &mut self.inner.state.borrow_mut(), - &self.module_map.borrow(), - ) - } -} - -impl JsRuntimeForSnapshot { - pub fn new( - mut options: RuntimeOptions, - runtime_snapshot_options: RuntimeSnapshotOptions, - ) -> JsRuntimeForSnapshot { - JsRuntime::init_v8(options.v8_platform.take(), true); - JsRuntimeForSnapshot(JsRuntime::new_inner( - options, - true, - runtime_snapshot_options.snapshot_module_load_cb, - )) - } - - /// Takes a snapshot and consumes the runtime. - /// - /// `Error` can usually be downcast to `JsError`. - pub fn snapshot(mut self) -> v8::StartupData { - // Ensure there are no live inspectors to prevent crashes. - self.inner.prepare_for_cleanup(); - - // Set the context to be snapshot's default context - { - let context = self.global_context(); - let mut scope = self.handle_scope(); - let local_context = v8::Local::new(&mut scope, context); - scope.set_default_context(local_context); - } - - // Serialize the module map and store its data in the snapshot. - { - let snapshotted_data = { - // `self.module_map` points directly to the v8 isolate data slot, which - // we must explicitly drop before destroying the isolate. We have to - // take and drop this `Rc` before that. - let module_map_rc = std::mem::take(&mut self.module_map); - let module_map = module_map_rc.borrow(); - module_map.serialize_for_snapshotting(&mut self.handle_scope()) - }; - - let context = self.global_context(); - let mut scope = self.handle_scope(); - snapshot_util::set_snapshotted_data( - &mut scope, - context, - snapshotted_data, - ); - } - - self - .0 - .inner - .prepare_for_snapshot() - .create_blob(v8::FunctionCodeHandling::Keep) - .unwrap() - } -} - -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub(crate) struct EventLoopPendingState { - has_pending_refed_ops: bool, - has_pending_dyn_imports: bool, - has_pending_dyn_module_evaluation: bool, - has_pending_module_evaluation: bool, - has_pending_background_tasks: bool, - has_tick_scheduled: bool, -} -impl EventLoopPendingState { - pub fn new( - scope: &mut v8::HandleScope<()>, - state: &mut JsRuntimeState, - module_map: &ModuleMap, - ) -> EventLoopPendingState { - let mut num_unrefed_ops = 0; - let mut num_pending_ops = 0; - for realm in &state.known_realms { - num_unrefed_ops += realm.num_unrefed_ops(); - num_pending_ops += realm.num_pending_ops(); - } - - EventLoopPendingState { - has_pending_refed_ops: num_pending_ops > num_unrefed_ops, - has_pending_dyn_imports: module_map.has_pending_dynamic_imports(), - has_pending_dyn_module_evaluation: !state - .pending_dyn_mod_evaluate - .is_empty(), - has_pending_module_evaluation: state.pending_mod_evaluate.is_some(), - has_pending_background_tasks: scope.has_pending_background_tasks(), - has_tick_scheduled: state.has_tick_scheduled, - } - } - - pub fn is_pending(&self) -> bool { - self.has_pending_refed_ops - || self.has_pending_dyn_imports - || self.has_pending_dyn_module_evaluation - || self.has_pending_module_evaluation - || self.has_pending_background_tasks - || self.has_tick_scheduled - } -} - -extern "C" fn near_heap_limit_callback( - data: *mut c_void, - current_heap_limit: usize, - initial_heap_limit: usize, -) -> usize -where - F: FnMut(usize, usize) -> usize, -{ - // SAFETY: The data is a pointer to the Rust callback function. It is stored - // in `JsRuntime::allocations` and thus is guaranteed to outlive the isolate. - let callback = unsafe { &mut *(data as *mut F) }; - callback(current_heap_limit, initial_heap_limit) -} - -impl JsRuntimeState { - pub(crate) fn inspector(&self) -> Rc> { - self.inspector.as_ref().unwrap().clone() - } - - /// Called by `bindings::host_import_module_dynamically_callback` - /// after initiating new dynamic import load. - pub fn notify_new_dynamic_import(&mut self) { - // Notify event loop to poll again soon. - self.op_state.borrow().waker.wake(); - } -} - -// Related to module loading -impl JsRuntime { - pub(crate) fn instantiate_module( - &mut self, - id: ModuleId, - ) -> Result<(), v8::Global> { - self - .module_map - .clone() - .borrow_mut() - .instantiate_module(&mut self.handle_scope(), id) - } - - fn dynamic_import_module_evaluate( - &mut self, - load_id: ModuleLoadId, - id: ModuleId, - ) -> Result<(), Error> { - let module_handle = self - .module_map - .borrow() - .get_handle(id) - .expect("ModuleInfo not found"); - - let status = { - let scope = &mut self.handle_scope(); - let module = module_handle.open(scope); - module.get_status() - }; - - match status { - v8::ModuleStatus::Instantiated | v8::ModuleStatus::Evaluated => {} - _ => return Ok(()), - } - - // IMPORTANT: Top-level-await is enabled, which means that return value - // of module evaluation is a promise. - // - // This promise is internal, and not the same one that gets returned to - // the user. We add an empty `.catch()` handler so that it does not result - // in an exception if it rejects. That will instead happen for the other - // promise if not handled by the user. - // - // For more details see: - // https://github.com/denoland/deno/issues/4908 - // https://v8.dev/features/top-level-await#module-execution-order - let global_realm = - self.inner.state.borrow_mut().global_realm.clone().unwrap(); - let scope = &mut global_realm.handle_scope(&mut self.inner.v8_isolate); - let tc_scope = &mut v8::TryCatch::new(scope); - let module = v8::Local::new(tc_scope, &module_handle); - let maybe_value = module.evaluate(tc_scope); - - // Update status after evaluating. - let status = module.get_status(); - - if let Some(value) = maybe_value { - assert!( - status == v8::ModuleStatus::Evaluated - || status == v8::ModuleStatus::Errored - ); - let promise = v8::Local::::try_from(value) - .expect("Expected to get promise as module evaluation result"); - let empty_fn = bindings::create_empty_fn(tc_scope).unwrap(); - promise.catch(tc_scope, empty_fn); - let promise_global = v8::Global::new(tc_scope, promise); - let module_global = v8::Global::new(tc_scope, module); - - let dyn_import_mod_evaluate = DynImportModEvaluate { - load_id, - module_id: id, - promise: promise_global, - module: module_global, - }; - - self - .inner - .state - .borrow_mut() - .pending_dyn_mod_evaluate - .push(dyn_import_mod_evaluate); - } else if tc_scope.has_terminated() || tc_scope.is_execution_terminating() { - return Err( - generic_error("Cannot evaluate dynamically imported module, because JavaScript execution has been terminated.") - ); - } else { - assert!(status == v8::ModuleStatus::Errored); - } - - Ok(()) - } - - // TODO(bartlomieju): make it return `ModuleEvaluationFuture`? - /// Evaluates an already instantiated ES module. - /// - /// Returns a receiver handle that resolves when module promise resolves. - /// Implementors must manually call [`JsRuntime::run_event_loop`] to drive - /// module evaluation future. - /// - /// `Error` can usually be downcast to `JsError` and should be awaited and - /// checked after [`JsRuntime::run_event_loop`] completion. - /// - /// This function panics if module has not been instantiated. - pub fn mod_evaluate( - &mut self, - id: ModuleId, - ) -> oneshot::Receiver> { - let global_realm = self.global_realm(); - let state_rc = self.inner.state.clone(); - let module_map_rc = self.module_map.clone(); - let scope = &mut self.handle_scope(); - let tc_scope = &mut v8::TryCatch::new(scope); - - let module = module_map_rc - .borrow() - .get_handle(id) - .map(|handle| v8::Local::new(tc_scope, handle)) - .expect("ModuleInfo not found"); - let mut status = module.get_status(); - assert_eq!( - status, - v8::ModuleStatus::Instantiated, - "Module not instantiated {id}" - ); - - let (sender, receiver) = oneshot::channel(); - - // IMPORTANT: Top-level-await is enabled, which means that return value - // of module evaluation is a promise. - // - // Because that promise is created internally by V8, when error occurs during - // module evaluation the promise is rejected, and since the promise has no rejection - // handler it will result in call to `bindings::promise_reject_callback` adding - // the promise to pending promise rejection table - meaning JsRuntime will return - // error on next poll(). - // - // This situation is not desirable as we want to manually return error at the - // end of this function to handle it further. It means we need to manually - // remove this promise from pending promise rejection table. - // - // For more details see: - // https://github.com/denoland/deno/issues/4908 - // https://v8.dev/features/top-level-await#module-execution-order - { - let mut state = state_rc.borrow_mut(); - assert!( - state.pending_mod_evaluate.is_none(), - "There is already pending top level module evaluation" - ); - state.pending_mod_evaluate = Some(ModEvaluate { - promise: None, - has_evaluated: false, - handled_promise_rejections: vec![], - sender, - }); - } - - let maybe_value = module.evaluate(tc_scope); - { - let mut state = state_rc.borrow_mut(); - let pending_mod_evaluate = state.pending_mod_evaluate.as_mut().unwrap(); - pending_mod_evaluate.has_evaluated = true; - } - - // Update status after evaluating. - status = module.get_status(); - - let has_dispatched_exception = - state_rc.borrow_mut().dispatched_exception.is_some(); - if has_dispatched_exception { - // This will be overridden in `exception_to_err_result()`. - let exception = v8::undefined(tc_scope).into(); - let pending_mod_evaluate = { - let mut state = state_rc.borrow_mut(); - state.pending_mod_evaluate.take().unwrap() - }; - pending_mod_evaluate - .sender - .send(exception_to_err_result(tc_scope, exception, false)) - .expect("Failed to send module evaluation error."); - } else if let Some(value) = maybe_value { - assert!( - status == v8::ModuleStatus::Evaluated - || status == v8::ModuleStatus::Errored - ); - let promise = v8::Local::::try_from(value) - .expect("Expected to get promise as module evaluation result"); - let promise_global = v8::Global::new(tc_scope, promise); - let mut state = state_rc.borrow_mut(); - { - let pending_mod_evaluate = state.pending_mod_evaluate.as_ref().unwrap(); - let pending_rejection_was_already_handled = pending_mod_evaluate - .handled_promise_rejections - .contains(&promise_global); - if !pending_rejection_was_already_handled { - global_realm - .0 - .state() - .borrow_mut() - .pending_promise_rejections - .retain(|(key, _)| key != &promise_global); - } - } - let promise_global = v8::Global::new(tc_scope, promise); - state.pending_mod_evaluate.as_mut().unwrap().promise = - Some(promise_global); - tc_scope.perform_microtask_checkpoint(); - } else if tc_scope.has_terminated() || tc_scope.is_execution_terminating() { - let pending_mod_evaluate = { - let mut state = state_rc.borrow_mut(); - state.pending_mod_evaluate.take().unwrap() - }; - pending_mod_evaluate.sender.send(Err( - generic_error("Cannot evaluate module, because JavaScript execution has been terminated.") - )).expect("Failed to send module evaluation error."); - } else { - assert!(status == v8::ModuleStatus::Errored); - } - - receiver - } - - fn dynamic_import_reject( - &mut self, - id: ModuleLoadId, - exception: v8::Global, - ) { - let module_map_rc = self.module_map.clone(); - let scope = &mut self.handle_scope(); - - let resolver_handle = module_map_rc - .borrow_mut() - .dynamic_import_map - .remove(&id) - .expect("Invalid dynamic import id"); - let resolver = resolver_handle.open(scope); - - // IMPORTANT: No borrows to `ModuleMap` can be held at this point because - // rejecting the promise might initiate another `import()` which will - // in turn call `bindings::host_import_module_dynamically_callback` which - // will reach into `ModuleMap` from within the isolate. - let exception = v8::Local::new(scope, exception); - resolver.reject(scope, exception).unwrap(); - scope.perform_microtask_checkpoint(); - } - - fn dynamic_import_resolve(&mut self, id: ModuleLoadId, mod_id: ModuleId) { - let state_rc = self.inner.state.clone(); - let module_map_rc = self.module_map.clone(); - let scope = &mut self.handle_scope(); - - let resolver_handle = module_map_rc - .borrow_mut() - .dynamic_import_map - .remove(&id) - .expect("Invalid dynamic import id"); - let resolver = resolver_handle.open(scope); - - let module = { - module_map_rc - .borrow() - .get_handle(mod_id) - .map(|handle| v8::Local::new(scope, handle)) - .expect("Dyn import module info not found") - }; - // Resolution success - assert_eq!(module.get_status(), v8::ModuleStatus::Evaluated); - - // IMPORTANT: No borrows to `ModuleMap` can be held at this point because - // resolving the promise might initiate another `import()` which will - // in turn call `bindings::host_import_module_dynamically_callback` which - // will reach into `ModuleMap` from within the isolate. - let module_namespace = module.get_module_namespace(); - resolver.resolve(scope, module_namespace).unwrap(); - state_rc.borrow_mut().dyn_module_evaluate_idle_counter = 0; - scope.perform_microtask_checkpoint(); - } - - fn prepare_dyn_imports( - &mut self, - cx: &mut Context, - ) -> Poll> { - if self - .module_map - .borrow() - .preparing_dynamic_imports - .is_empty() - { - return Poll::Ready(Ok(())); - } - - loop { - let poll_result = self - .module_map - .borrow_mut() - .preparing_dynamic_imports - .poll_next_unpin(cx); - - if let Poll::Ready(Some(prepare_poll)) = poll_result { - let dyn_import_id = prepare_poll.0; - let prepare_result = prepare_poll.1; - - match prepare_result { - Ok(load) => { - self - .module_map - .borrow_mut() - .pending_dynamic_imports - .push(load.into_future()); - } - Err(err) => { - let exception = to_v8_type_error(&mut self.handle_scope(), err); - self.dynamic_import_reject(dyn_import_id, exception); - } - } - // Continue polling for more prepared dynamic imports. - continue; - } - - // There are no active dynamic import loads, or none are ready. - return Poll::Ready(Ok(())); - } - } - - fn poll_dyn_imports(&mut self, cx: &mut Context) -> Poll> { - if self.module_map.borrow().pending_dynamic_imports.is_empty() { - return Poll::Ready(Ok(())); - } - - loop { - let poll_result = self - .module_map - .borrow_mut() - .pending_dynamic_imports - .poll_next_unpin(cx); - - if let Poll::Ready(Some(load_stream_poll)) = poll_result { - let maybe_result = load_stream_poll.0; - let mut load = load_stream_poll.1; - let dyn_import_id = load.id; - - if let Some(load_stream_result) = maybe_result { - match load_stream_result { - Ok((request, info)) => { - // A module (not necessarily the one dynamically imported) has been - // fetched. Create and register it, and if successful, poll for the - // next recursive-load event related to this dynamic import. - let register_result = load.register_and_recurse( - &mut self.handle_scope(), - &request, - info, - ); - - match register_result { - Ok(()) => { - // Keep importing until it's fully drained - self - .module_map - .borrow_mut() - .pending_dynamic_imports - .push(load.into_future()); - } - Err(err) => { - let exception = match err { - ModuleError::Exception(e) => e, - ModuleError::Other(e) => { - to_v8_type_error(&mut self.handle_scope(), e) - } - }; - self.dynamic_import_reject(dyn_import_id, exception) - } - } - } - Err(err) => { - // A non-javascript error occurred; this could be due to a an invalid - // module specifier, or a problem with the source map, or a failure - // to fetch the module source code. - let exception = to_v8_type_error(&mut self.handle_scope(), err); - self.dynamic_import_reject(dyn_import_id, exception); - } - } - } else { - // The top-level module from a dynamic import has been instantiated. - // Load is done. - let module_id = - load.root_module_id.expect("Root module should be loaded"); - let result = self.instantiate_module(module_id); - if let Err(exception) = result { - self.dynamic_import_reject(dyn_import_id, exception); - } - self.dynamic_import_module_evaluate(dyn_import_id, module_id)?; - } - - // Continue polling for more ready dynamic imports. - continue; - } - - // There are no active dynamic import loads, or none are ready. - return Poll::Ready(Ok(())); - } - } - - /// "deno_core" runs V8 with Top Level Await enabled. It means that each - /// module evaluation returns a promise from V8. - /// Feature docs: https://v8.dev/features/top-level-await - /// - /// This promise resolves after all dependent modules have also - /// resolved. Each dependent module may perform calls to "import()" and APIs - /// using async ops will add futures to the runtime's event loop. - /// It means that the promise returned from module evaluation will - /// resolve only after all futures in the event loop are done. - /// - /// Thus during turn of event loop we need to check if V8 has - /// resolved or rejected the promise. If the promise is still pending - /// then another turn of event loop must be performed. - fn evaluate_pending_module(&mut self) { - let maybe_module_evaluation = - self.inner.state.borrow_mut().pending_mod_evaluate.take(); - - if maybe_module_evaluation.is_none() { - return; - } - - let mut module_evaluation = maybe_module_evaluation.unwrap(); - let state_rc = self.inner.state.clone(); - let scope = &mut self.handle_scope(); - - let promise_global = module_evaluation.promise.clone().unwrap(); - let promise = promise_global.open(scope); - let promise_state = promise.state(); - - match promise_state { - v8::PromiseState::Pending => { - // NOTE: `poll_event_loop` will decide if - // runtime would be woken soon - state_rc.borrow_mut().pending_mod_evaluate = Some(module_evaluation); - } - v8::PromiseState::Fulfilled => { - scope.perform_microtask_checkpoint(); - // Receiver end might have been already dropped, ignore the result - let _ = module_evaluation.sender.send(Ok(())); - module_evaluation.handled_promise_rejections.clear(); - } - v8::PromiseState::Rejected => { - let exception = promise.result(scope); - scope.perform_microtask_checkpoint(); - - // Receiver end might have been already dropped, ignore the result - if module_evaluation - .handled_promise_rejections - .contains(&promise_global) - { - let _ = module_evaluation.sender.send(Ok(())); - module_evaluation.handled_promise_rejections.clear(); - } else { - let _ = module_evaluation - .sender - .send(exception_to_err_result(scope, exception, false)); - } - } - } - } - - // Returns true if some dynamic import was resolved. - fn evaluate_dyn_imports(&mut self) -> bool { - let pending = std::mem::take( - &mut self.inner.state.borrow_mut().pending_dyn_mod_evaluate, - ); - if pending.is_empty() { - return false; - } - let mut resolved_any = false; - let mut still_pending = vec![]; - for pending_dyn_evaluate in pending { - let maybe_result = { - let scope = &mut self.handle_scope(); - - let module_id = pending_dyn_evaluate.module_id; - let promise = pending_dyn_evaluate.promise.open(scope); - let _module = pending_dyn_evaluate.module.open(scope); - let promise_state = promise.state(); - - match promise_state { - v8::PromiseState::Pending => { - still_pending.push(pending_dyn_evaluate); - None - } - v8::PromiseState::Fulfilled => { - Some(Ok((pending_dyn_evaluate.load_id, module_id))) - } - v8::PromiseState::Rejected => { - let exception = promise.result(scope); - let exception = v8::Global::new(scope, exception); - Some(Err((pending_dyn_evaluate.load_id, exception))) - } - } - }; - - if let Some(result) = maybe_result { - resolved_any = true; - match result { - Ok((dyn_import_id, module_id)) => { - self.dynamic_import_resolve(dyn_import_id, module_id); - } - Err((dyn_import_id, exception)) => { - self.dynamic_import_reject(dyn_import_id, exception); - } - } - } - } - self.inner.state.borrow_mut().pending_dyn_mod_evaluate = still_pending; - resolved_any - } - - /// Asynchronously load specified module and all of its dependencies. - /// - /// The module will be marked as "main", and because of that - /// "import.meta.main" will return true when checked inside that module. - /// - /// User must call [`JsRuntime::mod_evaluate`] with returned `ModuleId` - /// manually after load is finished. - pub async fn load_main_module( - &mut self, - specifier: &ModuleSpecifier, - code: Option, - ) -> Result { - let module_map_rc = self.module_map.clone(); - if let Some(code) = code { - let specifier = specifier.as_str().to_owned().into(); - let scope = &mut self.handle_scope(); - // true for main module - module_map_rc - .borrow_mut() - .new_es_module(scope, true, specifier, code, false) - .map_err(|e| match e { - ModuleError::Exception(exception) => { - let exception = v8::Local::new(scope, exception); - exception_to_err_result::<()>(scope, exception, false).unwrap_err() - } - ModuleError::Other(error) => error, - })?; - } - - let mut load = - ModuleMap::load_main(module_map_rc.clone(), &specifier).await?; - - while let Some(load_result) = load.next().await { - let (request, info) = load_result?; - let scope = &mut self.handle_scope(); - load.register_and_recurse(scope, &request, info).map_err( - |e| match e { - ModuleError::Exception(exception) => { - let exception = v8::Local::new(scope, exception); - exception_to_err_result::<()>(scope, exception, false).unwrap_err() - } - ModuleError::Other(error) => error, - }, - )?; - } - - let root_id = load.root_module_id.expect("Root module should be loaded"); - self.instantiate_module(root_id).map_err(|e| { - let scope = &mut self.handle_scope(); - let exception = v8::Local::new(scope, e); - exception_to_err_result::<()>(scope, exception, false).unwrap_err() - })?; - Ok(root_id) - } - - /// Asynchronously load specified ES module and all of its dependencies. - /// - /// This method is meant to be used when loading some utility code that - /// might be later imported by the main module (ie. an entry point module). - /// - /// User must call [`JsRuntime::mod_evaluate`] with returned `ModuleId` - /// manually after load is finished. - pub async fn load_side_module( - &mut self, - specifier: &ModuleSpecifier, - code: Option, - ) -> Result { - let module_map_rc = self.module_map.clone(); - if let Some(code) = code { - let specifier = specifier.as_str().to_owned().into(); - let scope = &mut self.handle_scope(); - // false for side module (not main module) - module_map_rc - .borrow_mut() - .new_es_module(scope, false, specifier, code, false) - .map_err(|e| match e { - ModuleError::Exception(exception) => { - let exception = v8::Local::new(scope, exception); - exception_to_err_result::<()>(scope, exception, false).unwrap_err() - } - ModuleError::Other(error) => error, - })?; - } - - let mut load = - ModuleMap::load_side(module_map_rc.clone(), &specifier).await?; - - while let Some(load_result) = load.next().await { - let (request, info) = load_result?; - let scope = &mut self.handle_scope(); - load.register_and_recurse(scope, &request, info).map_err( - |e| match e { - ModuleError::Exception(exception) => { - let exception = v8::Local::new(scope, exception); - exception_to_err_result::<()>(scope, exception, false).unwrap_err() - } - ModuleError::Other(error) => error, - }, - )?; - } - - let root_id = load.root_module_id.expect("Root module should be loaded"); - self.instantiate_module(root_id).map_err(|e| { - let scope = &mut self.handle_scope(); - let exception = v8::Local::new(scope, e); - exception_to_err_result::<()>(scope, exception, false).unwrap_err() - })?; - Ok(root_id) - } - - fn check_promise_rejections(&mut self) -> Result<(), Error> { - let state = self.inner.state.clone(); - let scope = &mut self.handle_scope(); - let state = state.borrow(); - for realm in &state.known_realms { - realm.check_promise_rejections(scope)?; - } - Ok(()) - } - - // Polls pending ops and then runs `Deno.core.eventLoopTick` callback. - fn do_js_event_loop_tick(&mut self, cx: &mut Context) -> Result<(), Error> { - // Handle responses for each realm. - let state = self.inner.state.clone(); - let isolate = &mut self.inner.v8_isolate; - let realm_count = state.borrow().known_realms.len(); - for realm_idx in 0..realm_count { - let realm = state.borrow().known_realms.get(realm_idx).unwrap().clone(); - let context_state = realm.state(); - let mut context_state = context_state.borrow_mut(); - let scope = &mut realm.handle_scope(isolate); - - // We return async responses to JS in unbounded batches (may change), - // each batch is a flat vector of tuples: - // `[promise_id1, op_result1, promise_id2, op_result2, ...]` - // promise_id is a simple integer, op_result is an ops::OpResult - // which contains a value OR an error, encoded as a tuple. - // This batch is received in JS via the special `arguments` variable - // and then each tuple is used to resolve or reject promises - // - // This can handle 15 promises futures in a single batch without heap - // allocations. - let mut args: SmallVec<[v8::Local; 32]> = - SmallVec::with_capacity(32); - - loop { - let Poll::Ready(item) = context_state.pending_ops.poll_join_next(cx) else { - break; - }; - // TODO(mmastrac): If this task is really errored, things could be pretty bad - let (promise_id, op_id, mut resp) = item.unwrap(); - state - .borrow() - .op_state - .borrow() - .tracker - .track_async_completed(op_id); - context_state.unrefed_ops.remove(&promise_id); - args.push(v8::Integer::new(scope, promise_id).into()); - args.push(match resp.to_v8(scope) { - Ok(v) => v, - Err(e) => OpResult::Err(OpError::new(&|_| "TypeError", e.into())) - .to_v8(scope) - .unwrap(), - }); - } - - let has_tick_scheduled = - v8::Boolean::new(scope, self.inner.state.borrow().has_tick_scheduled); - args.push(has_tick_scheduled.into()); - - let js_event_loop_tick_cb_handle = - context_state.js_event_loop_tick_cb.clone().unwrap(); - let tc_scope = &mut v8::TryCatch::new(scope); - let js_event_loop_tick_cb = js_event_loop_tick_cb_handle.open(tc_scope); - let this = v8::undefined(tc_scope).into(); - drop(context_state); - js_event_loop_tick_cb.call(tc_scope, this, args.as_slice()); - - if let Some(exception) = tc_scope.exception() { - // TODO(@andreubotella): Returning here can cause async ops in other - // realms to never resolve. - return exception_to_err_result(tc_scope, exception, false); - } - - if tc_scope.has_terminated() || tc_scope.is_execution_terminating() { - return Ok(()); - } - } - - Ok(()) - } -} diff --git a/core/runtime/mod.rs b/core/runtime/mod.rs deleted file mode 100644 index aa546b8c78..0000000000 --- a/core/runtime/mod.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -mod bindings; -mod jsrealm; -mod jsruntime; -#[doc(hidden)] -pub mod ops; -mod snapshot_util; - -#[cfg(test)] -mod tests; - -pub const V8_WRAPPER_TYPE_INDEX: i32 = 0; -pub const V8_WRAPPER_OBJECT_INDEX: i32 = 1; - -pub(crate) use jsrealm::ContextState; -pub use jsrealm::JsRealm; -pub use jsruntime::CompiledWasmModuleStore; -pub use jsruntime::CrossIsolateStore; -pub(crate) use jsruntime::InitMode; -pub use jsruntime::JsRuntime; -pub use jsruntime::JsRuntimeForSnapshot; -pub use jsruntime::JsRuntimeState; -pub use jsruntime::RuntimeOptions; -pub use jsruntime::RuntimeSnapshotOptions; -pub use jsruntime::SharedArrayBufferStore; -pub use jsruntime::Snapshot; -pub use snapshot_util::create_snapshot; -pub use snapshot_util::get_js_files; -pub use snapshot_util::CreateSnapshotOptions; -pub use snapshot_util::CreateSnapshotOutput; -pub use snapshot_util::FilterFn; -pub(crate) use snapshot_util::SnapshottedData; - -pub use bindings::script_origin; diff --git a/core/runtime/ops.rs b/core/runtime/ops.rs deleted file mode 100644 index 5ecab5edf3..0000000000 --- a/core/runtime/ops.rs +++ /dev/null @@ -1,634 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use crate::ops::*; -use crate::OpResult; -use crate::PromiseId; -use anyhow::Error; -use futures::future::Either; -use futures::future::Future; -use futures::future::FutureExt; -use futures::task::noop_waker_ref; -use std::borrow::Cow; -use std::cell::RefCell; -use std::future::ready; -use std::mem::MaybeUninit; -use std::option::Option; -use std::task::Context; -use std::task::Poll; - -#[inline] -pub fn queue_fast_async_op( - ctx: &OpCtx, - promise_id: PromiseId, - op: impl Future> + 'static, -) { - let get_class = { - let state = RefCell::borrow(&ctx.state); - state.tracker.track_async(ctx.id); - state.get_error_class_fn - }; - let fut = op.map(|result| crate::_ops::to_op_result(get_class, result)); - ctx - .context_state - .borrow_mut() - .pending_ops - .spawn(OpCall::new(ctx, promise_id, fut)); -} - -#[inline] -pub fn map_async_op1( - ctx: &OpCtx, - op: impl Future> + 'static, -) -> impl Future { - let get_class = { - let state = RefCell::borrow(&ctx.state); - state.tracker.track_async(ctx.id); - state.get_error_class_fn - }; - - op.map(|res| crate::_ops::to_op_result(get_class, res)) -} - -#[inline] -pub fn map_async_op2( - ctx: &OpCtx, - op: impl Future + 'static, -) -> impl Future { - let state = RefCell::borrow(&ctx.state); - state.tracker.track_async(ctx.id); - - op.map(|res| OpResult::Ok(res.into())) -} - -#[inline] -pub fn map_async_op3( - ctx: &OpCtx, - op: Result> + 'static, Error>, -) -> impl Future { - let get_class = { - let state = RefCell::borrow(&ctx.state); - state.tracker.track_async(ctx.id); - state.get_error_class_fn - }; - - match op { - Err(err) => { - Either::Left(ready(OpResult::Err(OpError::new(get_class, err)))) - } - Ok(fut) => { - Either::Right(fut.map(|res| crate::_ops::to_op_result(get_class, res))) - } - } -} - -#[inline] -pub fn map_async_op4( - ctx: &OpCtx, - op: Result + 'static, Error>, -) -> impl Future { - let get_class = { - let state = RefCell::borrow(&ctx.state); - state.tracker.track_async(ctx.id); - state.get_error_class_fn - }; - - match op { - Err(err) => { - Either::Left(ready(OpResult::Err(OpError::new(get_class, err)))) - } - Ok(fut) => Either::Right(fut.map(|r| OpResult::Ok(r.into()))), - } -} - -pub fn queue_async_op<'s>( - ctx: &OpCtx, - scope: &'s mut v8::HandleScope, - deferred: bool, - promise_id: PromiseId, - op: impl Future + 'static, -) -> Option> { - // An op's realm (as given by `OpCtx::realm_idx`) must match the realm in - // which it is invoked. Otherwise, we might have cross-realm object exposure. - // deno_core doesn't currently support such exposure, even though embedders - // can cause them, so we panic in debug mode (since the check is expensive). - // TODO(mmastrac): Restore this - // debug_assert_eq!( - // runtime_state.borrow().context(ctx.realm_idx as usize, scope), - // Some(scope.get_current_context()) - // ); - - let id = ctx.id; - - // TODO(mmastrac): We have to poll every future here because that assumption is baked into a large number - // of ops. If we can figure out a way around this, we can remove this call to boxed_local and save a malloc per future. - let mut pinned = op.map(move |res| (promise_id, id, res)).boxed_local(); - - match pinned.poll_unpin(&mut Context::from_waker(noop_waker_ref())) { - Poll::Pending => {} - Poll::Ready(mut res) => { - if deferred { - ctx.context_state.borrow_mut().pending_ops.spawn(ready(res)); - return None; - } else { - ctx.state.borrow_mut().tracker.track_async_completed(ctx.id); - return Some(res.2.to_v8(scope).unwrap()); - } - } - } - - ctx.context_state.borrow_mut().pending_ops.spawn(pinned); - None -} - -macro_rules! try_number { - ($n:ident $type:ident $is:ident) => { - if $n.$is() { - // SAFETY: v8 handles can be transmuted - let n: &v8::Uint32 = unsafe { std::mem::transmute($n) }; - return n.value() as _; - } - }; -} - -pub fn to_u32(number: &v8::Value) -> u32 { - try_number!(number Uint32 is_uint32); - try_number!(number Int32 is_int32); - try_number!(number Number is_number); - if number.is_big_int() { - // SAFETY: v8 handles can be transmuted - let n: &v8::BigInt = unsafe { std::mem::transmute(number) }; - return n.u64_value().0 as _; - } - 0 -} - -pub fn to_i32(number: &v8::Value) -> i32 { - try_number!(number Uint32 is_uint32); - try_number!(number Int32 is_int32); - try_number!(number Number is_number); - if number.is_big_int() { - // SAFETY: v8 handles can be transmuted - let n: &v8::BigInt = unsafe { std::mem::transmute(number) }; - return n.i64_value().0 as _; - } - 0 -} - -#[allow(unused)] -pub fn to_u64(number: &v8::Value) -> u32 { - try_number!(number Uint32 is_uint32); - try_number!(number Int32 is_int32); - try_number!(number Number is_number); - if number.is_big_int() { - // SAFETY: v8 handles can be transmuted - let n: &v8::BigInt = unsafe { std::mem::transmute(number) }; - return n.u64_value().0 as _; - } - 0 -} - -#[allow(unused)] -pub fn to_i64(number: &v8::Value) -> i32 { - try_number!(number Uint32 is_uint32); - try_number!(number Int32 is_int32); - try_number!(number Number is_number); - if number.is_big_int() { - // SAFETY: v8 handles can be transmuted - let n: &v8::BigInt = unsafe { std::mem::transmute(number) }; - return n.i64_value().0 as _; - } - 0 -} - -/// Expands `inbuf` to `outbuf`, assuming that `outbuf` has at least 2x `input_length`. -#[inline(always)] -unsafe fn latin1_to_utf8( - input_length: usize, - inbuf: *const u8, - outbuf: *mut u8, -) -> usize { - let mut output = 0; - let mut input = 0; - while input < input_length { - let char = *(inbuf.add(input)); - if char < 0x80 { - *(outbuf.add(output)) = char; - output += 1; - } else { - // Top two bits - *(outbuf.add(output)) = (char >> 6) | 0b1100_0000; - // Bottom six bits - *(outbuf.add(output + 1)) = (char & 0b0011_1111) | 0b1000_0000; - output += 2; - } - input += 1; - } - output -} - -/// Converts a [`v8::fast_api::FastApiOneByteString`] to either an owned string, or a borrowed string, depending on whether it fits into the -/// provided buffer. -pub fn to_str_ptr<'a, const N: usize>( - string: &mut v8::fast_api::FastApiOneByteString, - buffer: &'a mut [MaybeUninit; N], -) -> Cow<'a, str> { - let input_buf = string.as_bytes(); - let input_len = input_buf.len(); - let output_len = buffer.len(); - - // We know that this string is full of either one or two-byte UTF-8 chars, so if it's < 1/2 of N we - // can skip the ASCII check and just start copying. - if input_len < N / 2 { - debug_assert!(output_len >= input_len * 2); - let buffer = buffer.as_mut_ptr() as *mut u8; - - let written = - // SAFETY: We checked that buffer is at least 2x the size of input_buf - unsafe { latin1_to_utf8(input_buf.len(), input_buf.as_ptr(), buffer) }; - - debug_assert!(written <= output_len); - - let slice = std::ptr::slice_from_raw_parts(buffer, written); - // SAFETY: We know it's valid UTF-8, so make a string - Cow::Borrowed(unsafe { std::str::from_utf8_unchecked(&*slice) }) - } else { - // TODO(mmastrac): We could be smarter here about not allocating - Cow::Owned(to_string_ptr(string)) - } -} - -/// Converts a [`v8::fast_api::FastApiOneByteString`] to an owned string. May over-allocate to avoid -/// re-allocation. -pub fn to_string_ptr( - string: &mut v8::fast_api::FastApiOneByteString, -) -> String { - let input_buf = string.as_bytes(); - let capacity = input_buf.len() * 2; - - // SAFETY: We're allocating a buffer of 2x the input size, writing valid UTF-8, then turning that into a string - unsafe { - // Create an uninitialized buffer of `capacity` bytes. We need to be careful here to avoid - // accidentally creating a slice of u8 which would be invalid. - let layout = std::alloc::Layout::from_size_align(capacity, 1).unwrap(); - let out = std::alloc::alloc(layout); - - let written = latin1_to_utf8(input_buf.len(), input_buf.as_ptr(), out); - - debug_assert!(written <= capacity); - // We know it's valid UTF-8, so make a string - String::from_raw_parts(out, written, capacity) - } -} - -/// Converts a [`v8::String`] to either an owned string, or a borrowed string, depending on whether it fits into the -/// provided buffer. -#[inline(always)] -pub fn to_str<'a, const N: usize>( - scope: &mut v8::Isolate, - string: &v8::Value, - buffer: &'a mut [MaybeUninit; N], -) -> Cow<'a, str> { - if !string.is_string() { - return Cow::Borrowed(""); - } - - // SAFETY: We checked is_string above - let string: &v8::String = unsafe { std::mem::transmute(string) }; - - string.to_rust_cow_lossy(scope, buffer) -} - -#[cfg(test)] -mod tests { - use crate::error::generic_error; - use crate::error::AnyError; - use crate::error::JsError; - use crate::FastString; - use crate::JsRuntime; - use crate::RuntimeOptions; - use deno_ops::op2; - use std::borrow::Cow; - use std::cell::Cell; - - crate::extension!( - testing, - ops = [ - op_test_fail, - op_test_add, - op_test_add_option, - op_test_result_void_switch, - op_test_result_void_ok, - op_test_result_void_err, - op_test_result_primitive_ok, - op_test_result_primitive_err, - op_test_string_owned, - op_test_string_ref, - op_test_string_cow, - op_test_string_roundtrip_char, - op_test_string_return, - op_test_string_option_return, - op_test_string_roundtrip, - op_test_generics, - ] - ); - - thread_local! { - static FAIL: Cell = Cell::new(false) - } - - #[op2(core, fast)] - pub fn op_test_fail() { - FAIL.with(|b| b.set(true)) - } - - /// Run a test for a single op. - fn run_test2(repeat: usize, op: &str, test: &str) -> Result<(), AnyError> { - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![testing::init_ops_and_esm()], - ..Default::default() - }); - runtime - .execute_script( - "", - FastString::Owned( - format!( - r" - const {{ op_test_fail, {op} }} = Deno.core.ensureFastOps(); - function assert(b) {{ - if (!b) {{ - op_test_fail(); - }} - }} - " - ) - .into(), - ), - ) - .unwrap(); - FAIL.with(|b| b.set(false)); - runtime.execute_script( - "", - FastString::Owned( - format!( - r" - for (let __index__ = 0; __index__ < {repeat}; __index__++) {{ - {test} - }} - " - ) - .into(), - ), - )?; - if FAIL.with(|b| b.get()) { - Err(generic_error(format!("{op} test failed ({test})"))) - } else { - Ok(()) - } - } - - #[tokio::test(flavor = "current_thread")] - pub async fn test_op_fail() { - assert!(run_test2(1, "", "assert(false)").is_err()); - } - - #[op2(core, fast)] - pub fn op_test_add(a: u32, b: u32) -> u32 { - a + b - } - - #[tokio::test(flavor = "current_thread")] - pub async fn test_op_add() -> Result<(), Box> { - Ok(run_test2( - 10000, - "op_test_add", - "assert(op_test_add(1, 11) == 12)", - )?) - } - - #[op2(core)] - pub fn op_test_add_option(a: u32, b: Option) -> u32 { - a + b.unwrap_or(100) - } - - #[tokio::test(flavor = "current_thread")] - pub async fn test_op_add_option() -> Result<(), Box> { - // This isn't fast, so we don't repeat it - run_test2( - 1, - "op_test_add_option", - "assert(op_test_add_option(1, 11) == 12)", - )?; - run_test2( - 1, - "op_test_add_option", - "assert(op_test_add_option(1, null) == 101)", - )?; - Ok(()) - } - - thread_local! { - static RETURN_COUNT: Cell = Cell::new(0); - } - - #[op2(core, fast)] - pub fn op_test_result_void_switch() -> Result<(), AnyError> { - let count = RETURN_COUNT.with(|count| { - let new = count.get() + 1; - count.set(new); - new - }); - if count > 5000 { - Err(generic_error("failed!!!")) - } else { - Ok(()) - } - } - - #[op2(core, fast)] - pub fn op_test_result_void_err() -> Result<(), AnyError> { - Err(generic_error("failed!!!")) - } - - #[op2(core, fast)] - pub fn op_test_result_void_ok() -> Result<(), AnyError> { - Ok(()) - } - - #[tokio::test(flavor = "current_thread")] - pub async fn test_op_result_void() -> Result<(), Box> { - // Test the non-switching kinds - run_test2( - 10000, - "op_test_result_void_err", - "try { op_test_result_void_err(); assert(false) } catch (e) {}", - )?; - run_test2(10000, "op_test_result_void_ok", "op_test_result_void_ok()")?; - Ok(()) - } - - #[tokio::test(flavor = "current_thread")] - pub async fn test_op_result_void_switch( - ) -> Result<(), Box> { - RETURN_COUNT.with(|count| count.set(0)); - let err = run_test2( - 10000, - "op_test_result_void_switch", - "op_test_result_void_switch();", - ) - .expect_err("Expected this to fail"); - let js_err = err.downcast::().unwrap(); - assert_eq!(js_err.message, Some("failed!!!".into())); - assert_eq!(RETURN_COUNT.with(|count| count.get()), 5001); - Ok(()) - } - - #[op2(core, fast)] - pub fn op_test_result_primitive_err() -> Result { - Err(generic_error("failed!!!")) - } - - #[op2(core, fast)] - pub fn op_test_result_primitive_ok() -> Result { - Ok(123) - } - - #[tokio::test] - pub async fn test_op_result_primitive( - ) -> Result<(), Box> { - run_test2( - 10000, - "op_test_result_primitive_err", - "try { op_test_result_primitive_err(); assert(false) } catch (e) {}", - )?; - run_test2( - 10000, - "op_test_result_primitive_ok", - "op_test_result_primitive_ok()", - )?; - Ok(()) - } - - #[op2(core, fast)] - pub fn op_test_string_owned(#[string] s: String) -> u32 { - s.len() as _ - } - - #[op2(core, fast)] - pub fn op_test_string_ref(#[string] s: &str) -> u32 { - s.len() as _ - } - - #[op2(core, fast)] - pub fn op_test_string_cow(#[string] s: Cow) -> u32 { - s.len() as _ - } - - #[op2(core, fast)] - pub fn op_test_string_roundtrip_char(#[string] s: Cow) -> u32 { - s.chars().next().unwrap() as u32 - } - - #[tokio::test] - pub async fn test_op_strings() -> Result<(), Box> { - for op in [ - "op_test_string_owned", - "op_test_string_cow", - "op_test_string_ref", - ] { - for (len, str) in [ - // ASCII - (3, "'abc'"), - // Latin-1 (one byte but two UTF-8 chars) - (2, "'\\u00a0'"), - // ASCII - (10000, "'a'.repeat(10000)"), - // Latin-1 - (20000, "'\\u00a0'.repeat(10000)"), - // 4-byte UTF-8 emoji (1F995 = 🦕) - (40000, "'\\u{1F995}'.repeat(10000)"), - ] { - let test = format!("assert({op}({str}) == {len})"); - run_test2(10000, op, &test)?; - } - } - - // Ensure that we're correctly encoding UTF-8 - run_test2( - 10000, - "op_test_string_roundtrip_char", - "assert(op_test_string_roundtrip_char('\\u00a0') == 0xa0)", - )?; - run_test2( - 10000, - "op_test_string_roundtrip_char", - "assert(op_test_string_roundtrip_char('\\u00ff') == 0xff)", - )?; - run_test2( - 10000, - "op_test_string_roundtrip_char", - "assert(op_test_string_roundtrip_char('\\u0080') == 0x80)", - )?; - run_test2( - 10000, - "op_test_string_roundtrip_char", - "assert(op_test_string_roundtrip_char('\\u0100') == 0x100)", - )?; - Ok(()) - } - - #[op2(core)] - #[string] - pub fn op_test_string_return( - #[string] a: Cow, - #[string] b: Cow, - ) -> String { - (a + b).to_string() - } - - #[op2(core)] - #[string] - pub fn op_test_string_option_return( - #[string] a: Cow, - #[string] b: Cow, - ) -> Option { - if a == "none" { - return None; - } - Some((a + b).to_string()) - } - - #[op2(core)] - #[string] - pub fn op_test_string_roundtrip(#[string] s: String) -> String { - s - } - - #[tokio::test] - pub async fn test_op_string_returns() -> Result<(), Box> - { - run_test2( - 1, - "op_test_string_return", - "assert(op_test_string_return('a', 'b') == 'ab')", - )?; - run_test2( - 1, - "op_test_string_option_return", - "assert(op_test_string_option_return('a', 'b') == 'ab')", - )?; - run_test2( - 1, - "op_test_string_option_return", - "assert(op_test_string_option_return('none', 'b') == null)", - )?; - run_test2( - 1, - "op_test_string_roundtrip", - "assert(op_test_string_roundtrip('\\u0080\\u00a0\\u00ff') == '\\u0080\\u00a0\\u00ff')", - )?; - Ok(()) - } - - // We don't actually test this one -- we just want it to compile - #[op2(core, fast)] - pub fn op_test_generics() {} -} diff --git a/core/runtime/serialize_deserialize_test.js b/core/runtime/serialize_deserialize_test.js deleted file mode 100644 index 70397cdf87..0000000000 --- a/core/runtime/serialize_deserialize_test.js +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -"use strict"; - -function assert(cond) { - if (!cond) { - throw Error("assert"); - } -} - -function assertArrayEquals(a1, a2) { - if (a1.length !== a2.length) throw Error("assert"); - - for (const index in a1) { - if (a1[index] !== a2[index]) { - throw Error(`assert: (index ${index}) ${a1[index]} !== ${a2[index]}`); - } - } -} - -function main() { - const emptyString = ""; - const emptyStringSerialized = [255, 15, 34, 0]; - assertArrayEquals( - Deno.core.ops.op_serialize(emptyString), - emptyStringSerialized, - ); - assert( - Deno.core.ops.op_deserialize( - new Uint8Array(emptyStringSerialized), - ) === - emptyString, - ); - - const primitiveValueArray = ["test", "a", null, undefined]; - // deno-fmt-ignore - const primitiveValueArraySerialized = [ - 255, 15, 65, 4, 34, 4, 116, 101, 115, 116, - 34, 1, 97, 48, 95, 36, 0, 4, - ]; - assertArrayEquals( - Deno.core.ops.op_serialize(primitiveValueArray), - primitiveValueArraySerialized, - ); - - assertArrayEquals( - Deno.core.ops.op_deserialize( - new Uint8Array(primitiveValueArraySerialized), - ), - primitiveValueArray, - ); - - const circularObject = { test: null, test2: "dd", test3: "aa" }; - circularObject.test = circularObject; - // deno-fmt-ignore - const circularObjectSerialized = [ - 255, 15, 111, 34, 4, 116, 101, 115, - 116, 94, 0, 34, 5, 116, 101, 115, - 116, 50, 34, 2, 100, 100, 34, 5, - 116, 101, 115, 116, 51, 34, 2, 97, - 97, 123, 3, - ]; - - assertArrayEquals( - Deno.core.ops.op_serialize(circularObject), - circularObjectSerialized, - ); - - const deserializedCircularObject = Deno.core.ops.op_deserialize( - new Uint8Array(circularObjectSerialized), - ); - assert(deserializedCircularObject.test == deserializedCircularObject); -} - -main(); diff --git a/core/runtime/snapshot_util.rs b/core/runtime/snapshot_util.rs deleted file mode 100644 index e664856c65..0000000000 --- a/core/runtime/snapshot_util.rs +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use std::path::Path; -use std::path::PathBuf; -use std::time::Instant; - -use crate::runtime::jsruntime::BUILTIN_SOURCES; -use crate::runtime::RuntimeSnapshotOptions; -use crate::ExtModuleLoaderCb; -use crate::Extension; -use crate::ExtensionFileSourceCode; -use crate::JsRuntimeForSnapshot; -use crate::RuntimeOptions; -use crate::Snapshot; - -pub type CompressionCb = dyn Fn(&mut Vec, &[u8]); - -pub struct CreateSnapshotOptions { - pub cargo_manifest_dir: &'static str, - pub snapshot_path: PathBuf, - pub startup_snapshot: Option, - pub extensions: Vec, - pub compression_cb: Option>, - pub snapshot_module_load_cb: Option, -} - -pub struct CreateSnapshotOutput { - /// Any files marked as LoadedFromFsDuringSnapshot are collected here and should be - /// printed as 'cargo:rerun-if-changed' lines from your build script. - pub files_loaded_during_snapshot: Vec, -} - -#[must_use = "The files listed by create_snapshot should be printed as 'cargo:rerun-if-changed' lines"] -pub fn create_snapshot( - create_snapshot_options: CreateSnapshotOptions, -) -> CreateSnapshotOutput { - let mut mark = Instant::now(); - - let js_runtime = JsRuntimeForSnapshot::new( - RuntimeOptions { - startup_snapshot: create_snapshot_options.startup_snapshot, - extensions: create_snapshot_options.extensions, - ..Default::default() - }, - RuntimeSnapshotOptions { - snapshot_module_load_cb: create_snapshot_options.snapshot_module_load_cb, - }, - ); - println!( - "JsRuntime for snapshot prepared, took {:#?} ({})", - Instant::now().saturating_duration_since(mark), - create_snapshot_options.snapshot_path.display() - ); - mark = Instant::now(); - - let mut files_loaded_during_snapshot = vec![]; - for source in &*BUILTIN_SOURCES { - if let ExtensionFileSourceCode::LoadedFromFsDuringSnapshot(path) = - &source.code - { - files_loaded_during_snapshot.push(path.clone()); - } - } - for source in js_runtime - .extensions() - .iter() - .flat_map(|e| vec![e.get_esm_sources(), e.get_js_sources()]) - .flatten() - { - if let ExtensionFileSourceCode::LoadedFromFsDuringSnapshot(path) = - &source.code - { - files_loaded_during_snapshot.push(path.clone()); - } - } - - let snapshot = js_runtime.snapshot(); - let snapshot_slice: &[u8] = &snapshot; - println!( - "Snapshot size: {}, took {:#?} ({})", - snapshot_slice.len(), - Instant::now().saturating_duration_since(mark), - create_snapshot_options.snapshot_path.display() - ); - mark = Instant::now(); - - let maybe_compressed_snapshot: Box> = - if let Some(compression_cb) = create_snapshot_options.compression_cb { - let mut vec = vec![]; - - vec.extend_from_slice( - &u32::try_from(snapshot.len()) - .expect("snapshot larger than 4gb") - .to_le_bytes(), - ); - - (compression_cb)(&mut vec, snapshot_slice); - - println!( - "Snapshot compressed size: {}, took {:#?} ({})", - vec.len(), - Instant::now().saturating_duration_since(mark), - create_snapshot_options.snapshot_path.display() - ); - mark = std::time::Instant::now(); - - Box::new(vec) - } else { - Box::new(snapshot_slice) - }; - - std::fs::write( - &create_snapshot_options.snapshot_path, - &*maybe_compressed_snapshot, - ) - .unwrap(); - println!( - "Snapshot written, took: {:#?} ({})", - Instant::now().saturating_duration_since(mark), - create_snapshot_options.snapshot_path.display(), - ); - CreateSnapshotOutput { - files_loaded_during_snapshot, - } -} - -pub type FilterFn = Box bool>; - -pub fn get_js_files( - cargo_manifest_dir: &'static str, - directory: &str, - filter: Option, -) -> Vec { - let manifest_dir = Path::new(cargo_manifest_dir); - let mut js_files = std::fs::read_dir(directory) - .unwrap() - .map(|dir_entry| { - let file = dir_entry.unwrap(); - manifest_dir.join(file.path()) - }) - .filter(|path| { - path.extension().unwrap_or_default() == "js" - && filter.as_ref().map(|filter| filter(path)).unwrap_or(true) - }) - .collect::>(); - js_files.sort(); - js_files -} - -fn data_error_to_panic(err: v8::DataError) -> ! { - match err { - v8::DataError::BadType { actual, expected } => { - panic!( - "Invalid type for snapshot data: expected {expected}, got {actual}" - ); - } - v8::DataError::NoData { expected } => { - panic!("No data for snapshot data: expected {expected}"); - } - } -} - -pub(crate) struct SnapshottedData { - pub module_map_data: v8::Global, - pub module_handles: Vec>, -} - -static MODULE_MAP_CONTEXT_DATA_INDEX: usize = 0; - -pub(crate) fn get_snapshotted_data( - scope: &mut v8::HandleScope<()>, - context: v8::Local, -) -> SnapshottedData { - let mut scope = v8::ContextScope::new(scope, context); - - // The 0th element is the module map itself, followed by X number of module - // handles. We need to deserialize the "next_module_id" field from the - // map to see how many module handles we expect. - let result = scope.get_context_data_from_snapshot_once::( - MODULE_MAP_CONTEXT_DATA_INDEX, - ); - - let val = match result { - Ok(v) => v, - Err(err) => data_error_to_panic(err), - }; - - let next_module_id = { - let info_data: v8::Local = - val.get_index(&mut scope, 1).unwrap().try_into().unwrap(); - info_data.length() - }; - - // Over allocate so executing a few scripts doesn't have to resize this vec. - let mut module_handles = Vec::with_capacity(next_module_id as usize + 16); - for i in 1..=next_module_id { - match scope.get_context_data_from_snapshot_once::(i as usize) { - Ok(val) => { - let module_global = v8::Global::new(&mut scope, val); - module_handles.push(module_global); - } - Err(err) => data_error_to_panic(err), - } - } - - SnapshottedData { - module_map_data: v8::Global::new(&mut scope, val), - module_handles, - } -} - -pub(crate) fn set_snapshotted_data( - scope: &mut v8::HandleScope<()>, - context: v8::Global, - snapshotted_data: SnapshottedData, -) { - let local_context = v8::Local::new(scope, context); - let local_data = v8::Local::new(scope, snapshotted_data.module_map_data); - let offset = scope.add_context_data(local_context, local_data); - assert_eq!(offset, MODULE_MAP_CONTEXT_DATA_INDEX); - - for (index, handle) in snapshotted_data.module_handles.into_iter().enumerate() - { - let module_handle = v8::Local::new(scope, handle); - let offset = scope.add_context_data(local_context, module_handle); - assert_eq!(offset, index + 1); - } -} - -/// Returns an isolate set up for snapshotting. -pub(crate) fn create_snapshot_creator( - external_refs: &'static v8::ExternalReferences, - maybe_startup_snapshot: Option, -) -> v8::OwnedIsolate { - if let Some(snapshot) = maybe_startup_snapshot { - match snapshot { - Snapshot::Static(data) => { - v8::Isolate::snapshot_creator_from_existing_snapshot( - data, - Some(external_refs), - ) - } - Snapshot::JustCreated(data) => { - v8::Isolate::snapshot_creator_from_existing_snapshot( - data, - Some(external_refs), - ) - } - Snapshot::Boxed(data) => { - v8::Isolate::snapshot_creator_from_existing_snapshot( - data, - Some(external_refs), - ) - } - } - } else { - v8::Isolate::snapshot_creator(Some(external_refs)) - } -} diff --git a/core/runtime/tests.rs b/core/runtime/tests.rs deleted file mode 100644 index 663645bb14..0000000000 --- a/core/runtime/tests.rs +++ /dev/null @@ -1,2396 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use crate::ascii_str; -use crate::error::custom_error; -use crate::error::generic_error; -use crate::error::AnyError; -use crate::error::JsError; -use crate::extensions::OpDecl; -use crate::include_ascii_string; -use crate::module_specifier::ModuleSpecifier; -use crate::modules::AssertedModuleType; -use crate::modules::ModuleCode; -use crate::modules::ModuleInfo; -use crate::modules::ModuleLoadId; -use crate::modules::ModuleLoader; -use crate::modules::ModuleSource; -use crate::modules::ModuleSourceFuture; -use crate::modules::ModuleType; -use crate::modules::ResolutionKind; -use crate::modules::SymbolicModule; -use crate::Extension; -use crate::JsBuffer; -use crate::*; -use anyhow::Error; -use cooked_waker::IntoWaker; -use cooked_waker::Wake; -use cooked_waker::WakeRef; -use deno_ops::op; -use futures::future::poll_fn; -use futures::future::Future; -use futures::FutureExt; -use std::cell::RefCell; -use std::pin::Pin; -use std::rc::Rc; -use std::sync::atomic::AtomicBool; -use std::sync::atomic::AtomicI8; -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering; -use std::sync::Arc; -use std::task::Context; -use std::task::Poll; -use std::time::Duration; - -// deno_ops macros generate code assuming deno_core in scope. -mod deno_core { - pub use crate::*; -} - -#[derive(Copy, Clone)] -pub enum Mode { - Async, - AsyncDeferred, - AsyncZeroCopy(bool), -} - -struct TestState { - mode: Mode, - dispatch_count: Arc, -} - -#[op] -async fn op_test( - rc_op_state: Rc>, - control: u8, - buf: Option, -) -> Result { - #![allow(clippy::await_holding_refcell_ref)] // False positive. - let op_state_ = rc_op_state.borrow(); - let test_state = op_state_.borrow::(); - test_state.dispatch_count.fetch_add(1, Ordering::Relaxed); - let mode = test_state.mode; - drop(op_state_); - match mode { - Mode::Async => { - assert_eq!(control, 42); - Ok(43) - } - Mode::AsyncDeferred => { - tokio::task::yield_now().await; - assert_eq!(control, 42); - Ok(43) - } - Mode::AsyncZeroCopy(has_buffer) => { - assert_eq!(buf.is_some(), has_buffer); - if let Some(buf) = buf { - assert_eq!(buf.len(), 1); - } - Ok(43) - } - } -} - -fn setup(mode: Mode) -> (JsRuntime, Arc) { - let dispatch_count = Arc::new(AtomicUsize::new(0)); - deno_core::extension!( - test_ext, - ops = [op_test], - options = { - mode: Mode, - dispatch_count: Arc, - }, - state = |state, options| { - state.put(TestState { - mode: options.mode, - dispatch_count: options.dispatch_count - }) - } - ); - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![test_ext::init_ops(mode, dispatch_count.clone())], - get_error_class_fn: Some(&|error| { - crate::error::get_custom_error_class(error).unwrap() - }), - ..Default::default() - }); - - runtime - .execute_script_static( - "setup.js", - r#" - function assert(cond) { - if (!cond) { - throw Error("assert"); - } - } - "#, - ) - .unwrap(); - assert_eq!(dispatch_count.load(Ordering::Relaxed), 0); - (runtime, dispatch_count) -} - -#[tokio::test] -async fn test_ref_unref_ops() { - let (mut runtime, _dispatch_count) = setup(Mode::AsyncDeferred); - runtime - .execute_script_static( - "filename.js", - r#" - - var promiseIdSymbol = Symbol.for("Deno.core.internalPromiseId"); - var p1 = Deno.core.opAsync("op_test", 42); - var p2 = Deno.core.opAsync("op_test", 42); - "#, - ) - .unwrap(); - { - let realm = runtime.global_realm(); - assert_eq!(realm.num_pending_ops(), 2); - assert_eq!(realm.num_unrefed_ops(), 0); - } - runtime - .execute_script_static( - "filename.js", - r#" - Deno.core.ops.op_unref_op(p1[promiseIdSymbol]); - Deno.core.ops.op_unref_op(p2[promiseIdSymbol]); - "#, - ) - .unwrap(); - { - let realm = runtime.global_realm(); - assert_eq!(realm.num_pending_ops(), 2); - assert_eq!(realm.num_unrefed_ops(), 2); - } - runtime - .execute_script_static( - "filename.js", - r#" - Deno.core.ops.op_ref_op(p1[promiseIdSymbol]); - Deno.core.ops.op_ref_op(p2[promiseIdSymbol]); - "#, - ) - .unwrap(); - { - let realm = runtime.global_realm(); - assert_eq!(realm.num_pending_ops(), 2); - assert_eq!(realm.num_unrefed_ops(), 0); - } -} - -#[test] -fn test_dispatch() { - let (mut runtime, dispatch_count) = setup(Mode::Async); - runtime - .execute_script_static( - "filename.js", - r#" - let control = 42; - - Deno.core.opAsync("op_test", control); - async function main() { - Deno.core.opAsync("op_test", control); - } - main(); - "#, - ) - .unwrap(); - assert_eq!(dispatch_count.load(Ordering::Relaxed), 2); -} - -#[test] -fn test_op_async_promise_id() { - let (mut runtime, _dispatch_count) = setup(Mode::Async); - runtime - .execute_script_static( - "filename.js", - r#" - - const p = Deno.core.opAsync("op_test", 42); - if (p[Symbol.for("Deno.core.internalPromiseId")] == undefined) { - throw new Error("missing id on returned promise"); - } - "#, - ) - .unwrap(); -} - -#[test] -fn test_dispatch_no_zero_copy_buf() { - let (mut runtime, dispatch_count) = setup(Mode::AsyncZeroCopy(false)); - runtime - .execute_script_static( - "filename.js", - r#" - - Deno.core.opAsync("op_test", 0); - "#, - ) - .unwrap(); - assert_eq!(dispatch_count.load(Ordering::Relaxed), 1); -} - -#[test] -fn test_dispatch_stack_zero_copy_bufs() { - let (mut runtime, dispatch_count) = setup(Mode::AsyncZeroCopy(true)); - runtime - .execute_script_static( - "filename.js", - r#" - const { op_test } = Deno.core.ensureFastOps(); - let zero_copy_a = new Uint8Array([0]); - op_test(0, zero_copy_a); - "#, - ) - .unwrap(); - assert_eq!(dispatch_count.load(Ordering::Relaxed), 1); -} - -#[test] -fn test_execute_script_return_value() { - let mut runtime = JsRuntime::new(Default::default()); - let value_global = - runtime.execute_script_static("a.js", "a = 1 + 2").unwrap(); - { - let scope = &mut runtime.handle_scope(); - let value = value_global.open(scope); - assert_eq!(value.integer_value(scope).unwrap(), 3); - } - let value_global = runtime - .execute_script_static("b.js", "b = 'foobar'") - .unwrap(); - { - let scope = &mut runtime.handle_scope(); - let value = value_global.open(scope); - assert!(value.is_string()); - assert_eq!( - value.to_string(scope).unwrap().to_rust_string_lossy(scope), - "foobar" - ); - } -} - -#[derive(Default)] -struct LoggingWaker { - woken: AtomicBool, -} - -impl Wake for LoggingWaker { - fn wake(self) { - self.woken.store(true, Ordering::SeqCst); - } -} - -impl WakeRef for LoggingWaker { - fn wake_by_ref(&self) { - self.woken.store(true, Ordering::SeqCst); - } -} - -/// This is a reproduction for a very obscure bug where the Deno runtime locks up we end up polling -/// an empty JoinSet and attempt to resolve ops after-the-fact. There's a small footgun in the JoinSet -/// API where polling it while empty returns Ready(None), which means that it never holds on to the -/// waker. This means that if we aren't testing for this particular return value and don't stash the waker -/// ourselves for a future async op to eventually queue, we can end up losing the waker entirely and the -/// op wakes up, notifies tokio, which notifies the JoinSet, which then has nobody to notify )`:. -#[tokio::test] -async fn test_wakers_for_async_ops() { - static STATE: AtomicI8 = AtomicI8::new(0); - - #[op] - async fn op_async_sleep() -> Result<(), Error> { - STATE.store(1, Ordering::SeqCst); - tokio::time::sleep(std::time::Duration::from_millis(1)).await; - STATE.store(2, Ordering::SeqCst); - Ok(()) - } - - STATE.store(0, Ordering::SeqCst); - - let logging_waker = Arc::new(LoggingWaker::default()); - let waker = logging_waker.clone().into_waker(); - - deno_core::extension!(test_ext, ops = [op_async_sleep]); - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![test_ext::init_ops()], - ..Default::default() - }); - - // Drain events until we get to Ready - loop { - logging_waker.woken.store(false, Ordering::SeqCst); - let res = runtime.poll_event_loop(&mut Context::from_waker(&waker), false); - let ready = matches!(res, Poll::Ready(Ok(()))); - assert!(ready || logging_waker.woken.load(Ordering::SeqCst)); - if ready { - break; - } - } - - // Start the AIIFE - runtime - .execute_script( - "", - FastString::from_static( - "(async () => { await Deno.core.opAsync('op_async_sleep'); })()", - ), - ) - .unwrap(); - - // Wait for future to finish - while STATE.load(Ordering::SeqCst) < 2 { - tokio::time::sleep(Duration::from_millis(1)).await; - } - - // This shouldn't take one minute, but if it does, things are definitely locked up - for _ in 0..Duration::from_secs(60).as_millis() { - if logging_waker.woken.load(Ordering::SeqCst) { - // Success - return; - } - tokio::time::sleep(Duration::from_millis(1)).await; - } - - panic!("The waker was never woken after the future completed"); -} - -#[tokio::test] -async fn test_poll_value() { - let mut runtime = JsRuntime::new(Default::default()); - poll_fn(move |cx| { - let value_global = runtime - .execute_script_static("a.js", "Promise.resolve(1 + 2)") - .unwrap(); - let v = runtime.poll_value(&value_global, cx); - { - let scope = &mut runtime.handle_scope(); - assert!( - matches!(v, Poll::Ready(Ok(v)) if v.open(scope).integer_value(scope).unwrap() == 3) - ); - } - - let value_global = runtime - .execute_script_static( - "a.js", - "Promise.resolve(new Promise(resolve => resolve(2 + 2)))", - ) - .unwrap(); - let v = runtime.poll_value(&value_global, cx); - { - let scope = &mut runtime.handle_scope(); - assert!( - matches!(v, Poll::Ready(Ok(v)) if v.open(scope).integer_value(scope).unwrap() == 4) - ); - } - - let value_global = runtime - .execute_script_static("a.js", "Promise.reject(new Error('fail'))") - .unwrap(); - let v = runtime.poll_value(&value_global, cx); - assert!( - matches!(v, Poll::Ready(Err(e)) if e.downcast_ref::().unwrap().exception_message == "Uncaught Error: fail") - ); - - let value_global = runtime - .execute_script_static("a.js", "new Promise(resolve => {})") - .unwrap(); - let v = runtime.poll_value(&value_global, cx); - matches!(v, Poll::Ready(Err(e)) if e.to_string() == "Promise resolution is still pending but the event loop has already resolved."); - Poll::Ready(()) - }).await; -} - -#[tokio::test] -async fn test_resolve_value() { - let mut runtime = JsRuntime::new(Default::default()); - let value_global = runtime - .execute_script_static("a.js", "Promise.resolve(1 + 2)") - .unwrap(); - let result_global = runtime.resolve_value(value_global).await.unwrap(); - { - let scope = &mut runtime.handle_scope(); - let value = result_global.open(scope); - assert_eq!(value.integer_value(scope).unwrap(), 3); - } - - let value_global = runtime - .execute_script_static( - "a.js", - "Promise.resolve(new Promise(resolve => resolve(2 + 2)))", - ) - .unwrap(); - let result_global = runtime.resolve_value(value_global).await.unwrap(); - { - let scope = &mut runtime.handle_scope(); - let value = result_global.open(scope); - assert_eq!(value.integer_value(scope).unwrap(), 4); - } - - let value_global = runtime - .execute_script_static("a.js", "Promise.reject(new Error('fail'))") - .unwrap(); - let err = runtime.resolve_value(value_global).await.unwrap_err(); - assert_eq!( - "Uncaught Error: fail", - err.downcast::().unwrap().exception_message - ); - - let value_global = runtime - .execute_script_static("a.js", "new Promise(resolve => {})") - .unwrap(); - let error_string = runtime - .resolve_value(value_global) - .await - .unwrap_err() - .to_string(); - assert_eq!( - "Promise resolution is still pending but the event loop has already resolved.", - error_string, - ); -} - -#[test] -fn terminate_execution_webassembly() { - let (mut runtime, _dispatch_count) = setup(Mode::Async); - let v8_isolate_handle = runtime.v8_isolate().thread_safe_handle(); - - // Run an infinite loop in WebAssembly code, which should be terminated. - let promise = runtime.execute_script_static("infinite_wasm_loop.js", - r#" - (async () => { - const wasmCode = new Uint8Array([ - 0, 97, 115, 109, 1, 0, 0, 0, 1, 4, 1, - 96, 0, 0, 3, 2, 1, 0, 7, 17, 1, 13, - 105, 110, 102, 105, 110, 105, 116, 101, 95, 108, 111, - 111, 112, 0, 0, 10, 9, 1, 7, 0, 3, 64, - 12, 0, 11, 11, - ]); - const wasmModule = await WebAssembly.compile(wasmCode); - globalThis.wasmInstance = new WebAssembly.Instance(wasmModule); - })() - "#).unwrap(); - futures::executor::block_on(runtime.resolve_value(promise)).unwrap(); - let terminator_thread = std::thread::spawn(move || { - std::thread::sleep(std::time::Duration::from_millis(1000)); - - // terminate execution - let ok = v8_isolate_handle.terminate_execution(); - assert!(ok); - }); - let err = runtime - .execute_script_static( - "infinite_wasm_loop2.js", - "globalThis.wasmInstance.exports.infinite_loop();", - ) - .unwrap_err(); - assert_eq!(err.to_string(), "Uncaught Error: execution terminated"); - // Cancel the execution-terminating exception in order to allow script - // execution again. - let ok = runtime.v8_isolate().cancel_terminate_execution(); - assert!(ok); - - // Verify that the isolate usable again. - runtime - .execute_script_static("simple.js", "1 + 1") - .expect("execution should be possible again"); - - terminator_thread.join().unwrap(); -} - -#[test] -fn terminate_execution() { - let (mut isolate, _dispatch_count) = setup(Mode::Async); - let v8_isolate_handle = isolate.v8_isolate().thread_safe_handle(); - - let terminator_thread = std::thread::spawn(move || { - // allow deno to boot and run - std::thread::sleep(std::time::Duration::from_millis(100)); - - // terminate execution - let ok = v8_isolate_handle.terminate_execution(); - assert!(ok); - }); - - // Rn an infinite loop, which should be terminated. - match isolate.execute_script_static("infinite_loop.js", "for(;;) {}") { - Ok(_) => panic!("execution should be terminated"), - Err(e) => { - assert_eq!(e.to_string(), "Uncaught Error: execution terminated") - } - }; - - // Cancel the execution-terminating exception in order to allow script - // execution again. - let ok = isolate.v8_isolate().cancel_terminate_execution(); - assert!(ok); - - // Verify that the isolate usable again. - isolate - .execute_script_static("simple.js", "1 + 1") - .expect("execution should be possible again"); - - terminator_thread.join().unwrap(); -} - -#[test] -fn dangling_shared_isolate() { - let v8_isolate_handle = { - // isolate is dropped at the end of this block - let (mut runtime, _dispatch_count) = setup(Mode::Async); - runtime.v8_isolate().thread_safe_handle() - }; - - // this should not SEGFAULT - v8_isolate_handle.terminate_execution(); -} - -#[test] -fn syntax_error() { - let mut runtime = JsRuntime::new(Default::default()); - let src = "hocuspocus("; - let r = runtime.execute_script_static("i.js", src); - let e = r.unwrap_err(); - let js_error = e.downcast::().unwrap(); - let frame = js_error.frames.first().unwrap(); - assert_eq!(frame.column_number, Some(12)); -} - -#[tokio::test] -async fn test_encode_decode() { - let (mut runtime, _dispatch_count) = setup(Mode::Async); - poll_fn(move |cx| { - runtime - .execute_script( - "encode_decode_test.js", - // Note: We make this to_owned because it contains non-ASCII chars - include_str!("encode_decode_test.js").to_owned().into(), - ) - .unwrap(); - if let Poll::Ready(Err(_)) = runtime.poll_event_loop(cx, false) { - unreachable!(); - } - Poll::Ready(()) - }) - .await; -} - -#[tokio::test] -async fn test_serialize_deserialize() { - let (mut runtime, _dispatch_count) = setup(Mode::Async); - poll_fn(move |cx| { - runtime - .execute_script( - "serialize_deserialize_test.js", - include_ascii_string!("serialize_deserialize_test.js"), - ) - .unwrap(); - if let Poll::Ready(Err(_)) = runtime.poll_event_loop(cx, false) { - unreachable!(); - } - Poll::Ready(()) - }) - .await; -} - -#[tokio::test] -async fn test_error_builder() { - #[op] - fn op_err() -> Result<(), Error> { - Err(custom_error("DOMExceptionOperationError", "abc")) - } - - pub fn get_error_class_name(_: &Error) -> &'static str { - "DOMExceptionOperationError" - } - - deno_core::extension!(test_ext, ops = [op_err]); - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![test_ext::init_ops()], - get_error_class_fn: Some(&get_error_class_name), - ..Default::default() - }); - poll_fn(move |cx| { - runtime - .execute_script_static( - "error_builder_test.js", - include_str!("error_builder_test.js"), - ) - .unwrap(); - if let Poll::Ready(Err(_)) = runtime.poll_event_loop(cx, false) { - unreachable!(); - } - Poll::Ready(()) - }) - .await; -} - -/// Ensure that putting the inspector into OpState doesn't cause crashes. The only valid place we currently allow -/// the inspector to be stashed without cleanup is the OpState, and this should not actually cause crashes. -#[test] -fn inspector() { - let mut runtime = JsRuntime::new(RuntimeOptions { - inspector: true, - ..Default::default() - }); - // This was causing a crash - runtime.op_state().borrow_mut().put(runtime.inspector()); - runtime.execute_script_static("check.js", "null").unwrap(); -} - -#[test] -fn will_snapshot() { - let snapshot = { - let mut runtime = - JsRuntimeForSnapshot::new(Default::default(), Default::default()); - runtime.execute_script_static("a.js", "a = 1 + 2").unwrap(); - runtime.snapshot() - }; - - let snapshot = Snapshot::JustCreated(snapshot); - let mut runtime2 = JsRuntime::new(RuntimeOptions { - startup_snapshot: Some(snapshot), - ..Default::default() - }); - runtime2 - .execute_script_static("check.js", "if (a != 3) throw Error('x')") - .unwrap(); -} - -#[test] -fn will_snapshot2() { - let startup_data = { - let mut runtime = - JsRuntimeForSnapshot::new(Default::default(), Default::default()); - runtime - .execute_script_static("a.js", "let a = 1 + 2") - .unwrap(); - runtime.snapshot() - }; - - let snapshot = Snapshot::JustCreated(startup_data); - let mut runtime = JsRuntimeForSnapshot::new( - RuntimeOptions { - startup_snapshot: Some(snapshot), - ..Default::default() - }, - Default::default(), - ); - - let startup_data = { - runtime - .execute_script_static("check_a.js", "if (a != 3) throw Error('x')") - .unwrap(); - runtime.execute_script_static("b.js", "b = 2 + 3").unwrap(); - runtime.snapshot() - }; - - let snapshot = Snapshot::JustCreated(startup_data); - { - let mut runtime = JsRuntime::new(RuntimeOptions { - startup_snapshot: Some(snapshot), - ..Default::default() - }); - runtime - .execute_script_static("check_b.js", "if (b != 5) throw Error('x')") - .unwrap(); - runtime - .execute_script_static("check2.js", "if (!Deno.core) throw Error('x')") - .unwrap(); - } -} - -#[test] -fn test_snapshot_callbacks() { - let snapshot = { - let mut runtime = - JsRuntimeForSnapshot::new(Default::default(), Default::default()); - runtime - .execute_script_static( - "a.js", - r#" - Deno.core.setMacrotaskCallback(() => { - return true; - }); - Deno.core.ops.op_set_format_exception_callback(()=> { - return null; - }) - Deno.core.setPromiseRejectCallback(() => { - return false; - }); - a = 1 + 2; - "#, - ) - .unwrap(); - runtime.snapshot() - }; - - let snapshot = Snapshot::JustCreated(snapshot); - let mut runtime2 = JsRuntime::new(RuntimeOptions { - startup_snapshot: Some(snapshot), - ..Default::default() - }); - runtime2 - .execute_script_static("check.js", "if (a != 3) throw Error('x')") - .unwrap(); -} - -#[test] -fn test_from_boxed_snapshot() { - let snapshot = { - let mut runtime = - JsRuntimeForSnapshot::new(Default::default(), Default::default()); - runtime.execute_script_static("a.js", "a = 1 + 2").unwrap(); - let snap: &[u8] = &runtime.snapshot(); - Vec::from(snap).into_boxed_slice() - }; - - let snapshot = Snapshot::Boxed(snapshot); - let mut runtime2 = JsRuntime::new(RuntimeOptions { - startup_snapshot: Some(snapshot), - ..Default::default() - }); - runtime2 - .execute_script_static("check.js", "if (a != 3) throw Error('x')") - .unwrap(); -} - -#[test] -fn test_get_module_namespace() { - #[derive(Default)] - struct ModsLoader; - - impl ModuleLoader for ModsLoader { - fn resolve( - &self, - specifier: &str, - referrer: &str, - _kind: ResolutionKind, - ) -> Result { - assert_eq!(specifier, "file:///main.js"); - assert_eq!(referrer, "."); - let s = crate::resolve_import(specifier, referrer).unwrap(); - Ok(s) - } - - fn load( - &self, - _module_specifier: &ModuleSpecifier, - _maybe_referrer: Option<&ModuleSpecifier>, - _is_dyn_import: bool, - ) -> Pin> { - async { Err(generic_error("Module loading is not supported")) } - .boxed_local() - } - } - - let loader = std::rc::Rc::new(ModsLoader::default()); - let mut runtime = JsRuntime::new(RuntimeOptions { - module_loader: Some(loader), - ..Default::default() - }); - - let specifier = crate::resolve_url("file:///main.js").unwrap(); - let source_code = ascii_str!( - r#" - export const a = "b"; - export default 1 + 2; - "# - ); - - let module_id = futures::executor::block_on( - runtime.load_main_module(&specifier, Some(source_code)), - ) - .unwrap(); - - #[allow(clippy::let_underscore_future)] - let _ = runtime.mod_evaluate(module_id); - - let module_namespace = runtime.get_module_namespace(module_id).unwrap(); - - let scope = &mut runtime.handle_scope(); - - let module_namespace = v8::Local::::new(scope, module_namespace); - - assert!(module_namespace.is_module_namespace_object()); - - let unknown_export_name = v8::String::new(scope, "none").unwrap(); - let binding = module_namespace.get(scope, unknown_export_name.into()); - - assert!(binding.is_some()); - assert!(binding.unwrap().is_undefined()); - - let empty_export_name = v8::String::new(scope, "").unwrap(); - let binding = module_namespace.get(scope, empty_export_name.into()); - - assert!(binding.is_some()); - assert!(binding.unwrap().is_undefined()); - - let a_export_name = v8::String::new(scope, "a").unwrap(); - let binding = module_namespace.get(scope, a_export_name.into()); - - assert!(binding.unwrap().is_string()); - assert_eq!(binding.unwrap(), v8::String::new(scope, "b").unwrap()); - - let default_export_name = v8::String::new(scope, "default").unwrap(); - let binding = module_namespace.get(scope, default_export_name.into()); - - assert!(binding.unwrap().is_number()); - assert_eq!(binding.unwrap(), v8::Number::new(scope, 3_f64)); -} - -#[test] -fn test_heap_limits() { - let create_params = - v8::Isolate::create_params().heap_limits(0, 5 * 1024 * 1024); - let mut runtime = JsRuntime::new(RuntimeOptions { - create_params: Some(create_params), - ..Default::default() - }); - let cb_handle = runtime.v8_isolate().thread_safe_handle(); - - let callback_invoke_count = Rc::new(AtomicUsize::new(0)); - let inner_invoke_count = Rc::clone(&callback_invoke_count); - - runtime.add_near_heap_limit_callback(move |current_limit, _initial_limit| { - inner_invoke_count.fetch_add(1, Ordering::SeqCst); - cb_handle.terminate_execution(); - current_limit * 2 - }); - let err = runtime - .execute_script_static( - "script name", - r#"let s = ""; while(true) { s += "Hello"; }"#, - ) - .expect_err("script should fail"); - assert_eq!( - "Uncaught Error: execution terminated", - err.downcast::().unwrap().exception_message - ); - assert!(callback_invoke_count.load(Ordering::SeqCst) > 0) -} - -#[test] -fn test_heap_limit_cb_remove() { - let mut runtime = JsRuntime::new(Default::default()); - - runtime.add_near_heap_limit_callback(|current_limit, _initial_limit| { - current_limit * 2 - }); - runtime.remove_near_heap_limit_callback(3 * 1024 * 1024); - assert!(runtime.allocations.near_heap_limit_callback_data.is_none()); -} - -#[test] -fn test_heap_limit_cb_multiple() { - let create_params = - v8::Isolate::create_params().heap_limits(0, 5 * 1024 * 1024); - let mut runtime = JsRuntime::new(RuntimeOptions { - create_params: Some(create_params), - ..Default::default() - }); - let cb_handle = runtime.v8_isolate().thread_safe_handle(); - - let callback_invoke_count_first = Rc::new(AtomicUsize::new(0)); - let inner_invoke_count_first = Rc::clone(&callback_invoke_count_first); - runtime.add_near_heap_limit_callback(move |current_limit, _initial_limit| { - inner_invoke_count_first.fetch_add(1, Ordering::SeqCst); - current_limit * 2 - }); - - let callback_invoke_count_second = Rc::new(AtomicUsize::new(0)); - let inner_invoke_count_second = Rc::clone(&callback_invoke_count_second); - runtime.add_near_heap_limit_callback(move |current_limit, _initial_limit| { - inner_invoke_count_second.fetch_add(1, Ordering::SeqCst); - cb_handle.terminate_execution(); - current_limit * 2 - }); - - let err = runtime - .execute_script_static( - "script name", - r#"let s = ""; while(true) { s += "Hello"; }"#, - ) - .expect_err("script should fail"); - assert_eq!( - "Uncaught Error: execution terminated", - err.downcast::().unwrap().exception_message - ); - assert_eq!(0, callback_invoke_count_first.load(Ordering::SeqCst)); - assert!(callback_invoke_count_second.load(Ordering::SeqCst) > 0); -} - -#[test] -fn es_snapshot() { - #[derive(Default)] - struct ModsLoader; - - impl ModuleLoader for ModsLoader { - fn resolve( - &self, - specifier: &str, - referrer: &str, - _kind: ResolutionKind, - ) -> Result { - let s = crate::resolve_import(specifier, referrer).unwrap(); - Ok(s) - } - - fn load( - &self, - _module_specifier: &ModuleSpecifier, - _maybe_referrer: Option<&ModuleSpecifier>, - _is_dyn_import: bool, - ) -> Pin> { - eprintln!("load() should not be called"); - unreachable!() - } - } - - fn create_module( - runtime: &mut JsRuntime, - i: usize, - main: bool, - ) -> ModuleInfo { - let specifier = crate::resolve_url(&format!("file:///{i}.js")).unwrap(); - let prev = i - 1; - let source_code = format!( - r#" - import {{ f{prev} }} from "file:///{prev}.js"; - export function f{i}() {{ return f{prev}() }} - "# - ) - .into(); - - let id = if main { - futures::executor::block_on( - runtime.load_main_module(&specifier, Some(source_code)), - ) - .unwrap() - } else { - futures::executor::block_on( - runtime.load_side_module(&specifier, Some(source_code)), - ) - .unwrap() - }; - assert_eq!(i, id); - - #[allow(clippy::let_underscore_future)] - let _ = runtime.mod_evaluate(id); - futures::executor::block_on(runtime.run_event_loop(false)).unwrap(); - - ModuleInfo { - id, - main, - name: specifier.into(), - requests: vec![crate::modules::ModuleRequest { - specifier: format!("file:///{prev}.js"), - asserted_module_type: AssertedModuleType::JavaScriptOrWasm, - }], - module_type: ModuleType::JavaScript, - } - } - - fn assert_module_map(runtime: &mut JsRuntime, modules: &Vec) { - let module_map = runtime.module_map.borrow(); - assert_eq!(module_map.handles.len(), modules.len()); - assert_eq!(module_map.info.len(), modules.len()); - assert_eq!( - module_map.by_name(AssertedModuleType::Json).len() - + module_map - .by_name(AssertedModuleType::JavaScriptOrWasm) - .len(), - modules.len() - ); - - assert_eq!(module_map.next_load_id, (modules.len() + 1) as ModuleLoadId); - - for info in modules { - assert!(module_map.handles.get(info.id).is_some()); - assert_eq!(module_map.info.get(info.id).unwrap(), info); - assert_eq!( - module_map - .by_name(AssertedModuleType::JavaScriptOrWasm) - .get(&info.name) - .unwrap(), - &SymbolicModule::Mod(info.id) - ); - } - } - - #[op] - fn op_test() -> Result { - Ok(String::from("test")) - } - - let loader = Rc::new(ModsLoader::default()); - let mut runtime = JsRuntimeForSnapshot::new( - RuntimeOptions { - module_loader: Some(loader.clone()), - extensions: vec![Extension::builder("text_ext") - .ops(vec![op_test::decl()]) - .build()], - ..Default::default() - }, - Default::default(), - ); - - let specifier = crate::resolve_url("file:///0.js").unwrap(); - let source_code = - ascii_str!(r#"export function f0() { return "hello world" }"#); - let id = futures::executor::block_on( - runtime.load_side_module(&specifier, Some(source_code)), - ) - .unwrap(); - - #[allow(clippy::let_underscore_future)] - let _ = runtime.mod_evaluate(id); - futures::executor::block_on(runtime.run_event_loop(false)).unwrap(); - - let mut modules = vec![]; - modules.push(ModuleInfo { - id, - main: false, - name: specifier.into(), - requests: vec![], - module_type: ModuleType::JavaScript, - }); - - modules.extend((1..200).map(|i| create_module(&mut runtime, i, false))); - - assert_module_map(&mut runtime, &modules); - - let snapshot = runtime.snapshot(); - - let mut runtime2 = JsRuntimeForSnapshot::new( - RuntimeOptions { - module_loader: Some(loader.clone()), - startup_snapshot: Some(Snapshot::JustCreated(snapshot)), - extensions: vec![Extension::builder("text_ext") - .ops(vec![op_test::decl()]) - .build()], - ..Default::default() - }, - Default::default(), - ); - - assert_module_map(&mut runtime2, &modules); - - modules.extend((200..400).map(|i| create_module(&mut runtime2, i, false))); - modules.push(create_module(&mut runtime2, 400, true)); - - assert_module_map(&mut runtime2, &modules); - - let snapshot2 = runtime2.snapshot(); - - let mut runtime3 = JsRuntime::new(RuntimeOptions { - module_loader: Some(loader), - startup_snapshot: Some(Snapshot::JustCreated(snapshot2)), - extensions: vec![Extension::builder("text_ext") - .ops(vec![op_test::decl()]) - .build()], - ..Default::default() - }); - - assert_module_map(&mut runtime3, &modules); - - let source_code = r#"(async () => { - const mod = await import("file:///400.js"); - return mod.f400() + " " + Deno.core.ops.op_test(); - })();"#; - let val = runtime3.execute_script_static(".", source_code).unwrap(); - let val = futures::executor::block_on(runtime3.resolve_value(val)).unwrap(); - { - let scope = &mut runtime3.handle_scope(); - let value = v8::Local::new(scope, val); - let str_ = value.to_string(scope).unwrap().to_rust_string_lossy(scope); - assert_eq!(str_, "hello world test"); - } -} - -#[test] -fn test_error_without_stack() { - let mut runtime = JsRuntime::new(RuntimeOptions::default()); - // SyntaxError - let result = runtime.execute_script_static( - "error_without_stack.js", - r#" -function main() { - console.log("asdf); -} -main(); -"#, - ); - let expected_error = r#"Uncaught SyntaxError: Invalid or unexpected token - at error_without_stack.js:3:15"#; - assert_eq!(result.unwrap_err().to_string(), expected_error); -} - -#[test] -fn test_error_stack() { - let mut runtime = JsRuntime::new(RuntimeOptions::default()); - let result = runtime.execute_script_static( - "error_stack.js", - r#" -function assert(cond) { - if (!cond) { - throw Error("assert"); - } -} -function main() { - assert(false); -} -main(); - "#, - ); - let expected_error = r#"Error: assert - at assert (error_stack.js:4:11) - at main (error_stack.js:8:3) - at error_stack.js:10:1"#; - assert_eq!(result.unwrap_err().to_string(), expected_error); -} - -#[tokio::test] -async fn test_error_async_stack() { - let mut runtime = JsRuntime::new(RuntimeOptions::default()); - poll_fn(move |cx| { - runtime - .execute_script_static( - "error_async_stack.js", - r#" - (async () => { - const p = (async () => { - await Promise.resolve().then(() => { - throw new Error("async"); - }); - })(); - try { - await p; - } catch (error) { - console.log(error.stack); - throw error; - } - })();"#, - ) - .unwrap(); - let expected_error = r#"Error: async - at error_async_stack.js:5:13 - at async error_async_stack.js:4:5 - at async error_async_stack.js:9:5"#; - - match runtime.poll_event_loop(cx, false) { - Poll::Ready(Err(e)) => { - assert_eq!(e.to_string(), expected_error); - } - _ => panic!(), - }; - Poll::Ready(()) - }) - .await; -} - -#[tokio::test] -async fn test_error_context() { - use anyhow::anyhow; - - #[op] - fn op_err_sync() -> Result<(), Error> { - Err(anyhow!("original sync error").context("higher-level sync error")) - } - - #[op] - async fn op_err_async() -> Result<(), Error> { - Err(anyhow!("original async error").context("higher-level async error")) - } - - deno_core::extension!(test_ext, ops = [op_err_sync, op_err_async]); - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![test_ext::init_ops()], - ..Default::default() - }); - - poll_fn(move |cx| { - runtime - .execute_script_static( - "test_error_context_sync.js", - r#" -let errMessage; -try { - Deno.core.ops.op_err_sync(); -} catch (err) { - errMessage = err.message; -} -if (errMessage !== "higher-level sync error: original sync error") { - throw new Error("unexpected error message from op_err_sync: " + errMessage); -} -"#, - ) - .unwrap(); - - let promise = runtime - .execute_script_static( - "test_error_context_async.js", - r#" - -(async () => { -let errMessage; -try { - await Deno.core.opAsync("op_err_async"); -} catch (err) { - errMessage = err.message; -} -if (errMessage !== "higher-level async error: original async error") { - throw new Error("unexpected error message from op_err_async: " + errMessage); -} -})() -"#, - ) - .unwrap(); - - match runtime.poll_value(&promise, cx) { - Poll::Ready(Ok(_)) => {} - Poll::Ready(Err(err)) => panic!("{err:?}"), - _ => panic!(), - } - Poll::Ready(()) - }) - .await; -} - -#[tokio::test] -async fn test_pump_message_loop() { - let mut runtime = JsRuntime::new(RuntimeOptions::default()); - poll_fn(move |cx| { - runtime - .execute_script_static( - "pump_message_loop.js", - r#" -function assertEquals(a, b) { -if (a === b) return; -throw a + " does not equal " + b; -} -const sab = new SharedArrayBuffer(16); -const i32a = new Int32Array(sab); -globalThis.resolved = false; -(function() { -const result = Atomics.waitAsync(i32a, 0, 0); -result.value.then( - (value) => { assertEquals("ok", value); globalThis.resolved = true; }, - () => { assertUnreachable(); -}); -})(); -const notify_return_value = Atomics.notify(i32a, 0, 1); -assertEquals(1, notify_return_value); -"#, - ) - .unwrap(); - - match runtime.poll_event_loop(cx, false) { - Poll::Ready(Ok(())) => {} - _ => panic!(), - }; - - // noop script, will resolve promise from first script - runtime - .execute_script_static("pump_message_loop2.js", r#"assertEquals(1, 1);"#) - .unwrap(); - - // check that promise from `Atomics.waitAsync` has been resolved - runtime - .execute_script_static( - "pump_message_loop3.js", - r#"assertEquals(globalThis.resolved, true);"#, - ) - .unwrap(); - Poll::Ready(()) - }) - .await; -} - -#[test] -fn test_v8_platform() { - let options = RuntimeOptions { - v8_platform: Some(v8::new_default_platform(0, false).make_shared()), - ..Default::default() - }; - let mut runtime = JsRuntime::new(options); - runtime.execute_script_static("", "").unwrap(); -} - -#[ignore] // TODO(@littledivy): Fast API ops when snapshot is not loaded. -#[test] -fn test_is_proxy() { - let mut runtime = JsRuntime::new(RuntimeOptions::default()); - let all_true: v8::Global = runtime - .execute_script_static( - "is_proxy.js", - r#" - (function () { - const o = { a: 1, b: 2}; - const p = new Proxy(o, {}); - return Deno.core.ops.op_is_proxy(p) && !Deno.core.ops.op_is_proxy(o) && !Deno.core.ops.op_is_proxy(42); - })() - "#, - ) - .unwrap(); - let mut scope = runtime.handle_scope(); - let all_true = v8::Local::::new(&mut scope, &all_true); - assert!(all_true.is_true()); -} - -#[tokio::test] -async fn test_async_opstate_borrow() { - struct InnerState(u64); - - #[op] - async fn op_async_borrow( - op_state: Rc>, - ) -> Result<(), Error> { - let n = { - let op_state = op_state.borrow(); - let inner_state = op_state.borrow::(); - inner_state.0 - }; - // Future must be Poll::Pending on first call - tokio::time::sleep(std::time::Duration::from_millis(1)).await; - if n != 42 { - unreachable!(); - } - Ok(()) - } - - deno_core::extension!( - test_ext, - ops = [op_async_borrow], - state = |state| state.put(InnerState(42)) - ); - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![test_ext::init_ops()], - ..Default::default() - }); - - runtime - .execute_script_static( - "op_async_borrow.js", - "Deno.core.opAsync(\"op_async_borrow\")", - ) - .unwrap(); - runtime.run_event_loop(false).await.unwrap(); -} - -#[tokio::test] -async fn test_sync_op_serialize_object_with_numbers_as_keys() { - #[op] - fn op_sync_serialize_object_with_numbers_as_keys( - value: serde_json::Value, - ) -> Result<(), Error> { - assert_eq!( - value.to_string(), - r#"{"lines":{"100":{"unit":"m"},"200":{"unit":"cm"}}}"# - ); - Ok(()) - } - - deno_core::extension!( - test_ext, - ops = [op_sync_serialize_object_with_numbers_as_keys] - ); - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![test_ext::init_ops()], - ..Default::default() - }); - - runtime - .execute_script_static( - "op_sync_serialize_object_with_numbers_as_keys.js", - r#" -Deno.core.ops.op_sync_serialize_object_with_numbers_as_keys({ -lines: { - 100: { - unit: "m" - }, - 200: { - unit: "cm" - } -} -}) -"#, - ) - .unwrap(); - runtime.run_event_loop(false).await.unwrap(); -} - -#[tokio::test] -async fn test_async_op_serialize_object_with_numbers_as_keys() { - #[op] - async fn op_async_serialize_object_with_numbers_as_keys( - value: serde_json::Value, - ) -> Result<(), Error> { - assert_eq!( - value.to_string(), - r#"{"lines":{"100":{"unit":"m"},"200":{"unit":"cm"}}}"# - ); - Ok(()) - } - - deno_core::extension!( - test_ext, - ops = [op_async_serialize_object_with_numbers_as_keys] - ); - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![test_ext::init_ops()], - ..Default::default() - }); - - runtime - .execute_script_static( - "op_async_serialize_object_with_numbers_as_keys.js", - r#" - -Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { -lines: { - 100: { - unit: "m" - }, - 200: { - unit: "cm" - } -} -}) -"#, - ) - .unwrap(); - runtime.run_event_loop(false).await.unwrap(); -} - -#[tokio::test] -async fn test_set_macrotask_callback_set_next_tick_callback() { - #[op] - async fn op_async_sleep() -> Result<(), Error> { - // Future must be Poll::Pending on first call - tokio::time::sleep(std::time::Duration::from_millis(1)).await; - Ok(()) - } - - deno_core::extension!(test_ext, ops = [op_async_sleep]); - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![test_ext::init_ops()], - ..Default::default() - }); - - runtime - .execute_script_static( - "macrotasks_and_nextticks.js", - r#" - - (async function () { - const results = []; - Deno.core.setMacrotaskCallback(() => { - results.push("macrotask"); - return true; - }); - Deno.core.setNextTickCallback(() => { - results.push("nextTick"); - Deno.core.ops.op_set_has_tick_scheduled(false); - }); - Deno.core.ops.op_set_has_tick_scheduled(true); - await Deno.core.opAsync('op_async_sleep'); - if (results[0] != "nextTick") { - throw new Error(`expected nextTick, got: ${results[0]}`); - } - if (results[1] != "macrotask") { - throw new Error(`expected macrotask, got: ${results[1]}`); - } - })(); - "#, - ) - .unwrap(); - runtime.run_event_loop(false).await.unwrap(); -} - -#[test] -fn test_has_tick_scheduled() { - use futures::task::ArcWake; - - static MACROTASK: AtomicUsize = AtomicUsize::new(0); - static NEXT_TICK: AtomicUsize = AtomicUsize::new(0); - - #[op] - fn op_macrotask() -> Result<(), AnyError> { - MACROTASK.fetch_add(1, Ordering::Relaxed); - Ok(()) - } - - #[op] - fn op_next_tick() -> Result<(), AnyError> { - NEXT_TICK.fetch_add(1, Ordering::Relaxed); - Ok(()) - } - - deno_core::extension!(test_ext, ops = [op_macrotask, op_next_tick]); - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![test_ext::init_ops()], - ..Default::default() - }); - - runtime - .execute_script_static( - "has_tick_scheduled.js", - r#" - Deno.core.setMacrotaskCallback(() => { - Deno.core.ops.op_macrotask(); - return true; // We're done. - }); - Deno.core.setNextTickCallback(() => Deno.core.ops.op_next_tick()); - Deno.core.ops.op_set_has_tick_scheduled(true); - "#, - ) - .unwrap(); - - struct ArcWakeImpl(Arc); - impl ArcWake for ArcWakeImpl { - fn wake_by_ref(arc_self: &Arc) { - arc_self.0.fetch_add(1, Ordering::Relaxed); - } - } - - let awoken_times = Arc::new(AtomicUsize::new(0)); - let waker = futures::task::waker(Arc::new(ArcWakeImpl(awoken_times.clone()))); - let cx = &mut Context::from_waker(&waker); - - assert!(matches!(runtime.poll_event_loop(cx, false), Poll::Pending)); - assert_eq!(1, MACROTASK.load(Ordering::Relaxed)); - assert_eq!(1, NEXT_TICK.load(Ordering::Relaxed)); - assert_eq!(awoken_times.swap(0, Ordering::Relaxed), 1); - assert!(matches!(runtime.poll_event_loop(cx, false), Poll::Pending)); - assert_eq!(awoken_times.swap(0, Ordering::Relaxed), 1); - assert!(matches!(runtime.poll_event_loop(cx, false), Poll::Pending)); - assert_eq!(awoken_times.swap(0, Ordering::Relaxed), 1); - assert!(matches!(runtime.poll_event_loop(cx, false), Poll::Pending)); - assert_eq!(awoken_times.swap(0, Ordering::Relaxed), 1); - - runtime.inner.state.borrow_mut().has_tick_scheduled = false; - assert!(matches!( - runtime.poll_event_loop(cx, false), - Poll::Ready(Ok(())) - )); - assert_eq!(awoken_times.load(Ordering::Relaxed), 0); - assert!(matches!( - runtime.poll_event_loop(cx, false), - Poll::Ready(Ok(())) - )); - assert_eq!(awoken_times.load(Ordering::Relaxed), 0); -} - -#[test] -fn terminate_during_module_eval() { - #[derive(Default)] - struct ModsLoader; - - impl ModuleLoader for ModsLoader { - fn resolve( - &self, - specifier: &str, - referrer: &str, - _kind: ResolutionKind, - ) -> Result { - assert_eq!(specifier, "file:///main.js"); - assert_eq!(referrer, "."); - let s = crate::resolve_import(specifier, referrer).unwrap(); - Ok(s) - } - - fn load( - &self, - _module_specifier: &ModuleSpecifier, - _maybe_referrer: Option<&ModuleSpecifier>, - _is_dyn_import: bool, - ) -> Pin> { - async move { - Ok(ModuleSource::for_test( - "console.log('hello world');", - "file:///main.js", - )) - } - .boxed_local() - } - } - - let loader = std::rc::Rc::new(ModsLoader::default()); - let mut runtime = JsRuntime::new(RuntimeOptions { - module_loader: Some(loader), - ..Default::default() - }); - - let specifier = crate::resolve_url("file:///main.js").unwrap(); - let source_code = ascii_str!("Deno.core.print('hello\\n')"); - - let module_id = futures::executor::block_on( - runtime.load_main_module(&specifier, Some(source_code)), - ) - .unwrap(); - - runtime.v8_isolate().terminate_execution(); - - let mod_result = - futures::executor::block_on(runtime.mod_evaluate(module_id)).unwrap(); - assert!(mod_result - .unwrap_err() - .to_string() - .contains("JavaScript execution has been terminated")); -} - -#[tokio::test] -async fn test_unhandled_rejection_order() { - let mut runtime = JsRuntime::new(Default::default()); - runtime - .execute_script_static( - "", - r#" - for (let i = 0; i < 100; i++) { - Promise.reject(i); - } - "#, - ) - .unwrap(); - let err = runtime.run_event_loop(false).await.unwrap_err(); - assert_eq!(err.to_string(), "Uncaught (in promise) 0"); -} - -#[tokio::test] -async fn test_set_promise_reject_callback() { - static PROMISE_REJECT: AtomicUsize = AtomicUsize::new(0); - - #[op] - fn op_promise_reject() -> Result<(), AnyError> { - PROMISE_REJECT.fetch_add(1, Ordering::Relaxed); - Ok(()) - } - - deno_core::extension!(test_ext, ops = [op_promise_reject]); - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![test_ext::init_ops()], - ..Default::default() - }); - - runtime - .execute_script_static( - "promise_reject_callback.js", - r#" - // Note: |promise| is not the promise created below, it's a child. - Deno.core.ops.op_set_promise_reject_callback((type, promise, reason) => { - if (type !== /* PromiseRejectWithNoHandler */ 0) { - throw Error("unexpected type: " + type); - } - if (reason.message !== "reject") { - throw Error("unexpected reason: " + reason); - } - Deno.core.ops.op_store_pending_promise_rejection(promise); - Deno.core.ops.op_promise_reject(); - }); - new Promise((_, reject) => reject(Error("reject"))); - "#, - ) - .unwrap(); - runtime.run_event_loop(false).await.unwrap_err(); - - assert_eq!(1, PROMISE_REJECT.load(Ordering::Relaxed)); - - runtime - .execute_script_static( - "promise_reject_callback.js", - r#" - { - const prev = Deno.core.ops.op_set_promise_reject_callback((...args) => { - prev(...args); - }); - } - new Promise((_, reject) => reject(Error("reject"))); - "#, - ) - .unwrap(); - runtime.run_event_loop(false).await.unwrap_err(); - - assert_eq!(2, PROMISE_REJECT.load(Ordering::Relaxed)); -} - -#[tokio::test] -async fn test_set_promise_reject_callback_realms() { - let mut runtime = JsRuntime::new(RuntimeOptions::default()); - let global_realm = runtime.global_realm(); - let realm1 = runtime.create_realm().unwrap(); - let realm2 = runtime.create_realm().unwrap(); - - let realm_expectations = &[ - (&global_realm, "global_realm", 42), - (&realm1, "realm1", 140), - (&realm2, "realm2", 720), - ]; - - // Set up promise reject callbacks. - for (realm, realm_name, number) in realm_expectations { - realm - .execute_script( - runtime.v8_isolate(), - "", - format!( - r#" - - globalThis.rejectValue = undefined; - Deno.core.setPromiseRejectCallback((_type, _promise, reason) => {{ - globalThis.rejectValue = `{realm_name}/${{reason}}`; - }}); - Deno.core.opAsync("op_void_async").then(() => Promise.reject({number})); - "# - ).into() - ) - .unwrap(); - } - - runtime.run_event_loop(false).await.unwrap(); - - for (realm, realm_name, number) in realm_expectations { - let reject_value = realm - .execute_script_static(runtime.v8_isolate(), "", "globalThis.rejectValue") - .unwrap(); - let scope = &mut realm.handle_scope(runtime.v8_isolate()); - let reject_value = v8::Local::new(scope, reject_value); - assert!(reject_value.is_string()); - let reject_value_string = reject_value.to_rust_string_lossy(scope); - assert_eq!(reject_value_string, format!("{realm_name}/{number}")); - } -} - -#[tokio::test] -async fn test_set_promise_reject_callback_top_level_await() { - static PROMISE_REJECT: AtomicUsize = AtomicUsize::new(0); - - #[op] - fn op_promise_reject() -> Result<(), AnyError> { - PROMISE_REJECT.fetch_add(1, Ordering::Relaxed); - Ok(()) - } - - deno_core::extension!(test_ext, ops = [op_promise_reject]); - - #[derive(Default)] - struct ModsLoader; - - impl ModuleLoader for ModsLoader { - fn resolve( - &self, - specifier: &str, - referrer: &str, - _kind: ResolutionKind, - ) -> Result { - assert_eq!(specifier, "file:///main.js"); - assert_eq!(referrer, "."); - let s = crate::resolve_import(specifier, referrer).unwrap(); - Ok(s) - } - - fn load( - &self, - _module_specifier: &ModuleSpecifier, - _maybe_referrer: Option<&ModuleSpecifier>, - _is_dyn_import: bool, - ) -> Pin> { - let code = r#" - Deno.core.ops.op_set_promise_reject_callback((type, promise, reason) => { - Deno.core.ops.op_promise_reject(); - }); - throw new Error('top level throw'); - "#; - - async move { Ok(ModuleSource::for_test(code, "file:///main.js")) } - .boxed_local() - } - } - - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![test_ext::init_ops()], - module_loader: Some(Rc::new(ModsLoader)), - ..Default::default() - }); - - let id = runtime - .load_main_module(&crate::resolve_url("file:///main.js").unwrap(), None) - .await - .unwrap(); - let receiver = runtime.mod_evaluate(id); - runtime.run_event_loop(false).await.unwrap(); - receiver.await.unwrap().unwrap_err(); - - assert_eq!(1, PROMISE_REJECT.load(Ordering::Relaxed)); -} - -#[test] -fn test_op_return_serde_v8_error() { - #[op] - fn op_err() -> Result, anyhow::Error> { - Ok([(1, 2), (3, 4)].into_iter().collect()) // Maps can't have non-string keys in serde_v8 - } - - deno_core::extension!(test_ext, ops = [op_err]); - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![test_ext::init_ops()], - ..Default::default() - }); - assert!(runtime - .execute_script_static( - "test_op_return_serde_v8_error.js", - "Deno.core.ops.op_err()" - ) - .is_err()); -} - -#[test] -fn test_op_high_arity() { - #[op] - fn op_add_4( - x1: i64, - x2: i64, - x3: i64, - x4: i64, - ) -> Result { - Ok(x1 + x2 + x3 + x4) - } - - deno_core::extension!(test_ext, ops = [op_add_4]); - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![test_ext::init_ops()], - ..Default::default() - }); - let r = runtime - .execute_script_static("test.js", "Deno.core.ops.op_add_4(1, 2, 3, 4)") - .unwrap(); - let scope = &mut runtime.handle_scope(); - assert_eq!(r.open(scope).integer_value(scope), Some(10)); -} - -#[test] -fn test_op_disabled() { - #[op] - fn op_foo() -> Result { - Ok(42) - } - - fn ops() -> Vec { - vec![op_foo::decl().disable()] - } - - deno_core::extension!(test_ext, ops_fn = ops); - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![test_ext::init_ops()], - ..Default::default() - }); - let err = runtime - .execute_script_static("test.js", "Deno.core.ops.op_foo()") - .unwrap_err(); - assert!(err - .to_string() - .contains("TypeError: Deno.core.ops.op_foo is not a function")); -} - -#[test] -fn test_op_detached_buffer() { - use serde_v8::DetachedBuffer; - - #[op] - fn op_sum_take(b: DetachedBuffer) -> Result { - Ok(b.as_ref().iter().clone().map(|x| *x as u64).sum()) - } - - #[op] - fn op_boomerang(b: DetachedBuffer) -> Result { - Ok(b) - } - - deno_core::extension!(test_ext, ops = [op_sum_take, op_boomerang]); - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![test_ext::init_ops()], - ..Default::default() - }); - - runtime - .execute_script_static( - "test.js", - r#" - const a1 = new Uint8Array([1,2,3]); - const a1b = a1.subarray(0, 3); - const a2 = new Uint8Array([5,10,15]); - const a2b = a2.subarray(0, 3); - if (!(a1.length > 0 && a1b.length > 0)) { - throw new Error("a1 & a1b should have a length"); - } - let sum = Deno.core.ops.op_sum_take(a1b); - if (sum !== 6) { - throw new Error(`Bad sum: ${sum}`); - } - if (a1.length > 0 || a1b.length > 0) { - throw new Error("expecting a1 & a1b to be detached"); - } - const a3 = Deno.core.ops.op_boomerang(a2b); - if (a3.byteLength != 3) { - throw new Error(`Expected a3.byteLength === 3, got ${a3.byteLength}`); - } - if (a3[0] !== 5 || a3[1] !== 10) { - throw new Error(`Invalid a3: ${a3[0]}, ${a3[1]}`); - } - if (a2.byteLength > 0 || a2b.byteLength > 0) { - throw new Error("expecting a2 & a2b to be detached, a3 re-attached"); - } - const wmem = new WebAssembly.Memory({ initial: 1, maximum: 2 }); - const w32 = new Uint32Array(wmem.buffer); - w32[0] = 1; w32[1] = 2; w32[2] = 3; - const assertWasmThrow = (() => { - try { - let sum = Deno.core.ops.op_sum_take(w32.subarray(0, 2)); - return false; - } catch(e) { - return e.message.includes('invalid type; expected: detachable'); - } - }); - if (!assertWasmThrow()) { - throw new Error("expected wasm mem to not be detachable"); - } - "#, - ) - .unwrap(); -} - -#[test] -fn test_op_unstable_disabling() { - #[op] - fn op_foo() -> Result { - Ok(42) - } - - #[op(unstable)] - fn op_bar() -> Result { - Ok(42) - } - - deno_core::extension!( - test_ext, - ops = [op_foo, op_bar], - middleware = |op| if op.is_unstable { op.disable() } else { op } - ); - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![test_ext::init_ops()], - ..Default::default() - }); - runtime - .execute_script_static( - "test.js", - r#" - if (Deno.core.ops.op_foo() !== 42) { - throw new Error("Expected op_foo() === 42"); - } - if (typeof Deno.core.ops.op_bar !== "undefined") { - throw new Error("Expected op_bar to be disabled") - } - "#, - ) - .unwrap(); -} - -#[test] -fn js_realm_simple() { - let mut runtime = JsRuntime::new(Default::default()); - let main_context = runtime.global_context(); - let main_global = { - let scope = &mut runtime.handle_scope(); - let local_global = main_context.open(scope).global(scope); - v8::Global::new(scope, local_global) - }; - - let realm = runtime.create_realm().unwrap(); - assert_ne!(realm.context(), &main_context); - assert_ne!(realm.global_object(runtime.v8_isolate()), main_global); - - let main_object = runtime.execute_script_static("", "Object").unwrap(); - let realm_object = realm - .execute_script_static(runtime.v8_isolate(), "", "Object") - .unwrap(); - assert_ne!(main_object, realm_object); -} - -#[test] -fn js_realm_init() { - #[op] - fn op_test() -> Result { - Ok(String::from("Test")) - } - - deno_core::extension!(test_ext, ops = [op_test]); - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![test_ext::init_ops()], - ..Default::default() - }); - let realm = runtime.create_realm().unwrap(); - let ret = realm - .execute_script_static(runtime.v8_isolate(), "", "Deno.core.ops.op_test()") - .unwrap(); - - let scope = &mut realm.handle_scope(runtime.v8_isolate()); - assert_eq!(ret, serde_v8::to_v8(scope, "Test").unwrap()); -} - -#[test] -fn js_realm_init_snapshot() { - let snapshot = { - let runtime = - JsRuntimeForSnapshot::new(Default::default(), Default::default()); - let snap: &[u8] = &runtime.snapshot(); - Vec::from(snap).into_boxed_slice() - }; - - #[op] - fn op_test() -> Result { - Ok(String::from("Test")) - } - - deno_core::extension!(test_ext, ops = [op_test]); - let mut runtime = JsRuntime::new(RuntimeOptions { - startup_snapshot: Some(Snapshot::Boxed(snapshot)), - extensions: vec![test_ext::init_ops()], - ..Default::default() - }); - let realm = runtime.create_realm().unwrap(); - let ret = realm - .execute_script_static(runtime.v8_isolate(), "", "Deno.core.ops.op_test()") - .unwrap(); - - let scope = &mut realm.handle_scope(runtime.v8_isolate()); - assert_eq!(ret, serde_v8::to_v8(scope, "Test").unwrap()); -} - -#[test] -fn js_realm_sync_ops() { - // Test that returning a RustToV8Buf and throwing an exception from a sync - // op result in objects with prototypes from the right realm. Note that we - // don't test the result of returning structs, because they will be - // serialized to objects with null prototype. - - #[op] - fn op_test(fail: bool) -> Result { - if !fail { - Ok(ToJsBuffer::empty()) - } else { - Err(crate::error::type_error("Test")) - } - } - - deno_core::extension!(test_ext, ops = [op_test]); - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![test_ext::init_ops()], - get_error_class_fn: Some(&|error| { - crate::error::get_custom_error_class(error).unwrap() - }), - ..Default::default() - }); - let new_realm = runtime.create_realm().unwrap(); - - // Test in both realms - for realm in [runtime.global_realm(), new_realm].into_iter() { - let ret = realm - .execute_script_static( - runtime.v8_isolate(), - "", - r#" - const buf = Deno.core.ops.op_test(false); - try { - Deno.core.ops.op_test(true); - } catch(e) { - err = e; - } - buf instanceof Uint8Array && buf.byteLength === 0 && - err instanceof TypeError && err.message === "Test" - "#, - ) - .unwrap(); - assert!(ret.open(runtime.v8_isolate()).is_true()); - } -} - -#[tokio::test] -async fn js_realm_async_ops() { - // Test that returning a RustToV8Buf and throwing an exception from a async - // op result in objects with prototypes from the right realm. Note that we - // don't test the result of returning structs, because they will be - // serialized to objects with null prototype. - - #[op] - async fn op_test(fail: bool) -> Result { - if !fail { - Ok(ToJsBuffer::empty()) - } else { - Err(crate::error::type_error("Test")) - } - } - - deno_core::extension!(test_ext, ops = [op_test]); - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![test_ext::init_ops()], - get_error_class_fn: Some(&|error| { - crate::error::get_custom_error_class(error).unwrap() - }), - ..Default::default() - }); - - let global_realm = runtime.global_realm(); - let new_realm = runtime.create_realm().unwrap(); - - let mut rets = vec![]; - - // Test in both realms - for realm in [global_realm, new_realm].into_iter() { - let ret = realm - .execute_script_static( - runtime.v8_isolate(), - "", - r#" - - (async function () { - const buf = await Deno.core.opAsync("op_test", false); - let err; - try { - await Deno.core.opAsync("op_test", true); - } catch(e) { - err = e; - } - return buf instanceof Uint8Array && buf.byteLength === 0 && - err instanceof TypeError && err.message === "Test" ; - })(); - "#, - ) - .unwrap(); - rets.push((realm, ret)); - } - - runtime.run_event_loop(false).await.unwrap(); - - for ret in rets { - let scope = &mut ret.0.handle_scope(runtime.v8_isolate()); - let value = v8::Local::new(scope, ret.1); - let promise = v8::Local::::try_from(value).unwrap(); - let result = promise.result(scope); - - assert!(result.is_boolean() && result.is_true()); - } -} - -#[ignore] -#[tokio::test] -async fn js_realm_gc() { - static INVOKE_COUNT: AtomicUsize = AtomicUsize::new(0); - struct PendingFuture {} - - impl Future for PendingFuture { - type Output = (); - fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> { - Poll::Pending - } - } - - impl Drop for PendingFuture { - fn drop(&mut self) { - assert_eq!(INVOKE_COUNT.fetch_sub(1, Ordering::SeqCst), 1); - } - } - - // Never resolves. - #[op] - async fn op_pending() { - assert_eq!(INVOKE_COUNT.fetch_add(1, Ordering::SeqCst), 0); - PendingFuture {}.await - } - - deno_core::extension!(test_ext, ops = [op_pending]); - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![test_ext::init_ops()], - ..Default::default() - }); - - // Detect a drop in OpState - let opstate_drop_detect = Rc::new(()); - runtime - .op_state() - .borrow_mut() - .put(opstate_drop_detect.clone()); - assert_eq!(Rc::strong_count(&opstate_drop_detect), 2); - - let other_realm = runtime.create_realm().unwrap(); - other_realm - .execute_script( - runtime.v8_isolate(), - "future", - ModuleCode::from_static("Deno.core.opAsync('op_pending')"), - ) - .unwrap(); - while INVOKE_COUNT.load(Ordering::SeqCst) == 0 { - poll_fn(|cx: &mut Context| runtime.poll_event_loop(cx, false)) - .await - .unwrap(); - } - drop(other_realm); - while INVOKE_COUNT.load(Ordering::SeqCst) == 1 { - poll_fn(|cx| runtime.poll_event_loop(cx, false)) - .await - .unwrap(); - } - drop(runtime); - - // Make sure the OpState was dropped properly when the runtime dropped - assert_eq!(Rc::strong_count(&opstate_drop_detect), 1); -} - -#[tokio::test] -async fn js_realm_ref_unref_ops() { - // Never resolves. - #[op] - async fn op_pending() { - futures::future::pending().await - } - - deno_core::extension!(test_ext, ops = [op_pending]); - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![test_ext::init_ops()], - ..Default::default() - }); - - poll_fn(move |cx| { - let main_realm = runtime.global_realm(); - let other_realm = runtime.create_realm().unwrap(); - - main_realm - .execute_script_static( - runtime.v8_isolate(), - "", - r#" - - var promise = Deno.core.opAsync("op_pending"); - "#, - ) - .unwrap(); - other_realm - .execute_script_static( - runtime.v8_isolate(), - "", - r#" - - var promise = Deno.core.opAsync("op_pending"); - "#, - ) - .unwrap(); - assert!(matches!(runtime.poll_event_loop(cx, false), Poll::Pending)); - - main_realm - .execute_script_static( - runtime.v8_isolate(), - "", - r#" - let promiseIdSymbol = Symbol.for("Deno.core.internalPromiseId"); - Deno.core.unrefOp(promise[promiseIdSymbol]); - "#, - ) - .unwrap(); - assert!(matches!(runtime.poll_event_loop(cx, false), Poll::Pending)); - - other_realm - .execute_script_static( - runtime.v8_isolate(), - "", - r#" - let promiseIdSymbol = Symbol.for("Deno.core.internalPromiseId"); - Deno.core.unrefOp(promise[promiseIdSymbol]); - "#, - ) - .unwrap(); - assert!(matches!( - runtime.poll_event_loop(cx, false), - Poll::Ready(Ok(())) - )); - Poll::Ready(()) - }) - .await; -} - -#[test] -fn test_array_by_copy() { - // Verify that "array by copy" proposal is enabled (https://github.com/tc39/proposal-change-array-by-copy) - let mut runtime = JsRuntime::new(Default::default()); - assert!(runtime - .execute_script_static( - "test_array_by_copy.js", - "const a = [1, 2, 3]; - const b = a.toReversed(); - if (!(a[0] === 1 && a[1] === 2 && a[2] === 3)) { - throw new Error('Expected a to be intact'); - } - if (!(b[0] === 3 && b[1] === 2 && b[2] === 1)) { - throw new Error('Expected b to be reversed'); - }", - ) - .is_ok()); -} - -#[cfg(debug_assertions)] -#[test] -#[should_panic(expected = "Found ops with duplicate names:")] -fn duplicate_op_names() { - mod a { - use super::*; - - #[op] - fn op_test() -> Result { - Ok(String::from("Test")) - } - } - - #[op] - fn op_test() -> Result { - Ok(String::from("Test")) - } - - deno_core::extension!(test_ext, ops = [a::op_test, op_test]); - JsRuntime::new(RuntimeOptions { - extensions: vec![test_ext::init_ops()], - ..Default::default() - }); -} - -#[test] -fn ops_in_js_have_proper_names() { - #[op] - fn op_test_sync() -> Result { - Ok(String::from("Test")) - } - - #[op] - async fn op_test_async() -> Result { - Ok(String::from("Test")) - } - - deno_core::extension!(test_ext, ops = [op_test_sync, op_test_async]); - let mut runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![test_ext::init_ops()], - ..Default::default() - }); - - let src = r#" - if (Deno.core.ops.op_test_sync.name !== "op_test_sync") { - throw new Error(); - } - - if (Deno.core.ops.op_test_async.name !== "op_test_async") { - throw new Error(); - } - - const { op_test_async } = Deno.core.ensureFastOps(); - if (op_test_async.name !== "op_test_async") { - throw new Error(); - } - "#; - runtime.execute_script_static("test", src).unwrap(); -} diff --git a/core/source_map.rs b/core/source_map.rs deleted file mode 100644 index 9172ce3410..0000000000 --- a/core/source_map.rs +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -//! This mod provides functions to remap a `JsError` based on a source map. - -use crate::resolve_url; -pub use sourcemap::SourceMap; -use std::collections::HashMap; -use std::rc::Rc; -use std::str; - -pub trait SourceMapGetter { - /// Returns the raw source map file. - fn get_source_map(&self, file_name: &str) -> Option>; - fn get_source_line( - &self, - file_name: &str, - line_number: usize, - ) -> Option; -} - -impl SourceMapGetter for Rc -where - T: SourceMapGetter, -{ - fn get_source_map(&self, file_name: &str) -> Option> { - (**self).get_source_map(file_name) - } - - fn get_source_line( - &self, - file_name: &str, - line_number: usize, - ) -> Option { - (**self).get_source_line(file_name, line_number) - } -} - -#[derive(Debug, Default)] -pub struct SourceMapCache { - maps: HashMap>, - source_lines: HashMap<(String, i64), Option>, -} - -pub fn apply_source_map( - file_name: String, - line_number: i64, - column_number: i64, - cache: &mut SourceMapCache, - getter: &G, -) -> (String, i64, i64) { - // Lookup expects 0-based line and column numbers, but ours are 1-based. - let line_number = line_number - 1; - let column_number = column_number - 1; - - let default_pos = (file_name.clone(), line_number, column_number); - let maybe_source_map = - cache.maps.entry(file_name.clone()).or_insert_with(|| { - getter - .get_source_map(&file_name) - .and_then(|raw_source_map| SourceMap::from_slice(&raw_source_map).ok()) - }); - let (file_name, line_number, column_number) = match maybe_source_map { - None => default_pos, - Some(source_map) => { - match source_map.lookup_token(line_number as u32, column_number as u32) { - None => default_pos, - Some(token) => match token.get_source() { - None => default_pos, - Some(source_file_name) => { - // The `source_file_name` written by tsc in the source map is - // sometimes only the basename of the URL, or has unwanted `<`/`>` - // around it. Use the `file_name` we get from V8 if - // `source_file_name` does not parse as a URL. - let file_name = match resolve_url(source_file_name) { - Ok(m) if m.scheme() == "blob" => file_name, - Ok(m) => m.to_string(), - Err(_) => file_name, - }; - ( - file_name, - i64::from(token.get_src_line()), - i64::from(token.get_src_col()), - ) - } - }, - } - } - }; - (file_name, line_number + 1, column_number + 1) -} - -const MAX_SOURCE_LINE_LENGTH: usize = 150; - -pub fn get_source_line( - file_name: &str, - line_number: i64, - cache: &mut SourceMapCache, - getter: &G, -) -> Option { - cache - .source_lines - .entry((file_name.to_string(), line_number)) - .or_insert_with(|| { - // Source lookup expects a 0-based line number, ours are 1-based. - let s = getter.get_source_line(file_name, (line_number - 1) as usize); - s.filter(|s| s.len() <= MAX_SOURCE_LINE_LENGTH) - }) - .clone() -} diff --git a/core/task.rs b/core/task.rs deleted file mode 100644 index 3e728a08fd..0000000000 --- a/core/task.rs +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use core::pin::Pin; -use core::task::Context; -use core::task::Poll; -use futures::Future; -use std::marker::PhantomData; -use tokio::runtime::Handle; -use tokio::runtime::RuntimeFlavor; - -/// Equivalent to [`tokio::task::JoinHandle`]. -#[repr(transparent)] -pub struct JoinHandle { - handle: tokio::task::JoinHandle>, - _r: PhantomData, -} - -impl JoinHandle { - /// Equivalent to [`tokio::task::JoinHandle::abort`]. - pub fn abort(&self) { - self.handle.abort() - } -} - -impl Future for JoinHandle { - type Output = Result; - - fn poll( - self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - // SAFETY: We are sure that handle is valid here - unsafe { - let me: &mut Self = Pin::into_inner_unchecked(self); - let handle = Pin::new_unchecked(&mut me.handle); - match handle.poll(cx) { - Poll::Pending => Poll::Pending, - Poll::Ready(Ok(r)) => Poll::Ready(Ok(r.into_inner())), - Poll::Ready(Err(e)) => Poll::Ready(Err(e)), - } - } - } -} - -/// Equivalent to [`tokio::task::spawn`], but does not require the future to be [`Send`]. Must only be -/// used on a [`RuntimeFlavor::CurrentThread`] executor, though this is only checked when running with -/// debug assertions. -#[inline(always)] -pub fn spawn + 'static, R: 'static>( - f: F, -) -> JoinHandle { - debug_assert!( - Handle::current().runtime_flavor() == RuntimeFlavor::CurrentThread - ); - // SAFETY: we know this is a current-thread executor - let future = unsafe { MaskFutureAsSend::new(f) }; - JoinHandle { - handle: tokio::task::spawn(future), - _r: Default::default(), - } -} - -/// Equivalent to [`tokio::task::spawn_blocking`]. Currently a thin wrapper around the tokio API, but this -/// may change in the future. -#[inline(always)] -pub fn spawn_blocking< - F: (FnOnce() -> R) + Send + 'static, - R: Send + 'static, ->( - f: F, -) -> JoinHandle { - let handle = tokio::task::spawn_blocking(|| MaskResultAsSend { result: f() }); - JoinHandle { - handle, - _r: Default::default(), - } -} - -#[repr(transparent)] -#[doc(hidden)] -pub struct MaskResultAsSend { - result: R, -} - -/// SAFETY: We ensure that Send bounds are only faked when tokio is running on a current-thread executor -unsafe impl Send for MaskResultAsSend {} - -impl MaskResultAsSend { - #[inline(always)] - pub fn into_inner(self) -> R { - self.result - } -} - -#[repr(transparent)] -pub struct MaskFutureAsSend { - future: F, -} - -impl MaskFutureAsSend { - /// Mark a non-`Send` future as `Send`. This is a trick to be able to use - /// `tokio::spawn()` (which requires `Send` futures) in a current thread - /// runtime. - /// - /// # Safety - /// - /// You must ensure that the future is actually used on the same - /// thread, ie. always use current thread runtime flavor from Tokio. - #[inline(always)] - pub unsafe fn new(future: F) -> Self { - Self { future } - } -} - -// SAFETY: we are cheating here - this struct is NOT really Send, -// but we need to mark it Send so that we can use `spawn()` in Tokio. -unsafe impl Send for MaskFutureAsSend {} - -impl Future for MaskFutureAsSend { - type Output = MaskResultAsSend; - - fn poll( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - // SAFETY: We are sure that future is valid here - unsafe { - let me: &mut MaskFutureAsSend = Pin::into_inner_unchecked(self); - let future = Pin::new_unchecked(&mut me.future); - match future.poll(cx) { - Poll::Pending => Poll::Pending, - Poll::Ready(result) => Poll::Ready(MaskResultAsSend { result }), - } - } - } -} diff --git a/core/task_queue.rs b/core/task_queue.rs deleted file mode 100644 index adb25a4f62..0000000000 --- a/core/task_queue.rs +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use futures::task::AtomicWaker; -use futures::Future; -use parking_lot::Mutex; -use std::collections::LinkedList; -use std::sync::atomic::AtomicBool; -use std::sync::atomic::Ordering; -use std::sync::Arc; - -#[derive(Debug, Default)] -struct TaskQueueTaskWaker { - is_ready: AtomicBool, - waker: AtomicWaker, -} - -#[derive(Debug, Default)] -struct TaskQueueTasks { - is_running: bool, - wakers: LinkedList>, -} - -/// A queue that executes tasks sequentially one after the other -/// ensuring order and that no task runs at the same time as another. -/// -/// Note that tokio's semaphore doesn't seem to maintain order -/// and so we can't use that in the code that uses this or use -/// that here. -#[derive(Debug, Default)] -pub struct TaskQueue { - tasks: Mutex, -} - -impl TaskQueue { - /// Acquires a permit where the tasks are executed one at a time - /// and in the order that they were acquired. - pub async fn acquire(&self) -> TaskQueuePermit { - let acquire = TaskQueuePermitAcquire::new(self); - acquire.await; - TaskQueuePermit(self) - } - - /// Alternate API that acquires a permit internally - /// for the duration of the future. - pub async fn queue(&self, future: impl Future) -> R { - let _permit = self.acquire().await; - future.await - } -} - -/// A permit that when dropped will allow another task to proceed. -pub struct TaskQueuePermit<'a>(&'a TaskQueue); - -impl<'a> Drop for TaskQueuePermit<'a> { - fn drop(&mut self) { - let next_item = { - let mut tasks = self.0.tasks.lock(); - let next_item = tasks.wakers.pop_front(); - tasks.is_running = next_item.is_some(); - next_item - }; - if let Some(next_item) = next_item { - next_item.is_ready.store(true, Ordering::SeqCst); - next_item.waker.wake(); - } - } -} - -struct TaskQueuePermitAcquire<'a> { - task_queue: &'a TaskQueue, - initialized: AtomicBool, - waker: Arc, -} - -impl<'a> TaskQueuePermitAcquire<'a> { - pub fn new(task_queue: &'a TaskQueue) -> Self { - Self { - task_queue, - initialized: Default::default(), - waker: Default::default(), - } - } -} - -impl<'a> Future for TaskQueuePermitAcquire<'a> { - type Output = (); - - fn poll( - self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - // update with the latest waker - self.waker.waker.register(cx.waker()); - - // ensure this is initialized - if !self.initialized.swap(true, Ordering::SeqCst) { - let mut tasks = self.task_queue.tasks.lock(); - if !tasks.is_running { - tasks.is_running = true; - return std::task::Poll::Ready(()); - } - tasks.wakers.push_back(self.waker.clone()); - return std::task::Poll::Pending; - } - - // check if we're ready to run - if self.waker.is_ready.load(Ordering::SeqCst) { - std::task::Poll::Ready(()) - } else { - std::task::Poll::Pending - } - } -} - -#[cfg(test)] -mod tests { - use parking_lot::Mutex; - use std::sync::Arc; - - use super::TaskQueue; - - #[tokio::test] - async fn task_queue_runs_one_after_other() { - let task_queue = TaskQueue::default(); - let mut tasks = Vec::new(); - let data = Arc::new(Mutex::new(0)); - for i in 0..100 { - let data = data.clone(); - tasks.push(task_queue.queue(async move { - crate::task::spawn_blocking(move || { - let mut data = data.lock(); - if *data != i { - panic!("Value was not equal."); - } - *data = i + 1; - }) - .await - .unwrap(); - })); - } - futures::future::join_all(tasks).await; - } -} diff --git a/ops/Cargo.toml b/ops/Cargo.toml deleted file mode 100644 index f9951f4e69..0000000000 --- a/ops/Cargo.toml +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -[package] -name = "deno_ops" -version = "0.69.0" -authors.workspace = true -edition.workspace = true -license.workspace = true -readme = "README.md" -repository.workspace = true -description = "Proc macro for writing Deno Ops" - -[lib] -path = "./lib.rs" -proc-macro = true - -[dependencies] -deno-proc-macro-rules.workspace = true -lazy-regex.workspace = true -once_cell.workspace = true -pmutil = "0.5.3" -proc-macro-crate = "1.1.3" -proc-macro2.workspace = true -quote.workspace = true -regex.workspace = true -strum.workspace = true -strum_macros.workspace = true -syn.workspace = true -syn2.workspace = true -thiserror.workspace = true -v8.workspace = true - -[dev-dependencies] -pretty_assertions.workspace = true -prettyplease = "0.1.21" -testing_macros = "0.2.7" -trybuild = "1.0.71" diff --git a/ops/README.md b/ops/README.md deleted file mode 100644 index 39d8606639..0000000000 --- a/ops/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# deno_ops - -`proc_macro` for generating highly optimized V8 functions from Deno ops. - -```rust -// Declare an op. -#[op(fast)] -pub fn op_add(_: &mut OpState, a: i32, b: i32) -> i32 { - a + b -} - -// Register with an extension. -Extension::builder() - .ops(vec![op_add::decl()]) - .build(); -``` - -## Performance - -The macro can optimize away code, short circuit fast paths and generate a Fast -API impl. - -Cases where code is optimized away: - -- `-> ()` skips serde_v8 and `rv.set` calls. -- `-> Result<(), E>` skips serde_v8 and `rv.set` calls for `Ok()` branch. -- `-> ResourceId` or `-> [int]` types will use specialized method like - `v8::ReturnValue::set_uint32`. A fast path for SMI. -- `-> Result` or `-> Result<[int], E>` types will be optimized - like above for the `Ok()` branch. - -### Fast calls - -The macro will infer and try to auto generate V8 fast API call trait impl for -`sync` ops with: - -- arguments: integers, bool, `&mut OpState`, `&[u8]`, `&mut [u8]`, `&[u32]`, - `&mut [u32]` -- return_type: integers, bool - -The `#[op(fast)]` attribute should be used to enforce fast call generation at -compile time. - -Trait gen for `async` ops & a ZeroCopyBuf equivalent type is planned and will be -added soon. - -### Wasm calls - -The `#[op(wasm)]` attribute should be used for calls expected to be called from -Wasm. This enables the fast call generation and allows seamless `WasmMemory` -integration for generic and fast calls. - -```rust -#[op(wasm)] -pub fn op_args_get( - offset: i32, - buffer_offset: i32, - memory: Option<&[u8]>, // Must be last parameter. Some(..) when entered from Wasm. -) { - // ... -} -``` diff --git a/ops/attrs.rs b/ops/attrs.rs deleted file mode 100644 index d0182fc693..0000000000 --- a/ops/attrs.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use syn::parse::Parse; -use syn::parse::ParseStream; -use syn::Error; -use syn::Ident; -use syn::Result; -use syn::Token; - -#[derive(Clone, Debug, Default)] -pub struct Attributes { - pub is_unstable: bool, - pub is_v8: bool, - pub must_be_fast: bool, - pub deferred: bool, - pub is_wasm: bool, - pub relation: Option, -} - -impl Parse for Attributes { - fn parse(input: ParseStream) -> Result { - let mut self_ = Self::default(); - let mut fast = false; - while let Ok(v) = input.parse::() { - match v.to_string().as_str() { - "unstable" => self_.is_unstable = true, - "v8" => self_.is_v8 = true, - "fast" => fast = true, - "deferred" => self_.deferred = true, - "wasm" => self_.is_wasm = true, - "slow" => { - if !fast { - return Err(Error::new( - input.span(), - "relational attributes can only be used with fast attribute", - )); - } - input.parse::()?; - self_.relation = Some(input.parse()?); - } - _ => { - return Err(Error::new( - input.span(), - "invalid attribute, expected one of: unstable, v8, fast, deferred, wasm", - )); - } - }; - let _ = input.parse::(); - } - - self_.must_be_fast = self_.is_wasm || fast; - - Ok(self_) - } -} diff --git a/ops/deno.rs b/ops/deno.rs deleted file mode 100644 index fbaf2a9e66..0000000000 --- a/ops/deno.rs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -#![cfg(not(test))] - -use proc_macro2::Span; -use proc_macro2::TokenStream; -use proc_macro_crate::crate_name; -use proc_macro_crate::FoundCrate; -use quote::quote; -use syn::Ident; - -/// Identifier to the `deno_core` crate. -/// -/// If macro called in deno_core, `crate` is used. -/// If macro called outside deno_core, `deno_core` OR the renamed -/// version from Cargo.toml is used. -pub(crate) fn import() -> TokenStream { - let found_crate = - crate_name("deno_core").expect("deno_core not present in `Cargo.toml`"); - - match found_crate { - FoundCrate::Itself => { - // TODO(@littledivy): This won't work for `deno_core` examples - // since `crate` does not refer to `deno_core`. - // examples must re-export deno_core to make this work - // until Span inspection APIs are stabilized. - // - // https://github.com/rust-lang/rust/issues/54725 - quote!(crate) - } - FoundCrate::Name(name) => { - let ident = Ident::new(&name, Span::call_site()); - quote!(#ident) - } - } -} diff --git a/ops/fast_call.rs b/ops/fast_call.rs deleted file mode 100644 index 63cff4c62f..0000000000 --- a/ops/fast_call.rs +++ /dev/null @@ -1,363 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -//! Code generation for V8 fast calls. - -use pmutil::q; -use pmutil::Quote; -use pmutil::ToTokensExt; -use proc_macro2::Span; -use proc_macro2::TokenStream; -use quote::quote; -use syn::parse_quote; -use syn::punctuated::Punctuated; -use syn::token::Comma; -use syn::Generics; -use syn::Ident; -use syn::ItemFn; - -use crate::optimizer::FastValue; -use crate::optimizer::Optimizer; - -pub(crate) struct FastImplItems { - pub(crate) impl_and_fn: TokenStream, - pub(crate) decl: TokenStream, - pub(crate) active: bool, -} - -pub(crate) fn generate( - core: &TokenStream, - optimizer: &mut Optimizer, - item_fn: &ItemFn, -) -> FastImplItems { - if !optimizer.fast_compatible { - return FastImplItems { - impl_and_fn: TokenStream::new(), - decl: quote! { None }, - active: false, - }; - } - - // TODO(@littledivy): Use `let..else` on 1.65.0 - let output_ty = match &optimizer.fast_result { - // Assert that the optimizer did not set a return type. - // - // @littledivy: This *could* potentially be used to optimize resolving - // promises but knowing the return type at compile time instead of - // serde_v8 serialization. - Some(_) if optimizer.is_async => &FastValue::Void, - Some(ty) => ty, - None if optimizer.is_async => &FastValue::Void, - None => { - return FastImplItems { - impl_and_fn: TokenStream::new(), - decl: quote! { None }, - active: false, - } - } - }; - - // We've got 2 idents. - // - // - op_foo, the public op declaration contains the user function. - // - op_foo_fast_fn, the fast call function. - let ident = item_fn.sig.ident.clone(); - let fast_fn_ident = - Ident::new(&format!("{ident}_fast_fn"), Span::call_site()); - - // Deal with generics. - let generics = &item_fn.sig.generics; - let (impl_generics, _, where_clause) = generics.split_for_impl(); - - // This goes in the FastFunction impl block. - // let mut segments = Punctuated::new(); - // { - // let mut arguments = PathArguments::None; - // if let Some(ref struct_generics) = struct_generics { - // arguments = PathArguments::AngleBracketed(parse_quote! { - // #struct_generics - // }); - // } - // segments.push_value(PathSegment { - // ident: fast_ident.clone(), - // arguments, - // }); - // } - - // Original inputs. - let mut inputs = item_fn.sig.inputs.clone(); - let mut transforms = q!({}); - let mut pre_transforms = q!({}); - - // Apply parameter transforms - for (index, input) in inputs.iter_mut().enumerate() { - if let Some(transform) = optimizer.transforms.get(&index) { - let quo: Quote = transform.apply_for_fast_call(core, input); - transforms.push_tokens(&quo); - } - } - - // Collect idents to be passed into function call, we can now freely - // modify the inputs. - let idents = inputs - .iter() - .map(|input| match input { - syn::FnArg::Typed(pat_type) => match &*pat_type.pat { - syn::Pat::Ident(pat_ident) => pat_ident.ident.clone(), - _ => panic!("unexpected pattern"), - }, - _ => panic!("unexpected argument"), - }) - .collect::>(); - - // Retain only *pure* parameters. - let mut fast_fn_inputs = if optimizer.has_opstate_in_parameters() { - inputs.into_iter().skip(1).collect() - } else { - inputs - }; - - let mut input_variants = optimizer - .fast_parameters - .iter() - .map(q_fast_ty_variant) - .collect::>(); - - // Apply *hard* optimizer hints. - if optimizer.has_fast_callback_option - || optimizer.has_wasm_memory - || optimizer.needs_opstate() - || optimizer.is_async - || optimizer.needs_fast_callback_option - { - let decl = parse_quote! { - fast_api_callback_options: *mut #core::v8::fast_api::FastApiCallbackOptions - }; - - if optimizer.has_fast_callback_option || optimizer.has_wasm_memory { - // Replace last parameter. - assert!(fast_fn_inputs.pop().is_some()); - fast_fn_inputs.push(decl); - } else { - fast_fn_inputs.push(decl); - } - - input_variants.push(q!({ CallbackOptions })); - } - - // (recv, p_id, ...) - // - // Optimizer has already set it in the fast parameter variant list. - if optimizer.is_async { - if fast_fn_inputs.is_empty() { - fast_fn_inputs.push(parse_quote! { __promise_id: i32 }); - } else { - fast_fn_inputs.insert(0, parse_quote! { __promise_id: i32 }); - } - } - - let mut output_transforms = q!({}); - - if optimizer.needs_opstate() - || optimizer.is_async - || optimizer.has_fast_callback_option - || optimizer.has_wasm_memory - { - // Dark arts 🪄 ✨ - // - // - V8 calling convention guarantees that the callback options pointer is non-null. - // - `data` union is always initialized as the `v8::Local` variant. - // - deno_core guarantees that `data` is a v8 External pointing to an OpCtx for the - // isolate's lifetime. - let prelude = q!({ - let __opts: &mut v8::fast_api::FastApiCallbackOptions = - unsafe { &mut *fast_api_callback_options }; - }); - - pre_transforms.push_tokens(&prelude); - } - - if optimizer.needs_opstate() || optimizer.is_async { - // Grab the op_state identifier, the first one. ¯\_(ツ)_/¯ - let op_state = match idents.first() { - Some(ident) if optimizer.has_opstate_in_parameters() => ident.clone(), - // fn op_foo() -> Result<...> - _ => Ident::new("op_state", Span::call_site()), - }; - - let ctx = q!({ - let __ctx = unsafe { - &*(v8::Local::::cast(unsafe { __opts.data.data }).value() - as *const _ops::OpCtx) - }; - }); - - pre_transforms.push_tokens(&ctx); - pre_transforms.push_tokens(&match optimizer.is_async { - false => q!( - Vars { - op_state: &op_state - }, - { - let op_state = &mut ::std::cell::RefCell::borrow_mut(&__ctx.state); - } - ), - true => q!( - Vars { - op_state: &op_state - }, - { - let op_state = __ctx.state.clone(); - } - ), - }); - - if optimizer.returns_result && !optimizer.is_async { - // Magic fallback 🪄 - // - // If Result is Ok(T), return T as fast value. - // - // Err(E) gets put into `last_fast_op_error` slot and - // - // V8 calls the slow path so we can take the slot - // value and throw. - let default = optimizer.fast_result.as_ref().unwrap().default_value(); - let result_wrap = q!(Vars { op_state, default }, { - match result { - Ok(result) => result, - Err(err) => { - op_state.last_fast_op_error.replace(err); - __opts.fallback = true; - default - } - } - }); - - output_transforms.push_tokens(&result_wrap); - } - } - - if optimizer.is_async { - let queue_future = if optimizer.returns_result { - q!({ - let result = _ops::queue_fast_async_op(__ctx, __promise_id, result); - }) - } else { - q!({ - let result = - _ops::queue_fast_async_op(__ctx, __promise_id, async move { - Ok(result.await) - }); - }) - }; - - output_transforms.push_tokens(&queue_future); - } - - if !optimizer.returns_result { - let default_output = q!({ result }); - output_transforms.push_tokens(&default_output); - } - - let output = q_fast_ty(output_ty); - // Generate the function body. - // - // fn f (_: Local, a: T, b: U) -> R { - // /* Transforms */ - // let a = a.into(); - // let b = b.into(); - // - // let r = op::call(a, b); - // - // /* Return transform */ - // r.into() - // } - let fast_fn = q!( - Vars { core, pre_transforms, op_name_fast: &fast_fn_ident, op_name: &ident, fast_fn_inputs, generics, where_clause, idents, transforms, output_transforms, output: &output }, - { - impl generics op_name generics where_clause { - #[allow(clippy::too_many_arguments)] - fn op_name_fast (_: core::v8::Local, fast_fn_inputs) -> output { - use core::v8; - use core::_ops; - pre_transforms - transforms - let result = Self::call (idents); - output_transforms - } - } - } - ); - - let output_variant = q_fast_ty_variant(output_ty); - let mut generics: Generics = parse_quote! { #impl_generics }; - generics.where_clause = where_clause.cloned(); - - // fast_api::FastFunction::new(&[ CType::T, CType::U ], CType::T, f::

as *const ::std::ffi::c_void) - let decl = q!( - Vars { - core: core, - fast_fn_ident: fast_fn_ident, - inputs: input_variants, - output: output_variant - }, - { - { - use core::v8::fast_api::CType; - use core::v8::fast_api::Type::*; - Some(core::v8::fast_api::FastFunction::new( - &[inputs], - CType::output, - Self::fast_fn_ident as *const ::std::ffi::c_void, - )) - } - } - ) - .dump(); - - let impl_and_fn = fast_fn.dump(); - - FastImplItems { - impl_and_fn, - decl, - active: true, - } -} - -/// Quote fast value type. -fn q_fast_ty(v: &FastValue) -> Quote { - match v { - FastValue::Void => q!({ () }), - FastValue::Bool => q!({ bool }), - FastValue::U32 => q!({ u32 }), - FastValue::I32 => q!({ i32 }), - FastValue::U64 => q!({ u64 }), - FastValue::I64 => q!({ i64 }), - FastValue::F32 => q!({ f32 }), - FastValue::F64 => q!({ f64 }), - FastValue::Pointer => q!({ *mut ::std::ffi::c_void }), - FastValue::V8Value => q!({ v8::Local }), - FastValue::Uint8Array - | FastValue::Uint32Array - | FastValue::Float64Array - | FastValue::SeqOneByteString => unreachable!(), - } -} - -/// Quote fast value type's variant. -fn q_fast_ty_variant(v: &FastValue) -> Quote { - match v { - FastValue::Void => q!({ Void }), - FastValue::Bool => q!({ Bool }), - FastValue::U32 => q!({ Uint32 }), - FastValue::I32 => q!({ Int32 }), - FastValue::U64 => q!({ Uint64 }), - FastValue::I64 => q!({ Int64 }), - FastValue::F32 => q!({ Float32 }), - FastValue::F64 => q!({ Float64 }), - FastValue::Pointer => q!({ Pointer }), - FastValue::V8Value => q!({ V8Value }), - FastValue::Uint8Array => q!({ TypedArray(CType::Uint8) }), - FastValue::Uint32Array => q!({ TypedArray(CType::Uint32) }), - FastValue::Float64Array => q!({ TypedArray(CType::Float64) }), - FastValue::SeqOneByteString => q!({ SeqOneByteString }), - } -} diff --git a/ops/lib.rs b/ops/lib.rs deleted file mode 100644 index 31398a14d3..0000000000 --- a/ops/lib.rs +++ /dev/null @@ -1,1025 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use attrs::Attributes; -use optimizer::BailoutReason; -use optimizer::Optimizer; -use proc_macro::TokenStream; -use proc_macro2::Span; -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; -use quote::ToTokens; -use std::error::Error; -use syn::parse; -use syn::parse_macro_input; -use syn::punctuated::Punctuated; -use syn::token::Comma; -use syn::FnArg; -use syn::GenericParam; -use syn::Ident; -use syn::ItemFn; -use syn::Lifetime; -use syn::LifetimeDef; - -mod attrs; -mod deno; -mod fast_call; -mod op2; -mod optimizer; - -const SCOPE_LIFETIME: &str = "'scope"; - -/// Add the 'scope lifetime to the function signature. -fn add_scope_lifetime(func: &mut ItemFn) { - let span = Span::call_site(); - let lifetime = LifetimeDef::new(Lifetime::new(SCOPE_LIFETIME, span)); - let generics = &mut func.sig.generics; - if !generics.lifetimes().any(|def| *def == lifetime) { - generics.params.push(GenericParam::Lifetime(lifetime)); - } -} - -struct Op { - orig: ItemFn, - item: ItemFn, - /// Is this an async op? - /// - `async fn` - /// - returns a Future - is_async: bool, - // optimizer: Optimizer, - core: TokenStream2, - attrs: Attributes, -} - -impl Op { - fn new(item: ItemFn, attrs: Attributes) -> Self { - // Preserve the original function. Change the name to `call`. - // - // impl op_foo { - // fn call() {} - // ... - // } - let mut orig = item.clone(); - orig.sig.ident = Ident::new("call", Span::call_site()); - - let is_async = item.sig.asyncness.is_some() || is_future(&item.sig.output); - let scope_params = exclude_non_lifetime_params(&item.sig.generics.params); - orig.sig.generics.params = scope_params; - orig.sig.generics.where_clause.take(); - add_scope_lifetime(&mut orig); - - #[cfg(test)] - let core = quote!(deno_core); - #[cfg(not(test))] - let core = deno::import(); - - Self { - orig, - item, - is_async, - core, - attrs, - } - } - - fn gen(mut self) -> TokenStream2 { - let mut optimizer = Optimizer::new(); - match optimizer.analyze(&mut self) { - Err(BailoutReason::MustBeSingleSegment) - | Err(BailoutReason::FastUnsupportedParamType) => { - optimizer.fast_compatible = false; - } - _ => {} - }; - - let Self { - core, - item, - is_async, - orig, - attrs, - } = self; - let name = &item.sig.ident; - - // TODO(mmastrac): this code is a little awkward but eventually it'll disappear in favour of op2 - let mut generics = item.sig.generics.clone(); - generics.where_clause.take(); - generics.params = exclude_lifetime_params(&generics.params); - let params = &generics.params.iter().collect::>(); - let where_clause = &item.sig.generics.where_clause; - - // First generate fast call bindings to opt-in to error handling in slow call - let fast_call::FastImplItems { - impl_and_fn, - decl, - active, - } = fast_call::generate(&core, &mut optimizer, &item); - - let docline = format!("Use `{name}::decl()` to get an op-declaration"); - - let is_v8 = attrs.is_v8; - let is_unstable = attrs.is_unstable; - - if let Some(v8_fn) = attrs.relation { - return quote! { - #[allow(non_camel_case_types)] - #[doc="Auto-generated by `deno_ops`, i.e: `#[op]`"] - #[doc=""] - #[doc=#docline] - #[doc="you can include in a `deno_core::Extension`."] - pub struct #name #generics { - _phantom_data: ::std::marker::PhantomData<(#(#params),*)> - } - - impl #generics #core::_ops::Op for #name #generics #where_clause { - const NAME: &'static str = stringify!(#name); - const DECL: #core::OpDecl = #core::OpDecl { - name: Self::name(), - v8_fn_ptr: #v8_fn::v8_fn_ptr as _, - enabled: true, - fast_fn: #decl, - is_async: #is_async, - is_unstable: #is_unstable, - is_v8: #is_v8, - // TODO(mmastrac) - arg_count: 0, - }; - } - - #[doc(hidden)] - impl #generics #name #generics #where_clause { - pub const fn name() -> &'static str { - stringify!(#name) - } - - pub const fn decl () -> #core::OpDecl { - #core::OpDecl { - name: Self::name(), - v8_fn_ptr: #v8_fn::v8_fn_ptr as _, - enabled: true, - fast_fn: #decl, - is_async: #is_async, - is_unstable: #is_unstable, - is_v8: #is_v8, - // TODO(mmastrac) - arg_count: 0, - } - } - - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - #orig - } - - #impl_and_fn - }; - } - - let has_fallible_fast_call = active && optimizer.returns_result; - - let (v8_body, arg_count) = if is_async { - let deferred: bool = attrs.deferred; - codegen_v8_async( - &core, - &item, - attrs, - item.sig.asyncness.is_some(), - deferred, - ) - } else { - codegen_v8_sync(&core, &item, attrs, has_fallible_fast_call) - }; - - // Generate wrapper - quote! { - #[allow(non_camel_case_types)] - #[doc="Auto-generated by `deno_ops`, i.e: `#[op]`"] - #[doc=""] - #[doc=#docline] - #[doc="you can include in a `deno_core::Extension`."] - pub struct #name #generics { - _phantom_data: ::std::marker::PhantomData<(#(#params),*)> - } - - impl #generics #core::_ops::Op for #name #generics #where_clause { - const NAME: &'static str = stringify!(#name); - const DECL: #core::OpDecl = #core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: #decl, - is_async: #is_async, - is_unstable: #is_unstable, - is_v8: #is_v8, - // TODO(mmastrac) - arg_count: 0, - }; - } - - #[doc(hidden)] - impl #generics #name #generics #where_clause { - pub const fn name() -> &'static str { - stringify!(#name) - } - - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr (info: *const #core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { #core::v8::CallbackScope::new(info) }; - let args = #core::v8::FunctionCallbackArguments::from_function_callback_info(info); - let rv = #core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - - pub const fn decl () -> #core::OpDecl { - #core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: #decl, - is_async: #is_async, - is_unstable: #is_unstable, - is_v8: #is_v8, - arg_count: #arg_count as u8, - } - } - - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - #orig - - pub fn v8_func<'scope>( - scope: &mut #core::v8::HandleScope<'scope>, - args: #core::v8::FunctionCallbackArguments, - mut rv: #core::v8::ReturnValue, - ) { - #v8_body - } - } - - #impl_and_fn - } - } -} - -#[proc_macro_attribute] -pub fn op(attr: TokenStream, item: TokenStream) -> TokenStream { - let margs = parse_macro_input!(attr as Attributes); - let func = parse::(item).expect("expected a function"); - let op = Op::new(func, margs); - op.gen().into() -} - -#[proc_macro_attribute] -pub fn op2(attr: TokenStream, item: TokenStream) -> TokenStream { - match crate::op2::op2(attr.into(), item.into()) { - Ok(output) => output.into(), - Err(err) => { - let mut err: &dyn Error = &err; - let mut output = "Failed to parse #[op2]:\n".to_owned(); - loop { - output += &format!(" - {err}\n"); - if let Some(source) = err.source() { - err = source; - } else { - break; - } - } - panic!("{output}"); - } - } -} - -/// Generate the body of a v8 func for an async op -fn codegen_v8_async( - core: &TokenStream2, - f: &syn::ItemFn, - margs: Attributes, - asyncness: bool, - deferred: bool, -) -> (TokenStream2, usize) { - let Attributes { is_v8, .. } = margs; - let special_args = f - .sig - .inputs - .iter() - .map_while(|a| { - (if is_v8 { scope_arg(a) } else { None }) - .or_else(|| rc_refcell_opstate_arg(a)) - }) - .collect::>(); - let rust_i0 = special_args.len(); - let args_head = special_args.into_iter().collect::(); - - let (arg_decls, args_tail, _) = codegen_args(core, f, rust_i0, 1, asyncness); - - let wrapper = match (asyncness, is_result(&f.sig.output)) { - (true, true) => { - quote! { - let fut = #core::_ops::map_async_op1(ctx, Self::call(#args_head #args_tail)); - let maybe_response = #core::_ops::queue_async_op( - ctx, - scope, - #deferred, - promise_id, - fut, - ); - } - } - (true, false) => { - quote! { - let fut = #core::_ops::map_async_op2(ctx, Self::call(#args_head #args_tail)); - let maybe_response = #core::_ops::queue_async_op( - ctx, - scope, - #deferred, - promise_id, - fut, - ); - } - } - (false, true) => { - quote! { - let fut = #core::_ops::map_async_op3(ctx, Self::call(#args_head #args_tail)); - let maybe_response = #core::_ops::queue_async_op( - ctx, - scope, - #deferred, - promise_id, - fut, - ); - } - } - (false, false) => { - quote! { - let fut = #core::_ops::map_async_op4(ctx, Self::call(#args_head #args_tail)); - let maybe_response = #core::_ops::queue_async_op( - ctx, - scope, - #deferred, - promise_id, - fut, - ); - } - } - }; - - let token_stream = quote! { - use #core::futures::FutureExt; - // SAFETY: #core guarantees args.data() is a v8 External pointing to an OpCtx for the isolates lifetime - let ctx = unsafe { - &*(#core::v8::Local::<#core::v8::External>::cast(args.data()).value() - as *const #core::_ops::OpCtx) - }; - - let promise_id = args.get(0); - let promise_id = #core::v8::Local::<#core::v8::Integer>::try_from(promise_id) - .map(|l| l.value() as #core::PromiseId) - .map_err(#core::anyhow::Error::from); - // Fail if promise id invalid (not an int) - let promise_id: #core::PromiseId = match promise_id { - Ok(promise_id) => promise_id, - Err(err) => { - #core::_ops::throw_type_error(scope, format!("invalid promise id: {}", err)); - return; - } - }; - - #arg_decls - #wrapper - - if let Some(response) = maybe_response { - rv.set(response); - } - }; - - // +1 arg for the promise ID - (token_stream, 1 + f.sig.inputs.len() - rust_i0) -} - -fn scope_arg(arg: &FnArg) -> Option { - if is_handle_scope(arg) { - Some(quote! { scope, }) - } else { - None - } -} - -fn opstate_arg(arg: &FnArg) -> Option { - match arg { - arg if is_rc_refcell_opstate(arg) => Some(quote! { ctx.state.clone(), }), - arg if is_mut_ref_opstate(arg) => { - Some(quote! { &mut std::cell::RefCell::borrow_mut(&ctx.state), }) - } - _ => None, - } -} - -fn rc_refcell_opstate_arg(arg: &FnArg) -> Option { - match arg { - arg if is_rc_refcell_opstate(arg) => Some(quote! { ctx.state.clone(), }), - arg if is_mut_ref_opstate(arg) => Some( - quote! { compile_error!("mutable opstate is not supported in async ops"), }, - ), - _ => None, - } -} - -/// Generate the body of a v8 func for a sync op -fn codegen_v8_sync( - core: &TokenStream2, - f: &syn::ItemFn, - margs: Attributes, - has_fallible_fast_call: bool, -) -> (TokenStream2, usize) { - let Attributes { is_v8, .. } = margs; - let special_args = f - .sig - .inputs - .iter() - .map_while(|a| { - (if is_v8 { scope_arg(a) } else { None }).or_else(|| opstate_arg(a)) - }) - .collect::>(); - let rust_i0 = special_args.len(); - let args_head = special_args.into_iter().collect::(); - let (arg_decls, args_tail, _) = codegen_args(core, f, rust_i0, 0, false); - let ret = codegen_sync_ret(core, &f.sig.output); - - let fast_error_handler = if has_fallible_fast_call { - quote! { - { - let op_state = &mut std::cell::RefCell::borrow_mut(&ctx.state); - if let Some(err) = op_state.last_fast_op_error.take() { - let exception = #core::error::to_v8_error(scope, op_state.get_error_class_fn, &err); - scope.throw_exception(exception); - return; - } - } - } - } else { - quote! {} - }; - - let token_stream = quote! { - // SAFETY: #core guarantees args.data() is a v8 External pointing to an OpCtx for the isolates lifetime - let ctx = unsafe { - &*(#core::v8::Local::<#core::v8::External>::cast(args.data()).value() - as *const #core::_ops::OpCtx) - }; - - #fast_error_handler - #arg_decls - - let result = Self::call(#args_head #args_tail); - - // use RefCell::borrow instead of state.borrow to avoid clash with std::borrow::Borrow - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - - #ret - }; - - (token_stream, f.sig.inputs.len() - rust_i0) -} - -/// (full declarations, idents, v8 argument count) -type ArgumentDecl = (TokenStream2, TokenStream2, usize); - -fn codegen_args( - core: &TokenStream2, - f: &syn::ItemFn, - rust_i0: usize, // Index of first generic arg in rust - v8_i0: usize, // Index of first generic arg in v8/js - asyncness: bool, -) -> ArgumentDecl { - let inputs = &f.sig.inputs.iter().skip(rust_i0).enumerate(); - let ident_seq: TokenStream2 = inputs - .clone() - .map(|(i, _)| format!("arg_{i}")) - .collect::>() - .join(", ") - .parse() - .unwrap(); - let decls: TokenStream2 = inputs - .clone() - .map(|(i, arg)| { - codegen_arg(core, arg, format!("arg_{i}").as_ref(), v8_i0 + i, asyncness) - }) - .collect(); - (decls, ident_seq, inputs.len()) -} - -fn codegen_arg( - core: &TokenStream2, - arg: &syn::FnArg, - name: &str, - idx: usize, - asyncness: bool, -) -> TokenStream2 { - let ident = quote::format_ident!("{name}"); - let (pat, ty) = match arg { - syn::FnArg::Typed(pat) => { - if is_optional_fast_callback_option(&pat.ty) - || is_optional_wasm_memory(&pat.ty) - { - return quote! { let #ident = None; }; - } - (&pat.pat, &pat.ty) - } - _ => unreachable!(), - }; - // Fast path if arg should be skipped - if matches!(**pat, syn::Pat::Wild(_)) { - return quote! { let #ident = (); }; - } - // Fast path for `String` - if let Some(is_ref) = is_string(&**ty) { - let ref_block = if is_ref { - quote! { let #ident = #ident.as_ref(); } - } else { - quote! {} - }; - return quote! { - let #ident = match #core::v8::Local::<#core::v8::String>::try_from(args.get(#idx as i32)) { - Ok(v8_string) => #core::serde_v8::to_utf8(v8_string, scope), - Err(_) => { - return #core::_ops::throw_type_error(scope, format!("Expected string at position {}", #idx)); - } - }; - #ref_block - }; - } - // Fast path for `Cow<'_, str>` - if is_cow_str(&**ty) { - return quote! { - let #ident = match #core::v8::Local::<#core::v8::String>::try_from(args.get(#idx as i32)) { - Ok(v8_string) => ::std::borrow::Cow::Owned(#core::serde_v8::to_utf8(v8_string, scope)), - Err(_) => { - return #core::_ops::throw_type_error(scope, format!("Expected string at position {}", #idx)); - } - }; - }; - } - // Fast path for `Option` - if is_option_string(&**ty) { - return quote! { - let #ident = match #core::v8::Local::<#core::v8::String>::try_from(args.get(#idx as i32)) { - Ok(v8_string) => Some(#core::serde_v8::to_utf8(v8_string, scope)), - Err(_) => None - }; - }; - } - // Fast path for &/&mut [u8] and &/&mut [u32] - match is_ref_slice(&**ty) { - None => {} - Some(SliceType::U32Mut) => { - assert!(!asyncness, "Memory slices are not allowed in async ops"); - let blck = codegen_u32_mut_slice(core, idx); - return quote! { - let #ident = #blck; - }; - } - Some(SliceType::F64Mut) => { - assert!(!asyncness, "Memory slices are not allowed in async ops"); - let blck = codegen_f64_mut_slice(core, idx); - return quote! { - let #ident = #blck; - }; - } - Some(_) => { - assert!(!asyncness, "Memory slices are not allowed in async ops"); - let blck = codegen_u8_slice(core, idx); - return quote! { - let #ident = #blck; - }; - } - } - // Fast path for `*const u8` - if is_ptr_u8(&**ty) { - let blk = codegen_u8_ptr(core, idx); - return quote! { - let #ident = #blk; - }; - } - // Fast path for `*const c_void` and `*mut c_void` - if is_ptr_cvoid(&**ty) { - let blk = codegen_cvoid_ptr(core, idx); - return quote! { - let #ident = #blk; - }; - } - // Otherwise deserialize it via serde_v8 - quote! { - let #ident = args.get(#idx as i32); - let #ident = match #core::serde_v8::from_v8(scope, #ident) { - Ok(v) => v, - Err(err) => { - let msg = format!("Error parsing args at position {}: {}", #idx, #core::anyhow::Error::from(err)); - return #core::_ops::throw_type_error(scope, msg); - } - }; - } -} - -fn codegen_u8_slice(core: &TokenStream2, idx: usize) -> TokenStream2 { - quote! {{ - let value = args.get(#idx as i32); - match #core::v8::Local::<#core::v8::ArrayBuffer>::try_from(value) { - Ok(b) => { - let byte_length = b.byte_length(); - if let Some(data) = b.data() { - let store = data.cast::().as_ptr(); - // SAFETY: rust guarantees that lifetime of slice is no longer than the call. - unsafe { ::std::slice::from_raw_parts_mut(store, byte_length) } - } else { - &mut [] - } - }, - Err(_) => { - if let Ok(view) = #core::v8::Local::<#core::v8::ArrayBufferView>::try_from(value) { - let len = view.byte_length(); - let offset = view.byte_offset(); - let buffer = match view.buffer(scope) { - Some(v) => v, - None => { - return #core::_ops::throw_type_error(scope, format!("Expected ArrayBufferView at position {}", #idx)); - } - }; - if let Some(data) = buffer.data() { - let store = data.cast::().as_ptr(); - // SAFETY: rust guarantees that lifetime of slice is no longer than the call. - unsafe { ::std::slice::from_raw_parts_mut(store.add(offset), len) } - } else { - &mut [] - } - } else { - return #core::_ops::throw_type_error(scope, format!("Expected ArrayBufferView at position {}", #idx)); - } - } - }} - } -} - -fn codegen_u8_ptr(core: &TokenStream2, idx: usize) -> TokenStream2 { - quote! {{ - let value = args.get(#idx as i32); - match #core::v8::Local::<#core::v8::ArrayBuffer>::try_from(value) { - Ok(b) => { - if let Some(data) = b.data() { - data.cast::().as_ptr() - } else { - std::ptr::null::() - } - }, - Err(_) => { - if let Ok(view) = #core::v8::Local::<#core::v8::ArrayBufferView>::try_from(value) { - let offset = view.byte_offset(); - let buffer = match view.buffer(scope) { - Some(v) => v, - None => { - return #core::_ops::throw_type_error(scope, format!("Expected ArrayBufferView at position {}", #idx)); - } - }; - let store = if let Some(data) = buffer.data() { - data.cast::().as_ptr() - } else { - std::ptr::null_mut::() - }; - unsafe { store.add(offset) } - } else { - return #core::_ops::throw_type_error(scope, format!("Expected ArrayBufferView at position {}", #idx)); - } - } - } - }} -} - -fn codegen_cvoid_ptr(core: &TokenStream2, idx: usize) -> TokenStream2 { - quote! {{ - let value = args.get(#idx as i32); - if value.is_null() { - std::ptr::null_mut() - } else if let Ok(b) = #core::v8::Local::<#core::v8::External>::try_from(value) { - b.value() - } else { - return #core::_ops::throw_type_error(scope, format!("Expected External at position {}", #idx)); - } - }} -} - -fn codegen_u32_mut_slice(core: &TokenStream2, idx: usize) -> TokenStream2 { - quote! { - if let Ok(view) = #core::v8::Local::<#core::v8::Uint32Array>::try_from(args.get(#idx as i32)) { - let (offset, len) = (view.byte_offset(), view.byte_length()); - let buffer = match view.buffer(scope) { - Some(v) => v, - None => { - return #core::_ops::throw_type_error(scope, format!("Expected Uint32Array at position {}", #idx)); - } - }; - if let Some(data) = buffer.data() { - let store = data.cast::().as_ptr(); - // SAFETY: buffer from Uint32Array. Rust guarantees that lifetime of slice is no longer than the call. - unsafe { ::std::slice::from_raw_parts_mut(store.add(offset) as *mut u32, len / 4) } - } else { - &mut [] - } - } else { - return #core::_ops::throw_type_error(scope, format!("Expected Uint32Array at position {}", #idx)); - } - } -} - -fn codegen_f64_mut_slice(core: &TokenStream2, idx: usize) -> TokenStream2 { - quote! { - if let Ok(view) = #core::v8::Local::<#core::v8::Float64Array>::try_from(args.get(#idx as i32)) { - let (offset, len) = (view.byte_offset(), view.byte_length()); - let buffer = match view.buffer(scope) { - Some(v) => v, - None => { - return #core::_ops::throw_type_error(scope, format!("Expected Float64Array at position {}", #idx)); - } - }; - if let Some(data) = buffer.data() { - let store = data.cast::().as_ptr(); - unsafe { ::std::slice::from_raw_parts_mut(store.add(offset) as *mut f64, len / 8) } - } else { - &mut [] - } - } else { - return #core::_ops::throw_type_error(scope, format!("Expected Float64Array at position {}", #idx)); - } - } -} - -fn codegen_sync_ret( - core: &TokenStream2, - output: &syn::ReturnType, -) -> TokenStream2 { - if is_void(output) { - return quote! {}; - } - - if is_u32_rv(output) { - return quote! { - rv.set_uint32(result as u32); - }; - } - - // Optimize Result<(), Err> to skip serde_v8 when Ok(...) - let ok_block = if is_unit_result(output) { - quote! {} - } else if is_u32_rv_result(output) { - quote! { - rv.set_uint32(result as u32); - } - } else if is_ptr_cvoid(output) || is_ptr_cvoid_rv(output) { - quote! { - if result.is_null() { - // External cannot contain a null pointer, null pointers are instead represented as null. - rv.set_null(); - } else { - rv.set(v8::External::new(scope, result as *mut ::std::ffi::c_void).into()); - } - } - } else { - quote! { - match #core::serde_v8::to_v8(scope, result) { - Ok(ret) => rv.set(ret), - Err(err) => #core::_ops::throw_type_error( - scope, - format!("Error serializing return: {}", #core::anyhow::Error::from(err)), - ), - }; - } - }; - - if !is_result(output) { - return ok_block; - } - - quote! { - match result { - Ok(result) => { - #ok_block - }, - Err(err) => { - let exception = #core::error::to_v8_error(scope, op_state.get_error_class_fn, &err); - scope.throw_exception(exception); - }, - }; - } -} - -fn is_void(ty: impl ToTokens) -> bool { - tokens(ty).is_empty() -} - -fn is_result(ty: impl ToTokens) -> bool { - let tokens = tokens(ty); - if tokens.trim_start_matches("-> ").starts_with("Result <") { - return true; - } - // Detect `io::Result<...>`, `anyhow::Result<...>`, etc... - // i.e: Result aliases/shorthands which are unfortunately "opaque" at macro-time - match tokens.find(":: Result <") { - Some(idx) => !tokens.split_at(idx).0.contains('<'), - None => false, - } -} - -fn is_string(ty: impl ToTokens) -> Option { - let toks = tokens(ty); - if toks == "String" { - return Some(false); - } - if toks == "& str" { - return Some(true); - } - None -} - -fn is_option_string(ty: impl ToTokens) -> bool { - tokens(ty) == "Option < String >" -} - -fn is_cow_str(ty: impl ToTokens) -> bool { - tokens(&ty).starts_with("Cow <") && tokens(&ty).ends_with("str >") -} - -enum SliceType { - U8, - U8Mut, - U32Mut, - F64Mut, -} - -fn is_ref_slice(ty: impl ToTokens) -> Option { - if is_u8_slice(&ty) { - return Some(SliceType::U8); - } - if is_u8_slice_mut(&ty) { - return Some(SliceType::U8Mut); - } - if is_u32_slice_mut(&ty) { - return Some(SliceType::U32Mut); - } - if is_f64_slice_mut(&ty) { - return Some(SliceType::F64Mut); - } - None -} - -fn is_u8_slice(ty: impl ToTokens) -> bool { - tokens(ty) == "& [u8]" -} - -fn is_u8_slice_mut(ty: impl ToTokens) -> bool { - tokens(ty) == "& mut [u8]" -} - -fn is_u32_slice_mut(ty: impl ToTokens) -> bool { - tokens(ty) == "& mut [u32]" -} - -fn is_f64_slice_mut(ty: impl ToTokens) -> bool { - tokens(ty) == "& mut [f64]" -} - -fn is_ptr_u8(ty: impl ToTokens) -> bool { - tokens(ty) == "* const u8" -} - -fn is_ptr_cvoid(ty: impl ToTokens) -> bool { - tokens(&ty) == "* const c_void" || tokens(&ty) == "* mut c_void" -} - -fn is_ptr_cvoid_rv(ty: impl ToTokens) -> bool { - tokens(&ty).contains("Result < * const c_void") - || tokens(&ty).contains("Result < * mut c_void") -} - -fn is_optional_fast_callback_option(ty: impl ToTokens) -> bool { - tokens(&ty).contains("Option < & mut FastApiCallbackOptions") -} - -fn is_optional_wasm_memory(ty: impl ToTokens) -> bool { - tokens(&ty).contains("Option < & mut [u8]") -} - -/// Detects if the type can be set using `rv.set_uint32` fast path -fn is_u32_rv(ty: impl ToTokens) -> bool { - ["u32", "u8", "u16"].iter().any(|&s| tokens(&ty) == s) || is_resource_id(&ty) -} - -/// Detects if the type is of the format Result -fn is_u32_rv_result(ty: impl ToTokens) -> bool { - is_result(&ty) - && (tokens(&ty).contains("Result < u32") - || tokens(&ty).contains("Result < u8") - || tokens(&ty).contains("Result < u16") - || is_resource_id(&ty)) -} - -/// Detects if a type is of the form Result<(), Err> -fn is_unit_result(ty: impl ToTokens) -> bool { - is_result(&ty) && tokens(&ty).contains("Result < ()") -} - -fn is_resource_id(arg: impl ToTokens) -> bool { - let re = lazy_regex::regex!(r#": (?:deno_core :: )?ResourceId$"#); - re.is_match(&tokens(arg)) -} - -fn is_mut_ref_opstate(arg: impl ToTokens) -> bool { - let re = lazy_regex::regex!(r#": & mut (?:deno_core :: )?OpState$"#); - re.is_match(&tokens(arg)) -} - -fn is_rc_refcell_opstate(arg: &syn::FnArg) -> bool { - let re = - lazy_regex::regex!(r#": Rc < RefCell < (?:deno_core :: )?OpState > >$"#); - re.is_match(&tokens(arg)) -} - -fn is_handle_scope(arg: &syn::FnArg) -> bool { - let re = lazy_regex::regex!( - r#": & mut (?:deno_core :: )?v8 :: HandleScope(?: < '\w+ >)?$"# - ); - re.is_match(&tokens(arg)) -} - -fn is_future(ty: impl ToTokens) -> bool { - tokens(&ty).contains("impl Future < Output =") -} - -fn tokens(x: impl ToTokens) -> String { - x.to_token_stream().to_string() -} - -fn exclude_lifetime_params( - generic_params: &Punctuated, -) -> Punctuated { - generic_params - .iter() - .filter(|t| !tokens(t).starts_with('\'')) - .cloned() - .collect::>() -} - -fn exclude_non_lifetime_params( - generic_params: &Punctuated, -) -> Punctuated { - generic_params - .iter() - .filter(|t| tokens(t).starts_with('\'')) - .cloned() - .collect::>() -} - -#[cfg(test)] -mod tests { - use crate::Attributes; - use crate::Op; - use pretty_assertions::assert_eq; - use std::path::PathBuf; - - #[testing_macros::fixture("optimizer_tests/**/*.rs")] - fn test_codegen(input: PathBuf) { - let update_expected = std::env::var("UPDATE_EXPECTED").is_ok(); - - let source = - std::fs::read_to_string(&input).expect("Failed to read test file"); - - let mut attrs = Attributes::default(); - if source.contains("// @test-attr:fast") { - attrs.must_be_fast = true; - } - if source.contains("// @test-attr:wasm") { - attrs.is_wasm = true; - attrs.must_be_fast = true; - } - - let item = syn::parse_str(&source).expect("Failed to parse test file"); - let op = Op::new(item, attrs); - - let expected = std::fs::read_to_string(input.with_extension("out")) - .expect("Failed to read expected output file"); - - // Print the raw tokens in case we fail to parse - let actual = op.gen(); - println!("-----Raw tokens-----\n{}----------\n", actual); - - // Validate syntax tree. - let tree = syn::parse2(actual).unwrap(); - let actual = prettyplease::unparse(&tree); - if update_expected { - std::fs::write(input.with_extension("out"), actual) - .expect("Failed to write expected file"); - } else { - assert_eq!(actual, expected); - } - } -} diff --git a/ops/op2/dispatch_fast.rs b/ops/op2/dispatch_fast.rs deleted file mode 100644 index f9d74416a5..0000000000 --- a/ops/op2/dispatch_fast.rs +++ /dev/null @@ -1,327 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use super::generator_state::GeneratorState; -use super::signature::Arg; -use super::signature::NumericArg; -use super::signature::ParsedSignature; -use super::signature::RetVal; -use super::signature::Special; -use super::V8MappingError; -use proc_macro2::Ident; -use proc_macro2::TokenStream; -use quote::format_ident; -use quote::quote; -use std::iter::zip; - -#[allow(unused)] -#[derive(Debug, Default, PartialEq, Clone)] -pub(crate) enum V8FastCallType { - #[default] - Void, - Bool, - U32, - I32, - U64, - I64, - F32, - F64, - Pointer, - V8Value, - Uint8Array, - Uint32Array, - Float64Array, - SeqOneByteString, - CallbackOptions, -} - -impl V8FastCallType { - /// Quote fast value type. - fn quote_rust_type(&self, deno_core: &TokenStream) -> TokenStream { - match self { - V8FastCallType::Void => quote!(()), - V8FastCallType::Bool => quote!(bool), - V8FastCallType::U32 => quote!(u32), - V8FastCallType::I32 => quote!(i32), - V8FastCallType::U64 => quote!(u64), - V8FastCallType::I64 => quote!(i64), - V8FastCallType::F32 => quote!(f32), - V8FastCallType::F64 => quote!(f64), - V8FastCallType::Pointer => quote!(*mut ::std::ffi::c_void), - V8FastCallType::V8Value => { - quote!(#deno_core::v8::Local<#deno_core::v8::Value>) - } - V8FastCallType::CallbackOptions => { - quote!(*mut #deno_core::v8::fast_api::FastApiCallbackOptions) - } - V8FastCallType::SeqOneByteString => { - quote!(*mut #deno_core::v8::fast_api::FastApiOneByteString) - } - V8FastCallType::Uint8Array - | V8FastCallType::Uint32Array - | V8FastCallType::Float64Array => unreachable!(), - } - } - - /// Quote fast value type's variant. - fn quote_ctype(&self) -> TokenStream { - match &self { - V8FastCallType::Void => quote!(CType::Void), - V8FastCallType::Bool => quote!(CType::Bool), - V8FastCallType::U32 => quote!(CType::Uint32), - V8FastCallType::I32 => quote!(CType::Int32), - V8FastCallType::U64 => quote!(CType::Uint64), - V8FastCallType::I64 => quote!(CType::Int64), - V8FastCallType::F32 => quote!(CType::Float32), - V8FastCallType::F64 => quote!(CType::Float64), - V8FastCallType::Pointer => quote!(CType::Pointer), - V8FastCallType::V8Value => quote!(CType::V8Value), - V8FastCallType::CallbackOptions => quote!(CType::CallbackOptions), - V8FastCallType::Uint8Array => unreachable!(), - V8FastCallType::Uint32Array => unreachable!(), - V8FastCallType::Float64Array => unreachable!(), - V8FastCallType::SeqOneByteString => quote!(CType::SeqOneByteString), - } - } - - /// Quote fast value type's variant. - fn quote_type(&self) -> TokenStream { - match &self { - V8FastCallType::Void => quote!(Type::Void), - V8FastCallType::Bool => quote!(Type::Bool), - V8FastCallType::U32 => quote!(Type::Uint32), - V8FastCallType::I32 => quote!(Type::Int32), - V8FastCallType::U64 => quote!(Type::Uint64), - V8FastCallType::I64 => quote!(Type::Int64), - V8FastCallType::F32 => quote!(Type::Float32), - V8FastCallType::F64 => quote!(Type::Float64), - V8FastCallType::Pointer => quote!(Type::Pointer), - V8FastCallType::V8Value => quote!(Type::V8Value), - V8FastCallType::CallbackOptions => quote!(Type::CallbackOptions), - V8FastCallType::Uint8Array => quote!(Type::TypedArray(CType::Uint8)), - V8FastCallType::Uint32Array => quote!(Type::TypedArray(CType::Uint32)), - V8FastCallType::Float64Array => quote!(Type::TypedArray(CType::Float64)), - V8FastCallType::SeqOneByteString => quote!(Type::SeqOneByteString), - } - } -} - -pub fn generate_dispatch_fast( - generator_state: &mut GeneratorState, - signature: &ParsedSignature, -) -> Result, V8MappingError> { - let mut inputs = vec![]; - for arg in &signature.args { - let Some(fv) = map_arg_to_v8_fastcall_type(arg)? else { - return Ok(None); - }; - inputs.push(fv); - } - let mut names = inputs - .iter() - .enumerate() - .map(|(i, _)| format_ident!("arg{i}")) - .collect::>(); - - let ret_val = match &signature.ret_val { - RetVal::Infallible(arg) => arg, - RetVal::Result(arg) => arg, - }; - - let output = match map_retval_to_v8_fastcall_type(ret_val)? { - None => return Ok(None), - Some(rv) => rv, - }; - - let GeneratorState { - fast_function, - deno_core, - result, - opctx, - fast_api_callback_options, - needs_fast_api_callback_options, - needs_fast_opctx, - .. - } = generator_state; - - let handle_error = match signature.ret_val { - RetVal::Infallible(_) => quote!(), - RetVal::Result(_) => { - *needs_fast_api_callback_options = true; - *needs_fast_opctx = true; - inputs.push(V8FastCallType::CallbackOptions); - quote! { - let #result = match #result { - Ok(#result) => #result, - Err(err) => { - // FASTCALL FALLBACK: This is where we set the errors for the slow-call error pickup path. There - // is no code running between this and the other FASTCALL FALLBACK comment, except some V8 code - // required to perform the fallback process. This is why the below call is safe. - - // The reason we need to do this is because V8 does not allow exceptions to be thrown from the - // fast call. Instead, you are required to set the fallback flag, which indicates to V8 that it - // should re-call the slow version of the function. Technically the slow call should perform the - // same operation and then throw the same error (because it should be idempotent), but in our - // case we stash the error and pick it up on the slow path before doing any work. - - // TODO(mmastrac): We should allow an #[op] flag to re-perform slow calls without the error path when - // the method is performance sensitive. - - // SAFETY: We guarantee that OpCtx has no mutable references once ops are live and being called, - // allowing us to perform this one little bit of mutable magic. - unsafe { #opctx.unsafely_set_last_error_for_ops_only(err); } - #fast_api_callback_options.fallback = true; - return ::std::default::Default::default(); - } - }; - } - } - }; - - let input_types = inputs.iter().map(|fv| fv.quote_type()).collect::>(); - let output_type = output.quote_ctype(); - - let fast_definition = quote! { - use #deno_core::v8::fast_api::Type; - use #deno_core::v8::fast_api::CType; - #deno_core::v8::fast_api::FastFunction::new( - &[ Type::V8Value, #( #input_types ),* ], - #output_type, - Self::#fast_function as *const ::std::ffi::c_void - ) - }; - - let output_type = output.quote_rust_type(deno_core); - let mut types = inputs - .iter() - .map(|rv| rv.quote_rust_type(deno_core)) - .collect::>(); - - let call_idents = names.clone(); - let call_args = zip(names.iter(), signature.args.iter()) - .map(|(name, arg)| map_v8_fastcall_arg_to_arg(deno_core, name, arg)) - .collect::>(); - - let with_fast_api_callback_options = if *needs_fast_api_callback_options { - types.push(V8FastCallType::CallbackOptions.quote_rust_type(deno_core)); - names.push(fast_api_callback_options.clone()); - quote! { - let #fast_api_callback_options = unsafe { &mut *#fast_api_callback_options }; - } - } else { - quote!() - }; - let with_opctx = if *needs_fast_opctx { - quote!( - let #opctx = unsafe { - &*(#deno_core::v8::Local::::cast(unsafe { #fast_api_callback_options.data.data }).value() - as *const #deno_core::_ops::OpCtx) - }; - ) - } else { - quote!() - }; - - let fast_fn = quote!( - fn #fast_function( - _: #deno_core::v8::Local<#deno_core::v8::Object>, - #( #names: #types, )* - ) -> #output_type { - #with_fast_api_callback_options - #with_opctx - #(#call_args)* - let #result = Self::call(#(#call_idents),*); - #handle_error - #result - } - ); - - Ok(Some((fast_definition, fast_fn))) -} - -fn map_v8_fastcall_arg_to_arg( - deno_core: &TokenStream, - arg_ident: &Ident, - arg: &Arg, -) -> TokenStream { - let arg_temp = format_ident!("{}_temp", arg_ident); - match arg { - Arg::Special(Special::RefStr) => { - quote! { - let mut #arg_temp: [::std::mem::MaybeUninit; 1024] = [::std::mem::MaybeUninit::uninit(); 1024]; - let #arg_ident = &#deno_core::_ops::to_str_ptr(unsafe { &mut *#arg_ident }, &mut #arg_temp); - } - } - Arg::Special(Special::String) => { - quote!(let #arg_ident = #deno_core::_ops::to_string_ptr(unsafe { &mut *#arg_ident });) - } - Arg::Special(Special::CowStr) => { - quote! { - let mut #arg_temp: [::std::mem::MaybeUninit; 1024] = [::std::mem::MaybeUninit::uninit(); 1024]; - let #arg_ident = #deno_core::_ops::to_str_ptr(unsafe { &mut *#arg_ident }, &mut #arg_temp); - } - } - _ => quote!(let #arg_ident = #arg_ident as _;), - } -} - -fn map_arg_to_v8_fastcall_type( - arg: &Arg, -) -> Result, V8MappingError> { - let rv = match arg { - Arg::OptionNumeric(_) | Arg::SerdeV8(_) => return Ok(None), - Arg::Numeric(NumericArg::bool) => V8FastCallType::Bool, - Arg::Numeric(NumericArg::u32) - | Arg::Numeric(NumericArg::u16) - | Arg::Numeric(NumericArg::u8) => V8FastCallType::U32, - Arg::Numeric(NumericArg::i32) - | Arg::Numeric(NumericArg::i16) - | Arg::Numeric(NumericArg::i8) - | Arg::Numeric(NumericArg::__SMI__) => V8FastCallType::I32, - Arg::Numeric(NumericArg::u64) | Arg::Numeric(NumericArg::usize) => { - V8FastCallType::U64 - } - Arg::Numeric(NumericArg::i64) | Arg::Numeric(NumericArg::isize) => { - V8FastCallType::I64 - } - // Ref strings that are one byte internally may be passed as a SeqOneByteString, - // which gives us a FastApiOneByteString. - Arg::Special(Special::RefStr) => V8FastCallType::SeqOneByteString, - // Owned strings can be fast, but we'll have to copy them. - Arg::Special(Special::String) => V8FastCallType::SeqOneByteString, - // Cow strings can be fast, but may require copying - Arg::Special(Special::CowStr) => V8FastCallType::SeqOneByteString, - _ => return Err(V8MappingError::NoMapping("a fast argument", arg.clone())), - }; - Ok(Some(rv)) -} - -fn map_retval_to_v8_fastcall_type( - arg: &Arg, -) -> Result, V8MappingError> { - let rv = match arg { - Arg::OptionNumeric(_) | Arg::SerdeV8(_) => return Ok(None), - Arg::Void => V8FastCallType::Void, - Arg::Numeric(NumericArg::bool) => V8FastCallType::Bool, - Arg::Numeric(NumericArg::u32) - | Arg::Numeric(NumericArg::u16) - | Arg::Numeric(NumericArg::u8) => V8FastCallType::U32, - Arg::Numeric(NumericArg::i32) - | Arg::Numeric(NumericArg::i16) - | Arg::Numeric(NumericArg::i8) => V8FastCallType::I32, - Arg::Numeric(NumericArg::u64) | Arg::Numeric(NumericArg::usize) => { - V8FastCallType::U64 - } - Arg::Numeric(NumericArg::i64) | Arg::Numeric(NumericArg::isize) => { - V8FastCallType::I64 - } - // We don't return special return types - Arg::Option(_) => return Ok(None), - Arg::Special(_) => return Ok(None), - _ => { - return Err(V8MappingError::NoMapping( - "a fast return value", - arg.clone(), - )) - } - }; - Ok(Some(rv)) -} diff --git a/ops/op2/dispatch_slow.rs b/ops/op2/dispatch_slow.rs deleted file mode 100644 index 2ec67cc76f..0000000000 --- a/ops/op2/dispatch_slow.rs +++ /dev/null @@ -1,401 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use super::generator_state::GeneratorState; -use super::signature::Arg; -use super::signature::NumericArg; -use super::signature::ParsedSignature; -use super::signature::RetVal; -use super::signature::Special; -use super::MacroConfig; -use super::V8MappingError; -use proc_macro2::TokenStream; -use quote::format_ident; -use quote::quote; - -pub(crate) fn generate_dispatch_slow( - config: &MacroConfig, - generator_state: &mut GeneratorState, - signature: &ParsedSignature, -) -> Result { - let mut output = TokenStream::new(); - - // Fast ops require the slow op to check op_ctx for the last error - if config.fast && matches!(signature.ret_val, RetVal::Result(_)) { - generator_state.needs_opctx = true; - let throw_exception = throw_exception(generator_state)?; - // If the fast op returned an error, we must throw it rather than doing work. - output.extend(quote!{ - // FASTCALL FALLBACK: This is where we pick up the errors for the slow-call error pickup - // path. There is no code running between this and the other FASTCALL FALLBACK comment, - // except some V8 code required to perform the fallback process. This is why the below call is safe. - - // SAFETY: We guarantee that OpCtx has no mutable references once ops are live and being called, - // allowing us to perform this one little bit of mutable magic. - if let Some(err) = unsafe { opctx.unsafely_take_last_error_for_ops_only() } { - #throw_exception - } - }); - } - - for (index, arg) in signature.args.iter().enumerate() { - output.extend(extract_arg(generator_state, index)?); - output.extend(from_arg(generator_state, index, arg)?); - } - output.extend(call(generator_state)?); - output.extend(return_value(generator_state, &signature.ret_val)?); - - let with_scope = if generator_state.needs_scope { - with_scope(generator_state) - } else { - quote!() - }; - - let with_opctx = if generator_state.needs_opctx { - with_opctx(generator_state) - } else { - quote!() - }; - - let with_retval = if generator_state.needs_retval { - with_retval(generator_state) - } else { - quote!() - }; - - let with_args = if generator_state.needs_args { - with_fn_args(generator_state) - } else { - quote!() - }; - - let GeneratorState { - deno_core, - info, - slow_function, - .. - } = &generator_state; - - Ok(quote! { - extern "C" fn #slow_function(#info: *const #deno_core::v8::FunctionCallbackInfo) { - #with_scope - #with_retval - #with_args - #with_opctx - - #output - }}) -} - -fn with_scope(generator_state: &mut GeneratorState) -> TokenStream { - let GeneratorState { - deno_core, - scope, - info, - .. - } = &generator_state; - - quote!(let #scope = &mut unsafe { #deno_core::v8::CallbackScope::new(&*#info) };) -} - -fn with_retval(generator_state: &mut GeneratorState) -> TokenStream { - let GeneratorState { - deno_core, - retval, - info, - .. - } = &generator_state; - - quote!(let mut #retval = #deno_core::v8::ReturnValue::from_function_callback_info(unsafe { &*#info });) -} - -fn with_fn_args(generator_state: &mut GeneratorState) -> TokenStream { - let GeneratorState { - deno_core, - fn_args, - info, - .. - } = &generator_state; - - quote!(let #fn_args = #deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe { &*#info });) -} - -fn with_opctx(generator_state: &mut GeneratorState) -> TokenStream { - let GeneratorState { - deno_core, - opctx, - fn_args, - needs_args, - .. - } = generator_state; - - *needs_args = true; - quote!(let #opctx = unsafe { - &*(#deno_core::v8::Local::<#deno_core::v8::External>::cast(#fn_args.data()).value() - as *const #deno_core::_ops::OpCtx) - };) -} - -pub fn extract_arg( - generator_state: &mut GeneratorState, - index: usize, -) -> Result { - let GeneratorState { fn_args, .. } = &generator_state; - let arg_ident = generator_state.args.get(index); - - Ok(quote!( - let #arg_ident = #fn_args.get(#index as i32); - )) -} - -pub fn from_arg( - mut generator_state: &mut GeneratorState, - index: usize, - arg: &Arg, -) -> Result { - let GeneratorState { - deno_core, - args, - scope, - needs_scope, - .. - } = &mut generator_state; - let arg_ident = args.get_mut(index).expect("Argument at index was missing"); - let arg_temp = format_ident!("{}_temp", arg_ident); - let res = match arg { - Arg::Numeric(NumericArg::bool) => quote! { - let #arg_ident = #arg_ident.is_true(); - }, - Arg::Numeric(NumericArg::u8) - | Arg::Numeric(NumericArg::u16) - | Arg::Numeric(NumericArg::u32) => { - quote! { - let #arg_ident = #deno_core::_ops::to_u32(&#arg_ident) as _; - } - } - Arg::Numeric(NumericArg::i8) - | Arg::Numeric(NumericArg::i16) - | Arg::Numeric(NumericArg::i32) - | Arg::Numeric(NumericArg::__SMI__) => { - quote! { - let #arg_ident = #deno_core::_ops::to_i32(&#arg_ident) as _; - } - } - Arg::Numeric(NumericArg::u64) | Arg::Numeric(NumericArg::usize) => { - quote! { - let #arg_ident = #deno_core::_ops::to_u64(&#arg_ident) as _; - } - } - Arg::Numeric(NumericArg::i64) | Arg::Numeric(NumericArg::isize) => { - quote! { - let #arg_ident = #deno_core::_ops::to_i64(&#arg_ident) as _; - } - } - Arg::OptionNumeric(numeric) => { - // Ends the borrow of generator_state - let arg_ident = arg_ident.clone(); - let some = from_arg(generator_state, index, &Arg::Numeric(*numeric))?; - quote! { - let #arg_ident = if #arg_ident.is_null_or_undefined() { - None - } else { - #some - Some(#arg_ident) - }; - } - } - Arg::Option(Special::String) => { - *needs_scope = true; - quote! { - let #arg_ident = #arg_ident.to_rust_string_lossy(#scope); - } - } - Arg::Special(Special::String) => { - *needs_scope = true; - quote! { - let #arg_ident = #arg_ident.to_rust_string_lossy(#scope); - } - } - Arg::Special(Special::RefStr) => { - *needs_scope = true; - quote! { - // Trade 1024 bytes of stack space for potentially non-allocating strings - let mut #arg_temp: [::std::mem::MaybeUninit; 1024] = [::std::mem::MaybeUninit::uninit(); 1024]; - let #arg_ident = &#deno_core::_ops::to_str(#scope, &#arg_ident, &mut #arg_temp); - } - } - Arg::Special(Special::CowStr) => { - *needs_scope = true; - quote! { - // Trade 1024 bytes of stack space for potentially non-allocating strings - let mut #arg_temp: [::std::mem::MaybeUninit; 1024] = [::std::mem::MaybeUninit::uninit(); 1024]; - let #arg_ident = #deno_core::_ops::to_str(#scope, &#arg_ident, &mut #arg_temp); - } - } - _ => return Err(V8MappingError::NoMapping("a slow argument", arg.clone())), - }; - Ok(res) -} - -pub fn call( - generator_state: &mut GeneratorState, -) -> Result { - let GeneratorState { result, .. } = &generator_state; - - let mut tokens = TokenStream::new(); - for arg in &generator_state.args { - tokens.extend(quote!( #arg , )); - } - Ok(quote! { - let #result = Self::call( #tokens ); - }) -} - -pub fn return_value( - generator_state: &mut GeneratorState, - ret_type: &RetVal, -) -> Result { - match ret_type { - RetVal::Infallible(ret_type) => { - return_value_infallible(generator_state, ret_type) - } - RetVal::Result(ret_type) => return_value_result(generator_state, ret_type), - } -} - -pub fn return_value_infallible( - generator_state: &mut GeneratorState, - ret_type: &Arg, -) -> Result { - let GeneratorState { - deno_core, - scope, - result, - retval, - needs_retval, - needs_scope, - .. - } = generator_state; - - let res = match ret_type { - Arg::Void => { - quote! {/* void */} - } - Arg::Numeric(NumericArg::u8) - | Arg::Numeric(NumericArg::u16) - | Arg::Numeric(NumericArg::u32) => { - *needs_retval = true; - quote!(#retval.set_uint32(#result as u32);) - } - Arg::Numeric(NumericArg::i8) - | Arg::Numeric(NumericArg::i16) - | Arg::Numeric(NumericArg::i32) => { - *needs_retval = true; - quote!(#retval.set_int32(#result as i32);) - } - Arg::Special(Special::String) => { - *needs_retval = true; - *needs_scope = true; - quote! { - if #result.is_empty() { - #retval.set_empty_string(); - } else { - // This should not fail in normal cases - // TODO(mmastrac): This has extra allocations that we need to get rid of, especially if the string - // is ASCII. We could make an "external Rust String" string in V8 from these and re-use the allocation. - let temp = #deno_core::v8::String::new(#scope, &#result).unwrap(); - #retval.set(temp.into()); - } - } - } - Arg::Option(Special::String) => { - *needs_retval = true; - *needs_scope = true; - // End the generator_state borrow - let (result, retval) = (result.clone(), retval.clone()); - let some = return_value_infallible( - generator_state, - &Arg::Special(Special::String), - )?; - quote! { - if let Some(#result) = #result { - #some - } else { - #retval.set_null(); - } - } - } - _ => { - return Err(V8MappingError::NoMapping( - "a slow return value", - ret_type.clone(), - )) - } - }; - - Ok(res) -} - -fn return_value_result( - generator_state: &mut GeneratorState, - ret_type: &Arg, -) -> Result { - let infallible = return_value_infallible(generator_state, ret_type)?; - let exception = throw_exception(generator_state)?; - - let GeneratorState { result, .. } = &generator_state; - - let tokens = quote!( - match #result { - Ok(#result) => { - #infallible - } - Err(err) => { - #exception - } - }; - ); - Ok(tokens) -} - -/// Generates code to throw an exception, adding required additional dependencies as needed. -fn throw_exception( - generator_state: &mut GeneratorState, -) -> Result { - let maybe_scope = if generator_state.needs_scope { - quote!() - } else { - with_scope(generator_state) - }; - - let maybe_opctx = if generator_state.needs_opctx { - quote!() - } else { - with_opctx(generator_state) - }; - - let maybe_args = if generator_state.needs_args { - quote!() - } else { - with_fn_args(generator_state) - }; - - let GeneratorState { - deno_core, - scope, - opctx, - .. - } = &generator_state; - - Ok(quote! { - #maybe_scope - #maybe_args - #maybe_opctx - let opstate = ::std::cell::RefCell::borrow(&*#opctx.state); - let exception = #deno_core::error::to_v8_error( - #scope, - opstate.get_error_class_fn, - &err, - ); - scope.throw_exception(exception); - return; - }) -} diff --git a/ops/op2/generator_state.rs b/ops/op2/generator_state.rs deleted file mode 100644 index e437ea47c2..0000000000 --- a/ops/op2/generator_state.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use proc_macro2::Ident; -use proc_macro2::TokenStream; - -pub struct GeneratorState { - /// The path to the `deno_core` crate (either `deno_core` or `crate`, the latter used if the op is `(core)`). - pub deno_core: TokenStream, - - /// Identifiers for each of the arguments of the original function - pub args: Vec, - /// The new identifier for the original function's contents. - pub call: Ident, - /// The result of the `call` function - pub result: Ident, - - /// The `v8::CallbackScope` used if necessary for the function. - pub scope: Ident, - /// The `v8::FunctionCallbackInfo` used to pass args into the slow function. - pub info: Ident, - /// The `v8::FunctionCallbackArguments` used to pass args into the slow function. - pub fn_args: Ident, - /// The `OpCtx` used for various information required for some ops. - pub opctx: Ident, - /// The `FastApiCallbackOptions` used in fast calls for fallback returns. - pub fast_api_callback_options: Ident, - /// The `v8::ReturnValue` used in the slow function - pub retval: Ident, - /// The "slow" function (ie: the one that isn't a fastcall) - pub slow_function: Ident, - /// The "fast" function (ie: a fastcall) - pub fast_function: Ident, - - pub needs_args: bool, - pub needs_retval: bool, - pub needs_scope: bool, - pub needs_opstate: bool, - pub needs_opctx: bool, - pub needs_fast_opctx: bool, - pub needs_fast_api_callback_options: bool, -} diff --git a/ops/op2/mod.rs b/ops/op2/mod.rs deleted file mode 100644 index 7f652fe1bc..0000000000 --- a/ops/op2/mod.rs +++ /dev/null @@ -1,326 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use deno_proc_macro_rules::rules; -use proc_macro2::Ident; -use proc_macro2::Span; -use proc_macro2::TokenStream; -use quote::format_ident; -use quote::quote; -use quote::ToTokens; -use std::iter::zip; -use syn2::parse2; -use syn2::parse_str; -use syn2::FnArg; -use syn2::ItemFn; -use syn2::Path; -use thiserror::Error; - -use self::dispatch_fast::generate_dispatch_fast; -use self::dispatch_slow::generate_dispatch_slow; -use self::generator_state::GeneratorState; -use self::signature::parse_signature; -use self::signature::Arg; -use self::signature::SignatureError; - -pub mod dispatch_fast; -pub mod dispatch_slow; -pub mod generator_state; -pub mod signature; - -#[derive(Debug, Error)] -pub enum Op2Error { - #[error("Failed to match a pattern for '{0}': (input was '{1}')")] - PatternMatchFailed(&'static str, String), - #[error("Invalid attribute: '{0}'")] - InvalidAttribute(String), - #[error("Failed to parse syntax tree")] - ParseError(#[from] syn2::Error), - #[error("Failed to map a parsed signature to a V8 call")] - V8MappingError(#[from] V8MappingError), - #[error("Failed to parse signature")] - SignatureError(#[from] SignatureError), - #[error("This op is fast-compatible and should be marked as (fast)")] - ShouldBeFast, - #[error("This op is not fast-compatible and should not be marked as (fast)")] - ShouldNotBeFast, -} - -#[derive(Debug, Error)] -pub enum V8MappingError { - #[error("Unable to map {1:?} to {0}")] - NoMapping(&'static str, Arg), -} - -#[derive(Default)] -pub(crate) struct MacroConfig { - pub core: bool, - pub fast: bool, -} - -impl MacroConfig { - pub fn from_flags(flags: Vec) -> Result { - let mut config: MacroConfig = Self::default(); - for flag in flags { - if flag == "core" { - config.core = true; - } else if flag == "fast" { - config.fast = true; - } else { - return Err(Op2Error::InvalidAttribute(flag.to_string())); - } - } - Ok(config) - } - - pub fn from_tokens(tokens: TokenStream) -> Result { - let attr_string = tokens.to_string(); - let config = std::panic::catch_unwind(|| { - rules!(tokens => { - () => { - Ok(MacroConfig::default()) - } - ($($flags:ident),+) => { - Self::from_flags(flags) - } - }) - }) - .map_err(|_| Op2Error::PatternMatchFailed("attribute", attr_string))??; - Ok(config) - } -} - -pub fn op2( - attr: TokenStream, - item: TokenStream, -) -> Result { - let func = parse2::(item)?; - let config = MacroConfig::from_tokens(attr)?; - generate_op2(config, func) -} - -fn generate_op2( - config: MacroConfig, - func: ItemFn, -) -> Result { - // Create a copy of the original function, named "call" - let call = Ident::new("call", Span::call_site()); - let mut op_fn = func.clone(); - op_fn.attrs.clear(); - op_fn.sig.generics.params.clear(); - op_fn.sig.ident = call.clone(); - - // Clear inert attributes - // TODO(mmastrac): This should limit itself to clearing ours only - for arg in op_fn.sig.inputs.iter_mut() { - match arg { - FnArg::Receiver(slf) => slf.attrs.clear(), - FnArg::Typed(ty) => ty.attrs.clear(), - } - } - - let signature = parse_signature(func.attrs, func.sig.clone())?; - let processed_args = - zip(signature.args.iter(), &func.sig.inputs).collect::>(); - - let mut args = vec![]; - let mut needs_args = false; - for (index, _) in processed_args.iter().enumerate() { - let input = format_ident!("arg{index}"); - args.push(input); - needs_args = true; - } - - let retval = Ident::new("rv", Span::call_site()); - let result = Ident::new("result", Span::call_site()); - let fn_args = Ident::new("args", Span::call_site()); - let scope = Ident::new("scope", Span::call_site()); - let info = Ident::new("info", Span::call_site()); - let opctx = Ident::new("opctx", Span::call_site()); - let slow_function = Ident::new("v8_fn_ptr", Span::call_site()); - let fast_function = Ident::new("v8_fn_ptr_fast", Span::call_site()); - let fast_api_callback_options = - Ident::new("fast_api_callback_options", Span::call_site()); - - let deno_core = if config.core { - syn2::parse_str::("crate") - } else { - syn2::parse_str::("deno_core") - } - .expect("Parsing crate should not fail") - .into_token_stream(); - - let mut generator_state = GeneratorState { - args, - fn_args, - call, - scope, - info, - opctx, - fast_api_callback_options, - deno_core, - result, - retval, - needs_args, - slow_function, - fast_function, - needs_retval: false, - needs_scope: false, - needs_opctx: false, - needs_opstate: false, - needs_fast_opctx: false, - needs_fast_api_callback_options: false, - }; - - let name = func.sig.ident; - - let slow_fn = - generate_dispatch_slow(&config, &mut generator_state, &signature)?; - let (fast_definition, fast_fn) = - match generate_dispatch_fast(&mut generator_state, &signature)? { - Some((fast_definition, fast_fn)) => { - if !config.fast { - return Err(Op2Error::ShouldBeFast); - } - (quote!(Some({#fast_definition})), fast_fn) - } - None => { - if config.fast { - return Err(Op2Error::ShouldNotBeFast); - } - (quote!(None), quote!()) - } - }; - - let GeneratorState { - deno_core, - slow_function, - .. - } = &generator_state; - - let arg_count: usize = generator_state.args.len(); - let vis = func.vis; - let generic = signature - .generic_bounds - .keys() - .map(|s| format_ident!("{s}")) - .collect::>(); - let bound = signature - .generic_bounds - .values() - .map(|p| parse_str::(p).expect("Failed to reparse path")) - .collect::>(); - - Ok(quote! { - #[allow(non_camel_case_types)] - #vis struct #name <#(#generic),*> { - // We need to mark these type parameters as used, so we use a PhantomData - _unconstructable: ::std::marker::PhantomData<(#(#generic),*)> - } - - impl <#(#generic : #bound),*> #deno_core::_ops::Op for #name <#(#generic),*> { - const NAME: &'static str = stringify!(#name); - const DECL: #deno_core::_ops::OpDecl = #deno_core::_ops::OpDecl { - name: stringify!(#name), - v8_fn_ptr: Self::#slow_function as _, - enabled: true, - fast_fn: #fast_definition, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: #arg_count as u8, - }; - } - - impl <#(#generic : #bound),*> #name <#(#generic),*> { - pub const fn name() -> &'static str { - stringify!(#name) - } - - pub const fn decl() -> #deno_core::_ops::OpDecl { - #deno_core::_ops::OpDecl { - name: stringify!(#name), - v8_fn_ptr: Self::#slow_function as _, - enabled: true, - fast_fn: #fast_definition, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: #arg_count as u8, - } - } - - #fast_fn - #slow_fn - - #[inline(always)] - #op_fn - } - }) -} - -#[cfg(test)] -mod tests { - use super::*; - use pretty_assertions::assert_eq; - use std::path::PathBuf; - use syn2::parse_str; - use syn2::File; - use syn2::Item; - - #[testing_macros::fixture("op2/test_cases/**/*.rs")] - fn test_signature_parser(input: PathBuf) { - let update_expected = std::env::var("UPDATE_EXPECTED").is_ok(); - - let source = - std::fs::read_to_string(&input).expect("Failed to read test file"); - let file = parse_str::(&source).expect("Failed to parse Rust file"); - let mut expected_out = vec![]; - for item in file.items { - if let Item::Fn(mut func) = item { - let mut config = None; - func.attrs.retain(|attr| { - let tokens = attr.into_token_stream(); - let attr_string = attr.clone().into_token_stream().to_string(); - println!("{}", attr_string); - use syn2 as syn; - if let Some(new_config) = rules!(tokens => { - (#[op2]) => { - Some(MacroConfig::default()) - } - (#[op2( $($x:ident),* )]) => { - Some(MacroConfig::from_flags(x).expect("Failed to parse attribute")) - } - (#[$_attr:meta]) => { - None - } - }) { - config = Some(new_config); - false - } else { - true - } - }); - let tokens = - generate_op2(config.unwrap(), func).expect("Failed to generate op"); - println!("======== Raw tokens ========:\n{}", tokens.clone()); - let tree = syn::parse2(tokens).unwrap(); - let actual = prettyplease::unparse(&tree); - println!("======== Generated ========:\n{}", actual); - expected_out.push(actual); - } - } - - let expected_out = expected_out.join("\n"); - - if update_expected { - std::fs::write(input.with_extension("out"), expected_out) - .expect("Failed to write expectation file"); - } else { - let expected = std::fs::read_to_string(input.with_extension("out")) - .expect("Failed to read expectation file"); - assert_eq!( - expected, expected_out, - "Failed to match expectation. Use UPDATE_EXPECTED=1." - ); - } - } -} diff --git a/ops/op2/signature.rs b/ops/op2/signature.rs deleted file mode 100644 index 5d472fcf32..0000000000 --- a/ops/op2/signature.rs +++ /dev/null @@ -1,741 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use deno_proc_macro_rules::rules; -use proc_macro2::Ident; -use proc_macro2::Span; -use quote::quote; -use quote::ToTokens; -use std::collections::BTreeMap; -use strum::IntoEnumIterator; -use strum::IntoStaticStr; -use strum_macros::EnumIter; -use strum_macros::EnumString; -use syn2::Attribute; -use syn2::FnArg; -use syn2::GenericParam; -use syn2::Generics; -use syn2::Pat; -use syn2::ReturnType; -use syn2::Signature; -use syn2::Type; -use syn2::TypePath; -use thiserror::Error; - -#[allow(non_camel_case_types)] -#[derive( - Copy, Clone, Debug, Eq, PartialEq, IntoStaticStr, EnumString, EnumIter, -)] -pub enum NumericArg { - /// A placeholder argument for arguments annotated with #[smi]. - __SMI__, - /// A placeholder argument for void data. - __VOID__, - bool, - i8, - u8, - i16, - u16, - i32, - u32, - i64, - u64, - f32, - f64, - isize, - usize, -} - -impl ToTokens for NumericArg { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - let ident = Ident::new(self.into(), Span::call_site()); - tokens.extend(quote! { #ident }) - } -} - -#[derive( - Copy, Clone, Debug, Eq, PartialEq, IntoStaticStr, EnumString, EnumIter, -)] -pub enum V8Arg { - External, - Object, - Array, - ArrayBuffer, - ArrayBufferView, - DataView, - TypedArray, - BigInt64Array, - BigUint64Array, - Float32Array, - Float64Array, - Int16Array, - Int32Array, - Int8Array, - Uint16Array, - Uint32Array, - Uint8Array, - Uint8ClampedArray, - BigIntObject, - BooleanObject, - Date, - Function, - Map, - NumberObject, - Promise, - PromiseResolver, - Proxy, - RegExp, - Set, - SharedArrayBuffer, - StringObject, - SymbolObject, - WasmMemoryObject, - WasmModuleObject, - Primitive, - BigInt, - Boolean, - Name, - String, - Symbol, - Number, - Integer, - Int32, - Uint32, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum Special { - HandleScope, - OpState, - String, - CowStr, - RefStr, - FastApiCallbackOptions, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum RefType { - Ref, - Mut, -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum Arg { - Void, - Special(Special), - Ref(RefType, Special), - RcRefCell(Special), - Option(Special), - OptionNumeric(NumericArg), - Slice(RefType, NumericArg), - Ptr(RefType, NumericArg), - V8Local(V8Arg), - Numeric(NumericArg), - SerdeV8(String), -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum RetVal { - Infallible(Arg), - Result(Arg), -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct ParsedSignature { - // The parsed arguments - pub args: Vec, - // The argument names - pub names: Vec, - // The parsed return value - pub ret_val: RetVal, - // One and only one lifetime allowed - pub lifetime: Option, - // Generic bounds: each generic must have one and only simple trait bound - pub generic_bounds: BTreeMap, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -enum AttributeModifier { - /// #[serde], for serde_v8 types. - Serde, - /// #[smi], for small integers - Smi, - /// #[string], for strings. - String, -} - -#[derive(Error, Debug)] -pub enum SignatureError { - #[error("Invalid argument: '{0}'")] - ArgError(String, #[source] ArgError), - #[error("Invalid return type")] - RetError(#[from] ArgError), - #[error("Only one lifetime is permitted")] - TooManyLifetimes, - #[error("Generic '{0}' must have one and only bound (either and 'where T: Trait', or )")] - GenericBoundCardinality(String), - #[error("Where clause predicate '{0}' (eg: where T: Trait) must appear in generics list (eg: )")] - WherePredicateMustAppearInGenerics(String), - #[error("All generics must appear only once in the generics parameter list or where clause")] - DuplicateGeneric(String), - #[error("Generic lifetime '{0}' may not have bounds (eg: <'a: 'b>)")] - LifetimesMayNotHaveBounds(String), - #[error("Invalid generic: '{0}' Only simple generics bounds are allowed (eg: T: Trait)")] - InvalidGeneric(String), - #[error("Invalid predicate: '{0}' Only simple where predicates are allowed (eg: T: Trait)")] - InvalidWherePredicate(String), -} - -#[derive(Error, Debug)] -pub enum ArgError { - #[error("Invalid self argument")] - InvalidSelf, - #[error("Invalid argument type: {0}")] - InvalidType(String), - #[error( - "Invalid argument type path (should this be #[smi] or #[serde]?): {0}" - )] - InvalidTypePath(String), - #[error("Too many attributes")] - TooManyAttributes, - #[error("Invalid #[serde] type: {0}")] - InvalidSerdeType(String), - #[error("Cannot use #[serde] for type: {0}")] - InvalidSerdeAttributeType(String), - #[error("Invalid v8 type: {0}")] - InvalidV8Type(String), - #[error("Internal error: {0}")] - InternalError(String), - #[error("Missing a #[string] attribute")] - MissingStringAttribute, -} - -#[derive(Copy, Clone, Default)] -struct Attributes { - primary: Option, -} - -fn stringify_token(tokens: impl ToTokens) -> String { - tokens - .into_token_stream() - .into_iter() - .map(|s| s.to_string()) - .collect::>() - .join("") -} - -pub fn parse_signature( - attributes: Vec, - signature: Signature, -) -> Result { - let mut args = vec![]; - let mut names = vec![]; - for input in signature.inputs { - let name = match &input { - FnArg::Receiver(_) => "self".to_owned(), - FnArg::Typed(ty) => match &*ty.pat { - Pat::Ident(ident) => ident.ident.to_string(), - _ => "(complex)".to_owned(), - }, - }; - names.push(name.clone()); - args.push( - parse_arg(input).map_err(|err| SignatureError::ArgError(name, err))?, - ); - } - let ret_val = - parse_return(parse_attributes(&attributes)?, &signature.output)?; - let lifetime = parse_lifetime(&signature.generics)?; - let generic_bounds = parse_generics(&signature.generics)?; - Ok(ParsedSignature { - args, - names, - ret_val, - lifetime, - generic_bounds, - }) -} - -/// Extract one lifetime from the [`syn2::Generics`], ensuring that the lifetime is valid -/// and has no bounds. -fn parse_lifetime( - generics: &Generics, -) -> Result, SignatureError> { - let mut res = None; - for param in &generics.params { - if let GenericParam::Lifetime(lt) = param { - if !lt.bounds.is_empty() { - return Err(SignatureError::LifetimesMayNotHaveBounds( - lt.lifetime.to_string(), - )); - } - if res.is_some() { - return Err(SignatureError::TooManyLifetimes); - } - res = Some(lt.lifetime.ident.to_string()); - } - } - Ok(res) -} - -/// Parse and validate generics. We require one and only one trait bound for each generic -/// parameter. Tries to sanity check and return reasonable errors for possible signature errors. -fn parse_generics( - generics: &Generics, -) -> Result, SignatureError> { - let mut where_clauses = BTreeMap::new(); - - // First, extract the where clause so we can detect duplicated predicates - if let Some(where_clause) = &generics.where_clause { - for predicate in &where_clause.predicates { - let predicate = predicate.to_token_stream(); - let (generic_name, bound) = std::panic::catch_unwind(|| { - use syn2 as syn; - rules!(predicate => { - ($t:ident : $bound:path) => (t.to_string(), stringify_token(bound)), - }) - }) - .map_err(|_| { - SignatureError::InvalidWherePredicate(predicate.to_string()) - })?; - if where_clauses.insert(generic_name.clone(), bound).is_some() { - return Err(SignatureError::DuplicateGeneric(generic_name)); - } - } - } - - let mut res = BTreeMap::new(); - for param in &generics.params { - if let GenericParam::Type(ty) = param { - let ty = ty.to_token_stream(); - let (name, bound) = std::panic::catch_unwind(|| { - use syn2 as syn; - rules!(ty => { - ($t:ident : $bound:path) => (t.to_string(), Some(stringify_token(bound))), - ($t:ident) => (t.to_string(), None), - }) - }).map_err(|_| SignatureError::InvalidGeneric(ty.to_string()))?; - let bound = match bound { - Some(bound) => { - if where_clauses.contains_key(&name) { - return Err(SignatureError::GenericBoundCardinality(name)); - } - bound - } - None => { - let Some(bound) = where_clauses.remove(&name) else { - return Err(SignatureError::GenericBoundCardinality(name)); - }; - bound - } - }; - if res.contains_key(&name) { - return Err(SignatureError::DuplicateGeneric(name)); - } - res.insert(name, bound); - } - } - if !where_clauses.is_empty() { - return Err(SignatureError::WherePredicateMustAppearInGenerics( - where_clauses.into_keys().next().unwrap(), - )); - } - - Ok(res) -} - -fn parse_attributes(attributes: &[Attribute]) -> Result { - let attrs = attributes - .iter() - .filter_map(parse_attribute) - .collect::>(); - - if attrs.is_empty() { - return Ok(Attributes::default()); - } - if attrs.len() > 1 { - return Err(ArgError::TooManyAttributes); - } - Ok(Attributes { - primary: Some(*attrs.get(0).unwrap()), - }) -} - -fn parse_attribute(attr: &Attribute) -> Option { - let tokens = attr.into_token_stream(); - use syn2 as syn; - std::panic::catch_unwind(|| { - rules!(tokens => { - (#[serde]) => Some(AttributeModifier::Serde), - (#[smi]) => Some(AttributeModifier::Smi), - (#[string]) => Some(AttributeModifier::String), - (#[$_attr:meta]) => None, - }) - }) - .expect("Failed to parse an attribute") -} - -fn parse_return( - attrs: Attributes, - rt: &ReturnType, -) -> Result { - match rt { - ReturnType::Default => Ok(RetVal::Infallible(Arg::Void)), - ReturnType::Type(_, ty) => { - let s = stringify_token(ty); - let tokens = ty.into_token_stream(); - use syn2 as syn; - - std::panic::catch_unwind(|| { - rules!(tokens => { - // x::y::Result, like io::Result and other specialty result types - ($($_package:ident ::)* Result < $ty:ty >) => { - Ok(RetVal::Result(parse_type(attrs, &ty)?)) - } - // x::y::Result - ($($_package:ident ::)* Result < $ty:ty, $_error:ty >) => { - Ok(RetVal::Result(parse_type(attrs, &ty)?)) - } - ($ty:ty) => { - Ok(RetVal::Infallible(parse_type(attrs, &ty)?)) - } - }) - }) - .map_err(|e| { - ArgError::InternalError(format!( - "parse_return({}) {}", - s, - e.downcast::<&str>().unwrap_or_default() - )) - })? - } - } -} - -fn parse_type_path(attrs: Attributes, tp: &TypePath) -> Result { - if tp.path.segments.len() == 1 { - let segment = tp.path.segments.first().unwrap().ident.to_string(); - for numeric in NumericArg::iter() { - if Into::<&'static str>::into(numeric) == segment.as_str() { - return Ok(Arg::Numeric(numeric)); - } - } - } - - use syn2 as syn; - - let tokens = tp.clone().into_token_stream(); - std::panic::catch_unwind(|| { - rules!(tokens => { - ( $( std :: str :: )? String ) => { - if attrs.primary == Some(AttributeModifier::String) { - Ok(Arg::Special(Special::String)) - } else { - Err(ArgError::MissingStringAttribute) - } - } - ( $( std :: str :: )? str ) => { - // We should not hit this path with a #[string] argument - Err(ArgError::MissingStringAttribute) - } - ( $( std :: borrow :: )? Cow < str > ) => { - if attrs.primary == Some(AttributeModifier::String) { - Ok(Arg::Special(Special::CowStr)) - } else { - Err(ArgError::MissingStringAttribute) - } - } - ( $( std :: ffi :: )? c_void ) => Ok(Arg::Numeric(NumericArg::__VOID__)), - ( OpState ) => Ok(Arg::Special(Special::OpState)), - ( v8 :: HandleScope ) => Ok(Arg::Special(Special::HandleScope)), - ( v8 :: FastApiCallbackOptions ) => Ok(Arg::Special(Special::FastApiCallbackOptions)), - ( v8 :: Local < $( $_scope:lifetime , )? v8 :: $v8:ident >) => Ok(Arg::V8Local(parse_v8_type(&v8)?)), - ( Rc < RefCell < $ty:ty > > ) => Ok(Arg::RcRefCell(parse_type_special(attrs, &ty)?)), - ( Option < $ty:ty > ) => { - match parse_type(attrs, &ty)? { - Arg::Special(special) => Ok(Arg::Option(special)), - Arg::Numeric(numeric) => Ok(Arg::OptionNumeric(numeric)), - _ => Err(ArgError::InvalidType(stringify_token(ty))) - } - } - ( $any:ty ) => Err(ArgError::InvalidTypePath(stringify_token(any))), - }) - }).map_err(|e| ArgError::InternalError(format!("parse_type_path {e:?}")))? -} - -fn parse_v8_type(v8: &Ident) -> Result { - let v8 = v8.to_string(); - V8Arg::try_from(v8.as_str()).map_err(|_| ArgError::InvalidV8Type(v8)) -} - -fn parse_type_special( - attrs: Attributes, - ty: &Type, -) -> Result { - match parse_type(attrs, ty)? { - Arg::Special(special) => Ok(special), - _ => Err(ArgError::InvalidType(stringify_token(ty))), - } -} - -fn parse_type(attrs: Attributes, ty: &Type) -> Result { - if let Some(primary) = attrs.primary { - match primary { - AttributeModifier::Serde => match ty { - Type::Path(of) => { - // If this type will parse without #[serde], it is illegal to use this type with #[serde] - if parse_type_path(Attributes::default(), of).is_ok() { - return Err(ArgError::InvalidSerdeAttributeType(stringify_token( - ty, - ))); - } - return Ok(Arg::SerdeV8(stringify_token(of.path.clone()))); - } - _ => return Err(ArgError::InvalidSerdeType(stringify_token(ty))), - }, - AttributeModifier::String => match ty { - Type::Path(of) => { - return parse_type_path(attrs, of); - } - Type::Reference(of) => { - let mut_type = if of.mutability.is_some() { - RefType::Mut - } else { - RefType::Ref - }; - let tokens = of.elem.clone().into_token_stream(); - use syn2 as syn; - return rules!(tokens => { - (str) => Ok(Arg::Special(Special::RefStr)), - ($_ty:ty) => Ok(Arg::Ref(mut_type, parse_type_special(attrs, &of.elem)?)), - }); - } - _ => return Err(ArgError::InvalidSerdeType(stringify_token(ty))), - }, - AttributeModifier::Smi => { - return Ok(Arg::Numeric(NumericArg::__SMI__)); - } - } - }; - match ty { - Type::Tuple(of) => { - if of.elems.is_empty() { - Ok(Arg::Void) - } else { - Err(ArgError::InvalidType(stringify_token(ty))) - } - } - Type::Reference(of) => { - let mut_type = if of.mutability.is_some() { - RefType::Mut - } else { - RefType::Ref - }; - match &*of.elem { - Type::Slice(of) => match parse_type(attrs, &of.elem)? { - Arg::Numeric(numeric) => Ok(Arg::Slice(mut_type, numeric)), - _ => Err(ArgError::InvalidType(stringify_token(ty))), - }, - Type::Path(of) => match parse_type_path(attrs, of)? { - Arg::Special(special) => Ok(Arg::Ref(mut_type, special)), - _ => Err(ArgError::InvalidType(stringify_token(ty))), - }, - _ => Err(ArgError::InvalidType(stringify_token(ty))), - } - } - Type::Ptr(of) => { - let mut_type = if of.mutability.is_some() { - RefType::Mut - } else { - RefType::Ref - }; - match &*of.elem { - Type::Path(of) => match parse_type_path(attrs, of)? { - Arg::Numeric(numeric) => Ok(Arg::Ptr(mut_type, numeric)), - _ => Err(ArgError::InvalidType(stringify_token(ty))), - }, - _ => Err(ArgError::InvalidType(stringify_token(ty))), - } - } - Type::Path(of) => parse_type_path(attrs, of), - _ => Err(ArgError::InvalidType(stringify_token(ty))), - } -} - -fn parse_arg(arg: FnArg) -> Result { - let FnArg::Typed(typed) = arg else { - return Err(ArgError::InvalidSelf); - }; - parse_type(parse_attributes(&typed.attrs)?, &typed.ty) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::op2::signature::parse_signature; - use syn2::parse_str; - use syn2::ItemFn; - - // We can't test pattern args :/ - // https://github.com/rust-lang/rfcs/issues/2688 - macro_rules! test { - ( - // Function attributes - $(# [ $fn_attr:ident ])? - // fn name < 'scope, GENERIC1, GENERIC2, ... > - fn $name:ident $( < $scope:lifetime $( , $generic:ident)* >)? - ( - // Argument attribute, argument - $( $(# [ $attr:ident ])? $ident:ident : $ty:ty ),* - ) - // Return value - $(-> $(# [ $ret_attr:ident ])? $ret:ty)? - // Where clause - $( where $($trait:ident : $bounds:path),* )? - ; - // Expected return value - $( < $( $lifetime_res:lifetime )? $(, $generic_res:ident : $bounds_res:path )* >)? ( $( $arg_res:expr ),* ) -> $ret_res:expr ) => { - #[test] - fn $name() { - test( - stringify!($( #[$fn_attr] )? fn op $( < $scope $( , $generic)* >)? ( $( $( #[$attr] )? $ident : $ty ),* ) $(-> $( #[$ret_attr] )? $ret)? $( where $($trait : $bounds),* )? {}), - stringify!($( < $( $lifetime_res )? $(, $generic_res : $bounds_res)* > )?), - stringify!($($arg_res),*), - stringify!($ret_res) - ); - } - }; - } - - fn test( - op: &str, - generics_expected: &str, - args_expected: &str, - return_expected: &str, - ) { - // Parse the provided macro input as an ItemFn - let item_fn = parse_str::(op) - .unwrap_or_else(|_| panic!("Failed to parse {op} as a ItemFn")); - - let attrs = item_fn.attrs; - let sig = parse_signature(attrs, item_fn.sig).unwrap_or_else(|err| { - panic!("Failed to successfully parse signature from {op} ({err:?})") - }); - println!("Raw parsed signatures = {sig:?}"); - - let mut generics_res = vec![]; - if let Some(lifetime) = sig.lifetime { - generics_res.push(format!("'{lifetime}")); - } - for (name, bounds) in sig.generic_bounds { - generics_res.push(format!("{name} : {bounds}")); - } - if !generics_res.is_empty() { - assert_eq!( - generics_expected, - format!("< {} >", generics_res.join(", ")) - ); - } - assert_eq!( - args_expected, - format!("{:?}", sig.args).trim_matches(|c| c == '[' || c == ']') - ); - assert_eq!(return_expected, format!("{:?}", sig.ret_val)); - } - - macro_rules! expect_fail { - ($name:ident, $error:expr, $f:item) => { - #[test] - pub fn $name() { - expect_fail(stringify!($f), stringify!($error)); - } - }; - } - - fn expect_fail(op: &str, error: &str) { - // Parse the provided macro input as an ItemFn - let item_fn = parse_str::(op) - .unwrap_or_else(|_| panic!("Failed to parse {op} as a ItemFn")); - let attrs = item_fn.attrs; - let err = parse_signature(attrs, item_fn.sig) - .expect_err("Expected function to fail to parse"); - assert_eq!(format!("{err:?}"), error.to_owned()); - } - - test!( - fn op_state_and_number(opstate: &mut OpState, a: u32) -> (); - (Ref(Mut, OpState), Numeric(u32)) -> Infallible(Void) - ); - test!( - fn op_slices(r#in: &[u8], out: &mut [u8]); - (Slice(Ref, u8), Slice(Mut, u8)) -> Infallible(Void) - ); - test!( - #[serde] fn op_serde(#[serde] input: package::SerdeInputType) -> Result; - (SerdeV8("package::SerdeInputType")) -> Result(SerdeV8("package::SerdeReturnType")) - ); - test!( - fn op_local(input: v8::Local) -> Result, Error>; - (V8Local(String)) -> Result(V8Local(String)) - ); - test!( - fn op_resource(#[smi] rid: ResourceId, buffer: &[u8]); - (Numeric(__SMI__), Slice(Ref, u8)) -> Infallible(Void) - ); - test!( - fn op_option_numeric_result(state: &mut OpState) -> Result, AnyError>; - (Ref(Mut, OpState)) -> Result(OptionNumeric(u32)) - ); - test!( - fn op_ffi_read_f64(state: &mut OpState, ptr: * mut c_void, offset: isize) -> Result ; - (Ref(Mut, OpState), Ptr(Mut, __VOID__), Numeric(isize)) -> Result(Numeric(f64)) - ); - test!( - fn op_print(#[string] msg: &str, is_err: bool) -> Result<(), Error>; - (Special(RefStr), Numeric(bool)) -> Result(Void) - ); - test!( - fn op_scope<'s>(#[string] msg: &'s str); - <'s> (Special(RefStr)) -> Infallible(Void) - ); - test!( - fn op_scope_and_generics<'s, AB, BC>(#[string] msg: &'s str) where AB: some::Trait, BC: OtherTrait; - <'s, AB: some::Trait, BC: OtherTrait> (Special(RefStr)) -> Infallible(Void) - ); - - expect_fail!(op_with_two_lifetimes, TooManyLifetimes, fn f<'a, 'b>() {}); - expect_fail!( - op_with_lifetime_bounds, - LifetimesMayNotHaveBounds("'a"), - fn f<'a: 'b, 'b>() {} - ); - expect_fail!( - op_with_missing_bounds, - GenericBoundCardinality("B"), - fn f<'a, B>() {} - ); - expect_fail!( - op_with_duplicate_bounds, - GenericBoundCardinality("B"), - fn f<'a, B: Trait>() - where - B: Trait, - { - } - ); - expect_fail!( - op_with_extra_bounds, - WherePredicateMustAppearInGenerics("C"), - fn f<'a, B>() - where - B: Trait, - C: Trait, - { - } - ); - - #[test] - fn test_parse_result() { - let rt = parse_str::("-> Result < (), Error >") - .expect("Failed to parse"); - println!("{:?}", parse_return(Attributes::default(), &rt)); - } -} diff --git a/ops/op2/test_cases/sync/add.out b/ops/op2/test_cases/sync/add.out deleted file mode 100644 index a73f032aac..0000000000 --- a/ops/op2/test_cases/sync/add.out +++ /dev/null @@ -1,78 +0,0 @@ -#[allow(non_camel_case_types)] -struct op_add { - _unconstructable: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_add { - const NAME: &'static str = stringify!(op_add); - const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl { - name: stringify!(op_add), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: Some({ - use deno_core::v8::fast_api::Type; - use deno_core::v8::fast_api::CType; - deno_core::v8::fast_api::FastFunction::new( - &[Type::V8Value, Type::Uint32, Type::Uint32], - CType::Uint32, - Self::v8_fn_ptr_fast as *const ::std::ffi::c_void, - ) - }), - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 2usize as u8, - }; -} -impl op_add { - pub const fn name() -> &'static str { - stringify!(op_add) - } - pub const fn decl() -> deno_core::_ops::OpDecl { - deno_core::_ops::OpDecl { - name: stringify!(op_add), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: Some({ - use deno_core::v8::fast_api::Type; - use deno_core::v8::fast_api::CType; - deno_core::v8::fast_api::FastFunction::new( - &[Type::V8Value, Type::Uint32, Type::Uint32], - CType::Uint32, - Self::v8_fn_ptr_fast as *const ::std::ffi::c_void, - ) - }), - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 2usize as u8, - } - } - fn v8_fn_ptr_fast( - _: deno_core::v8::Local, - arg0: u32, - arg1: u32, - ) -> u32 { - let arg0 = arg0 as _; - let arg1 = arg1 as _; - let result = Self::call(arg0, arg1); - result - } - extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let mut rv = deno_core::v8::ReturnValue::from_function_callback_info(unsafe { - &*info - }); - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe { - &*info - }); - let arg0 = args.get(0usize as i32); - let arg0 = deno_core::_ops::to_u32(&arg0) as _; - let arg1 = args.get(1usize as i32); - let arg1 = deno_core::_ops::to_u32(&arg1) as _; - let result = Self::call(arg0, arg1); - rv.set_uint32(result as u32); - } - #[inline(always)] - fn call(a: u32, b: u32) -> u32 { - a + b - } -} diff --git a/ops/op2/test_cases/sync/add.rs b/ops/op2/test_cases/sync/add.rs deleted file mode 100644 index 74dbb18934..0000000000 --- a/ops/op2/test_cases/sync/add.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -#[op2(fast)] -fn op_add(a: u32, b: u32) -> u32 { - a + b -} diff --git a/ops/op2/test_cases/sync/add_options.out b/ops/op2/test_cases/sync/add_options.out deleted file mode 100644 index 9fada187f5..0000000000 --- a/ops/op2/test_cases/sync/add_options.out +++ /dev/null @@ -1,57 +0,0 @@ -#[allow(non_camel_case_types)] -pub struct op_test_add_option { - _unconstructable: ::std::marker::PhantomData<()>, -} -impl crate::_ops::Op for op_test_add_option { - const NAME: &'static str = stringify!(op_test_add_option); - const DECL: crate::_ops::OpDecl = crate::_ops::OpDecl { - name: stringify!(op_test_add_option), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: None, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 2usize as u8, - }; -} -impl op_test_add_option { - pub const fn name() -> &'static str { - stringify!(op_test_add_option) - } - pub const fn decl() -> crate::_ops::OpDecl { - crate::_ops::OpDecl { - name: stringify!(op_test_add_option), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: None, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 2usize as u8, - } - } - extern "C" fn v8_fn_ptr(info: *const crate::v8::FunctionCallbackInfo) { - let mut rv = crate::v8::ReturnValue::from_function_callback_info(unsafe { - &*info - }); - let args = crate::v8::FunctionCallbackArguments::from_function_callback_info(unsafe { - &*info - }); - let arg0 = args.get(0usize as i32); - let arg0 = crate::_ops::to_u32(&arg0) as _; - let arg1 = args.get(1usize as i32); - let arg1 = if arg1.is_null_or_undefined() { - None - } else { - let arg1 = crate::_ops::to_u32(&arg1) as _; - Some(arg1) - }; - let result = Self::call(arg0, arg1); - rv.set_uint32(result as u32); - } - #[inline(always)] - pub fn call(a: u32, b: Option) -> u32 { - a + b.unwrap_or(100) - } -} diff --git a/ops/op2/test_cases/sync/add_options.rs b/ops/op2/test_cases/sync/add_options.rs deleted file mode 100644 index a5f2c8f4a7..0000000000 --- a/ops/op2/test_cases/sync/add_options.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -#[op2(core)] -pub fn op_test_add_option(a: u32, b: Option) -> u32 { - a + b.unwrap_or(100) -} diff --git a/ops/op2/test_cases/sync/doc_comment.out b/ops/op2/test_cases/sync/doc_comment.out deleted file mode 100644 index d7e8005d95..0000000000 --- a/ops/op2/test_cases/sync/doc_comment.out +++ /dev/null @@ -1,59 +0,0 @@ -#[allow(non_camel_case_types)] -pub struct op_has_doc_comment { - _unconstructable: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_has_doc_comment { - const NAME: &'static str = stringify!(op_has_doc_comment); - const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl { - name: stringify!(op_has_doc_comment), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: Some({ - use deno_core::v8::fast_api::Type; - use deno_core::v8::fast_api::CType; - deno_core::v8::fast_api::FastFunction::new( - &[Type::V8Value], - CType::Void, - Self::v8_fn_ptr_fast as *const ::std::ffi::c_void, - ) - }), - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0usize as u8, - }; -} -impl op_has_doc_comment { - pub const fn name() -> &'static str { - stringify!(op_has_doc_comment) - } - pub const fn decl() -> deno_core::_ops::OpDecl { - deno_core::_ops::OpDecl { - name: stringify!(op_has_doc_comment), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: Some({ - use deno_core::v8::fast_api::Type; - use deno_core::v8::fast_api::CType; - deno_core::v8::fast_api::FastFunction::new( - &[Type::V8Value], - CType::Void, - Self::v8_fn_ptr_fast as *const ::std::ffi::c_void, - ) - }), - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0usize as u8, - } - } - fn v8_fn_ptr_fast(_: deno_core::v8::Local) -> () { - let result = Self::call(); - result - } - extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let result = Self::call(); - } - #[inline(always)] - pub fn call() -> () {} -} diff --git a/ops/op2/test_cases/sync/doc_comment.rs b/ops/op2/test_cases/sync/doc_comment.rs deleted file mode 100644 index b729a64bd7..0000000000 --- a/ops/op2/test_cases/sync/doc_comment.rs +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -/// This is a doc comment. -#[op2(fast)] -pub fn op_has_doc_comment() -> () {} diff --git a/ops/op2/test_cases/sync/generics.out b/ops/op2/test_cases/sync/generics.out deleted file mode 100644 index 26e3af9b7d..0000000000 --- a/ops/op2/test_cases/sync/generics.out +++ /dev/null @@ -1,59 +0,0 @@ -#[allow(non_camel_case_types)] -pub struct op_generics { - _unconstructable: ::std::marker::PhantomData<(T)>, -} -impl deno_core::_ops::Op for op_generics { - const NAME: &'static str = stringify!(op_generics); - const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl { - name: stringify!(op_generics), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: Some({ - use deno_core::v8::fast_api::Type; - use deno_core::v8::fast_api::CType; - deno_core::v8::fast_api::FastFunction::new( - &[Type::V8Value], - CType::Void, - Self::v8_fn_ptr_fast as *const ::std::ffi::c_void, - ) - }), - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0usize as u8, - }; -} -impl op_generics { - pub const fn name() -> &'static str { - stringify!(op_generics) - } - pub const fn decl() -> deno_core::_ops::OpDecl { - deno_core::_ops::OpDecl { - name: stringify!(op_generics), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: Some({ - use deno_core::v8::fast_api::Type; - use deno_core::v8::fast_api::CType; - deno_core::v8::fast_api::FastFunction::new( - &[Type::V8Value], - CType::Void, - Self::v8_fn_ptr_fast as *const ::std::ffi::c_void, - ) - }), - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0usize as u8, - } - } - fn v8_fn_ptr_fast(_: deno_core::v8::Local) -> () { - let result = Self::call(); - result - } - extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let result = Self::call(); - } - #[inline(always)] - pub fn call() {} -} diff --git a/ops/op2/test_cases/sync/generics.rs b/ops/op2/test_cases/sync/generics.rs deleted file mode 100644 index b412a7f934..0000000000 --- a/ops/op2/test_cases/sync/generics.rs +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -#[op2(fast)] -pub fn op_generics() {} diff --git a/ops/op2/test_cases/sync/result_primitive.out b/ops/op2/test_cases/sync/result_primitive.out deleted file mode 100644 index 4f296a893b..0000000000 --- a/ops/op2/test_cases/sync/result_primitive.out +++ /dev/null @@ -1,122 +0,0 @@ -#[allow(non_camel_case_types)] -pub struct op_u32_with_result { - _unconstructable: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_u32_with_result { - const NAME: &'static str = stringify!(op_u32_with_result); - const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl { - name: stringify!(op_u32_with_result), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: Some({ - use deno_core::v8::fast_api::Type; - use deno_core::v8::fast_api::CType; - deno_core::v8::fast_api::FastFunction::new( - &[Type::V8Value, Type::CallbackOptions], - CType::Uint32, - Self::v8_fn_ptr_fast as *const ::std::ffi::c_void, - ) - }), - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0usize as u8, - }; -} -impl op_u32_with_result { - pub const fn name() -> &'static str { - stringify!(op_u32_with_result) - } - pub const fn decl() -> deno_core::_ops::OpDecl { - deno_core::_ops::OpDecl { - name: stringify!(op_u32_with_result), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: Some({ - use deno_core::v8::fast_api::Type; - use deno_core::v8::fast_api::CType; - deno_core::v8::fast_api::FastFunction::new( - &[Type::V8Value, Type::CallbackOptions], - CType::Uint32, - Self::v8_fn_ptr_fast as *const ::std::ffi::c_void, - ) - }), - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0usize as u8, - } - } - fn v8_fn_ptr_fast( - _: deno_core::v8::Local, - fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions, - ) -> u32 { - let fast_api_callback_options = unsafe { &mut *fast_api_callback_options }; - let opctx = unsafe { - &*(deno_core::v8::Local::< - v8::External, - >::cast(unsafe { fast_api_callback_options.data.data }) - .value() as *const deno_core::_ops::OpCtx) - }; - let result = Self::call(); - let result = match result { - Ok(result) => result, - Err(err) => { - unsafe { - opctx.unsafely_set_last_error_for_ops_only(err); - } - fast_api_callback_options.fallback = true; - return ::std::default::Default::default(); - } - }; - result - } - extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let mut rv = deno_core::v8::ReturnValue::from_function_callback_info(unsafe { - &*info - }); - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe { - &*info - }); - let opctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - if let Some(err) = unsafe { opctx.unsafely_take_last_error_for_ops_only() } { - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(&*info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe { - &*info - }); - let opstate = ::std::cell::RefCell::borrow(&*opctx.state); - let exception = deno_core::error::to_v8_error( - scope, - opstate.get_error_class_fn, - &err, - ); - scope.throw_exception(exception); - return; - } - let result = Self::call(); - match result { - Ok(result) => { - rv.set_uint32(result as u32); - } - Err(err) => { - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(&*info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe { - &*info - }); - let opstate = ::std::cell::RefCell::borrow(&*opctx.state); - let exception = deno_core::error::to_v8_error( - scope, - opstate.get_error_class_fn, - &err, - ); - scope.throw_exception(exception); - return; - } - }; - } - #[inline(always)] - pub fn call() -> Result {} -} diff --git a/ops/op2/test_cases/sync/result_primitive.rs b/ops/op2/test_cases/sync/result_primitive.rs deleted file mode 100644 index df89c2432f..0000000000 --- a/ops/op2/test_cases/sync/result_primitive.rs +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -#[op2(fast)] -pub fn op_u32_with_result() -> Result {} diff --git a/ops/op2/test_cases/sync/result_void.out b/ops/op2/test_cases/sync/result_void.out deleted file mode 100644 index 74aa84a8d8..0000000000 --- a/ops/op2/test_cases/sync/result_void.out +++ /dev/null @@ -1,117 +0,0 @@ -#[allow(non_camel_case_types)] -pub struct op_void_with_result { - _unconstructable: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_void_with_result { - const NAME: &'static str = stringify!(op_void_with_result); - const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl { - name: stringify!(op_void_with_result), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: Some({ - use deno_core::v8::fast_api::Type; - use deno_core::v8::fast_api::CType; - deno_core::v8::fast_api::FastFunction::new( - &[Type::V8Value, Type::CallbackOptions], - CType::Void, - Self::v8_fn_ptr_fast as *const ::std::ffi::c_void, - ) - }), - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0usize as u8, - }; -} -impl op_void_with_result { - pub const fn name() -> &'static str { - stringify!(op_void_with_result) - } - pub const fn decl() -> deno_core::_ops::OpDecl { - deno_core::_ops::OpDecl { - name: stringify!(op_void_with_result), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: Some({ - use deno_core::v8::fast_api::Type; - use deno_core::v8::fast_api::CType; - deno_core::v8::fast_api::FastFunction::new( - &[Type::V8Value, Type::CallbackOptions], - CType::Void, - Self::v8_fn_ptr_fast as *const ::std::ffi::c_void, - ) - }), - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0usize as u8, - } - } - fn v8_fn_ptr_fast( - _: deno_core::v8::Local, - fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions, - ) -> () { - let fast_api_callback_options = unsafe { &mut *fast_api_callback_options }; - let opctx = unsafe { - &*(deno_core::v8::Local::< - v8::External, - >::cast(unsafe { fast_api_callback_options.data.data }) - .value() as *const deno_core::_ops::OpCtx) - }; - let result = Self::call(); - let result = match result { - Ok(result) => result, - Err(err) => { - unsafe { - opctx.unsafely_set_last_error_for_ops_only(err); - } - fast_api_callback_options.fallback = true; - return ::std::default::Default::default(); - } - }; - result - } - extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe { - &*info - }); - let opctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - if let Some(err) = unsafe { opctx.unsafely_take_last_error_for_ops_only() } { - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(&*info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe { - &*info - }); - let opstate = ::std::cell::RefCell::borrow(&*opctx.state); - let exception = deno_core::error::to_v8_error( - scope, - opstate.get_error_class_fn, - &err, - ); - scope.throw_exception(exception); - return; - } - let result = Self::call(); - match result { - Ok(result) => {} - Err(err) => { - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(&*info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe { - &*info - }); - let opstate = ::std::cell::RefCell::borrow(&*opctx.state); - let exception = deno_core::error::to_v8_error( - scope, - opstate.get_error_class_fn, - &err, - ); - scope.throw_exception(exception); - return; - } - }; - } - #[inline(always)] - pub fn call() -> Result<(), AnyError> {} -} diff --git a/ops/op2/test_cases/sync/result_void.rs b/ops/op2/test_cases/sync/result_void.rs deleted file mode 100644 index ef3aa7b321..0000000000 --- a/ops/op2/test_cases/sync/result_void.rs +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -#[op2(fast)] -pub fn op_void_with_result() -> Result<(), AnyError> {} diff --git a/ops/op2/test_cases/sync/smi.out b/ops/op2/test_cases/sync/smi.out deleted file mode 100644 index 24b81ae477..0000000000 --- a/ops/op2/test_cases/sync/smi.out +++ /dev/null @@ -1,76 +0,0 @@ -#[allow(non_camel_case_types)] -struct op_add { - _unconstructable: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_add { - const NAME: &'static str = stringify!(op_add); - const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl { - name: stringify!(op_add), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: Some({ - use deno_core::v8::fast_api::Type; - use deno_core::v8::fast_api::CType; - deno_core::v8::fast_api::FastFunction::new( - &[Type::V8Value, Type::Int32, Type::Uint32], - CType::Uint32, - Self::v8_fn_ptr_fast as *const ::std::ffi::c_void, - ) - }), - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 2usize as u8, - }; -} -impl op_add { - pub const fn name() -> &'static str { - stringify!(op_add) - } - pub const fn decl() -> deno_core::_ops::OpDecl { - deno_core::_ops::OpDecl { - name: stringify!(op_add), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: Some({ - use deno_core::v8::fast_api::Type; - use deno_core::v8::fast_api::CType; - deno_core::v8::fast_api::FastFunction::new( - &[Type::V8Value, Type::Int32, Type::Uint32], - CType::Uint32, - Self::v8_fn_ptr_fast as *const ::std::ffi::c_void, - ) - }), - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 2usize as u8, - } - } - fn v8_fn_ptr_fast( - _: deno_core::v8::Local, - arg0: i32, - arg1: u32, - ) -> u32 { - let arg0 = arg0 as _; - let arg1 = arg1 as _; - let result = Self::call(arg0, arg1); - result - } - extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let mut rv = deno_core::v8::ReturnValue::from_function_callback_info(unsafe { - &*info - }); - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe { - &*info - }); - let arg0 = args.get(0usize as i32); - let arg0 = deno_core::_ops::to_i32(&arg0) as _; - let arg1 = args.get(1usize as i32); - let arg1 = deno_core::_ops::to_u32(&arg1) as _; - let result = Self::call(arg0, arg1); - rv.set_uint32(result as u32); - } - #[inline(always)] - fn call(id: ResourceId, extra: u16) -> u32 {} -} diff --git a/ops/op2/test_cases/sync/smi.rs b/ops/op2/test_cases/sync/smi.rs deleted file mode 100644 index a5a441845f..0000000000 --- a/ops/op2/test_cases/sync/smi.rs +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -#[op2(fast)] -fn op_add(#[smi] id: ResourceId, extra: u16) -> u32 {} diff --git a/ops/op2/test_cases/sync/string_cow.out b/ops/op2/test_cases/sync/string_cow.out deleted file mode 100644 index 7d388e5989..0000000000 --- a/ops/op2/test_cases/sync/string_cow.out +++ /dev/null @@ -1,75 +0,0 @@ -#[allow(non_camel_case_types)] -struct op_string_cow { - _unconstructable: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_string_cow { - const NAME: &'static str = stringify!(op_string_cow); - const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl { - name: stringify!(op_string_cow), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: Some({ - use deno_core::v8::fast_api::Type; - use deno_core::v8::fast_api::CType; - deno_core::v8::fast_api::FastFunction::new( - &[Type::V8Value, Type::SeqOneByteString], - CType::Uint32, - Self::v8_fn_ptr_fast as *const ::std::ffi::c_void, - ) - }), - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 1usize as u8, - }; -} -impl op_string_cow { - pub const fn name() -> &'static str { - stringify!(op_string_cow) - } - pub const fn decl() -> deno_core::_ops::OpDecl { - deno_core::_ops::OpDecl { - name: stringify!(op_string_cow), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: Some({ - use deno_core::v8::fast_api::Type; - use deno_core::v8::fast_api::CType; - deno_core::v8::fast_api::FastFunction::new( - &[Type::V8Value, Type::SeqOneByteString], - CType::Uint32, - Self::v8_fn_ptr_fast as *const ::std::ffi::c_void, - ) - }), - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 1usize as u8, - } - } - fn v8_fn_ptr_fast( - _: deno_core::v8::Local, - arg0: *mut deno_core::v8::fast_api::FastApiOneByteString, - ) -> u32 { - let mut arg0_temp: [::std::mem::MaybeUninit; 1024] = [::std::mem::MaybeUninit::uninit(); 1024]; - let arg0 = deno_core::_ops::to_str_ptr(unsafe { &mut *arg0 }, &mut arg0_temp); - let result = Self::call(arg0); - result - } - extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(&*info) }; - let mut rv = deno_core::v8::ReturnValue::from_function_callback_info(unsafe { - &*info - }); - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe { - &*info - }); - let arg0 = args.get(0usize as i32); - let mut arg0_temp: [::std::mem::MaybeUninit; 1024] = [::std::mem::MaybeUninit::uninit(); 1024]; - let arg0 = deno_core::_ops::to_str(scope, &arg0, &mut arg0_temp); - let result = Self::call(arg0); - rv.set_uint32(result as u32); - } - #[inline(always)] - fn call(s: Cow) -> u32 {} -} diff --git a/ops/op2/test_cases/sync/string_cow.rs b/ops/op2/test_cases/sync/string_cow.rs deleted file mode 100644 index ed4dfca824..0000000000 --- a/ops/op2/test_cases/sync/string_cow.rs +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -#[op2(fast)] -fn op_string_cow(#[string] s: Cow) -> u32 {} diff --git a/ops/op2/test_cases/sync/string_option_return.out b/ops/op2/test_cases/sync/string_option_return.out deleted file mode 100644 index 6143ac2172..0000000000 --- a/ops/op2/test_cases/sync/string_option_return.out +++ /dev/null @@ -1,53 +0,0 @@ -#[allow(non_camel_case_types)] -pub struct op_string_return { - _unconstructable: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_string_return { - const NAME: &'static str = stringify!(op_string_return); - const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl { - name: stringify!(op_string_return), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: None, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0usize as u8, - }; -} -impl op_string_return { - pub const fn name() -> &'static str { - stringify!(op_string_return) - } - pub const fn decl() -> deno_core::_ops::OpDecl { - deno_core::_ops::OpDecl { - name: stringify!(op_string_return), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: None, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0usize as u8, - } - } - extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(&*info) }; - let mut rv = deno_core::v8::ReturnValue::from_function_callback_info(unsafe { - &*info - }); - let result = Self::call(); - if let Some(result) = result { - if result.is_empty() { - rv.set_empty_string(); - } else { - let temp = deno_core::v8::String::new(scope, &result).unwrap(); - rv.set(temp.into()); - } - } else { - rv.set_null(); - } - } - #[inline(always)] - pub fn call() -> Option {} -} diff --git a/ops/op2/test_cases/sync/string_option_return.rs b/ops/op2/test_cases/sync/string_option_return.rs deleted file mode 100644 index 932836d2f9..0000000000 --- a/ops/op2/test_cases/sync/string_option_return.rs +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -#[op2] -#[string] -pub fn op_string_return() -> Option {} diff --git a/ops/op2/test_cases/sync/string_owned.out b/ops/op2/test_cases/sync/string_owned.out deleted file mode 100644 index 7418a311c3..0000000000 --- a/ops/op2/test_cases/sync/string_owned.out +++ /dev/null @@ -1,73 +0,0 @@ -#[allow(non_camel_case_types)] -struct op_string_owned { - _unconstructable: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_string_owned { - const NAME: &'static str = stringify!(op_string_owned); - const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl { - name: stringify!(op_string_owned), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: Some({ - use deno_core::v8::fast_api::Type; - use deno_core::v8::fast_api::CType; - deno_core::v8::fast_api::FastFunction::new( - &[Type::V8Value, Type::SeqOneByteString], - CType::Uint32, - Self::v8_fn_ptr_fast as *const ::std::ffi::c_void, - ) - }), - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 1usize as u8, - }; -} -impl op_string_owned { - pub const fn name() -> &'static str { - stringify!(op_string_owned) - } - pub const fn decl() -> deno_core::_ops::OpDecl { - deno_core::_ops::OpDecl { - name: stringify!(op_string_owned), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: Some({ - use deno_core::v8::fast_api::Type; - use deno_core::v8::fast_api::CType; - deno_core::v8::fast_api::FastFunction::new( - &[Type::V8Value, Type::SeqOneByteString], - CType::Uint32, - Self::v8_fn_ptr_fast as *const ::std::ffi::c_void, - ) - }), - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 1usize as u8, - } - } - fn v8_fn_ptr_fast( - _: deno_core::v8::Local, - arg0: *mut deno_core::v8::fast_api::FastApiOneByteString, - ) -> u32 { - let arg0 = deno_core::_ops::to_string_ptr(unsafe { &mut *arg0 }); - let result = Self::call(arg0); - result - } - extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(&*info) }; - let mut rv = deno_core::v8::ReturnValue::from_function_callback_info(unsafe { - &*info - }); - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe { - &*info - }); - let arg0 = args.get(0usize as i32); - let arg0 = arg0.to_rust_string_lossy(scope); - let result = Self::call(arg0); - rv.set_uint32(result as u32); - } - #[inline(always)] - fn call(s: String) -> u32 {} -} diff --git a/ops/op2/test_cases/sync/string_owned.rs b/ops/op2/test_cases/sync/string_owned.rs deleted file mode 100644 index b81d7ece98..0000000000 --- a/ops/op2/test_cases/sync/string_owned.rs +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -#[op2(fast)] -fn op_string_owned(#[string] s: String) -> u32 {} diff --git a/ops/op2/test_cases/sync/string_ref.out b/ops/op2/test_cases/sync/string_ref.out deleted file mode 100644 index 1b853fccc0..0000000000 --- a/ops/op2/test_cases/sync/string_ref.out +++ /dev/null @@ -1,75 +0,0 @@ -#[allow(non_camel_case_types)] -struct op_string_owned { - _unconstructable: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_string_owned { - const NAME: &'static str = stringify!(op_string_owned); - const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl { - name: stringify!(op_string_owned), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: Some({ - use deno_core::v8::fast_api::Type; - use deno_core::v8::fast_api::CType; - deno_core::v8::fast_api::FastFunction::new( - &[Type::V8Value, Type::SeqOneByteString], - CType::Uint32, - Self::v8_fn_ptr_fast as *const ::std::ffi::c_void, - ) - }), - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 1usize as u8, - }; -} -impl op_string_owned { - pub const fn name() -> &'static str { - stringify!(op_string_owned) - } - pub const fn decl() -> deno_core::_ops::OpDecl { - deno_core::_ops::OpDecl { - name: stringify!(op_string_owned), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: Some({ - use deno_core::v8::fast_api::Type; - use deno_core::v8::fast_api::CType; - deno_core::v8::fast_api::FastFunction::new( - &[Type::V8Value, Type::SeqOneByteString], - CType::Uint32, - Self::v8_fn_ptr_fast as *const ::std::ffi::c_void, - ) - }), - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 1usize as u8, - } - } - fn v8_fn_ptr_fast( - _: deno_core::v8::Local, - arg0: *mut deno_core::v8::fast_api::FastApiOneByteString, - ) -> u32 { - let mut arg0_temp: [::std::mem::MaybeUninit; 1024] = [::std::mem::MaybeUninit::uninit(); 1024]; - let arg0 = &deno_core::_ops::to_str_ptr(unsafe { &mut *arg0 }, &mut arg0_temp); - let result = Self::call(arg0); - result - } - extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(&*info) }; - let mut rv = deno_core::v8::ReturnValue::from_function_callback_info(unsafe { - &*info - }); - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe { - &*info - }); - let arg0 = args.get(0usize as i32); - let mut arg0_temp: [::std::mem::MaybeUninit; 1024] = [::std::mem::MaybeUninit::uninit(); 1024]; - let arg0 = &deno_core::_ops::to_str(scope, &arg0, &mut arg0_temp); - let result = Self::call(arg0); - rv.set_uint32(result as u32); - } - #[inline(always)] - fn call(s: &str) -> u32 {} -} diff --git a/ops/op2/test_cases/sync/string_ref.rs b/ops/op2/test_cases/sync/string_ref.rs deleted file mode 100644 index a7efa9f0cc..0000000000 --- a/ops/op2/test_cases/sync/string_ref.rs +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -#[op2(fast)] -fn op_string_owned(#[string] s: &str) -> u32 {} diff --git a/ops/op2/test_cases/sync/string_return.out b/ops/op2/test_cases/sync/string_return.out deleted file mode 100644 index 5e68b93141..0000000000 --- a/ops/op2/test_cases/sync/string_return.out +++ /dev/null @@ -1,49 +0,0 @@ -#[allow(non_camel_case_types)] -pub struct op_string_return { - _unconstructable: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_string_return { - const NAME: &'static str = stringify!(op_string_return); - const DECL: deno_core::_ops::OpDecl = deno_core::_ops::OpDecl { - name: stringify!(op_string_return), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: None, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0usize as u8, - }; -} -impl op_string_return { - pub const fn name() -> &'static str { - stringify!(op_string_return) - } - pub const fn decl() -> deno_core::_ops::OpDecl { - deno_core::_ops::OpDecl { - name: stringify!(op_string_return), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: None, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0usize as u8, - } - } - extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(&*info) }; - let mut rv = deno_core::v8::ReturnValue::from_function_callback_info(unsafe { - &*info - }); - let result = Self::call(); - if result.is_empty() { - rv.set_empty_string(); - } else { - let temp = deno_core::v8::String::new(scope, &result).unwrap(); - rv.set(temp.into()); - } - } - #[inline(always)] - pub fn call() -> String {} -} diff --git a/ops/op2/test_cases/sync/string_return.rs b/ops/op2/test_cases/sync/string_return.rs deleted file mode 100644 index 667b68a14f..0000000000 --- a/ops/op2/test_cases/sync/string_return.rs +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -#[op2] -#[string] -pub fn op_string_return() -> String {} diff --git a/ops/optimizer.rs b/ops/optimizer.rs deleted file mode 100644 index 28911162fa..0000000000 --- a/ops/optimizer.rs +++ /dev/null @@ -1,1004 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -//! Optimizer for #[op] - -use std::collections::BTreeMap; -use std::fmt::Debug; -use std::fmt::Formatter; - -use pmutil::q; -use pmutil::Quote; -use proc_macro2::TokenStream; - -use syn::parse_quote; -use syn::punctuated::Punctuated; -use syn::token::Colon2; -use syn::AngleBracketedGenericArguments; -use syn::FnArg; -use syn::GenericArgument; -use syn::PatType; -use syn::Path; -use syn::PathArguments; -use syn::PathSegment; -use syn::ReturnType; -use syn::Signature; -use syn::Type; -use syn::TypePath; -use syn::TypePtr; -use syn::TypeReference; -use syn::TypeSlice; -use syn::TypeTuple; - -use crate::Op; - -#[derive(Debug)] -pub(crate) enum BailoutReason { - // Recoverable errors - MustBeSingleSegment, - FastUnsupportedParamType, -} - -#[derive(Debug, PartialEq)] -enum StringType { - Cow, - Ref, - Owned, -} - -#[derive(Debug, PartialEq)] -enum TransformKind { - // serde_v8::Value - V8Value, - SliceU32(bool), - SliceU8(bool), - SliceF64(bool), - SeqOneByteString(StringType), - PtrU8, - PtrVoid, - WasmMemory, -} - -impl Transform { - fn serde_v8_value(index: usize) -> Self { - Transform { - kind: TransformKind::V8Value, - index, - } - } - - fn slice_u32(index: usize, is_mut: bool) -> Self { - Transform { - kind: TransformKind::SliceU32(is_mut), - index, - } - } - - fn slice_u8(index: usize, is_mut: bool) -> Self { - Transform { - kind: TransformKind::SliceU8(is_mut), - index, - } - } - - fn slice_f64(index: usize, is_mut: bool) -> Self { - Transform { - kind: TransformKind::SliceF64(is_mut), - index, - } - } - - fn seq_one_byte_string(index: usize, is_ref: StringType) -> Self { - Transform { - kind: TransformKind::SeqOneByteString(is_ref), - index, - } - } - - fn wasm_memory(index: usize) -> Self { - Transform { - kind: TransformKind::WasmMemory, - index, - } - } - - fn u8_ptr(index: usize) -> Self { - Transform { - kind: TransformKind::PtrU8, - index, - } - } - - fn void_ptr(index: usize) -> Self { - Transform { - kind: TransformKind::PtrVoid, - index, - } - } -} - -#[derive(Debug, PartialEq)] -pub(crate) struct Transform { - kind: TransformKind, - index: usize, -} - -impl Transform { - pub(crate) fn apply_for_fast_call( - &self, - core: &TokenStream, - input: &mut FnArg, - ) -> Quote { - let (ty, ident) = match input { - FnArg::Typed(PatType { - ref mut ty, - ref pat, - .. - }) => { - let ident = match &**pat { - syn::Pat::Ident(ident) => &ident.ident, - _ => unreachable!("error not recovered"), - }; - (ty, ident) - } - _ => unreachable!("error not recovered"), - }; - - match &self.kind { - // serde_v8::Value - TransformKind::V8Value => { - *ty = parse_quote! { #core::v8::Local<#core::v8::Value> }; - - q!(Vars { var: &ident }, { - let var = serde_v8::Value { v8_value: var }; - }) - } - // &[u32] - TransformKind::SliceU32(_) => { - *ty = - parse_quote! { *const #core::v8::fast_api::FastApiTypedArray }; - - q!(Vars { var: &ident }, { - // V8 guarantees that ArrayBuffers are always 4-byte aligned - // (seems to be always 8-byte aligned on 64-bit machines) - // but Deno FFI makes it possible to create ArrayBuffers at any - // alignment. Thus this check is needed. - let var = match unsafe { &*var }.get_storage_if_aligned() { - Some(v) => v, - None => { - unsafe { &mut *fast_api_callback_options }.fallback = true; - return Default::default(); - } - }; - }) - } - // &[u8] - TransformKind::SliceU8(_) => { - *ty = - parse_quote! { *const #core::v8::fast_api::FastApiTypedArray }; - - q!(Vars { var: &ident }, { - // SAFETY: U8 slice is always byte-aligned. - let var = - unsafe { (&*var).get_storage_if_aligned().unwrap_unchecked() }; - }) - } - TransformKind::SliceF64(_) => { - *ty = - parse_quote! { *const #core::v8::fast_api::FastApiTypedArray }; - - q!(Vars { var: &ident }, { - let var = match unsafe { &*var }.get_storage_if_aligned() { - Some(v) => v, - None => { - unsafe { &mut *fast_api_callback_options }.fallback = true; - return Default::default(); - } - }; - }) - } - // &str - TransformKind::SeqOneByteString(str_ty) => { - *ty = parse_quote! { *const #core::v8::fast_api::FastApiOneByteString }; - match str_ty { - StringType::Ref => q!(Vars { var: &ident }, { - let var = match ::std::str::from_utf8(unsafe { &*var }.as_bytes()) { - Ok(v) => v, - Err(_) => { - unsafe { &mut *fast_api_callback_options }.fallback = true; - return Default::default(); - } - }; - }), - StringType::Cow => q!(Vars { var: &ident }, { - let var = ::std::borrow::Cow::Borrowed( - match ::std::str::from_utf8(unsafe { &*var }.as_bytes()) { - Ok(v) => v, - Err(_) => { - unsafe { &mut *fast_api_callback_options }.fallback = true; - return Default::default(); - } - }, - ); - }), - StringType::Owned => q!(Vars { var: &ident }, { - let var = match ::std::str::from_utf8(unsafe { &*var }.as_bytes()) { - Ok(v) => v.to_owned(), - Err(_) => { - unsafe { &mut *fast_api_callback_options }.fallback = true; - return Default::default(); - } - }; - }), - } - } - TransformKind::WasmMemory => { - // Note: `ty` is correctly set to __opts by the fast call tier. - // U8 slice is always byte-aligned. - q!(Vars { var: &ident, core }, { - let var = unsafe { - &*(__opts.wasm_memory - as *const core::v8::fast_api::FastApiTypedArray) - } - .get_storage_if_aligned(); - }) - } - // *const u8 - TransformKind::PtrU8 => { - *ty = - parse_quote! { *const #core::v8::fast_api::FastApiTypedArray }; - - q!(Vars { var: &ident }, { - // SAFETY: U8 slice is always byte-aligned. - let var = - unsafe { (&*var).get_storage_if_aligned().unwrap_unchecked() } - .as_ptr(); - }) - } - TransformKind::PtrVoid => { - *ty = parse_quote! { *mut ::std::ffi::c_void }; - - q!(Vars {}, {}) - } - } - } -} - -fn get_fast_scalar(s: &str) -> Option { - match s { - "bool" => Some(FastValue::Bool), - "u32" => Some(FastValue::U32), - "i32" => Some(FastValue::I32), - "u64" | "usize" => Some(FastValue::U64), - "i64" | "isize" => Some(FastValue::I64), - "f32" => Some(FastValue::F32), - "f64" => Some(FastValue::F64), - "* const c_void" | "* mut c_void" => Some(FastValue::Pointer), - "ResourceId" => Some(FastValue::U32), - _ => None, - } -} - -fn can_return_fast(v: &FastValue) -> bool { - !matches!( - v, - FastValue::U64 - | FastValue::I64 - | FastValue::Uint8Array - | FastValue::Uint32Array - ) -} - -#[derive(Debug, PartialEq, Clone)] -pub(crate) enum FastValue { - Void, - Bool, - U32, - I32, - U64, - I64, - F32, - F64, - Pointer, - V8Value, - Uint8Array, - Uint32Array, - Float64Array, - SeqOneByteString, -} - -impl FastValue { - pub fn default_value(&self) -> Quote { - match self { - FastValue::Pointer => q!({ ::std::ptr::null_mut() }), - FastValue::Void => q!({}), - _ => q!({ Default::default() }), - } - } -} - -impl Default for FastValue { - fn default() -> Self { - Self::Void - } -} - -#[derive(Default, PartialEq)] -pub(crate) struct Optimizer { - pub(crate) returns_result: bool, - - pub(crate) has_ref_opstate: bool, - - pub(crate) has_rc_opstate: bool, - - // Do we need an explicit FastApiCallbackOptions argument? - pub(crate) has_fast_callback_option: bool, - // Do we depend on FastApiCallbackOptions? - pub(crate) needs_fast_callback_option: bool, - - pub(crate) has_wasm_memory: bool, - - pub(crate) fast_result: Option, - pub(crate) fast_parameters: Vec, - - pub(crate) transforms: BTreeMap, - pub(crate) fast_compatible: bool, - - pub(crate) is_async: bool, -} - -impl Debug for Optimizer { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - writeln!(f, "=== Optimizer Dump ===")?; - writeln!(f, "returns_result: {}", self.returns_result)?; - writeln!(f, "has_ref_opstate: {}", self.has_ref_opstate)?; - writeln!(f, "has_rc_opstate: {}", self.has_rc_opstate)?; - writeln!( - f, - "has_fast_callback_option: {}", - self.has_fast_callback_option - )?; - writeln!( - f, - "needs_fast_callback_option: {}", - self.needs_fast_callback_option - )?; - writeln!(f, "fast_result: {:?}", self.fast_result)?; - writeln!(f, "fast_parameters: {:?}", self.fast_parameters)?; - writeln!(f, "transforms: {:?}", self.transforms)?; - writeln!(f, "is_async: {}", self.is_async)?; - writeln!(f, "fast_compatible: {}", self.fast_compatible)?; - Ok(()) - } -} - -impl Optimizer { - pub(crate) fn new() -> Self { - Default::default() - } - - pub(crate) const fn has_opstate_in_parameters(&self) -> bool { - self.has_ref_opstate || self.has_rc_opstate - } - - pub(crate) const fn needs_opstate(&self) -> bool { - self.has_ref_opstate || self.has_rc_opstate || self.returns_result - } - - pub(crate) fn analyze(&mut self, op: &mut Op) -> Result<(), BailoutReason> { - // Fast async ops are opt-in as they have a lazy polling behavior. - if op.is_async && !op.attrs.must_be_fast { - self.fast_compatible = false; - return Ok(()); - } - - if op.attrs.is_v8 { - self.fast_compatible = false; - return Ok(()); - } - - self.is_async = op.is_async; - self.fast_compatible = true; - // Just assume for now. We will validate later. - self.has_wasm_memory = op.attrs.is_wasm; - - let sig = &op.item.sig; - - // Analyze return type - match &sig { - Signature { - output: ReturnType::Default, - .. - } => self.fast_result = Some(FastValue::default()), - Signature { - output: ReturnType::Type(_, ty), - .. - } if !self.is_async => self.analyze_return_type(ty)?, - - // No need to error on the return type for async ops, its OK if - // it's not a fast value. - Signature { - output: ReturnType::Type(_, ty), - .. - } => { - let _ = self.analyze_return_type(ty); - // Recover. - self.fast_result = None; - self.fast_compatible = true; - } - }; - - // The receiver, which we don't actually care about. - self.fast_parameters.push(FastValue::V8Value); - - if self.is_async { - // The promise ID. - self.fast_parameters.push(FastValue::I32); - } - - // Analyze parameters - for (index, param) in sig.inputs.iter().enumerate() { - self.analyze_param_type(index, param)?; - } - - // TODO(@littledivy): https://github.com/denoland/deno/issues/17159 - if self.returns_result - && self.fast_parameters.contains(&FastValue::SeqOneByteString) - { - self.fast_compatible = false; - } - - Ok(()) - } - - fn analyze_return_type(&mut self, ty: &Type) -> Result<(), BailoutReason> { - match ty { - Type::Tuple(TypeTuple { elems, .. }) if elems.is_empty() => { - self.fast_result = Some(FastValue::Void); - } - Type::Path(TypePath { - path: Path { segments, .. }, - .. - }) => { - let segment = single_segment(segments)?; - - match segment { - // Result - PathSegment { - ident, arguments, .. - } if ident == "Result" => { - self.returns_result = true; - - if let PathArguments::AngleBracketed( - AngleBracketedGenericArguments { args, .. }, - ) = arguments - { - match args.first() { - Some(GenericArgument::Type(Type::Path(TypePath { - path: Path { segments, .. }, - .. - }))) => { - let PathSegment { ident, .. } = single_segment(segments)?; - // Is `T` a scalar FastValue? - if let Some(val) = get_fast_scalar(ident.to_string().as_str()) - { - if can_return_fast(&val) { - self.fast_result = Some(val); - return Ok(()); - } - } - - self.fast_compatible = false; - return Err(BailoutReason::FastUnsupportedParamType); - } - Some(GenericArgument::Type(Type::Tuple(TypeTuple { - elems, - .. - }))) - if elems.is_empty() => - { - self.fast_result = Some(FastValue::Void); - } - Some(GenericArgument::Type(Type::Ptr(TypePtr { - mutability: Some(_), - elem, - .. - }))) => { - match &**elem { - Type::Path(TypePath { - path: Path { segments, .. }, - .. - }) => { - // Is `T` a c_void? - let segment = single_segment(segments)?; - match segment { - PathSegment { ident, .. } if ident == "c_void" => { - self.fast_result = Some(FastValue::Pointer); - return Ok(()); - } - _ => { - return Err(BailoutReason::FastUnsupportedParamType) - } - } - } - _ => return Err(BailoutReason::FastUnsupportedParamType), - } - } - _ => return Err(BailoutReason::FastUnsupportedParamType), - } - } - } - // Is `T` a scalar FastValue? - PathSegment { ident, .. } => { - if let Some(val) = get_fast_scalar(ident.to_string().as_str()) { - self.fast_result = Some(val); - return Ok(()); - } - - self.fast_compatible = false; - return Err(BailoutReason::FastUnsupportedParamType); - } - }; - } - Type::Ptr(TypePtr { - mutability: Some(_), - elem, - .. - }) => { - match &**elem { - Type::Path(TypePath { - path: Path { segments, .. }, - .. - }) => { - // Is `T` a c_void? - let segment = single_segment(segments)?; - match segment { - PathSegment { ident, .. } if ident == "c_void" => { - self.fast_result = Some(FastValue::Pointer); - return Ok(()); - } - _ => return Err(BailoutReason::FastUnsupportedParamType), - } - } - _ => return Err(BailoutReason::FastUnsupportedParamType), - } - } - _ => return Err(BailoutReason::FastUnsupportedParamType), - }; - - Ok(()) - } - - fn analyze_param_type( - &mut self, - index: usize, - arg: &FnArg, - ) -> Result<(), BailoutReason> { - match arg { - FnArg::Typed(typed) => match &*typed.ty { - Type::Path(TypePath { - path: Path { segments, .. }, - .. - }) if segments.len() == 2 => { - match double_segment(segments)? { - // -> serde_v8::Value - [PathSegment { ident: first, .. }, PathSegment { ident: last, .. }] - if first == "serde_v8" && last == "Value" => - { - self.fast_parameters.push(FastValue::V8Value); - assert!(self - .transforms - .insert(index, Transform::serde_v8_value(index)) - .is_none()); - } - _ => return Err(BailoutReason::FastUnsupportedParamType), - } - } - Type::Path(TypePath { - path: Path { segments, .. }, - .. - }) => { - let segment = single_segment(segments)?; - - match segment { - // -> Option - PathSegment { - ident, arguments, .. - } if ident == "Option" => { - if let PathArguments::AngleBracketed( - AngleBracketedGenericArguments { args, .. }, - ) = arguments - { - // -> Option<&mut T> - if let Some(GenericArgument::Type(Type::Reference( - TypeReference { elem, .. }, - ))) = args.last() - { - if self.has_wasm_memory { - // -> Option<&mut [u8]> - if let Type::Slice(TypeSlice { elem, .. }) = &**elem { - if let Type::Path(TypePath { - path: Path { segments, .. }, - .. - }) = &**elem - { - let segment = single_segment(segments)?; - - match segment { - // Is `T` a u8? - PathSegment { ident, .. } if ident == "u8" => { - assert!(self - .transforms - .insert(index, Transform::wasm_memory(index)) - .is_none()); - } - _ => { - return Err(BailoutReason::FastUnsupportedParamType) - } - } - } - } - } else if let Type::Path(TypePath { - path: Path { segments, .. }, - .. - }) = &**elem - { - let segment = single_segment(segments)?; - match segment { - // Is `T` a FastApiCallbackOptions? - PathSegment { ident, .. } - if ident == "FastApiCallbackOptions" => - { - self.has_fast_callback_option = true; - } - _ => return Err(BailoutReason::FastUnsupportedParamType), - } - } else { - return Err(BailoutReason::FastUnsupportedParamType); - } - } else { - return Err(BailoutReason::FastUnsupportedParamType); - } - } - } - // -> Rc - PathSegment { - ident, arguments, .. - } if ident == "Rc" => { - if let PathArguments::AngleBracketed( - AngleBracketedGenericArguments { args, .. }, - ) = arguments - { - match args.last() { - Some(GenericArgument::Type(Type::Path(TypePath { - path: Path { segments, .. }, - .. - }))) => { - let segment = single_segment(segments)?; - match segment { - // -> Rc> - PathSegment { - ident, arguments, .. - } if ident == "RefCell" => { - if let PathArguments::AngleBracketed( - AngleBracketedGenericArguments { args, .. }, - ) = arguments - { - match args.last() { - // -> Rc> - Some(GenericArgument::Type(Type::Path( - TypePath { - path: Path { segments, .. }, - .. - }, - ))) => { - let segment = single_segment(segments)?; - match segment { - PathSegment { ident, .. } - if ident == "OpState" => - { - self.has_rc_opstate = true; - } - _ => { - return Err( - BailoutReason::FastUnsupportedParamType, - ) - } - } - } - _ => { - return Err( - BailoutReason::FastUnsupportedParamType, - ) - } - } - } - } - _ => return Err(BailoutReason::FastUnsupportedParamType), - } - } - _ => return Err(BailoutReason::FastUnsupportedParamType), - } - } - } - // Cow<'_, str> - PathSegment { - ident, arguments, .. - } if ident == "Cow" => { - if let PathArguments::AngleBracketed( - AngleBracketedGenericArguments { args, .. }, - ) = arguments - { - assert_eq!(args.len(), 2); - - let ty = &args[1]; - match ty { - GenericArgument::Type(Type::Path(TypePath { - path: Path { segments, .. }, - .. - })) => { - let segment = single_segment(segments)?; - match segment { - PathSegment { ident, .. } if ident == "str" => { - self.needs_fast_callback_option = true; - self.fast_parameters.push(FastValue::SeqOneByteString); - assert!(self - .transforms - .insert( - index, - Transform::seq_one_byte_string( - index, - StringType::Cow - ) - ) - .is_none()); - } - _ => return Err(BailoutReason::FastUnsupportedParamType), - } - } - _ => return Err(BailoutReason::FastUnsupportedParamType), - } - } - } - // Is `T` a fast scalar? - PathSegment { ident, .. } => { - if let Some(val) = get_fast_scalar(ident.to_string().as_str()) { - self.fast_parameters.push(val); - } else if ident == "String" { - self.needs_fast_callback_option = true; - // Is `T` an owned String? - self.fast_parameters.push(FastValue::SeqOneByteString); - assert!(self - .transforms - .insert( - index, - Transform::seq_one_byte_string(index, StringType::Owned) - ) - .is_none()); - } else { - return Err(BailoutReason::FastUnsupportedParamType); - } - } - }; - } - // &mut T - Type::Reference(TypeReference { - elem, mutability, .. - }) => match &**elem { - Type::Path(TypePath { - path: Path { segments, .. }, - .. - }) => { - let segment = single_segment(segments)?; - match segment { - // Is `T` a OpState? - PathSegment { ident, .. } - if ident == "OpState" && !self.is_async => - { - self.has_ref_opstate = true; - } - // Is `T` a str? - PathSegment { ident, .. } if ident == "str" => { - self.needs_fast_callback_option = true; - self.fast_parameters.push(FastValue::SeqOneByteString); - assert!(self - .transforms - .insert( - index, - Transform::seq_one_byte_string(index, StringType::Ref) - ) - .is_none()); - } - _ => return Err(BailoutReason::FastUnsupportedParamType), - } - } - // &mut [T] - Type::Slice(TypeSlice { elem, .. }) => match &**elem { - Type::Path(TypePath { - path: Path { segments, .. }, - .. - }) => { - let segment = single_segment(segments)?; - let is_mut_ref = mutability.is_some(); - match segment { - // Is `T` a u8? - PathSegment { ident, .. } if ident == "u8" => { - self.fast_parameters.push(FastValue::Uint8Array); - assert!(self - .transforms - .insert(index, Transform::slice_u8(index, is_mut_ref)) - .is_none()); - } - // Is `T` a u32? - PathSegment { ident, .. } if ident == "u32" => { - self.needs_fast_callback_option = true; - self.fast_parameters.push(FastValue::Uint32Array); - assert!(self - .transforms - .insert(index, Transform::slice_u32(index, is_mut_ref)) - .is_none()); - } - // Is `T` a f64? - PathSegment { ident, .. } if ident == "f64" => { - self.needs_fast_callback_option = true; - self.fast_parameters.push(FastValue::Float64Array); - assert!(self - .transforms - .insert(index, Transform::slice_f64(index, is_mut_ref)) - .is_none()); - } - _ => return Err(BailoutReason::FastUnsupportedParamType), - } - } - _ => return Err(BailoutReason::FastUnsupportedParamType), - }, - _ => return Err(BailoutReason::FastUnsupportedParamType), - }, - // *const T - Type::Ptr(TypePtr { - elem, - const_token: Some(_), - .. - }) => match &**elem { - Type::Path(TypePath { - path: Path { segments, .. }, - .. - }) => { - let segment = single_segment(segments)?; - match segment { - // Is `T` a u8? - PathSegment { ident, .. } if ident == "u8" => { - self.fast_parameters.push(FastValue::Uint8Array); - assert!(self - .transforms - .insert(index, Transform::u8_ptr(index)) - .is_none()); - } - _ => return Err(BailoutReason::FastUnsupportedParamType), - } - } - _ => return Err(BailoutReason::FastUnsupportedParamType), - }, - // *const T - Type::Ptr(TypePtr { - elem, - mutability: Some(_), - .. - }) => match &**elem { - Type::Path(TypePath { - path: Path { segments, .. }, - .. - }) => { - let segment = single_segment(segments)?; - match segment { - // Is `T` a c_void? - PathSegment { ident, .. } if ident == "c_void" => { - self.fast_parameters.push(FastValue::Pointer); - assert!(self - .transforms - .insert(index, Transform::void_ptr(index)) - .is_none()); - } - _ => return Err(BailoutReason::FastUnsupportedParamType), - } - } - _ => return Err(BailoutReason::FastUnsupportedParamType), - }, - _ => return Err(BailoutReason::FastUnsupportedParamType), - }, - _ => return Err(BailoutReason::FastUnsupportedParamType), - }; - Ok(()) - } -} - -fn single_segment( - segments: &Punctuated, -) -> Result<&PathSegment, BailoutReason> { - if segments.len() != 1 { - return Err(BailoutReason::MustBeSingleSegment); - } - - match segments.last() { - Some(segment) => Ok(segment), - None => Err(BailoutReason::MustBeSingleSegment), - } -} - -fn double_segment( - segments: &Punctuated, -) -> Result<[&PathSegment; 2], BailoutReason> { - match (segments.first(), segments.last()) { - (Some(first), Some(last)) => Ok([first, last]), - // Caller ensures that there are only two segments. - _ => unreachable!(), - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::Attributes; - use crate::Op; - use pretty_assertions::assert_eq; - use std::path::PathBuf; - use syn::parse_quote; - - #[test] - fn test_single_segment() { - let segments = parse_quote!(foo); - assert!(single_segment(&segments).is_ok()); - - let segments = parse_quote!(foo::bar); - assert!(single_segment(&segments).is_err()); - } - - #[test] - fn test_double_segment() { - let segments = parse_quote!(foo::bar); - assert!(double_segment(&segments).is_ok()); - assert_eq!(double_segment(&segments).unwrap()[0].ident, "foo"); - assert_eq!(double_segment(&segments).unwrap()[1].ident, "bar"); - } - - #[testing_macros::fixture("optimizer_tests/**/*.rs")] - fn test_analyzer(input: PathBuf) { - let update_expected = std::env::var("UPDATE_EXPECTED").is_ok(); - - let source = - std::fs::read_to_string(&input).expect("Failed to read test file"); - let expected = std::fs::read_to_string(input.with_extension("expected")) - .expect("Failed to read expected file"); - - let mut attrs = Attributes::default(); - if source.contains("// @test-attr:wasm") { - attrs.must_be_fast = true; - attrs.is_wasm = true; - } - if source.contains("// @test-attr:fast") { - attrs.must_be_fast = true; - } - - let item = syn::parse_str(&source).expect("Failed to parse test file"); - let mut op = Op::new(item, attrs); - let mut optimizer = Optimizer::new(); - if let Err(e) = optimizer.analyze(&mut op) { - let e_str = format!("{e:?}"); - if update_expected { - std::fs::write(input.with_extension("expected"), e_str) - .expect("Failed to write expected file"); - } else { - assert_eq!(e_str, expected); - } - return; - } - - if update_expected { - std::fs::write( - input.with_extension("expected"), - format!("{optimizer:#?}"), - ) - .expect("Failed to write expected file"); - } else { - assert_eq!(format!("{optimizer:#?}"), expected); - } - } -} diff --git a/ops/optimizer_tests/async_nop.expected b/ops/optimizer_tests/async_nop.expected deleted file mode 100644 index 42a1180bd8..0000000000 --- a/ops/optimizer_tests/async_nop.expected +++ /dev/null @@ -1,11 +0,0 @@ -=== Optimizer Dump === -returns_result: false -has_ref_opstate: false -has_rc_opstate: false -has_fast_callback_option: false -needs_fast_callback_option: false -fast_result: Some(Void) -fast_parameters: [V8Value, I32] -transforms: {} -is_async: true -fast_compatible: true diff --git a/ops/optimizer_tests/async_nop.out b/ops/optimizer_tests/async_nop.out deleted file mode 100644 index 85c55f2f4f..0000000000 --- a/ops/optimizer_tests/async_nop.out +++ /dev/null @@ -1,137 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `op_void_async::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct op_void_async { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_void_async { - const NAME: &'static str = stringify!(op_void_async); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, Int32, CallbackOptions], - CType::Void, - Self::op_void_async_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: true, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl op_void_async { - pub const fn name() -> &'static str { - stringify!(op_void_async) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, Int32, CallbackOptions], - CType::Void, - Self::op_void_async_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: true, - is_unstable: false, - is_v8: false, - arg_count: 1usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - async fn call<'scope>() {} - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - use deno_core::futures::FutureExt; - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - let promise_id = args.get(0); - let promise_id = deno_core::v8::Local::< - deno_core::v8::Integer, - >::try_from(promise_id) - .map(|l| l.value() as deno_core::PromiseId) - .map_err(deno_core::anyhow::Error::from); - let promise_id: deno_core::PromiseId = match promise_id { - Ok(promise_id) => promise_id, - Err(err) => { - deno_core::_ops::throw_type_error( - scope, - format!("invalid promise id: {}", err), - ); - return; - } - }; - let fut = deno_core::_ops::map_async_op2(ctx, Self::call()); - let maybe_response = deno_core::_ops::queue_async_op( - ctx, - scope, - false, - promise_id, - fut, - ); - if let Some(response) = maybe_response { - rv.set(response); - } - } -} -impl op_void_async { - #[allow(clippy::too_many_arguments)] - fn op_void_async_fast_fn( - _: deno_core::v8::Local, - __promise_id: i32, - fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions, - ) -> () { - use deno_core::v8; - use deno_core::_ops; - let __opts: &mut v8::fast_api::FastApiCallbackOptions = unsafe { - &mut *fast_api_callback_options - }; - let __ctx = unsafe { - &*(v8::Local::::cast(unsafe { __opts.data.data }).value() - as *const _ops::OpCtx) - }; - let op_state = __ctx.state.clone(); - let result = Self::call(); - let result = _ops::queue_fast_async_op( - __ctx, - __promise_id, - async move { Ok(result.await) }, - ); - result - } -} diff --git a/ops/optimizer_tests/async_nop.rs b/ops/optimizer_tests/async_nop.rs deleted file mode 100644 index 95a1522f1a..0000000000 --- a/ops/optimizer_tests/async_nop.rs +++ /dev/null @@ -1,3 +0,0 @@ -async fn op_void_async() { - // @test-attr:fast -} diff --git a/ops/optimizer_tests/async_result.expected b/ops/optimizer_tests/async_result.expected deleted file mode 100644 index 87d46977d8..0000000000 --- a/ops/optimizer_tests/async_result.expected +++ /dev/null @@ -1,11 +0,0 @@ -=== Optimizer Dump === -returns_result: true -has_ref_opstate: false -has_rc_opstate: true -has_fast_callback_option: false -needs_fast_callback_option: false -fast_result: None -fast_parameters: [V8Value, I32, U32] -transforms: {} -is_async: true -fast_compatible: true diff --git a/ops/optimizer_tests/async_result.out b/ops/optimizer_tests/async_result.out deleted file mode 100644 index 91117e4a1b..0000000000 --- a/ops/optimizer_tests/async_result.out +++ /dev/null @@ -1,150 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `op_async_result::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct op_async_result { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_async_result { - const NAME: &'static str = stringify!(op_async_result); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, Int32, Uint32, CallbackOptions], - CType::Void, - Self::op_async_result_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: true, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl op_async_result { - pub const fn name() -> &'static str { - stringify!(op_async_result) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, Int32, Uint32, CallbackOptions], - CType::Void, - Self::op_async_result_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: true, - is_unstable: false, - is_v8: false, - arg_count: 2usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - async fn call<'scope>( - state: Rc>, - rid: ResourceId, - ) -> Result {} - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - use deno_core::futures::FutureExt; - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - let promise_id = args.get(0); - let promise_id = deno_core::v8::Local::< - deno_core::v8::Integer, - >::try_from(promise_id) - .map(|l| l.value() as deno_core::PromiseId) - .map_err(deno_core::anyhow::Error::from); - let promise_id: deno_core::PromiseId = match promise_id { - Ok(promise_id) => promise_id, - Err(err) => { - deno_core::_ops::throw_type_error( - scope, - format!("invalid promise id: {}", err), - ); - return; - } - }; - let arg_0 = args.get(1usize as i32); - let arg_0 = match deno_core::serde_v8::from_v8(scope, arg_0) { - Ok(v) => v, - Err(err) => { - let msg = format!( - "Error parsing args at position {}: {}", 1usize, - deno_core::anyhow::Error::from(err) - ); - return deno_core::_ops::throw_type_error(scope, msg); - } - }; - let fut = deno_core::_ops::map_async_op1( - ctx, - Self::call(ctx.state.clone(), arg_0), - ); - let maybe_response = deno_core::_ops::queue_async_op( - ctx, - scope, - false, - promise_id, - fut, - ); - if let Some(response) = maybe_response { - rv.set(response); - } - } -} -impl op_async_result { - #[allow(clippy::too_many_arguments)] - fn op_async_result_fast_fn( - _: deno_core::v8::Local, - __promise_id: i32, - rid: ResourceId, - fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions, - ) -> () { - use deno_core::v8; - use deno_core::_ops; - let __opts: &mut v8::fast_api::FastApiCallbackOptions = unsafe { - &mut *fast_api_callback_options - }; - let __ctx = unsafe { - &*(v8::Local::::cast(unsafe { __opts.data.data }).value() - as *const _ops::OpCtx) - }; - let state = __ctx.state.clone(); - let result = Self::call(state, rid); - let result = _ops::queue_fast_async_op(__ctx, __promise_id, result); - } -} diff --git a/ops/optimizer_tests/async_result.rs b/ops/optimizer_tests/async_result.rs deleted file mode 100644 index 54ac20d3cc..0000000000 --- a/ops/optimizer_tests/async_result.rs +++ /dev/null @@ -1,6 +0,0 @@ -async fn op_async_result( - state: Rc>, - rid: ResourceId, -) -> Result { - // @test-attr:fast -} diff --git a/ops/optimizer_tests/callback_options.expected b/ops/optimizer_tests/callback_options.expected deleted file mode 100644 index 245fdfd55d..0000000000 --- a/ops/optimizer_tests/callback_options.expected +++ /dev/null @@ -1,11 +0,0 @@ -=== Optimizer Dump === -returns_result: false -has_ref_opstate: false -has_rc_opstate: false -has_fast_callback_option: true -needs_fast_callback_option: false -fast_result: Some(Void) -fast_parameters: [V8Value] -transforms: {} -is_async: false -fast_compatible: true diff --git a/ops/optimizer_tests/callback_options.out b/ops/optimizer_tests/callback_options.out deleted file mode 100644 index a868d63932..0000000000 --- a/ops/optimizer_tests/callback_options.out +++ /dev/null @@ -1,106 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `op_fallback::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct op_fallback { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_fallback { - const NAME: &'static str = stringify!(op_fallback); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, CallbackOptions], - CType::Void, - Self::op_fallback_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl op_fallback { - pub const fn name() -> &'static str { - stringify!(op_fallback) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, CallbackOptions], - CType::Void, - Self::op_fallback_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 1usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - fn call<'scope>(options: Option<&mut FastApiCallbackOptions>) { - if let Some(options) = options { - options.fallback = true; - } - } - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - let arg_0 = None; - let result = Self::call(arg_0); - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - } -} -impl op_fallback { - #[allow(clippy::too_many_arguments)] - fn op_fallback_fast_fn( - _: deno_core::v8::Local, - fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions, - ) -> () { - use deno_core::v8; - use deno_core::_ops; - let __opts: &mut v8::fast_api::FastApiCallbackOptions = unsafe { - &mut *fast_api_callback_options - }; - let result = Self::call(options); - result - } -} diff --git a/ops/optimizer_tests/callback_options.rs b/ops/optimizer_tests/callback_options.rs deleted file mode 100644 index c210171d21..0000000000 --- a/ops/optimizer_tests/callback_options.rs +++ /dev/null @@ -1,5 +0,0 @@ -fn op_fallback(options: Option<&mut FastApiCallbackOptions>) { - if let Some(options) = options { - options.fallback = true; - } -} diff --git a/ops/optimizer_tests/cow_str.expected b/ops/optimizer_tests/cow_str.expected deleted file mode 100644 index 9db8cfaf31..0000000000 --- a/ops/optimizer_tests/cow_str.expected +++ /dev/null @@ -1,11 +0,0 @@ -=== Optimizer Dump === -returns_result: false -has_ref_opstate: false -has_rc_opstate: false -has_fast_callback_option: false -needs_fast_callback_option: true -fast_result: Some(Void) -fast_parameters: [V8Value, SeqOneByteString] -transforms: {0: Transform { kind: SeqOneByteString(Cow), index: 0 }} -is_async: false -fast_compatible: true diff --git a/ops/optimizer_tests/cow_str.out b/ops/optimizer_tests/cow_str.out deleted file mode 100644 index 15c92f3468..0000000000 --- a/ops/optimizer_tests/cow_str.out +++ /dev/null @@ -1,121 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `op_cow_str::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct op_cow_str { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_cow_str { - const NAME: &'static str = stringify!(op_cow_str); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, SeqOneByteString, CallbackOptions], - CType::Void, - Self::op_cow_str_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl op_cow_str { - pub const fn name() -> &'static str { - stringify!(op_cow_str) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, SeqOneByteString, CallbackOptions], - CType::Void, - Self::op_cow_str_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 1usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - fn call<'scope>(c: Cow<'_, str>) {} - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - let arg_0 = match deno_core::v8::Local::< - deno_core::v8::String, - >::try_from(args.get(0usize as i32)) { - Ok(v8_string) => { - ::std::borrow::Cow::Owned(deno_core::serde_v8::to_utf8(v8_string, scope)) - } - Err(_) => { - return deno_core::_ops::throw_type_error( - scope, - format!("Expected string at position {}", 0usize), - ); - } - }; - let result = Self::call(arg_0); - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - } -} -impl op_cow_str { - #[allow(clippy::too_many_arguments)] - fn op_cow_str_fast_fn( - _: deno_core::v8::Local, - c: *const deno_core::v8::fast_api::FastApiOneByteString, - fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions, - ) -> () { - use deno_core::v8; - use deno_core::_ops; - let c = ::std::borrow::Cow::Borrowed( - match ::std::str::from_utf8(unsafe { &*c }.as_bytes()) { - Ok(v) => v, - Err(_) => { - unsafe { &mut *fast_api_callback_options }.fallback = true; - return Default::default(); - } - }, - ); - let result = Self::call(c); - result - } -} diff --git a/ops/optimizer_tests/cow_str.rs b/ops/optimizer_tests/cow_str.rs deleted file mode 100644 index b7214bdc7e..0000000000 --- a/ops/optimizer_tests/cow_str.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn op_cow_str(c: Cow<'_, str>) { - // ... -} diff --git a/ops/optimizer_tests/f64_slice.expected b/ops/optimizer_tests/f64_slice.expected deleted file mode 100644 index 32182b004a..0000000000 --- a/ops/optimizer_tests/f64_slice.expected +++ /dev/null @@ -1,11 +0,0 @@ -=== Optimizer Dump === -returns_result: false -has_ref_opstate: false -has_rc_opstate: false -has_fast_callback_option: false -needs_fast_callback_option: true -fast_result: Some(Void) -fast_parameters: [V8Value, Float64Array] -transforms: {0: Transform { kind: SliceF64(true), index: 0 }} -is_async: false -fast_compatible: true diff --git a/ops/optimizer_tests/f64_slice.out b/ops/optimizer_tests/f64_slice.out deleted file mode 100644 index 1836777310..0000000000 --- a/ops/optimizer_tests/f64_slice.out +++ /dev/null @@ -1,137 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `op_f64_buf::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct op_f64_buf { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_f64_buf { - const NAME: &'static str = stringify!(op_f64_buf); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, TypedArray(CType::Float64), CallbackOptions], - CType::Void, - Self::op_f64_buf_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl op_f64_buf { - pub const fn name() -> &'static str { - stringify!(op_f64_buf) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, TypedArray(CType::Float64), CallbackOptions], - CType::Void, - Self::op_f64_buf_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 1usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - fn call<'scope>(buffer: &mut [f64]) {} - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - let arg_0 = if let Ok(view) - = deno_core::v8::Local::< - deno_core::v8::Float64Array, - >::try_from(args.get(0usize as i32)) { - let (offset, len) = (view.byte_offset(), view.byte_length()); - let buffer = match view.buffer(scope) { - Some(v) => v, - None => { - return deno_core::_ops::throw_type_error( - scope, - format!("Expected Float64Array at position {}", 0usize), - ); - } - }; - if let Some(data) = buffer.data() { - let store = data.cast::().as_ptr(); - unsafe { - ::std::slice::from_raw_parts_mut( - store.add(offset) as *mut f64, - len / 8, - ) - } - } else { - &mut [] - } - } else { - return deno_core::_ops::throw_type_error( - scope, - format!("Expected Float64Array at position {}", 0usize), - ); - }; - let result = Self::call(arg_0); - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - } -} -impl op_f64_buf { - #[allow(clippy::too_many_arguments)] - fn op_f64_buf_fast_fn( - _: deno_core::v8::Local, - buffer: *const deno_core::v8::fast_api::FastApiTypedArray, - fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions, - ) -> () { - use deno_core::v8; - use deno_core::_ops; - let buffer = match unsafe { &*buffer }.get_storage_if_aligned() { - Some(v) => v, - None => { - unsafe { &mut *fast_api_callback_options }.fallback = true; - return Default::default(); - } - }; - let result = Self::call(buffer); - result - } -} diff --git a/ops/optimizer_tests/f64_slice.rs b/ops/optimizer_tests/f64_slice.rs deleted file mode 100644 index fa2778531e..0000000000 --- a/ops/optimizer_tests/f64_slice.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn op_f64_buf(buffer: &mut [f64]) { - // @test-attr:fast -} diff --git a/ops/optimizer_tests/incompatible_1.expected b/ops/optimizer_tests/incompatible_1.expected deleted file mode 100644 index 250ff1022d..0000000000 --- a/ops/optimizer_tests/incompatible_1.expected +++ /dev/null @@ -1 +0,0 @@ -FastUnsupportedParamType \ No newline at end of file diff --git a/ops/optimizer_tests/incompatible_1.out b/ops/optimizer_tests/incompatible_1.out deleted file mode 100644 index d51a1eda84..0000000000 --- a/ops/optimizer_tests/incompatible_1.out +++ /dev/null @@ -1,93 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `op_sync_serialize_object_with_numbers_as_keys::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct op_sync_serialize_object_with_numbers_as_keys { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_sync_serialize_object_with_numbers_as_keys { - const NAME: &'static str = stringify!(op_sync_serialize_object_with_numbers_as_keys); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: None, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl op_sync_serialize_object_with_numbers_as_keys { - pub const fn name() -> &'static str { - stringify!(op_sync_serialize_object_with_numbers_as_keys) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: None, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 1usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - fn call<'scope>(value: serde_json::Value) -> Result<(), Error> { - assert_eq!( - value.to_string(), r#"{"lines":{"100":{"unit":"m"},"200":{"unit":"cm"}}}"# - ); - Ok(()) - } - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - let arg_0 = args.get(0usize as i32); - let arg_0 = match deno_core::serde_v8::from_v8(scope, arg_0) { - Ok(v) => v, - Err(err) => { - let msg = format!( - "Error parsing args at position {}: {}", 0usize, - deno_core::anyhow::Error::from(err) - ); - return deno_core::_ops::throw_type_error(scope, msg); - } - }; - let result = Self::call(arg_0); - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - match result { - Ok(result) => {} - Err(err) => { - let exception = deno_core::error::to_v8_error( - scope, - op_state.get_error_class_fn, - &err, - ); - scope.throw_exception(exception); - } - }; - } -} diff --git a/ops/optimizer_tests/incompatible_1.rs b/ops/optimizer_tests/incompatible_1.rs deleted file mode 100644 index 326189aa19..0000000000 --- a/ops/optimizer_tests/incompatible_1.rs +++ /dev/null @@ -1,9 +0,0 @@ -fn op_sync_serialize_object_with_numbers_as_keys( - value: serde_json::Value, -) -> Result<(), Error> { - assert_eq!( - value.to_string(), - r#"{"lines":{"100":{"unit":"m"},"200":{"unit":"cm"}}}"# - ); - Ok(()) -} diff --git a/ops/optimizer_tests/issue16934.expected b/ops/optimizer_tests/issue16934.expected deleted file mode 100644 index 6b75ff5bf3..0000000000 --- a/ops/optimizer_tests/issue16934.expected +++ /dev/null @@ -1,11 +0,0 @@ -=== Optimizer Dump === -returns_result: false -has_ref_opstate: false -has_rc_opstate: false -has_fast_callback_option: false -needs_fast_callback_option: false -fast_result: None -fast_parameters: [] -transforms: {} -is_async: false -fast_compatible: false diff --git a/ops/optimizer_tests/issue16934.out b/ops/optimizer_tests/issue16934.out deleted file mode 100644 index 0f85b11577..0000000000 --- a/ops/optimizer_tests/issue16934.out +++ /dev/null @@ -1,115 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `send_stdin::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct send_stdin { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for send_stdin { - const NAME: &'static str = stringify!(send_stdin); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: None, - is_async: true, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl send_stdin { - pub const fn name() -> &'static str { - stringify!(send_stdin) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: None, - is_async: true, - is_unstable: false, - is_v8: false, - arg_count: 2usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - async fn call<'scope>( - state: &mut OpState, - cmd: String, - ) -> Result<(), anyhow::Error> { - let instance = state.borrow::().clone(); - instance.send_command(&cmd, CausedBy::Unknown).await?; - Ok(()) - } - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - use deno_core::futures::FutureExt; - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - let promise_id = args.get(0); - let promise_id = deno_core::v8::Local::< - deno_core::v8::Integer, - >::try_from(promise_id) - .map(|l| l.value() as deno_core::PromiseId) - .map_err(deno_core::anyhow::Error::from); - let promise_id: deno_core::PromiseId = match promise_id { - Ok(promise_id) => promise_id, - Err(err) => { - deno_core::_ops::throw_type_error( - scope, - format!("invalid promise id: {}", err), - ); - return; - } - }; - let arg_0 = match deno_core::v8::Local::< - deno_core::v8::String, - >::try_from(args.get(1usize as i32)) { - Ok(v8_string) => deno_core::serde_v8::to_utf8(v8_string, scope), - Err(_) => { - return deno_core::_ops::throw_type_error( - scope, - format!("Expected string at position {}", 1usize), - ); - } - }; - let fut = deno_core::_ops::map_async_op1( - ctx, - Self::call( - compile_error!("mutable opstate is not supported in async ops"), - arg_0, - ), - ); - let maybe_response = deno_core::_ops::queue_async_op( - ctx, - scope, - false, - promise_id, - fut, - ); - if let Some(response) = maybe_response { - rv.set(response); - } - } -} diff --git a/ops/optimizer_tests/issue16934.rs b/ops/optimizer_tests/issue16934.rs deleted file mode 100644 index 1e77f12812..0000000000 --- a/ops/optimizer_tests/issue16934.rs +++ /dev/null @@ -1,11 +0,0 @@ -async fn send_stdin( - state: &mut OpState, - cmd: String, -) -> Result<(), anyhow::Error> { - // https://github.com/denoland/deno/issues/16934 - // - // OpState borrowed across await point is not allowed, as it will likely panic at runtime. - let instance = state.borrow::().clone(); - instance.send_command(&cmd, CausedBy::Unknown).await?; - Ok(()) -} diff --git a/ops/optimizer_tests/issue16934_fast.expected b/ops/optimizer_tests/issue16934_fast.expected deleted file mode 100644 index 250ff1022d..0000000000 --- a/ops/optimizer_tests/issue16934_fast.expected +++ /dev/null @@ -1 +0,0 @@ -FastUnsupportedParamType \ No newline at end of file diff --git a/ops/optimizer_tests/issue16934_fast.out b/ops/optimizer_tests/issue16934_fast.out deleted file mode 100644 index 2bbbc02bb7..0000000000 --- a/ops/optimizer_tests/issue16934_fast.out +++ /dev/null @@ -1,110 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `send_stdin::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct send_stdin { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for send_stdin { - const NAME: &'static str = stringify!(send_stdin); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: None, - is_async: true, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl send_stdin { - pub const fn name() -> &'static str { - stringify!(send_stdin) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: None, - is_async: true, - is_unstable: false, - is_v8: false, - arg_count: 2usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - async fn call<'scope>(state: &mut OpState, v: i32) -> Result<(), anyhow::Error> { - Ok(()) - } - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - use deno_core::futures::FutureExt; - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - let promise_id = args.get(0); - let promise_id = deno_core::v8::Local::< - deno_core::v8::Integer, - >::try_from(promise_id) - .map(|l| l.value() as deno_core::PromiseId) - .map_err(deno_core::anyhow::Error::from); - let promise_id: deno_core::PromiseId = match promise_id { - Ok(promise_id) => promise_id, - Err(err) => { - deno_core::_ops::throw_type_error( - scope, - format!("invalid promise id: {}", err), - ); - return; - } - }; - let arg_0 = args.get(1usize as i32); - let arg_0 = match deno_core::serde_v8::from_v8(scope, arg_0) { - Ok(v) => v, - Err(err) => { - let msg = format!( - "Error parsing args at position {}: {}", 1usize, - deno_core::anyhow::Error::from(err) - ); - return deno_core::_ops::throw_type_error(scope, msg); - } - }; - let fut = deno_core::_ops::map_async_op1( - ctx, - Self::call( - compile_error!("mutable opstate is not supported in async ops"), - arg_0, - ), - ); - let maybe_response = deno_core::_ops::queue_async_op( - ctx, - scope, - false, - promise_id, - fut, - ); - if let Some(response) = maybe_response { - rv.set(response); - } - } -} diff --git a/ops/optimizer_tests/issue16934_fast.rs b/ops/optimizer_tests/issue16934_fast.rs deleted file mode 100644 index 8d3488e9d0..0000000000 --- a/ops/optimizer_tests/issue16934_fast.rs +++ /dev/null @@ -1,8 +0,0 @@ -async fn send_stdin(state: &mut OpState, v: i32) -> Result<(), anyhow::Error> { - // @test-attr:fast - // - // https://github.com/denoland/deno/issues/16934 - // - // OpState borrowed across await point is not allowed, as it will likely panic at runtime. - Ok(()) -} diff --git a/ops/optimizer_tests/op_blob_revoke_object_url.expected b/ops/optimizer_tests/op_blob_revoke_object_url.expected deleted file mode 100644 index a412eceb80..0000000000 --- a/ops/optimizer_tests/op_blob_revoke_object_url.expected +++ /dev/null @@ -1,11 +0,0 @@ -=== Optimizer Dump === -returns_result: true -has_ref_opstate: true -has_rc_opstate: false -has_fast_callback_option: false -needs_fast_callback_option: true -fast_result: Some(Void) -fast_parameters: [V8Value, SeqOneByteString] -transforms: {1: Transform { kind: SeqOneByteString(Owned), index: 1 }} -is_async: false -fast_compatible: false diff --git a/ops/optimizer_tests/op_blob_revoke_object_url.out b/ops/optimizer_tests/op_blob_revoke_object_url.out deleted file mode 100644 index 85c6460e9b..0000000000 --- a/ops/optimizer_tests/op_blob_revoke_object_url.out +++ /dev/null @@ -1,93 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `op_blob_revoke_object_url::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct op_blob_revoke_object_url { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_blob_revoke_object_url { - const NAME: &'static str = stringify!(op_blob_revoke_object_url); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: None, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl op_blob_revoke_object_url { - pub const fn name() -> &'static str { - stringify!(op_blob_revoke_object_url) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: None, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 1usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - pub fn call<'scope>(state: &mut OpState, url: String) -> Result<(), AnyError> { - let url = Url::parse(&url)?; - let blob_store = state.borrow::>(); - blob_store.remove_object_url(&url); - Ok(()) - } - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - let arg_0 = match deno_core::v8::Local::< - deno_core::v8::String, - >::try_from(args.get(0usize as i32)) { - Ok(v8_string) => deno_core::serde_v8::to_utf8(v8_string, scope), - Err(_) => { - return deno_core::_ops::throw_type_error( - scope, - format!("Expected string at position {}", 0usize), - ); - } - }; - let result = Self::call(&mut std::cell::RefCell::borrow_mut(&ctx.state), arg_0); - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - match result { - Ok(result) => {} - Err(err) => { - let exception = deno_core::error::to_v8_error( - scope, - op_state.get_error_class_fn, - &err, - ); - scope.throw_exception(exception); - } - }; - } -} diff --git a/ops/optimizer_tests/op_blob_revoke_object_url.rs b/ops/optimizer_tests/op_blob_revoke_object_url.rs deleted file mode 100644 index 9e82d7b378..0000000000 --- a/ops/optimizer_tests/op_blob_revoke_object_url.rs +++ /dev/null @@ -1,10 +0,0 @@ -pub fn op_blob_revoke_object_url( - state: &mut OpState, - url: String, -) -> Result<(), AnyError> { - // TODO(@littledivy): fast compatible https://github.com/denoland/deno/issues/17159 - let url = Url::parse(&url)?; - let blob_store = state.borrow::>(); - blob_store.remove_object_url(&url); - Ok(()) -} diff --git a/ops/optimizer_tests/op_ffi_ptr_value.expected b/ops/optimizer_tests/op_ffi_ptr_value.expected deleted file mode 100644 index 00a28591c0..0000000000 --- a/ops/optimizer_tests/op_ffi_ptr_value.expected +++ /dev/null @@ -1,11 +0,0 @@ -=== Optimizer Dump === -returns_result: false -has_ref_opstate: false -has_rc_opstate: false -has_fast_callback_option: false -needs_fast_callback_option: true -fast_result: Some(Void) -fast_parameters: [V8Value, Pointer, Uint32Array] -transforms: {0: Transform { kind: PtrVoid, index: 0 }, 1: Transform { kind: SliceU32(true), index: 1 }} -is_async: false -fast_compatible: true diff --git a/ops/optimizer_tests/op_ffi_ptr_value.out b/ops/optimizer_tests/op_ffi_ptr_value.out deleted file mode 100644 index 8fcce53c69..0000000000 --- a/ops/optimizer_tests/op_ffi_ptr_value.out +++ /dev/null @@ -1,152 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `op_ffi_ptr_value::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct op_ffi_ptr_value { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_ffi_ptr_value { - const NAME: &'static str = stringify!(op_ffi_ptr_value); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, Pointer, TypedArray(CType::Uint32), CallbackOptions], - CType::Void, - Self::op_ffi_ptr_value_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl op_ffi_ptr_value { - pub const fn name() -> &'static str { - stringify!(op_ffi_ptr_value) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, Pointer, TypedArray(CType::Uint32), CallbackOptions], - CType::Void, - Self::op_ffi_ptr_value_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 2usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - pub fn call<'scope>(ptr: *mut c_void, out: &mut [u32]) {} - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - let arg_0 = { - let value = args.get(0usize as i32); - if value.is_null() { - std::ptr::null_mut() - } else if let Ok(b) - = deno_core::v8::Local::::try_from(value) { - b.value() - } else { - return deno_core::_ops::throw_type_error( - scope, - format!("Expected External at position {}", 0usize), - ); - } - }; - let arg_1 = if let Ok(view) - = deno_core::v8::Local::< - deno_core::v8::Uint32Array, - >::try_from(args.get(1usize as i32)) { - let (offset, len) = (view.byte_offset(), view.byte_length()); - let buffer = match view.buffer(scope) { - Some(v) => v, - None => { - return deno_core::_ops::throw_type_error( - scope, - format!("Expected Uint32Array at position {}", 1usize), - ); - } - }; - if let Some(data) = buffer.data() { - let store = data.cast::().as_ptr(); - unsafe { - ::std::slice::from_raw_parts_mut( - store.add(offset) as *mut u32, - len / 4, - ) - } - } else { - &mut [] - } - } else { - return deno_core::_ops::throw_type_error( - scope, - format!("Expected Uint32Array at position {}", 1usize), - ); - }; - let result = Self::call(arg_0, arg_1); - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - } -} -impl op_ffi_ptr_value { - #[allow(clippy::too_many_arguments)] - fn op_ffi_ptr_value_fast_fn( - _: deno_core::v8::Local, - ptr: *mut ::std::ffi::c_void, - out: *const deno_core::v8::fast_api::FastApiTypedArray, - fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions, - ) -> () { - use deno_core::v8; - use deno_core::_ops; - let out = match unsafe { &*out }.get_storage_if_aligned() { - Some(v) => v, - None => { - unsafe { &mut *fast_api_callback_options }.fallback = true; - return Default::default(); - } - }; - let result = Self::call(ptr, out); - result - } -} diff --git a/ops/optimizer_tests/op_ffi_ptr_value.rs b/ops/optimizer_tests/op_ffi_ptr_value.rs deleted file mode 100644 index 4c3364507b..0000000000 --- a/ops/optimizer_tests/op_ffi_ptr_value.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub fn op_ffi_ptr_value(ptr: *mut c_void, out: &mut [u32]) { - // ... -} diff --git a/ops/optimizer_tests/op_print.expected b/ops/optimizer_tests/op_print.expected deleted file mode 100644 index 6095c138e6..0000000000 --- a/ops/optimizer_tests/op_print.expected +++ /dev/null @@ -1,11 +0,0 @@ -=== Optimizer Dump === -returns_result: true -has_ref_opstate: true -has_rc_opstate: false -has_fast_callback_option: false -needs_fast_callback_option: true -fast_result: Some(Void) -fast_parameters: [V8Value, SeqOneByteString, Bool] -transforms: {1: Transform { kind: SeqOneByteString(Ref), index: 1 }} -is_async: false -fast_compatible: false diff --git a/ops/optimizer_tests/op_print.out b/ops/optimizer_tests/op_print.out deleted file mode 100644 index 5023a6b030..0000000000 --- a/ops/optimizer_tests/op_print.out +++ /dev/null @@ -1,108 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `op_print::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct op_print { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_print { - const NAME: &'static str = stringify!(op_print); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: None, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl op_print { - pub const fn name() -> &'static str { - stringify!(op_print) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: None, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 2usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - fn call<'scope>( - state: &mut OpState, - msg: &str, - is_err: bool, - ) -> Result<(), AnyError> {} - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - let arg_0 = match deno_core::v8::Local::< - deno_core::v8::String, - >::try_from(args.get(0usize as i32)) { - Ok(v8_string) => deno_core::serde_v8::to_utf8(v8_string, scope), - Err(_) => { - return deno_core::_ops::throw_type_error( - scope, - format!("Expected string at position {}", 0usize), - ); - } - }; - let arg_0 = arg_0.as_ref(); - let arg_1 = args.get(1usize as i32); - let arg_1 = match deno_core::serde_v8::from_v8(scope, arg_1) { - Ok(v) => v, - Err(err) => { - let msg = format!( - "Error parsing args at position {}: {}", 1usize, - deno_core::anyhow::Error::from(err) - ); - return deno_core::_ops::throw_type_error(scope, msg); - } - }; - let result = Self::call( - &mut std::cell::RefCell::borrow_mut(&ctx.state), - arg_0, - arg_1, - ); - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - match result { - Ok(result) => {} - Err(err) => { - let exception = deno_core::error::to_v8_error( - scope, - op_state.get_error_class_fn, - &err, - ); - scope.throw_exception(exception); - } - }; - } -} diff --git a/ops/optimizer_tests/op_print.rs b/ops/optimizer_tests/op_print.rs deleted file mode 100644 index 776ee84188..0000000000 --- a/ops/optimizer_tests/op_print.rs +++ /dev/null @@ -1,7 +0,0 @@ -fn op_print( - state: &mut OpState, - msg: &str, - is_err: bool, -) -> Result<(), AnyError> { - // TODO(@littledivy): fast compatible https://github.com/denoland/deno/issues/17159 -} diff --git a/ops/optimizer_tests/op_state.expected b/ops/optimizer_tests/op_state.expected deleted file mode 100644 index 241ea76936..0000000000 --- a/ops/optimizer_tests/op_state.expected +++ /dev/null @@ -1,11 +0,0 @@ -=== Optimizer Dump === -returns_result: false -has_ref_opstate: true -has_rc_opstate: false -has_fast_callback_option: false -needs_fast_callback_option: false -fast_result: Some(Void) -fast_parameters: [V8Value, I32] -transforms: {} -is_async: false -fast_compatible: true diff --git a/ops/optimizer_tests/op_state.out b/ops/optimizer_tests/op_state.out deleted file mode 100644 index 1b6d86b061..0000000000 --- a/ops/optimizer_tests/op_state.out +++ /dev/null @@ -1,120 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `op_set_exit_code::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct op_set_exit_code { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_set_exit_code { - const NAME: &'static str = stringify!(op_set_exit_code); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, Int32, CallbackOptions], - CType::Void, - Self::op_set_exit_code_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl op_set_exit_code { - pub const fn name() -> &'static str { - stringify!(op_set_exit_code) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, Int32, CallbackOptions], - CType::Void, - Self::op_set_exit_code_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 1usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - fn call<'scope>(state: &mut OpState, code: i32) { - state.borrow_mut::().set(code); - } - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - let arg_0 = args.get(0usize as i32); - let arg_0 = match deno_core::serde_v8::from_v8(scope, arg_0) { - Ok(v) => v, - Err(err) => { - let msg = format!( - "Error parsing args at position {}: {}", 0usize, - deno_core::anyhow::Error::from(err) - ); - return deno_core::_ops::throw_type_error(scope, msg); - } - }; - let result = Self::call(&mut std::cell::RefCell::borrow_mut(&ctx.state), arg_0); - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - } -} -impl op_set_exit_code { - #[allow(clippy::too_many_arguments)] - fn op_set_exit_code_fast_fn( - _: deno_core::v8::Local, - code: i32, - fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions, - ) -> () { - use deno_core::v8; - use deno_core::_ops; - let __opts: &mut v8::fast_api::FastApiCallbackOptions = unsafe { - &mut *fast_api_callback_options - }; - let __ctx = unsafe { - &*(v8::Local::::cast(unsafe { __opts.data.data }).value() - as *const _ops::OpCtx) - }; - let state = &mut ::std::cell::RefCell::borrow_mut(&__ctx.state); - let result = Self::call(state, code); - result - } -} diff --git a/ops/optimizer_tests/op_state.rs b/ops/optimizer_tests/op_state.rs deleted file mode 100644 index 04e9a886df..0000000000 --- a/ops/optimizer_tests/op_state.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn op_set_exit_code(state: &mut OpState, code: i32) { - state.borrow_mut::().set(code); -} diff --git a/ops/optimizer_tests/op_state_basic1.expected b/ops/optimizer_tests/op_state_basic1.expected deleted file mode 100644 index e325dd2f70..0000000000 --- a/ops/optimizer_tests/op_state_basic1.expected +++ /dev/null @@ -1,11 +0,0 @@ -=== Optimizer Dump === -returns_result: false -has_ref_opstate: true -has_rc_opstate: false -has_fast_callback_option: false -needs_fast_callback_option: false -fast_result: Some(U32) -fast_parameters: [V8Value, U32, U32] -transforms: {} -is_async: false -fast_compatible: true diff --git a/ops/optimizer_tests/op_state_basic1.out b/ops/optimizer_tests/op_state_basic1.out deleted file mode 100644 index 284232a3ab..0000000000 --- a/ops/optimizer_tests/op_state_basic1.out +++ /dev/null @@ -1,148 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `foo::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct foo { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for foo { - const NAME: &'static str = stringify!(foo); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, Uint32, Uint32, CallbackOptions], - CType::Uint32, - Self::foo_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl foo { - pub const fn name() -> &'static str { - stringify!(foo) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, Uint32, Uint32, CallbackOptions], - CType::Uint32, - Self::foo_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 2usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - fn call<'scope>(state: &mut OpState, a: u32, b: u32) -> u32 { - a + b - } - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - let arg_0 = args.get(0usize as i32); - let arg_0 = match deno_core::serde_v8::from_v8(scope, arg_0) { - Ok(v) => v, - Err(err) => { - let msg = format!( - "Error parsing args at position {}: {}", 0usize, - deno_core::anyhow::Error::from(err) - ); - return deno_core::_ops::throw_type_error(scope, msg); - } - }; - let arg_1 = args.get(1usize as i32); - let arg_1 = match deno_core::serde_v8::from_v8(scope, arg_1) { - Ok(v) => v, - Err(err) => { - let msg = format!( - "Error parsing args at position {}: {}", 1usize, - deno_core::anyhow::Error::from(err) - ); - return deno_core::_ops::throw_type_error(scope, msg); - } - }; - let result = Self::call( - &mut std::cell::RefCell::borrow_mut(&ctx.state), - arg_0, - arg_1, - ); - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - match deno_core::serde_v8::to_v8(scope, result) { - Ok(ret) => rv.set(ret), - Err(err) => { - deno_core::_ops::throw_type_error( - scope, - format!( - "Error serializing return: {}", - deno_core::anyhow::Error::from(err) - ), - ) - } - }; - } -} -impl foo { - #[allow(clippy::too_many_arguments)] - fn foo_fast_fn( - _: deno_core::v8::Local, - a: u32, - b: u32, - fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions, - ) -> u32 { - use deno_core::v8; - use deno_core::_ops; - let __opts: &mut v8::fast_api::FastApiCallbackOptions = unsafe { - &mut *fast_api_callback_options - }; - let __ctx = unsafe { - &*(v8::Local::::cast(unsafe { __opts.data.data }).value() - as *const _ops::OpCtx) - }; - let state = &mut ::std::cell::RefCell::borrow_mut(&__ctx.state); - let result = Self::call(state, a, b); - result - } -} diff --git a/ops/optimizer_tests/op_state_basic1.rs b/ops/optimizer_tests/op_state_basic1.rs deleted file mode 100644 index 9c89b41ceb..0000000000 --- a/ops/optimizer_tests/op_state_basic1.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn foo(state: &mut OpState, a: u32, b: u32) -> u32 { - a + b -} diff --git a/ops/optimizer_tests/op_state_generics.expected b/ops/optimizer_tests/op_state_generics.expected deleted file mode 100644 index c29258d756..0000000000 --- a/ops/optimizer_tests/op_state_generics.expected +++ /dev/null @@ -1,11 +0,0 @@ -=== Optimizer Dump === -returns_result: false -has_ref_opstate: true -has_rc_opstate: false -has_fast_callback_option: false -needs_fast_callback_option: false -fast_result: Some(Void) -fast_parameters: [V8Value] -transforms: {} -is_async: false -fast_compatible: true diff --git a/ops/optimizer_tests/op_state_generics.out b/ops/optimizer_tests/op_state_generics.out deleted file mode 100644 index a3e476d9e4..0000000000 --- a/ops/optimizer_tests/op_state_generics.out +++ /dev/null @@ -1,115 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `op_foo::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct op_foo { - _phantom_data: ::std::marker::PhantomData<(SP)>, -} -impl deno_core::_ops::Op for op_foo -where - SP: SomePermission + 'static, -{ - const NAME: &'static str = stringify!(op_foo); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, CallbackOptions], - CType::Void, - Self::op_foo_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl op_foo -where - SP: SomePermission + 'static, -{ - pub const fn name() -> &'static str { - stringify!(op_foo) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, CallbackOptions], - CType::Void, - Self::op_foo_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - pub fn call<'scope>(state: &mut OpState) {} - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - let result = Self::call(&mut std::cell::RefCell::borrow_mut(&ctx.state)); - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - } -} -impl op_foo -where - SP: SomePermission + 'static, -{ - #[allow(clippy::too_many_arguments)] - fn op_foo_fast_fn( - _: deno_core::v8::Local, - fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions, - ) -> () { - use deno_core::v8; - use deno_core::_ops; - let __opts: &mut v8::fast_api::FastApiCallbackOptions = unsafe { - &mut *fast_api_callback_options - }; - let __ctx = unsafe { - &*(v8::Local::::cast(unsafe { __opts.data.data }).value() - as *const _ops::OpCtx) - }; - let state = &mut ::std::cell::RefCell::borrow_mut(&__ctx.state); - let result = Self::call(state); - result - } -} diff --git a/ops/optimizer_tests/op_state_generics.rs b/ops/optimizer_tests/op_state_generics.rs deleted file mode 100644 index 7fa498981e..0000000000 --- a/ops/optimizer_tests/op_state_generics.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub fn op_foo(state: &mut OpState) -where - SP: SomePermission + 'static, -{ -} diff --git a/ops/optimizer_tests/op_state_result.expected b/ops/optimizer_tests/op_state_result.expected deleted file mode 100644 index 15ac033f34..0000000000 --- a/ops/optimizer_tests/op_state_result.expected +++ /dev/null @@ -1,11 +0,0 @@ -=== Optimizer Dump === -returns_result: true -has_ref_opstate: true -has_rc_opstate: false -has_fast_callback_option: false -needs_fast_callback_option: false -fast_result: Some(U32) -fast_parameters: [V8Value, U32, U32] -transforms: {} -is_async: false -fast_compatible: true diff --git a/ops/optimizer_tests/op_state_result.out b/ops/optimizer_tests/op_state_result.out deleted file mode 100644 index 67793d93a8..0000000000 --- a/ops/optimizer_tests/op_state_result.out +++ /dev/null @@ -1,168 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `foo::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct foo { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for foo { - const NAME: &'static str = stringify!(foo); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, Uint32, Uint32, CallbackOptions], - CType::Uint32, - Self::foo_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl foo { - pub const fn name() -> &'static str { - stringify!(foo) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, Uint32, Uint32, CallbackOptions], - CType::Uint32, - Self::foo_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 2usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - fn call<'scope>(state: &mut OpState, a: u32, b: u32) -> Result { - Ok(a + b) - } - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - { - let op_state = &mut std::cell::RefCell::borrow_mut(&ctx.state); - if let Some(err) = op_state.last_fast_op_error.take() { - let exception = deno_core::error::to_v8_error( - scope, - op_state.get_error_class_fn, - &err, - ); - scope.throw_exception(exception); - return; - } - } - let arg_0 = args.get(0usize as i32); - let arg_0 = match deno_core::serde_v8::from_v8(scope, arg_0) { - Ok(v) => v, - Err(err) => { - let msg = format!( - "Error parsing args at position {}: {}", 0usize, - deno_core::anyhow::Error::from(err) - ); - return deno_core::_ops::throw_type_error(scope, msg); - } - }; - let arg_1 = args.get(1usize as i32); - let arg_1 = match deno_core::serde_v8::from_v8(scope, arg_1) { - Ok(v) => v, - Err(err) => { - let msg = format!( - "Error parsing args at position {}: {}", 1usize, - deno_core::anyhow::Error::from(err) - ); - return deno_core::_ops::throw_type_error(scope, msg); - } - }; - let result = Self::call( - &mut std::cell::RefCell::borrow_mut(&ctx.state), - arg_0, - arg_1, - ); - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - match result { - Ok(result) => { - rv.set_uint32(result as u32); - } - Err(err) => { - let exception = deno_core::error::to_v8_error( - scope, - op_state.get_error_class_fn, - &err, - ); - scope.throw_exception(exception); - } - }; - } -} -impl foo { - #[allow(clippy::too_many_arguments)] - fn foo_fast_fn( - _: deno_core::v8::Local, - a: u32, - b: u32, - fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions, - ) -> u32 { - use deno_core::v8; - use deno_core::_ops; - let __opts: &mut v8::fast_api::FastApiCallbackOptions = unsafe { - &mut *fast_api_callback_options - }; - let __ctx = unsafe { - &*(v8::Local::::cast(unsafe { __opts.data.data }).value() - as *const _ops::OpCtx) - }; - let state = &mut ::std::cell::RefCell::borrow_mut(&__ctx.state); - let result = Self::call(state, a, b); - match result { - Ok(result) => result, - Err(err) => { - state.last_fast_op_error.replace(err); - __opts.fallback = true; - Default::default() - } - } - } -} diff --git a/ops/optimizer_tests/op_state_result.rs b/ops/optimizer_tests/op_state_result.rs deleted file mode 100644 index 331005c08b..0000000000 --- a/ops/optimizer_tests/op_state_result.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn foo(state: &mut OpState, a: u32, b: u32) -> Result { - Ok(a + b) -} diff --git a/ops/optimizer_tests/op_state_warning.expected b/ops/optimizer_tests/op_state_warning.expected deleted file mode 100644 index 7b33ad1ef4..0000000000 --- a/ops/optimizer_tests/op_state_warning.expected +++ /dev/null @@ -1,11 +0,0 @@ -=== Optimizer Dump === -returns_result: true -has_ref_opstate: true -has_rc_opstate: false -has_fast_callback_option: false -needs_fast_callback_option: false -fast_result: Some(U32) -fast_parameters: [V8Value] -transforms: {} -is_async: false -fast_compatible: true diff --git a/ops/optimizer_tests/op_state_warning.out b/ops/optimizer_tests/op_state_warning.out deleted file mode 100644 index 51947f90a1..0000000000 --- a/ops/optimizer_tests/op_state_warning.out +++ /dev/null @@ -1,157 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `op_listen::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct op_listen { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_listen { - const NAME: &'static str = stringify!(op_listen); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, CallbackOptions], - CType::Uint32, - Self::op_listen_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl op_listen { - pub const fn name() -> &'static str { - stringify!(op_listen) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, CallbackOptions], - CType::Uint32, - Self::op_listen_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - fn call<'scope>(state: &mut OpState) -> Result { - log::debug!("listen"); - let addr = "127.0.0.1:4570".parse::().unwrap(); - let std_listener = std::net::TcpListener::bind(&addr)?; - std_listener.set_nonblocking(true)?; - let listener = TcpListener::try_from(std_listener)?; - let rid = state.resource_table.add(listener); - Ok(rid) - } - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - { - let op_state = &mut std::cell::RefCell::borrow_mut(&ctx.state); - if let Some(err) = op_state.last_fast_op_error.take() { - let exception = deno_core::error::to_v8_error( - scope, - op_state.get_error_class_fn, - &err, - ); - scope.throw_exception(exception); - return; - } - } - let result = Self::call(&mut std::cell::RefCell::borrow_mut(&ctx.state)); - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - match result { - Ok(result) => { - match deno_core::serde_v8::to_v8(scope, result) { - Ok(ret) => rv.set(ret), - Err(err) => { - deno_core::_ops::throw_type_error( - scope, - format!( - "Error serializing return: {}", - deno_core::anyhow::Error::from(err) - ), - ) - } - }; - } - Err(err) => { - let exception = deno_core::error::to_v8_error( - scope, - op_state.get_error_class_fn, - &err, - ); - scope.throw_exception(exception); - } - }; - } -} -impl op_listen { - #[allow(clippy::too_many_arguments)] - fn op_listen_fast_fn( - _: deno_core::v8::Local, - fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions, - ) -> u32 { - use deno_core::v8; - use deno_core::_ops; - let __opts: &mut v8::fast_api::FastApiCallbackOptions = unsafe { - &mut *fast_api_callback_options - }; - let __ctx = unsafe { - &*(v8::Local::::cast(unsafe { __opts.data.data }).value() - as *const _ops::OpCtx) - }; - let state = &mut ::std::cell::RefCell::borrow_mut(&__ctx.state); - let result = Self::call(state); - match result { - Ok(result) => result, - Err(err) => { - state.last_fast_op_error.replace(err); - __opts.fallback = true; - Default::default() - } - } - } -} diff --git a/ops/optimizer_tests/op_state_warning.rs b/ops/optimizer_tests/op_state_warning.rs deleted file mode 100644 index a725e6f7e3..0000000000 --- a/ops/optimizer_tests/op_state_warning.rs +++ /dev/null @@ -1,9 +0,0 @@ -fn op_listen(state: &mut OpState) -> Result { - log::debug!("listen"); - let addr = "127.0.0.1:4570".parse::().unwrap(); - let std_listener = std::net::TcpListener::bind(&addr)?; - std_listener.set_nonblocking(true)?; - let listener = TcpListener::try_from(std_listener)?; - let rid = state.resource_table.add(listener); - Ok(rid) -} diff --git a/ops/optimizer_tests/op_state_with_transforms.expected b/ops/optimizer_tests/op_state_with_transforms.expected deleted file mode 100644 index f5f236b7d9..0000000000 --- a/ops/optimizer_tests/op_state_with_transforms.expected +++ /dev/null @@ -1,11 +0,0 @@ -=== Optimizer Dump === -returns_result: false -has_ref_opstate: true -has_rc_opstate: false -has_fast_callback_option: false -needs_fast_callback_option: false -fast_result: Some(Void) -fast_parameters: [V8Value, Uint8Array] -transforms: {1: Transform { kind: SliceU8(true), index: 1 }} -is_async: false -fast_compatible: true diff --git a/ops/optimizer_tests/op_state_with_transforms.out b/ops/optimizer_tests/op_state_with_transforms.out deleted file mode 100644 index 09303e2698..0000000000 --- a/ops/optimizer_tests/op_state_with_transforms.out +++ /dev/null @@ -1,162 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `op_now::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct op_now { - _phantom_data: ::std::marker::PhantomData<(TP)>, -} -impl deno_core::_ops::Op for op_now -where - TP: TimersPermission + 'static, -{ - const NAME: &'static str = stringify!(op_now); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, TypedArray(CType::Uint8), CallbackOptions], - CType::Void, - Self::op_now_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl op_now -where - TP: TimersPermission + 'static, -{ - pub const fn name() -> &'static str { - stringify!(op_now) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, TypedArray(CType::Uint8), CallbackOptions], - CType::Void, - Self::op_now_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 1usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - pub fn call<'scope>(state: &mut OpState, buf: &mut [u8]) {} - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - let arg_0 = { - let value = args.get(0usize as i32); - match deno_core::v8::Local::::try_from(value) { - Ok(b) => { - let byte_length = b.byte_length(); - if let Some(data) = b.data() { - let store = data.cast::().as_ptr(); - unsafe { ::std::slice::from_raw_parts_mut(store, byte_length) } - } else { - &mut [] - } - } - Err(_) => { - if let Ok(view) - = deno_core::v8::Local::< - deno_core::v8::ArrayBufferView, - >::try_from(value) { - let len = view.byte_length(); - let offset = view.byte_offset(); - let buffer = match view.buffer(scope) { - Some(v) => v, - None => { - return deno_core::_ops::throw_type_error( - scope, - format!("Expected ArrayBufferView at position {}", 0usize), - ); - } - }; - if let Some(data) = buffer.data() { - let store = data.cast::().as_ptr(); - unsafe { - ::std::slice::from_raw_parts_mut(store.add(offset), len) - } - } else { - &mut [] - } - } else { - return deno_core::_ops::throw_type_error( - scope, - format!("Expected ArrayBufferView at position {}", 0usize), - ); - } - } - } - }; - let result = Self::call(&mut std::cell::RefCell::borrow_mut(&ctx.state), arg_0); - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - } -} -impl op_now -where - TP: TimersPermission + 'static, -{ - #[allow(clippy::too_many_arguments)] - fn op_now_fast_fn( - _: deno_core::v8::Local, - buf: *const deno_core::v8::fast_api::FastApiTypedArray, - fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions, - ) -> () { - use deno_core::v8; - use deno_core::_ops; - let __opts: &mut v8::fast_api::FastApiCallbackOptions = unsafe { - &mut *fast_api_callback_options - }; - let __ctx = unsafe { - &*(v8::Local::::cast(unsafe { __opts.data.data }).value() - as *const _ops::OpCtx) - }; - let state = &mut ::std::cell::RefCell::borrow_mut(&__ctx.state); - let buf = unsafe { (&*buf).get_storage_if_aligned().unwrap_unchecked() }; - let result = Self::call(state, buf); - result - } -} diff --git a/ops/optimizer_tests/op_state_with_transforms.rs b/ops/optimizer_tests/op_state_with_transforms.rs deleted file mode 100644 index 4e7e616f3d..0000000000 --- a/ops/optimizer_tests/op_state_with_transforms.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub fn op_now(state: &mut OpState, buf: &mut [u8]) -where - TP: TimersPermission + 'static, -{ -} diff --git a/ops/optimizer_tests/opstate_with_arity.expected b/ops/optimizer_tests/opstate_with_arity.expected deleted file mode 100644 index b38455d0dd..0000000000 --- a/ops/optimizer_tests/opstate_with_arity.expected +++ /dev/null @@ -1,11 +0,0 @@ -=== Optimizer Dump === -returns_result: true -has_ref_opstate: false -has_rc_opstate: false -has_fast_callback_option: false -needs_fast_callback_option: false -fast_result: Some(U32) -fast_parameters: [V8Value, U32, U32, U32, U32] -transforms: {} -is_async: false -fast_compatible: true diff --git a/ops/optimizer_tests/opstate_with_arity.out b/ops/optimizer_tests/opstate_with_arity.out deleted file mode 100644 index d7e85328ab..0000000000 --- a/ops/optimizer_tests/opstate_with_arity.out +++ /dev/null @@ -1,188 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `op_add_4::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct op_add_4 { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_add_4 { - const NAME: &'static str = stringify!(op_add_4); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, Uint32, Uint32, Uint32, Uint32, CallbackOptions], - CType::Uint32, - Self::op_add_4_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl op_add_4 { - pub const fn name() -> &'static str { - stringify!(op_add_4) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, Uint32, Uint32, Uint32, Uint32, CallbackOptions], - CType::Uint32, - Self::op_add_4_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 4usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - fn call<'scope>(x1: u32, x2: u32, x3: u32, x4: u32) -> Result { - Ok(x1 + x2 + x3 + x4) - } - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - { - let op_state = &mut std::cell::RefCell::borrow_mut(&ctx.state); - if let Some(err) = op_state.last_fast_op_error.take() { - let exception = deno_core::error::to_v8_error( - scope, - op_state.get_error_class_fn, - &err, - ); - scope.throw_exception(exception); - return; - } - } - let arg_0 = args.get(0usize as i32); - let arg_0 = match deno_core::serde_v8::from_v8(scope, arg_0) { - Ok(v) => v, - Err(err) => { - let msg = format!( - "Error parsing args at position {}: {}", 0usize, - deno_core::anyhow::Error::from(err) - ); - return deno_core::_ops::throw_type_error(scope, msg); - } - }; - let arg_1 = args.get(1usize as i32); - let arg_1 = match deno_core::serde_v8::from_v8(scope, arg_1) { - Ok(v) => v, - Err(err) => { - let msg = format!( - "Error parsing args at position {}: {}", 1usize, - deno_core::anyhow::Error::from(err) - ); - return deno_core::_ops::throw_type_error(scope, msg); - } - }; - let arg_2 = args.get(2usize as i32); - let arg_2 = match deno_core::serde_v8::from_v8(scope, arg_2) { - Ok(v) => v, - Err(err) => { - let msg = format!( - "Error parsing args at position {}: {}", 2usize, - deno_core::anyhow::Error::from(err) - ); - return deno_core::_ops::throw_type_error(scope, msg); - } - }; - let arg_3 = args.get(3usize as i32); - let arg_3 = match deno_core::serde_v8::from_v8(scope, arg_3) { - Ok(v) => v, - Err(err) => { - let msg = format!( - "Error parsing args at position {}: {}", 3usize, - deno_core::anyhow::Error::from(err) - ); - return deno_core::_ops::throw_type_error(scope, msg); - } - }; - let result = Self::call(arg_0, arg_1, arg_2, arg_3); - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - match result { - Ok(result) => { - rv.set_uint32(result as u32); - } - Err(err) => { - let exception = deno_core::error::to_v8_error( - scope, - op_state.get_error_class_fn, - &err, - ); - scope.throw_exception(exception); - } - }; - } -} -impl op_add_4 { - #[allow(clippy::too_many_arguments)] - fn op_add_4_fast_fn( - _: deno_core::v8::Local, - x1: u32, - x2: u32, - x3: u32, - x4: u32, - fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions, - ) -> u32 { - use deno_core::v8; - use deno_core::_ops; - let __opts: &mut v8::fast_api::FastApiCallbackOptions = unsafe { - &mut *fast_api_callback_options - }; - let __ctx = unsafe { - &*(v8::Local::::cast(unsafe { __opts.data.data }).value() - as *const _ops::OpCtx) - }; - let op_state = &mut ::std::cell::RefCell::borrow_mut(&__ctx.state); - let result = Self::call(x1, x2, x3, x4); - match result { - Ok(result) => result, - Err(err) => { - op_state.last_fast_op_error.replace(err); - __opts.fallback = true; - Default::default() - } - } - } -} diff --git a/ops/optimizer_tests/opstate_with_arity.rs b/ops/optimizer_tests/opstate_with_arity.rs deleted file mode 100644 index 7212ca9752..0000000000 --- a/ops/optimizer_tests/opstate_with_arity.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn op_add_4(x1: u32, x2: u32, x3: u32, x4: u32) -> Result { - Ok(x1 + x2 + x3 + x4) -} diff --git a/ops/optimizer_tests/option_arg.expected b/ops/optimizer_tests/option_arg.expected deleted file mode 100644 index 250ff1022d..0000000000 --- a/ops/optimizer_tests/option_arg.expected +++ /dev/null @@ -1 +0,0 @@ -FastUnsupportedParamType \ No newline at end of file diff --git a/ops/optimizer_tests/option_arg.out b/ops/optimizer_tests/option_arg.out deleted file mode 100644 index 5880ec88e1..0000000000 --- a/ops/optimizer_tests/option_arg.out +++ /dev/null @@ -1,91 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `op_try_close::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct op_try_close { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_try_close { - const NAME: &'static str = stringify!(op_try_close); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: None, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl op_try_close { - pub const fn name() -> &'static str { - stringify!(op_try_close) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: None, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 1usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - pub fn call<'scope>( - state: &mut OpState, - rid: Option, - ) -> Result<(), Error> {} - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - let arg_0 = args.get(0usize as i32); - let arg_0 = match deno_core::serde_v8::from_v8(scope, arg_0) { - Ok(v) => v, - Err(err) => { - let msg = format!( - "Error parsing args at position {}: {}", 0usize, - deno_core::anyhow::Error::from(err) - ); - return deno_core::_ops::throw_type_error(scope, msg); - } - }; - let result = Self::call(&mut std::cell::RefCell::borrow_mut(&ctx.state), arg_0); - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - match result { - Ok(result) => {} - Err(err) => { - let exception = deno_core::error::to_v8_error( - scope, - op_state.get_error_class_fn, - &err, - ); - scope.throw_exception(exception); - } - }; - } -} diff --git a/ops/optimizer_tests/option_arg.rs b/ops/optimizer_tests/option_arg.rs deleted file mode 100644 index 47a02974d7..0000000000 --- a/ops/optimizer_tests/option_arg.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub fn op_try_close( - state: &mut OpState, - rid: Option, -) -> Result<(), Error> { - // ... -} diff --git a/ops/optimizer_tests/owned_string.expected b/ops/optimizer_tests/owned_string.expected deleted file mode 100644 index 4c47a05250..0000000000 --- a/ops/optimizer_tests/owned_string.expected +++ /dev/null @@ -1,11 +0,0 @@ -=== Optimizer Dump === -returns_result: false -has_ref_opstate: false -has_rc_opstate: false -has_fast_callback_option: false -needs_fast_callback_option: true -fast_result: Some(U32) -fast_parameters: [V8Value, SeqOneByteString] -transforms: {0: Transform { kind: SeqOneByteString(Owned), index: 0 }} -is_async: false -fast_compatible: true diff --git a/ops/optimizer_tests/owned_string.out b/ops/optimizer_tests/owned_string.out deleted file mode 100644 index 58c7f72ae8..0000000000 --- a/ops/optimizer_tests/owned_string.out +++ /dev/null @@ -1,131 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `op_string_length::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct op_string_length { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_string_length { - const NAME: &'static str = stringify!(op_string_length); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, SeqOneByteString, CallbackOptions], - CType::Uint32, - Self::op_string_length_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl op_string_length { - pub const fn name() -> &'static str { - stringify!(op_string_length) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, SeqOneByteString, CallbackOptions], - CType::Uint32, - Self::op_string_length_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 1usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - fn call<'scope>(string: String) -> u32 { - string.len() as u32 - } - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - let arg_0 = match deno_core::v8::Local::< - deno_core::v8::String, - >::try_from(args.get(0usize as i32)) { - Ok(v8_string) => deno_core::serde_v8::to_utf8(v8_string, scope), - Err(_) => { - return deno_core::_ops::throw_type_error( - scope, - format!("Expected string at position {}", 0usize), - ); - } - }; - let result = Self::call(arg_0); - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - match deno_core::serde_v8::to_v8(scope, result) { - Ok(ret) => rv.set(ret), - Err(err) => { - deno_core::_ops::throw_type_error( - scope, - format!( - "Error serializing return: {}", - deno_core::anyhow::Error::from(err) - ), - ) - } - }; - } -} -impl op_string_length { - #[allow(clippy::too_many_arguments)] - fn op_string_length_fast_fn( - _: deno_core::v8::Local, - string: *const deno_core::v8::fast_api::FastApiOneByteString, - fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions, - ) -> u32 { - use deno_core::v8; - use deno_core::_ops; - let string = match ::std::str::from_utf8(unsafe { &*string }.as_bytes()) { - Ok(v) => v.to_owned(), - Err(_) => { - unsafe { &mut *fast_api_callback_options }.fallback = true; - return Default::default(); - } - }; - let result = Self::call(string); - result - } -} diff --git a/ops/optimizer_tests/owned_string.rs b/ops/optimizer_tests/owned_string.rs deleted file mode 100644 index 102cf00fb9..0000000000 --- a/ops/optimizer_tests/owned_string.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn op_string_length(string: String) -> u32 { - string.len() as u32 -} diff --git a/ops/optimizer_tests/param_mut_binding_warning.expected b/ops/optimizer_tests/param_mut_binding_warning.expected deleted file mode 100644 index 250ff1022d..0000000000 --- a/ops/optimizer_tests/param_mut_binding_warning.expected +++ /dev/null @@ -1 +0,0 @@ -FastUnsupportedParamType \ No newline at end of file diff --git a/ops/optimizer_tests/param_mut_binding_warning.out b/ops/optimizer_tests/param_mut_binding_warning.out deleted file mode 100644 index 43a33d6e31..0000000000 --- a/ops/optimizer_tests/param_mut_binding_warning.out +++ /dev/null @@ -1,111 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `op_read_sync::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct op_read_sync { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_read_sync { - const NAME: &'static str = stringify!(op_read_sync); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: None, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl op_read_sync { - pub const fn name() -> &'static str { - stringify!(op_read_sync) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: None, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 2usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - fn call<'scope>( - state: &mut OpState, - rid: ResourceId, - mut buf: ZeroCopyBuf, - ) -> Result { - Ok(23) - } - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - let arg_0 = args.get(0usize as i32); - let arg_0 = match deno_core::serde_v8::from_v8(scope, arg_0) { - Ok(v) => v, - Err(err) => { - let msg = format!( - "Error parsing args at position {}: {}", 0usize, - deno_core::anyhow::Error::from(err) - ); - return deno_core::_ops::throw_type_error(scope, msg); - } - }; - let arg_1 = args.get(1usize as i32); - let arg_1 = match deno_core::serde_v8::from_v8(scope, arg_1) { - Ok(v) => v, - Err(err) => { - let msg = format!( - "Error parsing args at position {}: {}", 1usize, - deno_core::anyhow::Error::from(err) - ); - return deno_core::_ops::throw_type_error(scope, msg); - } - }; - let result = Self::call( - &mut std::cell::RefCell::borrow_mut(&ctx.state), - arg_0, - arg_1, - ); - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - match result { - Ok(result) => { - rv.set_uint32(result as u32); - } - Err(err) => { - let exception = deno_core::error::to_v8_error( - scope, - op_state.get_error_class_fn, - &err, - ); - scope.throw_exception(exception); - } - }; - } -} diff --git a/ops/optimizer_tests/param_mut_binding_warning.rs b/ops/optimizer_tests/param_mut_binding_warning.rs deleted file mode 100644 index c47122728a..0000000000 --- a/ops/optimizer_tests/param_mut_binding_warning.rs +++ /dev/null @@ -1,11 +0,0 @@ -fn op_read_sync( - state: &mut OpState, - rid: ResourceId, - mut buf: ZeroCopyBuf, -) -> Result { - // Should not warn about unused `mut buf` binding. - // - // This was caused due to incorrect codegen by fast_call.rs - // on an incompatible op function. - Ok(23) -} diff --git a/ops/optimizer_tests/raw_ptr.expected b/ops/optimizer_tests/raw_ptr.expected deleted file mode 100644 index badd6e3f49..0000000000 --- a/ops/optimizer_tests/raw_ptr.expected +++ /dev/null @@ -1,11 +0,0 @@ -=== Optimizer Dump === -returns_result: false -has_ref_opstate: true -has_rc_opstate: false -has_fast_callback_option: false -needs_fast_callback_option: true -fast_result: Some(Void) -fast_parameters: [V8Value, Uint8Array, Uint32Array] -transforms: {1: Transform { kind: PtrU8, index: 1 }, 2: Transform { kind: SliceU32(true), index: 2 }} -is_async: false -fast_compatible: true diff --git a/ops/optimizer_tests/raw_ptr.out b/ops/optimizer_tests/raw_ptr.out deleted file mode 100644 index 18d2aaa4a8..0000000000 --- a/ops/optimizer_tests/raw_ptr.out +++ /dev/null @@ -1,211 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `op_ffi_ptr_of::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct op_ffi_ptr_of { - _phantom_data: ::std::marker::PhantomData<(FP)>, -} -impl deno_core::_ops::Op for op_ffi_ptr_of -where - FP: FfiPermissions + 'static, -{ - const NAME: &'static str = stringify!(op_ffi_ptr_of); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[ - V8Value, - TypedArray(CType::Uint8), - TypedArray(CType::Uint32), - CallbackOptions, - ], - CType::Void, - Self::op_ffi_ptr_of_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl op_ffi_ptr_of -where - FP: FfiPermissions + 'static, -{ - pub const fn name() -> &'static str { - stringify!(op_ffi_ptr_of) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[ - V8Value, - TypedArray(CType::Uint8), - TypedArray(CType::Uint32), - CallbackOptions, - ], - CType::Void, - Self::op_ffi_ptr_of_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 2usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - fn call<'scope>(state: &mut OpState, buf: *const u8, out: &mut [u32]) {} - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - let arg_0 = { - let value = args.get(0usize as i32); - match deno_core::v8::Local::::try_from(value) { - Ok(b) => { - if let Some(data) = b.data() { - data.cast::().as_ptr() - } else { - std::ptr::null::() - } - } - Err(_) => { - if let Ok(view) - = deno_core::v8::Local::< - deno_core::v8::ArrayBufferView, - >::try_from(value) { - let offset = view.byte_offset(); - let buffer = match view.buffer(scope) { - Some(v) => v, - None => { - return deno_core::_ops::throw_type_error( - scope, - format!("Expected ArrayBufferView at position {}", 0usize), - ); - } - }; - let store = if let Some(data) = buffer.data() { - data.cast::().as_ptr() - } else { - std::ptr::null_mut::() - }; - unsafe { store.add(offset) } - } else { - return deno_core::_ops::throw_type_error( - scope, - format!("Expected ArrayBufferView at position {}", 0usize), - ); - } - } - } - }; - let arg_1 = if let Ok(view) - = deno_core::v8::Local::< - deno_core::v8::Uint32Array, - >::try_from(args.get(1usize as i32)) { - let (offset, len) = (view.byte_offset(), view.byte_length()); - let buffer = match view.buffer(scope) { - Some(v) => v, - None => { - return deno_core::_ops::throw_type_error( - scope, - format!("Expected Uint32Array at position {}", 1usize), - ); - } - }; - if let Some(data) = buffer.data() { - let store = data.cast::().as_ptr(); - unsafe { - ::std::slice::from_raw_parts_mut( - store.add(offset) as *mut u32, - len / 4, - ) - } - } else { - &mut [] - } - } else { - return deno_core::_ops::throw_type_error( - scope, - format!("Expected Uint32Array at position {}", 1usize), - ); - }; - let result = Self::call( - &mut std::cell::RefCell::borrow_mut(&ctx.state), - arg_0, - arg_1, - ); - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - } -} -impl op_ffi_ptr_of -where - FP: FfiPermissions + 'static, -{ - #[allow(clippy::too_many_arguments)] - fn op_ffi_ptr_of_fast_fn( - _: deno_core::v8::Local, - buf: *const deno_core::v8::fast_api::FastApiTypedArray, - out: *const deno_core::v8::fast_api::FastApiTypedArray, - fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions, - ) -> () { - use deno_core::v8; - use deno_core::_ops; - let __opts: &mut v8::fast_api::FastApiCallbackOptions = unsafe { - &mut *fast_api_callback_options - }; - let __ctx = unsafe { - &*(v8::Local::::cast(unsafe { __opts.data.data }).value() - as *const _ops::OpCtx) - }; - let state = &mut ::std::cell::RefCell::borrow_mut(&__ctx.state); - let buf = unsafe { (&*buf).get_storage_if_aligned().unwrap_unchecked() } - .as_ptr(); - let out = match unsafe { &*out }.get_storage_if_aligned() { - Some(v) => v, - None => { - unsafe { &mut *fast_api_callback_options }.fallback = true; - return Default::default(); - } - }; - let result = Self::call(state, buf, out); - result - } -} diff --git a/ops/optimizer_tests/raw_ptr.rs b/ops/optimizer_tests/raw_ptr.rs deleted file mode 100644 index 249b3b35b8..0000000000 --- a/ops/optimizer_tests/raw_ptr.rs +++ /dev/null @@ -1,6 +0,0 @@ -fn op_ffi_ptr_of(state: &mut OpState, buf: *const u8, out: &mut [u32]) -where - FP: FfiPermissions + 'static, -{ - // .. -} diff --git a/ops/optimizer_tests/serde_v8_value.expected b/ops/optimizer_tests/serde_v8_value.expected deleted file mode 100644 index 411fbec0bc..0000000000 --- a/ops/optimizer_tests/serde_v8_value.expected +++ /dev/null @@ -1,11 +0,0 @@ -=== Optimizer Dump === -returns_result: false -has_ref_opstate: false -has_rc_opstate: false -has_fast_callback_option: false -needs_fast_callback_option: false -fast_result: Some(Bool) -fast_parameters: [V8Value, V8Value] -transforms: {0: Transform { kind: V8Value, index: 0 }} -is_async: false -fast_compatible: true diff --git a/ops/optimizer_tests/serde_v8_value.out b/ops/optimizer_tests/serde_v8_value.out deleted file mode 100644 index 20cc975840..0000000000 --- a/ops/optimizer_tests/serde_v8_value.out +++ /dev/null @@ -1,124 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `op_is_proxy::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct op_is_proxy { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_is_proxy { - const NAME: &'static str = stringify!(op_is_proxy); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, V8Value], - CType::Bool, - Self::op_is_proxy_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl op_is_proxy { - pub const fn name() -> &'static str { - stringify!(op_is_proxy) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, V8Value], - CType::Bool, - Self::op_is_proxy_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 1usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - fn call<'scope>(value: serde_v8::Value) -> bool { - value.v8_value.is_proxy() - } - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - let arg_0 = args.get(0usize as i32); - let arg_0 = match deno_core::serde_v8::from_v8(scope, arg_0) { - Ok(v) => v, - Err(err) => { - let msg = format!( - "Error parsing args at position {}: {}", 0usize, - deno_core::anyhow::Error::from(err) - ); - return deno_core::_ops::throw_type_error(scope, msg); - } - }; - let result = Self::call(arg_0); - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - match deno_core::serde_v8::to_v8(scope, result) { - Ok(ret) => rv.set(ret), - Err(err) => { - deno_core::_ops::throw_type_error( - scope, - format!( - "Error serializing return: {}", - deno_core::anyhow::Error::from(err) - ), - ) - } - }; - } -} -impl op_is_proxy { - #[allow(clippy::too_many_arguments)] - fn op_is_proxy_fast_fn( - _: deno_core::v8::Local, - value: deno_core::v8::Local, - ) -> bool { - use deno_core::v8; - use deno_core::_ops; - let value = serde_v8::Value { v8_value: value }; - let result = Self::call(value); - result - } -} diff --git a/ops/optimizer_tests/serde_v8_value.rs b/ops/optimizer_tests/serde_v8_value.rs deleted file mode 100644 index c986930d9a..0000000000 --- a/ops/optimizer_tests/serde_v8_value.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn op_is_proxy(value: serde_v8::Value) -> bool { - value.v8_value.is_proxy() -} diff --git a/ops/optimizer_tests/strings.expected b/ops/optimizer_tests/strings.expected deleted file mode 100644 index 4a6bb15563..0000000000 --- a/ops/optimizer_tests/strings.expected +++ /dev/null @@ -1,11 +0,0 @@ -=== Optimizer Dump === -returns_result: false -has_ref_opstate: false -has_rc_opstate: false -has_fast_callback_option: false -needs_fast_callback_option: true -fast_result: Some(U32) -fast_parameters: [V8Value, SeqOneByteString] -transforms: {0: Transform { kind: SeqOneByteString(Ref), index: 0 }} -is_async: false -fast_compatible: true diff --git a/ops/optimizer_tests/strings.out b/ops/optimizer_tests/strings.out deleted file mode 100644 index c59214f6a7..0000000000 --- a/ops/optimizer_tests/strings.out +++ /dev/null @@ -1,132 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `op_string_length::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct op_string_length { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_string_length { - const NAME: &'static str = stringify!(op_string_length); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, SeqOneByteString, CallbackOptions], - CType::Uint32, - Self::op_string_length_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl op_string_length { - pub const fn name() -> &'static str { - stringify!(op_string_length) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, SeqOneByteString, CallbackOptions], - CType::Uint32, - Self::op_string_length_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 1usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - fn call<'scope>(string: &str) -> u32 { - string.len() as u32 - } - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - let arg_0 = match deno_core::v8::Local::< - deno_core::v8::String, - >::try_from(args.get(0usize as i32)) { - Ok(v8_string) => deno_core::serde_v8::to_utf8(v8_string, scope), - Err(_) => { - return deno_core::_ops::throw_type_error( - scope, - format!("Expected string at position {}", 0usize), - ); - } - }; - let arg_0 = arg_0.as_ref(); - let result = Self::call(arg_0); - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - match deno_core::serde_v8::to_v8(scope, result) { - Ok(ret) => rv.set(ret), - Err(err) => { - deno_core::_ops::throw_type_error( - scope, - format!( - "Error serializing return: {}", - deno_core::anyhow::Error::from(err) - ), - ) - } - }; - } -} -impl op_string_length { - #[allow(clippy::too_many_arguments)] - fn op_string_length_fast_fn( - _: deno_core::v8::Local, - string: *const deno_core::v8::fast_api::FastApiOneByteString, - fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions, - ) -> u32 { - use deno_core::v8; - use deno_core::_ops; - let string = match ::std::str::from_utf8(unsafe { &*string }.as_bytes()) { - Ok(v) => v, - Err(_) => { - unsafe { &mut *fast_api_callback_options }.fallback = true; - return Default::default(); - } - }; - let result = Self::call(string); - result - } -} diff --git a/ops/optimizer_tests/strings.rs b/ops/optimizer_tests/strings.rs deleted file mode 100644 index 860f1e8ec9..0000000000 --- a/ops/optimizer_tests/strings.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn op_string_length(string: &str) -> u32 { - string.len() as u32 -} diff --git a/ops/optimizer_tests/strings_result.expected b/ops/optimizer_tests/strings_result.expected deleted file mode 100644 index 3866751fd2..0000000000 --- a/ops/optimizer_tests/strings_result.expected +++ /dev/null @@ -1,11 +0,0 @@ -=== Optimizer Dump === -returns_result: true -has_ref_opstate: false -has_rc_opstate: false -has_fast_callback_option: false -needs_fast_callback_option: true -fast_result: Some(U32) -fast_parameters: [V8Value, SeqOneByteString] -transforms: {0: Transform { kind: SeqOneByteString(Ref), index: 0 }} -is_async: false -fast_compatible: false diff --git a/ops/optimizer_tests/strings_result.out b/ops/optimizer_tests/strings_result.out deleted file mode 100644 index 45ca1fac75..0000000000 --- a/ops/optimizer_tests/strings_result.out +++ /dev/null @@ -1,93 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `op_string_length::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct op_string_length { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_string_length { - const NAME: &'static str = stringify!(op_string_length); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: None, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl op_string_length { - pub const fn name() -> &'static str { - stringify!(op_string_length) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: None, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 1usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - fn call<'scope>(string: &str) -> Result { - Ok(string.len() as u32) - } - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - let arg_0 = match deno_core::v8::Local::< - deno_core::v8::String, - >::try_from(args.get(0usize as i32)) { - Ok(v8_string) => deno_core::serde_v8::to_utf8(v8_string, scope), - Err(_) => { - return deno_core::_ops::throw_type_error( - scope, - format!("Expected string at position {}", 0usize), - ); - } - }; - let arg_0 = arg_0.as_ref(); - let result = Self::call(arg_0); - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - match result { - Ok(result) => { - rv.set_uint32(result as u32); - } - Err(err) => { - let exception = deno_core::error::to_v8_error( - scope, - op_state.get_error_class_fn, - &err, - ); - scope.throw_exception(exception); - } - }; - } -} diff --git a/ops/optimizer_tests/strings_result.rs b/ops/optimizer_tests/strings_result.rs deleted file mode 100644 index f89efaab1e..0000000000 --- a/ops/optimizer_tests/strings_result.rs +++ /dev/null @@ -1,4 +0,0 @@ -// https://github.com/denoland/deno/issues/16979 -fn op_string_length(string: &str) -> Result { - Ok(string.len() as u32) -} diff --git a/ops/optimizer_tests/u64_result.expected b/ops/optimizer_tests/u64_result.expected deleted file mode 100644 index 250ff1022d..0000000000 --- a/ops/optimizer_tests/u64_result.expected +++ /dev/null @@ -1 +0,0 @@ -FastUnsupportedParamType \ No newline at end of file diff --git a/ops/optimizer_tests/u64_result.out b/ops/optimizer_tests/u64_result.out deleted file mode 100644 index 5a4df68b10..0000000000 --- a/ops/optimizer_tests/u64_result.out +++ /dev/null @@ -1,94 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `op_bench_now::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct op_bench_now { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_bench_now { - const NAME: &'static str = stringify!(op_bench_now); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: None, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl op_bench_now { - pub const fn name() -> &'static str { - stringify!(op_bench_now) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: None, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - fn call<'scope>(state: &mut OpState) -> Result { - let ns = state.borrow::().elapsed().as_nanos(); - let ns_u64 = u64::try_from(ns)?; - Ok(ns_u64) - } - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - let result = Self::call(&mut std::cell::RefCell::borrow_mut(&ctx.state)); - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - match result { - Ok(result) => { - match deno_core::serde_v8::to_v8(scope, result) { - Ok(ret) => rv.set(ret), - Err(err) => { - deno_core::_ops::throw_type_error( - scope, - format!( - "Error serializing return: {}", - deno_core::anyhow::Error::from(err) - ), - ) - } - }; - } - Err(err) => { - let exception = deno_core::error::to_v8_error( - scope, - op_state.get_error_class_fn, - &err, - ); - scope.throw_exception(exception); - } - }; - } -} diff --git a/ops/optimizer_tests/u64_result.rs b/ops/optimizer_tests/u64_result.rs deleted file mode 100644 index 1cc783db8b..0000000000 --- a/ops/optimizer_tests/u64_result.rs +++ /dev/null @@ -1,5 +0,0 @@ -fn op_bench_now(state: &mut OpState) -> Result { - let ns = state.borrow::().elapsed().as_nanos(); - let ns_u64 = u64::try_from(ns)?; - Ok(ns_u64) -} diff --git a/ops/optimizer_tests/uint8array.expected b/ops/optimizer_tests/uint8array.expected deleted file mode 100644 index 49554a2e83..0000000000 --- a/ops/optimizer_tests/uint8array.expected +++ /dev/null @@ -1,11 +0,0 @@ -=== Optimizer Dump === -returns_result: false -has_ref_opstate: false -has_rc_opstate: false -has_fast_callback_option: false -needs_fast_callback_option: false -fast_result: Some(Bool) -fast_parameters: [V8Value, Uint8Array, Uint8Array] -transforms: {0: Transform { kind: SliceU8(false), index: 0 }, 1: Transform { kind: SliceU8(true), index: 1 }} -is_async: false -fast_compatible: true diff --git a/ops/optimizer_tests/uint8array.out b/ops/optimizer_tests/uint8array.out deleted file mode 100644 index 335bf42f6a..0000000000 --- a/ops/optimizer_tests/uint8array.out +++ /dev/null @@ -1,205 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `op_import_spki_x25519::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct op_import_spki_x25519 { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_import_spki_x25519 { - const NAME: &'static str = stringify!(op_import_spki_x25519); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, TypedArray(CType::Uint8), TypedArray(CType::Uint8)], - CType::Bool, - Self::op_import_spki_x25519_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl op_import_spki_x25519 { - pub const fn name() -> &'static str { - stringify!(op_import_spki_x25519) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, TypedArray(CType::Uint8), TypedArray(CType::Uint8)], - CType::Bool, - Self::op_import_spki_x25519_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 2usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - pub fn call<'scope>(key_data: &[u8], out: &mut [u8]) -> bool {} - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - let arg_0 = { - let value = args.get(0usize as i32); - match deno_core::v8::Local::::try_from(value) { - Ok(b) => { - let byte_length = b.byte_length(); - if let Some(data) = b.data() { - let store = data.cast::().as_ptr(); - unsafe { ::std::slice::from_raw_parts_mut(store, byte_length) } - } else { - &mut [] - } - } - Err(_) => { - if let Ok(view) - = deno_core::v8::Local::< - deno_core::v8::ArrayBufferView, - >::try_from(value) { - let len = view.byte_length(); - let offset = view.byte_offset(); - let buffer = match view.buffer(scope) { - Some(v) => v, - None => { - return deno_core::_ops::throw_type_error( - scope, - format!("Expected ArrayBufferView at position {}", 0usize), - ); - } - }; - if let Some(data) = buffer.data() { - let store = data.cast::().as_ptr(); - unsafe { - ::std::slice::from_raw_parts_mut(store.add(offset), len) - } - } else { - &mut [] - } - } else { - return deno_core::_ops::throw_type_error( - scope, - format!("Expected ArrayBufferView at position {}", 0usize), - ); - } - } - } - }; - let arg_1 = { - let value = args.get(1usize as i32); - match deno_core::v8::Local::::try_from(value) { - Ok(b) => { - let byte_length = b.byte_length(); - if let Some(data) = b.data() { - let store = data.cast::().as_ptr(); - unsafe { ::std::slice::from_raw_parts_mut(store, byte_length) } - } else { - &mut [] - } - } - Err(_) => { - if let Ok(view) - = deno_core::v8::Local::< - deno_core::v8::ArrayBufferView, - >::try_from(value) { - let len = view.byte_length(); - let offset = view.byte_offset(); - let buffer = match view.buffer(scope) { - Some(v) => v, - None => { - return deno_core::_ops::throw_type_error( - scope, - format!("Expected ArrayBufferView at position {}", 1usize), - ); - } - }; - if let Some(data) = buffer.data() { - let store = data.cast::().as_ptr(); - unsafe { - ::std::slice::from_raw_parts_mut(store.add(offset), len) - } - } else { - &mut [] - } - } else { - return deno_core::_ops::throw_type_error( - scope, - format!("Expected ArrayBufferView at position {}", 1usize), - ); - } - } - } - }; - let result = Self::call(arg_0, arg_1); - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - match deno_core::serde_v8::to_v8(scope, result) { - Ok(ret) => rv.set(ret), - Err(err) => { - deno_core::_ops::throw_type_error( - scope, - format!( - "Error serializing return: {}", - deno_core::anyhow::Error::from(err) - ), - ) - } - }; - } -} -impl op_import_spki_x25519 { - #[allow(clippy::too_many_arguments)] - fn op_import_spki_x25519_fast_fn( - _: deno_core::v8::Local, - key_data: *const deno_core::v8::fast_api::FastApiTypedArray, - out: *const deno_core::v8::fast_api::FastApiTypedArray, - ) -> bool { - use deno_core::v8; - use deno_core::_ops; - let key_data = unsafe { - (&*key_data).get_storage_if_aligned().unwrap_unchecked() - }; - let out = unsafe { (&*out).get_storage_if_aligned().unwrap_unchecked() }; - let result = Self::call(key_data, out); - result - } -} diff --git a/ops/optimizer_tests/uint8array.rs b/ops/optimizer_tests/uint8array.rs deleted file mode 100644 index f4507b21f5..0000000000 --- a/ops/optimizer_tests/uint8array.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub fn op_import_spki_x25519(key_data: &[u8], out: &mut [u8]) -> bool { - // ... -} diff --git a/ops/optimizer_tests/unit_result.expected b/ops/optimizer_tests/unit_result.expected deleted file mode 100644 index 693a771e9a..0000000000 --- a/ops/optimizer_tests/unit_result.expected +++ /dev/null @@ -1,11 +0,0 @@ -=== Optimizer Dump === -returns_result: true -has_ref_opstate: false -has_rc_opstate: false -has_fast_callback_option: false -needs_fast_callback_option: false -fast_result: Some(Void) -fast_parameters: [V8Value] -transforms: {} -is_async: false -fast_compatible: true diff --git a/ops/optimizer_tests/unit_result.out b/ops/optimizer_tests/unit_result.out deleted file mode 100644 index 8942972557..0000000000 --- a/ops/optimizer_tests/unit_result.out +++ /dev/null @@ -1,137 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `op_unit_result::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct op_unit_result { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_unit_result { - const NAME: &'static str = stringify!(op_unit_result); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, CallbackOptions], - CType::Void, - Self::op_unit_result_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl op_unit_result { - pub const fn name() -> &'static str { - stringify!(op_unit_result) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, CallbackOptions], - CType::Void, - Self::op_unit_result_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - fn call<'scope>() -> Result<(), AnyError> { - Ok(()) - } - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - { - let op_state = &mut std::cell::RefCell::borrow_mut(&ctx.state); - if let Some(err) = op_state.last_fast_op_error.take() { - let exception = deno_core::error::to_v8_error( - scope, - op_state.get_error_class_fn, - &err, - ); - scope.throw_exception(exception); - return; - } - } - let result = Self::call(); - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - match result { - Ok(result) => {} - Err(err) => { - let exception = deno_core::error::to_v8_error( - scope, - op_state.get_error_class_fn, - &err, - ); - scope.throw_exception(exception); - } - }; - } -} -impl op_unit_result { - #[allow(clippy::too_many_arguments)] - fn op_unit_result_fast_fn( - _: deno_core::v8::Local, - fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions, - ) -> () { - use deno_core::v8; - use deno_core::_ops; - let __opts: &mut v8::fast_api::FastApiCallbackOptions = unsafe { - &mut *fast_api_callback_options - }; - let __ctx = unsafe { - &*(v8::Local::::cast(unsafe { __opts.data.data }).value() - as *const _ops::OpCtx) - }; - let op_state = &mut ::std::cell::RefCell::borrow_mut(&__ctx.state); - let result = Self::call(); - match result { - Ok(result) => result, - Err(err) => { - op_state.last_fast_op_error.replace(err); - __opts.fallback = true; - } - } - } -} diff --git a/ops/optimizer_tests/unit_result.rs b/ops/optimizer_tests/unit_result.rs deleted file mode 100644 index 207896929c..0000000000 --- a/ops/optimizer_tests/unit_result.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn op_unit_result() -> Result<(), AnyError> { - Ok(()) -} diff --git a/ops/optimizer_tests/unit_result2.expected b/ops/optimizer_tests/unit_result2.expected deleted file mode 100644 index d799a77b2f..0000000000 --- a/ops/optimizer_tests/unit_result2.expected +++ /dev/null @@ -1,11 +0,0 @@ -=== Optimizer Dump === -returns_result: true -has_ref_opstate: true -has_rc_opstate: false -has_fast_callback_option: false -needs_fast_callback_option: false -fast_result: Some(Void) -fast_parameters: [V8Value, U32, Bool] -transforms: {} -is_async: false -fast_compatible: true diff --git a/ops/optimizer_tests/unit_result2.out b/ops/optimizer_tests/unit_result2.out deleted file mode 100644 index d091b91cd0..0000000000 --- a/ops/optimizer_tests/unit_result2.out +++ /dev/null @@ -1,172 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `op_set_nodelay::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct op_set_nodelay { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_set_nodelay { - const NAME: &'static str = stringify!(op_set_nodelay); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, Uint32, Bool, CallbackOptions], - CType::Void, - Self::op_set_nodelay_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl op_set_nodelay { - pub const fn name() -> &'static str { - stringify!(op_set_nodelay) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, Uint32, Bool, CallbackOptions], - CType::Void, - Self::op_set_nodelay_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 2usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - pub fn call<'scope>( - state: &mut OpState, - rid: ResourceId, - nodelay: bool, - ) -> Result<(), AnyError> { - let resource: Rc = state - .resource_table - .get::(rid)?; - resource.set_nodelay(nodelay) - } - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - { - let op_state = &mut std::cell::RefCell::borrow_mut(&ctx.state); - if let Some(err) = op_state.last_fast_op_error.take() { - let exception = deno_core::error::to_v8_error( - scope, - op_state.get_error_class_fn, - &err, - ); - scope.throw_exception(exception); - return; - } - } - let arg_0 = args.get(0usize as i32); - let arg_0 = match deno_core::serde_v8::from_v8(scope, arg_0) { - Ok(v) => v, - Err(err) => { - let msg = format!( - "Error parsing args at position {}: {}", 0usize, - deno_core::anyhow::Error::from(err) - ); - return deno_core::_ops::throw_type_error(scope, msg); - } - }; - let arg_1 = args.get(1usize as i32); - let arg_1 = match deno_core::serde_v8::from_v8(scope, arg_1) { - Ok(v) => v, - Err(err) => { - let msg = format!( - "Error parsing args at position {}: {}", 1usize, - deno_core::anyhow::Error::from(err) - ); - return deno_core::_ops::throw_type_error(scope, msg); - } - }; - let result = Self::call( - &mut std::cell::RefCell::borrow_mut(&ctx.state), - arg_0, - arg_1, - ); - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - match result { - Ok(result) => {} - Err(err) => { - let exception = deno_core::error::to_v8_error( - scope, - op_state.get_error_class_fn, - &err, - ); - scope.throw_exception(exception); - } - }; - } -} -impl op_set_nodelay { - #[allow(clippy::too_many_arguments)] - fn op_set_nodelay_fast_fn( - _: deno_core::v8::Local, - rid: ResourceId, - nodelay: bool, - fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions, - ) -> () { - use deno_core::v8; - use deno_core::_ops; - let __opts: &mut v8::fast_api::FastApiCallbackOptions = unsafe { - &mut *fast_api_callback_options - }; - let __ctx = unsafe { - &*(v8::Local::::cast(unsafe { __opts.data.data }).value() - as *const _ops::OpCtx) - }; - let state = &mut ::std::cell::RefCell::borrow_mut(&__ctx.state); - let result = Self::call(state, rid, nodelay); - match result { - Ok(result) => result, - Err(err) => { - state.last_fast_op_error.replace(err); - __opts.fallback = true; - } - } - } -} diff --git a/ops/optimizer_tests/unit_result2.rs b/ops/optimizer_tests/unit_result2.rs deleted file mode 100644 index 83b73e1948..0000000000 --- a/ops/optimizer_tests/unit_result2.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub fn op_set_nodelay( - state: &mut OpState, - rid: ResourceId, - nodelay: bool, -) -> Result<(), AnyError> { - let resource: Rc = - state.resource_table.get::(rid)?; - resource.set_nodelay(nodelay) -} diff --git a/ops/optimizer_tests/unit_ret.expected b/ops/optimizer_tests/unit_ret.expected deleted file mode 100644 index 5d414e1e33..0000000000 --- a/ops/optimizer_tests/unit_ret.expected +++ /dev/null @@ -1,11 +0,0 @@ -=== Optimizer Dump === -returns_result: false -has_ref_opstate: false -has_rc_opstate: false -has_fast_callback_option: false -needs_fast_callback_option: false -fast_result: Some(Void) -fast_parameters: [V8Value] -transforms: {} -is_async: false -fast_compatible: true diff --git a/ops/optimizer_tests/unit_ret.out b/ops/optimizer_tests/unit_ret.out deleted file mode 100644 index 1a721c4079..0000000000 --- a/ops/optimizer_tests/unit_ret.out +++ /dev/null @@ -1,109 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `op_unit::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct op_unit { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_unit { - const NAME: &'static str = stringify!(op_unit); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value], - CType::Void, - Self::op_unit_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl op_unit { - pub const fn name() -> &'static str { - stringify!(op_unit) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value], - CType::Void, - Self::op_unit_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - fn call<'scope>() -> () { - () - } - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - let result = Self::call(); - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - match deno_core::serde_v8::to_v8(scope, result) { - Ok(ret) => rv.set(ret), - Err(err) => { - deno_core::_ops::throw_type_error( - scope, - format!( - "Error serializing return: {}", - deno_core::anyhow::Error::from(err) - ), - ) - } - }; - } -} -impl op_unit { - #[allow(clippy::too_many_arguments)] - fn op_unit_fast_fn(_: deno_core::v8::Local) -> () { - use deno_core::v8; - use deno_core::_ops; - let result = Self::call(); - result - } -} diff --git a/ops/optimizer_tests/unit_ret.rs b/ops/optimizer_tests/unit_ret.rs deleted file mode 100644 index 4cf3651db7..0000000000 --- a/ops/optimizer_tests/unit_ret.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn op_unit() -> () { - () -} diff --git a/ops/optimizer_tests/wasm_op.expected b/ops/optimizer_tests/wasm_op.expected deleted file mode 100644 index 8d3719cf79..0000000000 --- a/ops/optimizer_tests/wasm_op.expected +++ /dev/null @@ -1,11 +0,0 @@ -=== Optimizer Dump === -returns_result: false -has_ref_opstate: false -has_rc_opstate: false -has_fast_callback_option: false -needs_fast_callback_option: false -fast_result: Some(Void) -fast_parameters: [V8Value] -transforms: {0: Transform { kind: WasmMemory, index: 0 }} -is_async: false -fast_compatible: true diff --git a/ops/optimizer_tests/wasm_op.out b/ops/optimizer_tests/wasm_op.out deleted file mode 100644 index 023506fc26..0000000000 --- a/ops/optimizer_tests/wasm_op.out +++ /dev/null @@ -1,107 +0,0 @@ -#[allow(non_camel_case_types)] -///Auto-generated by `deno_ops`, i.e: `#[op]` -/// -///Use `op_wasm::decl()` to get an op-declaration -///you can include in a `deno_core::Extension`. -pub struct op_wasm { - _phantom_data: ::std::marker::PhantomData<()>, -} -impl deno_core::_ops::Op for op_wasm { - const NAME: &'static str = stringify!(op_wasm); - const DECL: deno_core::OpDecl = deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, CallbackOptions], - CType::Void, - Self::op_wasm_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 0, - }; -} -#[doc(hidden)] -impl op_wasm { - pub const fn name() -> &'static str { - stringify!(op_wasm) - } - #[allow(clippy::not_unsafe_ptr_arg_deref)] - pub extern "C" fn v8_fn_ptr(info: *const deno_core::v8::FunctionCallbackInfo) { - let info = unsafe { &*info }; - let scope = &mut unsafe { deno_core::v8::CallbackScope::new(info) }; - let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info( - info, - ); - let rv = deno_core::v8::ReturnValue::from_function_callback_info(info); - Self::v8_func(scope, args, rv); - } - pub const fn decl() -> deno_core::OpDecl { - deno_core::OpDecl { - name: Self::name(), - v8_fn_ptr: Self::v8_fn_ptr as _, - enabled: true, - fast_fn: { - use deno_core::v8::fast_api::CType; - use deno_core::v8::fast_api::Type::*; - Some( - deno_core::v8::fast_api::FastFunction::new( - &[V8Value, CallbackOptions], - CType::Void, - Self::op_wasm_fast_fn as *const ::std::ffi::c_void, - ), - ) - }, - is_async: false, - is_unstable: false, - is_v8: false, - arg_count: 1usize as u8, - } - } - #[inline] - #[allow(clippy::too_many_arguments)] - #[allow(clippy::extra_unused_lifetimes)] - fn call<'scope>(memory: Option<&mut [u8]>) {} - pub fn v8_func<'scope>( - scope: &mut deno_core::v8::HandleScope<'scope>, - args: deno_core::v8::FunctionCallbackArguments, - mut rv: deno_core::v8::ReturnValue, - ) { - let ctx = unsafe { - &*(deno_core::v8::Local::::cast(args.data()).value() - as *const deno_core::_ops::OpCtx) - }; - let arg_0 = None; - let result = Self::call(arg_0); - let op_state = ::std::cell::RefCell::borrow(&*ctx.state); - op_state.tracker.track_sync(ctx.id); - } -} -impl op_wasm { - #[allow(clippy::too_many_arguments)] - fn op_wasm_fast_fn( - _: deno_core::v8::Local, - fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions, - ) -> () { - use deno_core::v8; - use deno_core::_ops; - let __opts: &mut v8::fast_api::FastApiCallbackOptions = unsafe { - &mut *fast_api_callback_options - }; - let memory = unsafe { - &*(__opts.wasm_memory - as *const deno_core::v8::fast_api::FastApiTypedArray) - } - .get_storage_if_aligned(); - let result = Self::call(memory); - result - } -} diff --git a/ops/optimizer_tests/wasm_op.rs b/ops/optimizer_tests/wasm_op.rs deleted file mode 100644 index b18f32fd15..0000000000 --- a/ops/optimizer_tests/wasm_op.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn op_wasm(memory: Option<&mut [u8]>) { - // @test-attr:wasm -} diff --git a/ops/tests/compile_fail/mem_slices.rs b/ops/tests/compile_fail/mem_slices.rs deleted file mode 100644 index da74ac5777..0000000000 --- a/ops/tests/compile_fail/mem_slices.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use deno_ops::op; - -#[op] -fn sync_test(slice: &mut [u32]) { - // -} - -#[op] -async fn async_test(slice: &[u8]) { - // Memory slices are not allowed in async ops. -} - -#[op] -fn async_test2(slice: &mut [u8]) -> impl Future { - // Memory slices are not allowed in async ops, even when not implemented as an - // async function. - async {} -} - -fn main() { - // pass -} diff --git a/ops/tests/compile_fail/mem_slices.stderr b/ops/tests/compile_fail/mem_slices.stderr deleted file mode 100644 index c45acfcf9d..0000000000 --- a/ops/tests/compile_fail/mem_slices.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: custom attribute panicked - --> tests/compile_fail/mem_slices.rs:10:1 - | -10 | #[op] - | ^^^^^ - | - = help: message: Memory slices are not allowed in async ops - -error: custom attribute panicked - --> tests/compile_fail/mem_slices.rs:15:1 - | -15 | #[op] - | ^^^^^ - | - = help: message: Memory slices are not allowed in async ops diff --git a/serde_v8/Cargo.toml b/serde_v8/Cargo.toml deleted file mode 100644 index 527bc27a06..0000000000 --- a/serde_v8/Cargo.toml +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -[package] -name = "serde_v8" -version = "0.102.0" -authors.workspace = true -edition.workspace = true -license.workspace = true -readme = "README.md" -repository.workspace = true -description = "Rust to V8 serialization and deserialization" - -[lib] -path = "lib.rs" - -[dependencies] -bytes.workspace = true -derive_more = "0.99.17" -num-bigint.workspace = true -serde.workspace = true -serde_bytes.workspace = true -smallvec = { workspace = true, features = ["union"] } -thiserror.workspace = true -v8.workspace = true - -[dev-dependencies] -bencher.workspace = true -serde_json.workspace = true - -[[example]] -name = "basic" - -[[bench]] -name = "de" -harness = false - -[[bench]] -name = "ser" -harness = false diff --git a/serde_v8/README.md b/serde_v8/README.md deleted file mode 100644 index d36f69375a..0000000000 --- a/serde_v8/README.md +++ /dev/null @@ -1,57 +0,0 @@ -# serde_v8 - -Author: Aaron O'Mullan - -Serde support for encoding/decoding (rusty_)v8 values. - -Broadly `serde_v8` aims to provide an expressive but ~maximally efficient -encoding layer to biject rust & v8/js values. It's a core component of deno's -op-layer and is used to encode/decode all non-buffer values. - -**Original issue:** -[denoland/deno#9540](https://github.com/denoland/deno/issues/9540) - -## Quickstart - -`serde_v8` fits naturally into the serde ecosystem, so if you've already used -`serde` or `serde_json`, `serde_v8`'s API should be very familiar. - -`serde_v8` exposes two key-functions: - -- `to_v8`: maps `rust->v8`, similar to `serde_json::to_string`, ... -- `from_v8`: maps `v8->rust`, similar to `serde_json::from_str`, ... - -## Best practices - -Whilst `serde_v8` is compatible with `serde_json::Value` it's important to keep -in mind that `serde_json::Value` is essentially a loosely-typed value (think -nested HashMaps), so when writing ops we recommend directly using rust -structs/tuples or primitives, since mapping to `serde_json::Value` will add -extra overhead and result in slower ops. - -I also recommend avoiding unnecessary "wrappers", if your op takes a -single-keyed struct, consider unwrapping that as a plain value unless you plan -to add fields in the near-future. - -Instead of returning "nothing" via `Ok(json!({}))`, change your return type to -rust's unit type `()` and returning `Ok(())`, `serde_v8` will efficiently encode -that as a JS `null`. - -## Advanced features - -If you need to mix rust & v8 values in structs/tuples, you can use the special -`serde_v8::Value` type, which will passthrough the original v8 value untouched -when encoding/decoding. - -## TODO - -- [ ] Experiment with KeyCache to optimize struct keys -- [ ] Experiment with external v8 strings -- [ ] Explore using - [json-stringifier.cc](https://chromium.googlesource.com/v8/v8/+/refs/heads/master/src/json/json-stringifier.cc)'s - fast-paths for arrays -- [ ] Improve tests to test parity with `serde_json` (should be mostly - interchangeable) -- [ ] Consider a `Payload` type that's deserializable by itself (holds scope & - value) -- [ ] Ensure we return errors instead of panicking on `.unwrap()`s diff --git a/serde_v8/benches/de.rs b/serde_v8/benches/de.rs deleted file mode 100644 index b7cd0bf321..0000000000 --- a/serde_v8/benches/de.rs +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use bencher::benchmark_group; -use bencher::benchmark_main; -use bencher::Bencher; - -use serde::Deserialize; - -use serde_v8::utils::js_exec; -use serde_v8::utils::v8_do; -use serde_v8::ByteString; - -#[derive(Debug, Deserialize, PartialEq)] -struct MathOp { - arg1: u64, - arg2: u64, - operator: Option, -} - -fn dedo( - code: &str, - f: impl FnOnce(&mut v8::HandleScope, v8::Local), -) { - v8_do(|| { - let isolate = &mut v8::Isolate::new(v8::CreateParams::default()); - let handle_scope = &mut v8::HandleScope::new(isolate); - let context = v8::Context::new(handle_scope); - let scope = &mut v8::ContextScope::new(handle_scope, context); - let v = js_exec(scope, code); - - f(scope, v); - }) -} - -fn dedo_json(code: &str, f: impl FnOnce(String)) { - let code = format!("JSON.stringify({code})"); - dedo(&code[..], |scope, v| { - let s: String = serde_v8::from_v8(scope, v).unwrap(); - f(s); - }) -} - -fn de_struct_v8(b: &mut Bencher) { - dedo("({arg1: 10, arg2: 123 })", |scope, obj| { - let mut total = 0; - b.iter(move || { - let op: MathOp = serde_v8::from_v8(scope, obj).unwrap(); - total = total + op.arg1 + op.arg2; - }); - }); -} - -fn de_struct_v8_opt(b: &mut Bencher) { - dedo("({arg1: 10, arg2: 123 })", |scope, v| { - let k_arg1 = v8::String::new(scope, "arg1").unwrap().into(); - let k_arg2 = v8::String::new(scope, "arg2").unwrap().into(); - let obj = v8::Local::::try_from(v).unwrap(); - let mut total = 0; - b.iter(move || { - let v_arg1 = obj.get(scope, k_arg1).unwrap(); - let v_arg2 = obj.get(scope, k_arg2).unwrap(); - let op = MathOp { - arg1: serde_v8::from_v8(scope, v_arg1).unwrap(), - arg2: serde_v8::from_v8(scope, v_arg2).unwrap(), - operator: None, - }; - total = total + op.arg1 + op.arg2; - }); - }); -} - -fn de_struct_json(b: &mut Bencher) { - dedo_json("({arg1: 10, arg2: 123 })", |s| { - let mut total = 0; - b.iter(move || { - let op: MathOp = serde_json::from_str(&s).unwrap(); - total = total + op.arg1 + op.arg2; - }); - }); -} - -fn de_struct_json_deopt(b: &mut Bencher) { - // JSON.stringify() in loop (semi-simulating ABI loop) - dedo("({arg1: 10, arg2: 123 })", |scope, obj| { - let mut total = 0; - b.iter(move || { - let mut scope = v8::HandleScope::new(scope); - let s = v8::json::stringify(&mut scope, obj).unwrap(); - let rs = s.to_rust_string_lossy(&mut scope); - let op: MathOp = serde_json::from_str(&rs).unwrap(); - total = total + op.arg1 + op.arg2; - }); - }); -} - -macro_rules! dualbench { - ($v8_fn:ident, $json_fn:ident, $src:expr, $t:ty) => { - fn $v8_fn(b: &mut Bencher) { - dedo($src, |scope, v| { - b.iter(move || { - let _: $t = serde_v8::from_v8(scope, v).unwrap(); - }); - }); - } - - fn $json_fn(b: &mut Bencher) { - dedo_json($src, |s| { - b.iter(move || { - let _: $t = serde_json::from_str(&s).unwrap(); - }); - }); - } - }; -} - -dualbench!(de_bool_v8, de_bool_json, "true", bool); -dualbench!(de_int_v8, de_int_json, "12345", u32); -dualbench!( - de_array_v8, - de_array_json, - "[1,2,3,4,5,6,7,8,9,10]", - Vec -); -dualbench!(de_str_v8, de_str_json, "'hello world'", String); -dualbench!(de_tuple_v8, de_tuple_json, "[1,false]", (u8, bool)); - -fn de_tuple_v8_opt(b: &mut Bencher) { - dedo("[1,false]", |scope, obj| { - let arr = v8::Local::::try_from(obj).unwrap(); - let obj = v8::Local::::from(arr); - - b.iter(move || { - let v1 = obj.get_index(scope, 0).unwrap(); - let v2 = obj.get_index(scope, 1).unwrap(); - let _: (u8, bool) = ( - serde_v8::from_v8(scope, v1).unwrap(), - serde_v8::from_v8(scope, v2).unwrap(), - ); - }); - }); -} - -fn de_bstr_v8_12_b(b: &mut Bencher) { - dedo(r#""hello world\n""#, |scope, v| { - b.iter(move || { - let _: ByteString = serde_v8::from_v8(scope, v).unwrap(); - }); - }); -} - -fn de_bstr_v8_1024_b(b: &mut Bencher) { - dedo( - r#""hello world\n".repeat(1e2).slice(0, 1024)"#, - |scope, v| { - b.iter(move || { - let _: ByteString = serde_v8::from_v8(scope, v).unwrap(); - }); - }, - ); -} - -fn de_sob_str_6b(b: &mut Bencher) { - dedo("'byebye'", |scope, v| { - b.iter(move || { - let _: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap(); - }) - }) -} - -fn de_sob_str_1kb(b: &mut Bencher) { - dedo("'deno'.repeat(256)", |scope, v| { - b.iter(move || { - let _: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap(); - }) - }) -} - -fn de_sob_buf_1b(b: &mut Bencher) { - dedo("new Uint8Array([97])", |scope, v| { - b.iter(move || { - let _: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap(); - }) - }) -} - -fn de_sob_buf_1kb(b: &mut Bencher) { - dedo("(new Uint8Array(1*1024)).fill(42)", |scope, v| { - b.iter(move || { - let _: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap(); - }) - }) -} - -fn de_sob_buf_16kb(b: &mut Bencher) { - dedo("(new Uint8Array(16*1024)).fill(42)", |scope, v| { - b.iter(move || { - let _: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap(); - }) - }) -} - -fn de_sob_buf_512kb(b: &mut Bencher) { - dedo("(new Uint8Array(512*1024)).fill(42)", |scope, v| { - b.iter(move || { - let _: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap(); - }) - }) -} - -benchmark_group!( - benches, - de_struct_v8, - de_struct_v8_opt, - de_struct_json, - de_struct_json_deopt, - de_bool_v8, - de_bool_json, - de_int_v8, - de_int_json, - de_array_v8, - de_array_json, - de_str_v8, - de_str_json, - de_tuple_v8, - de_tuple_json, - de_tuple_v8_opt, - de_bstr_v8_12_b, - de_bstr_v8_1024_b, - de_sob_str_6b, - de_sob_str_1kb, - de_sob_buf_1b, - de_sob_buf_1kb, - de_sob_buf_16kb, - de_sob_buf_512kb, -); - -benchmark_main!(benches); diff --git a/serde_v8/benches/ser.rs b/serde_v8/benches/ser.rs deleted file mode 100644 index 83274fae3a..0000000000 --- a/serde_v8/benches/ser.rs +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use bencher::benchmark_group; -use bencher::benchmark_main; -use bencher::Bencher; - -use serde::Serialize; - -use serde_v8::utils::v8_do; -use serde_v8::ByteString; - -#[derive(Serialize)] -struct MathOp { - arg1: u64, - arg2: u64, - operator: Option, -} - -fn serdo(f: impl FnOnce(&mut v8::HandleScope)) { - v8_do(|| { - let isolate = &mut v8::Isolate::new(v8::CreateParams::default()); - let handle_scope = &mut v8::HandleScope::new(isolate); - let context = v8::Context::new(handle_scope); - let scope = &mut v8::ContextScope::new(handle_scope, context); - - f(scope); - }) -} - -macro_rules! dualbench { - ($v8_fn:ident, $json_fn:ident, $src:expr) => { - fn $v8_fn(b: &mut Bencher) { - serdo(|scope| { - let v = $src; - b.iter(move || { - let _ = serde_v8::to_v8(scope, &v).unwrap(); - }); - }); - } - - fn $json_fn(b: &mut Bencher) { - let v = $src; - b.iter(move || { - let _ = serde_json::to_string(&v).unwrap(); - }); - } - }; -} - -dualbench!( - ser_struct_v8, - ser_struct_json, - MathOp { - arg1: 10, - arg2: 123, - operator: None - } -); -dualbench!(ser_bool_v8, ser_bool_json, true); -dualbench!(ser_int_v8, ser_int_json, 12345); -dualbench!( - ser_array_v8, - ser_array_json, - vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10] -); -dualbench!(ser_str_v8, ser_str_json, "hello world"); -dualbench!(ser_tuple_v8, ser_tuple_json, (1, false)); - -fn ser_struct_v8_manual(b: &mut Bencher) { - serdo(|scope| { - let v = MathOp { - arg1: 10, - arg2: 123, - operator: None, - }; - b.iter(|| { - let obj = v8::Object::new(scope); - let k1 = v8::String::new(scope, "arg1").unwrap(); - let k2 = v8::String::new(scope, "arg2").unwrap(); - let k3 = v8::String::new(scope, "operator").unwrap(); - // let k1 = v8::String::new_from_utf8(scope, "arg1".as_ref(), v8::NewStringType::Internalized).unwrap(); - // let k2 = v8::String::new_from_utf8(scope, "arg2".as_ref(), v8::NewStringType::Internalized).unwrap(); - // let k3 = v8::String::new_from_utf8(scope, "operator".as_ref(), v8::NewStringType::Internalized).unwrap(); - let v1 = v8::Number::new(scope, v.arg1 as f64); - let v2 = v8::Number::new(scope, v.arg2 as f64); - let v3 = v8::null(scope); - obj.set(scope, k1.into(), v1.into()).unwrap(); - obj.set(scope, k2.into(), v2.into()).unwrap(); - obj.set(scope, k3.into(), v3.into()).unwrap(); - }); - }); -} - -fn ser_bstr_12_b(b: &mut Bencher) { - serdo(|scope| { - let bstr = ByteString::from("hello world\n"); - b.iter(|| { - let _ = serde_v8::to_v8(scope, &bstr).unwrap(); - }); - }); -} - -fn ser_bstr_1024_b(b: &mut Bencher) { - serdo(|scope| { - let mut s = "hello world\n".repeat(100); - s.truncate(1024); - let bstr = ByteString::from(s); - b.iter(|| { - let _ = serde_v8::to_v8(scope, &bstr).unwrap(); - }); - }); -} - -benchmark_group!( - benches, - ser_struct_v8, - ser_struct_json, - ser_bool_v8, - ser_bool_json, - ser_int_v8, - ser_int_json, - ser_array_v8, - ser_array_json, - ser_str_v8, - ser_str_json, - ser_tuple_v8, - ser_tuple_json, - ser_struct_v8_manual, - ser_bstr_12_b, - ser_bstr_1024_b, -); -benchmark_main!(benches); diff --git a/serde_v8/de.rs b/serde_v8/de.rs deleted file mode 100644 index eb07271b45..0000000000 --- a/serde_v8/de.rs +++ /dev/null @@ -1,787 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use serde::de::SeqAccess as _; -use serde::de::Visitor; -use serde::de::{self}; -use serde::Deserialize; - -use crate::error::value_to_type_str; -use crate::error::Error; -use crate::error::Result; -use crate::keys::v8_struct_key; -use crate::keys::KeyCache; -use crate::magic; -use crate::magic::transl8::visit_magic; -use crate::magic::transl8::FromV8; -use crate::magic::transl8::MagicType; -use crate::payload::ValueType; -use crate::AnyValue; -use crate::BigInt; -use crate::ByteString; -use crate::DetachedBuffer; -use crate::JsBuffer; -use crate::StringOrBuffer; -use crate::U16String; - -pub struct Deserializer<'a, 'b, 's> { - input: v8::Local<'a, v8::Value>, - scope: &'b mut v8::HandleScope<'s>, - _key_cache: Option<&'b mut KeyCache>, -} - -impl<'a, 'b, 's> Deserializer<'a, 'b, 's> { - pub fn new( - scope: &'b mut v8::HandleScope<'s>, - input: v8::Local<'a, v8::Value>, - key_cache: Option<&'b mut KeyCache>, - ) -> Self { - Deserializer { - input, - scope, - _key_cache: key_cache, - } - } -} - -// from_v8 deserializes a v8::Value into a Deserializable / rust struct -pub fn from_v8<'de, 'a, 'b, 's, T>( - scope: &'b mut v8::HandleScope<'s>, - input: v8::Local<'a, v8::Value>, -) -> Result -where - T: Deserialize<'de>, -{ - let mut deserializer = Deserializer::new(scope, input, None); - let t = T::deserialize(&mut deserializer)?; - Ok(t) -} - -// like from_v8 except accepts a KeyCache to optimize struct key decoding -pub fn from_v8_cached<'de, 'a, 'b, 's, T>( - scope: &'b mut v8::HandleScope<'s>, - input: v8::Local<'a, v8::Value>, - key_cache: &mut KeyCache, -) -> Result -where - T: Deserialize<'de>, -{ - let mut deserializer = Deserializer::new(scope, input, Some(key_cache)); - let t = T::deserialize(&mut deserializer)?; - Ok(t) -} - -macro_rules! deserialize_signed { - ($dmethod:ident, $vmethod:ident, $t:tt) => { - fn $dmethod(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - visitor.$vmethod( - if let Ok(x) = v8::Local::::try_from(self.input) { - x.value() as $t - } else if let Ok(x) = v8::Local::::try_from(self.input) { - x.i64_value().0 as $t - } else { - return Err(Error::ExpectedInteger(value_to_type_str(self.input))); - }, - ) - } - }; -} - -macro_rules! deserialize_unsigned { - ($dmethod:ident, $vmethod:ident, $t:tt) => { - fn $dmethod(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - visitor.$vmethod( - if let Ok(x) = v8::Local::::try_from(self.input) { - x.value() as $t - } else if let Ok(x) = v8::Local::::try_from(self.input) { - x.u64_value().0 as $t - } else { - return Err(Error::ExpectedInteger(value_to_type_str(self.input))); - }, - ) - } - }; -} - -impl<'de, 'a, 'b, 's, 'x> de::Deserializer<'de> - for &'x mut Deserializer<'a, 'b, 's> -{ - type Error = Error; - - fn deserialize_any(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match ValueType::from_v8(self.input) { - ValueType::Null => self.deserialize_unit(visitor), - ValueType::Bool => self.deserialize_bool(visitor), - // Handle floats & ints separately to work with loosely-typed serde_json - ValueType::Number => { - if self.input.is_uint32() { - self.deserialize_u32(visitor) - } else if self.input.is_int32() { - self.deserialize_i32(visitor) - } else { - self.deserialize_f64(visitor) - } - } - ValueType::BigInt => Err(Error::UnsupportedType), - ValueType::String => self.deserialize_string(visitor), - ValueType::Array => self.deserialize_seq(visitor), - ValueType::Object => self.deserialize_map(visitor), - // Map to Vec when deserialized via deserialize_any - // e.g: for untagged enums or StringOrBuffer - ValueType::ArrayBufferView | ValueType::ArrayBuffer => { - magic::v8slice::V8Slice::from_v8(&mut *self.scope, self.input) - .and_then(|zb| visitor.visit_byte_buf(Vec::from(&*zb))) - } - } - } - - fn deserialize_bool(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - // Relaxed typechecking, will map all non-true vals to false - visitor.visit_bool(self.input.is_true()) - } - - // signed - deserialize_signed!(deserialize_i8, visit_i8, i8); - deserialize_signed!(deserialize_i16, visit_i16, i16); - deserialize_signed!(deserialize_i32, visit_i32, i32); - deserialize_signed!(deserialize_i64, visit_i64, i64); - // unsigned - deserialize_unsigned!(deserialize_u8, visit_u8, u8); - deserialize_unsigned!(deserialize_u16, visit_u16, u16); - deserialize_unsigned!(deserialize_u32, visit_u32, u32); - deserialize_unsigned!(deserialize_u64, visit_u64, u64); - - fn deserialize_f32(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_f64(visitor) - } - fn deserialize_f64(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - visitor.visit_f64( - if let Ok(x) = v8::Local::::try_from(self.input) { - x.value() - } else if let Ok(x) = v8::Local::::try_from(self.input) { - bigint_to_f64(x) - } else { - return Err(Error::ExpectedNumber(value_to_type_str(self.input))); - }, - ) - } - - fn deserialize_char(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_str(visitor) - } - - fn deserialize_str(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_string(visitor) - } - - fn deserialize_string(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - if self.input.is_string() || self.input.is_string_object() { - let v8_string = self.input.to_string(self.scope).unwrap(); - let string = to_utf8(v8_string, self.scope); - visitor.visit_string(string) - } else { - Err(Error::ExpectedString(value_to_type_str(self.input))) - } - } - - fn deserialize_option(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - if self.input.is_null_or_undefined() { - visitor.visit_none() - } else { - visitor.visit_some(self) - } - } - - fn deserialize_unit(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - visitor.visit_unit() - } - - fn deserialize_unit_struct( - self, - _name: &'static str, - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - self.deserialize_unit(visitor) - } - - // As is done here, serializers are encouraged to treat newtype structs as - // insignificant wrappers around the data they contain. That means not - // parsing anything other than the contained value. - fn deserialize_newtype_struct( - self, - _name: &'static str, - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - visitor.visit_newtype_struct(self) - } - - fn deserialize_seq(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - let arr = v8::Local::::try_from(self.input) - .map_err(|_| Error::ExpectedArray(value_to_type_str(self.input)))?; - visitor.visit_seq(SeqAccess::new(arr.into(), self.scope, 0..arr.length())) - } - - // Like deserialize_seq except it prefers tuple's length over input array's length - fn deserialize_tuple(self, len: usize, visitor: V) -> Result - where - V: Visitor<'de>, - { - let obj = v8::Local::::try_from(self.input).unwrap(); - if obj.is_array() { - // If the obj is an array fail if it's length differs from the tuple length - let array = v8::Local::::try_from(self.input).unwrap(); - let array_len = array.length() as usize; - if array_len != len { - return Err(Error::LengthMismatch(array_len, len)); - } - } - visitor.visit_seq(SeqAccess::new(obj, self.scope, 0..len as u32)) - } - - // Tuple structs look just like sequences in JSON. - fn deserialize_tuple_struct( - self, - _name: &'static str, - len: usize, - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - self.deserialize_tuple(len, visitor) - } - - fn deserialize_map(self, visitor: V) -> Result - where - V: de::Visitor<'de>, - { - // Assume object, then get_own_property_names - let obj = v8::Local::::try_from(self.input) - .map_err(|_| Error::ExpectedObject(value_to_type_str(self.input)))?; - - if v8::Local::::try_from(self.input).is_ok() { - let pairs_array = v8::Local::::try_from(self.input) - .unwrap() - .as_array(self.scope); - let map = MapPairsAccess { - pos: 0, - len: pairs_array.length(), - obj: pairs_array, - scope: self.scope, - }; - visitor.visit_map(map) - } else { - visitor.visit_map(MapObjectAccess::new(obj, self.scope)) - } - } - - fn deserialize_struct( - self, - name: &'static str, - fields: &'static [&'static str], - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - match name { - JsBuffer::MAGIC_NAME => { - visit_magic(visitor, JsBuffer::from_v8(self.scope, self.input)?) - } - DetachedBuffer::MAGIC_NAME => { - visit_magic(visitor, DetachedBuffer::from_v8(self.scope, self.input)?) - } - ByteString::MAGIC_NAME => { - visit_magic(visitor, ByteString::from_v8(self.scope, self.input)?) - } - U16String::MAGIC_NAME => { - visit_magic(visitor, U16String::from_v8(self.scope, self.input)?) - } - StringOrBuffer::MAGIC_NAME => { - visit_magic(visitor, StringOrBuffer::from_v8(self.scope, self.input)?) - } - BigInt::MAGIC_NAME => { - visit_magic(visitor, BigInt::from_v8(self.scope, self.input)?) - } - magic::Value::MAGIC_NAME => { - visit_magic(visitor, magic::Value::from_v8(self.scope, self.input)?) - } - AnyValue::MAGIC_NAME => { - visit_magic(visitor, AnyValue::from_v8(self.scope, self.input)?) - } - _ => { - // Regular struct - let obj = v8::Local::::try_from(self.input) - .map_err(|_| Error::ExpectedObject(value_to_type_str(self.input)))?; - - // Fields names are a hint and must be inferred when not provided - if fields.is_empty() { - visitor.visit_map(MapObjectAccess::new(obj, self.scope)) - } else { - visitor.visit_map(StructAccess { - obj, - scope: self.scope, - keys: fields.iter(), - next_value: None, - }) - } - } - } - } - - /// To be compatible with `serde-json`, we expect enums to be: - /// - `"Variant"`: strings for unit variants, i.e: Enum::Variant - /// - `{ Variant: payload }`: single K/V pairs, converted to `Enum::Variant { payload }` - fn deserialize_enum( - self, - _name: &str, - _variants: &'static [&'static str], - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - // Unit variant - if self.input.is_string() || self.input.is_string_object() { - let payload = v8::undefined(self.scope).into(); - visitor.visit_enum(EnumAccess { - scope: self.scope, - tag: self.input, - payload, - }) - } - // Struct or tuple variant - else if self.input.is_object() { - // Assume object - let obj = v8::Local::::try_from(self.input).unwrap(); - // Unpack single-key - let tag = { - let prop_names = - obj.get_own_property_names(self.scope, Default::default()); - let prop_names = prop_names - .ok_or_else(|| Error::ExpectedEnum(value_to_type_str(self.input)))?; - let prop_names_len = prop_names.length(); - if prop_names_len != 1 { - return Err(Error::LengthMismatch(prop_names_len as usize, 1)); - } - prop_names.get_index(self.scope, 0).unwrap() - }; - - let payload = obj.get(self.scope, tag).unwrap(); - visitor.visit_enum(EnumAccess { - scope: self.scope, - tag, - payload, - }) - } else { - Err(Error::ExpectedEnum(value_to_type_str(self.input))) - } - } - - // An identifier in Serde is the type that identifies a field of a struct or - // the variant of an enum. In JSON, struct fields and enum variants are - // represented as strings. In other formats they may be represented as - // numeric indices. - fn deserialize_identifier(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_str(visitor) - } - - fn deserialize_ignored_any(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - visitor.visit_none() - } - - fn deserialize_bytes(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - magic::buffer::JsBuffer::from_v8(self.scope, self.input) - .and_then(|zb| visitor.visit_bytes(&zb)) - } - - fn deserialize_byte_buf(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - magic::buffer::JsBuffer::from_v8(self.scope, self.input) - .and_then(|zb| visitor.visit_byte_buf(Vec::from(&*zb))) - } -} - -struct MapObjectAccess<'a, 's> { - obj: v8::Local<'a, v8::Object>, - keys: SeqAccess<'a, 's>, - next_value: Option>, -} - -impl<'a, 's> MapObjectAccess<'a, 's> { - pub fn new( - obj: v8::Local<'a, v8::Object>, - scope: &'a mut v8::HandleScope<'s>, - ) -> Self { - let keys = match obj.get_own_property_names( - scope, - v8::GetPropertyNamesArgsBuilder::new() - .key_conversion(v8::KeyConversionMode::ConvertToString) - .build(), - ) { - Some(keys) => SeqAccess::new(keys.into(), scope, 0..keys.length()), - None => SeqAccess::new(obj, scope, 0..0), - }; - - Self { - obj, - keys, - next_value: None, - } - } -} - -impl<'de> de::MapAccess<'de> for MapObjectAccess<'_, '_> { - type Error = Error; - - fn next_key_seed>( - &mut self, - seed: K, - ) -> Result> { - while let Some(key) = self.keys.next_element::()? { - let v8_val = self.obj.get(self.keys.scope, key.v8_value).unwrap(); - if v8_val.is_undefined() { - // Historically keys/value pairs with undefined values are not added to the output - continue; - } - self.next_value = Some(v8_val); - let mut deserializer = - Deserializer::new(self.keys.scope, key.v8_value, None); - return seed.deserialize(&mut deserializer).map(Some); - } - Ok(None) - } - - fn next_value_seed>( - &mut self, - seed: V, - ) -> Result { - let v8_val = self - .next_value - .take() - .expect("Call next_key_seed before next_value_seed"); - let mut deserializer = Deserializer::new(self.keys.scope, v8_val, None); - seed.deserialize(&mut deserializer) - } - - fn size_hint(&self) -> Option { - self.keys.size_hint() - } -} - -struct MapPairsAccess<'a, 's> { - obj: v8::Local<'a, v8::Array>, - pos: u32, - len: u32, - scope: &'a mut v8::HandleScope<'s>, -} - -impl<'de> de::MapAccess<'de> for MapPairsAccess<'_, '_> { - type Error = Error; - - fn next_key_seed>( - &mut self, - seed: K, - ) -> Result> { - if self.pos < self.len { - let v8_key = self.obj.get_index(self.scope, self.pos).unwrap(); - self.pos += 1; - let mut deserializer = Deserializer::new(self.scope, v8_key, None); - let k = seed.deserialize(&mut deserializer)?; - Ok(Some(k)) - } else { - Ok(None) - } - } - - fn next_value_seed>( - &mut self, - seed: V, - ) -> Result { - debug_assert!(self.pos < self.len); - let v8_val = self.obj.get_index(self.scope, self.pos).unwrap(); - self.pos += 1; - let mut deserializer = Deserializer::new(self.scope, v8_val, None); - seed.deserialize(&mut deserializer) - } - - fn size_hint(&self) -> Option { - Some((self.len - self.pos) as usize / 2) - } -} - -struct StructAccess<'a, 's> { - obj: v8::Local<'a, v8::Object>, - scope: &'a mut v8::HandleScope<'s>, - keys: std::slice::Iter<'static, &'static str>, - next_value: Option>, -} - -impl<'de> de::MapAccess<'de> for StructAccess<'_, '_> { - type Error = Error; - - fn next_key_seed(&mut self, seed: K) -> Result> - where - K: de::DeserializeSeed<'de>, - { - for field in self.keys.by_ref() { - let key = v8_struct_key(self.scope, field).into(); - let val = self.obj.get(self.scope, key).unwrap(); - if val.is_undefined() { - // Historically keys/value pairs with undefined values are not added to the output - continue; - } - self.next_value = Some(val); - let mut deserializer = Deserializer::new(self.scope, key, None); - return seed.deserialize(&mut deserializer).map(Some); - } - Ok(None) - } - - fn next_value_seed(&mut self, seed: V) -> Result - where - V: de::DeserializeSeed<'de>, - { - let val = self - .next_value - .take() - .expect("Call next_key_seed before next_value_seed"); - let mut deserializer = Deserializer::new(self.scope, val, None); - seed.deserialize(&mut deserializer) - } -} - -struct SeqAccess<'a, 's> { - obj: v8::Local<'a, v8::Object>, - scope: &'a mut v8::HandleScope<'s>, - range: std::ops::Range, -} - -impl<'a, 's> SeqAccess<'a, 's> { - pub fn new( - obj: v8::Local<'a, v8::Object>, - scope: &'a mut v8::HandleScope<'s>, - range: std::ops::Range, - ) -> Self { - Self { obj, scope, range } - } -} - -impl<'de> de::SeqAccess<'de> for SeqAccess<'_, '_> { - type Error = Error; - - fn next_element_seed>( - &mut self, - seed: T, - ) -> Result> { - if let Some(pos) = self.range.next() { - let val = self.obj.get_index(self.scope, pos).unwrap(); - let mut deserializer = Deserializer::new(self.scope, val, None); - seed.deserialize(&mut deserializer).map(Some) - } else { - Ok(None) - } - } - - fn size_hint(&self) -> Option { - self.range.size_hint().1 - } -} - -struct EnumAccess<'a, 'b, 's> { - tag: v8::Local<'a, v8::Value>, - payload: v8::Local<'a, v8::Value>, - scope: &'b mut v8::HandleScope<'s>, - // p1: std::marker::PhantomData<&'x ()>, -} - -impl<'de, 'a, 'b, 's> de::EnumAccess<'de> for EnumAccess<'a, 'b, 's> { - type Error = Error; - type Variant = VariantDeserializer<'a, 'b, 's>; - - fn variant_seed>( - self, - seed: V, - ) -> Result<(V::Value, Self::Variant)> { - let seed = { - let mut dtag = Deserializer::new(self.scope, self.tag, None); - seed.deserialize(&mut dtag) - }; - let dpayload = VariantDeserializer::<'a, 'b, 's> { - scope: self.scope, - value: self.payload, - }; - - Ok((seed?, dpayload)) - } -} - -struct VariantDeserializer<'a, 'b, 's> { - value: v8::Local<'a, v8::Value>, - scope: &'b mut v8::HandleScope<'s>, -} - -impl<'de, 'a, 'b, 's> de::VariantAccess<'de> - for VariantDeserializer<'a, 'b, 's> -{ - type Error = Error; - - fn unit_variant(self) -> Result<()> { - let mut d = Deserializer::new(self.scope, self.value, None); - de::Deserialize::deserialize(&mut d) - } - - fn newtype_variant_seed>( - self, - seed: T, - ) -> Result { - let mut d = Deserializer::new(self.scope, self.value, None); - seed.deserialize(&mut d) - } - - fn tuple_variant>( - self, - len: usize, - visitor: V, - ) -> Result { - let mut d = Deserializer::new(self.scope, self.value, None); - de::Deserializer::deserialize_tuple(&mut d, len, visitor) - } - - fn struct_variant>( - self, - fields: &'static [&'static str], - visitor: V, - ) -> Result { - let mut d = Deserializer::new(self.scope, self.value, None); - de::Deserializer::deserialize_struct(&mut d, "", fields, visitor) - } -} - -fn bigint_to_f64(b: v8::Local) -> f64 { - // log2(f64::MAX) == log2(1.7976931348623157e+308) == 1024 - let mut words: [u64; 16] = [0; 16]; // 1024/64 => 16 64bit words - let (neg, words) = b.to_words_array(&mut words); - if b.word_count() > 16 { - return match neg { - true => f64::NEG_INFINITY, - false => f64::INFINITY, - }; - } - let sign = if neg { -1.0 } else { 1.0 }; - let x: f64 = words - .iter() - .enumerate() - .map(|(i, w)| (*w as f64) * 2.0f64.powi(64 * i as i32)) - .sum(); - sign * x -} - -pub fn to_utf8( - s: v8::Local, - scope: &mut v8::HandleScope, -) -> String { - to_utf8_fast(s, scope).unwrap_or_else(|| to_utf8_slow(s, scope)) -} - -fn to_utf8_fast( - s: v8::Local, - scope: &mut v8::HandleScope, -) -> Option { - // Over-allocate by 20% to avoid checking string twice - let str_chars = s.length(); - let capacity = (str_chars as f64 * 1.2) as usize; - let mut buf = Vec::with_capacity(capacity); - - let mut nchars = 0; - let bytes_len = s.write_utf8_uninit( - scope, - buf.spare_capacity_mut(), - Some(&mut nchars), - v8::WriteOptions::NO_NULL_TERMINATION - | v8::WriteOptions::REPLACE_INVALID_UTF8, - ); - - if nchars < str_chars { - return None; - } - - // SAFETY: write_utf8_uninit guarantees `bytes_len` bytes are initialized & valid utf8 - unsafe { - buf.set_len(bytes_len); - Some(String::from_utf8_unchecked(buf)) - } -} - -fn to_utf8_slow( - s: v8::Local, - scope: &mut v8::HandleScope, -) -> String { - let capacity = s.utf8_length(scope); - let mut buf = Vec::with_capacity(capacity); - - let bytes_len = s.write_utf8_uninit( - scope, - buf.spare_capacity_mut(), - None, - v8::WriteOptions::NO_NULL_TERMINATION - | v8::WriteOptions::REPLACE_INVALID_UTF8, - ); - - // SAFETY: write_utf8_uninit guarantees `bytes_len` bytes are initialized & valid utf8 - unsafe { - buf.set_len(bytes_len); - String::from_utf8_unchecked(buf) - } -} diff --git a/serde_v8/error.rs b/serde_v8/error.rs deleted file mode 100644 index 16d7882b70..0000000000 --- a/serde_v8/error.rs +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use std::fmt::Display; - -pub type Result = std::result::Result; - -#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)] -#[non_exhaustive] -pub enum Error { - #[error("{0}")] - Message(String), - - #[error("serde_v8 error: invalid type; expected: boolean, got: {0}")] - ExpectedBoolean(&'static str), - - #[error("serde_v8 error: invalid type; expected: integer, got: {0}")] - ExpectedInteger(&'static str), - - #[error("serde_v8 error: invalid type; expected: number, got: {0}")] - ExpectedNumber(&'static str), - - #[error("serde_v8 error: invalid type; expected: string, got: {0}")] - ExpectedString(&'static str), - - #[error("serde_v8 error: invalid type; expected: array, got: {0}")] - ExpectedArray(&'static str), - - #[error("serde_v8 error: invalid type; expected: map, got: {0}")] - ExpectedMap(&'static str), - - #[error("serde_v8 error: invalid type; expected: enum, got: {0}")] - ExpectedEnum(&'static str), - - #[error("serde_v8 error: invalid type; expected: object, got: {0}")] - ExpectedObject(&'static str), - - #[error("serde_v8 error: invalid type; expected: buffer, got: {0}")] - ExpectedBuffer(&'static str), - - #[error("serde_v8 error: invalid type; expected: detachable, got: {0}")] - ExpectedDetachable(&'static str), - - #[error("serde_v8 error: invalid type; expected: external, got: {0}")] - ExpectedExternal(&'static str), - - #[error("serde_v8 error: invalid type; expected: bigint, got: {0}")] - ExpectedBigInt(&'static str), - - #[error("serde_v8 error: invalid type, expected: utf8")] - ExpectedUtf8, - #[error("serde_v8 error: invalid type, expected: latin1")] - ExpectedLatin1, - - #[error("serde_v8 error: unsupported type")] - UnsupportedType, - - #[error("serde_v8 error: length mismatch, got: {0}, expected: {1}")] - LengthMismatch(usize, usize), - - #[error("serde_v8 error: can't create slice from resizable ArrayBuffer")] - ResizableBackingStoreNotSupported, -} - -impl serde::ser::Error for Error { - fn custom(msg: T) -> Self { - Error::Message(msg.to_string()) - } -} - -impl serde::de::Error for Error { - fn custom(msg: T) -> Self { - Error::Message(msg.to_string()) - } -} - -pub(crate) fn value_to_type_str(value: v8::Local) -> &'static str { - if value.is_module_namespace_object() { - "Module" - } else if value.is_wasm_module_object() { - "WASM module" - } else if value.is_wasm_memory_object() { - "WASM memory object" - } else if value.is_proxy() { - "Proxy" - } else if value.is_shared_array_buffer() { - "SharedArrayBuffer" - } else if value.is_data_view() { - "DataView" - } else if value.is_big_uint64_array() { - "BigUint64Array" - } else if value.is_big_int64_array() { - "BigInt64Array" - } else if value.is_float64_array() { - "Float64Array" - } else if value.is_float32_array() { - "Float32Array" - } else if value.is_int32_array() { - "Int32Array" - } else if value.is_uint32_array() { - "Uint32Array" - } else if value.is_int16_array() { - "Int16Array" - } else if value.is_uint16_array() { - "Uint16Array" - } else if value.is_int8_array() { - "Int8Array" - } else if value.is_uint8_clamped_array() { - "Uint8ClampedArray" - } else if value.is_uint8_array() { - "Uint8Array" - } else if value.is_typed_array() { - "TypedArray" - } else if value.is_array_buffer_view() { - "ArrayBufferView" - } else if value.is_array_buffer() { - "ArrayBuffer" - } else if value.is_weak_set() { - "WeakSet" - } else if value.is_weak_map() { - "WeakMap" - } else if value.is_set_iterator() { - "Set Iterator" - } else if value.is_map_iterator() { - "Map Iterator" - } else if value.is_set() { - "Set" - } else if value.is_map() { - "Map" - } else if value.is_promise() { - "Promise" - } else if value.is_generator_function() { - "Generator function" - } else if value.is_async_function() { - "Async function" - } else if value.is_reg_exp() { - "RegExp" - } else if value.is_date() { - "Date" - } else if value.is_number() { - "Number" - } else if value.is_boolean() { - "Boolean" - } else if value.is_big_int() { - "bigint" - } else if value.is_array() { - "array" - } else if value.is_function() { - "function" - } else if value.is_symbol() { - "symbol" - } else if value.is_string() { - "string" - } else if value.is_null() { - "null" - } else if value.is_undefined() { - "undefined" - } else { - "unknown" - } -} diff --git a/serde_v8/examples/basic.rs b/serde_v8/examples/basic.rs deleted file mode 100644 index 29fb980854..0000000000 --- a/serde_v8/examples/basic.rs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use serde::Deserialize; - -#[derive(Debug, Deserialize)] -struct MathOp { - pub a: u64, - pub b: u64, - pub operator: Option, -} - -fn main() { - let platform = v8::new_default_platform(0, false).make_shared(); - v8::V8::initialize_platform(platform); - v8::V8::initialize(); - - { - let isolate = &mut v8::Isolate::new(v8::CreateParams::default()); - let handle_scope = &mut v8::HandleScope::new(isolate); - let context = v8::Context::new(handle_scope); - let scope = &mut v8::ContextScope::new(handle_scope, context); - - fn exec<'s>( - scope: &mut v8::HandleScope<'s>, - src: &str, - ) -> v8::Local<'s, v8::Value> { - let code = v8::String::new(scope, src).unwrap(); - let script = v8::Script::compile(scope, code, None).unwrap(); - script.run(scope).unwrap() - } - - let v = exec(scope, "32"); - let x32: u64 = serde_v8::from_v8(scope, v).unwrap(); - println!("x32 = {x32}"); - - let v = exec(scope, "({a: 1, b: 3, c: 'ignored'})"); - let mop: MathOp = serde_v8::from_v8(scope, v).unwrap(); - println!( - "mop = {{ a: {}, b: {}, operator: {:?} }}", - mop.a, mop.b, mop.operator - ); - - let v = exec(scope, "[1,2,3,4,5]"); - let arr: Vec = serde_v8::from_v8(scope, v).unwrap(); - println!("arr = {arr:?}"); - - let v = exec(scope, "['hello', 'world']"); - let hi: Vec = serde_v8::from_v8(scope, v).unwrap(); - println!("hi = {hi:?}"); - - let v: v8::Local = v8::Number::new(scope, 12345.0).into(); - let x: f64 = serde_v8::from_v8(scope, v).unwrap(); - println!("x = {x}"); - } - - // SAFETY: all isolates have been destroyed, so we can now safely let V8 clean - // up its resources. - unsafe { - v8::V8::dispose(); - } - v8::V8::dispose_platform(); -} diff --git a/serde_v8/keys.rs b/serde_v8/keys.rs deleted file mode 100644 index d44306fd6e..0000000000 --- a/serde_v8/keys.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use std::collections::HashMap; - -// KeyCache stores a pool struct keys mapped to v8, -// to minimize allocs and speed up decoding/encoding `v8::Object`s -// TODO: experiment with in from_v8/to_v8 -pub struct KeyCache(HashMap<&'static str, v8::Global>); - -// creates an optimized v8::String for a struct field -// TODO: experiment with external strings -// TODO: evaluate if own KeyCache is better than v8's dedupe -pub fn v8_struct_key<'s>( - scope: &mut v8::HandleScope<'s>, - field: &'static str, -) -> v8::Local<'s, v8::String> { - // Internalized v8 strings are significantly faster than "normal" v8 strings - // since v8 deduplicates re-used strings minimizing new allocations - // see: https://github.com/v8/v8/blob/14ac92e02cc3db38131a57e75e2392529f405f2f/include/v8.h#L3165-L3171 - v8::String::new_from_utf8( - scope, - field.as_ref(), - v8::NewStringType::Internalized, - ) - .unwrap() - - // TODO: consider external strings later - // right now non-deduped external strings (without KeyCache) - // are slower than the deduped internalized strings by ~2.5x - // since they're a new string in v8's eyes and needs to be hashed, etc... - // v8::String::new_external_onebyte_static(scope, field).unwrap() -} diff --git a/serde_v8/lib.rs b/serde_v8/lib.rs deleted file mode 100644 index 59c8fd41fe..0000000000 --- a/serde_v8/lib.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -mod de; -mod error; -mod keys; -mod magic; -mod payload; -mod ser; -mod serializable; -pub mod utils; - -pub use de::from_v8; -pub use de::from_v8_cached; -pub use de::to_utf8; -pub use de::Deserializer; -pub use error::Error; -pub use error::Result; -pub use keys::KeyCache; -pub use magic::any_value::AnyValue; -pub use magic::bigint::BigInt; -pub use magic::buffer::JsBuffer; -pub use magic::buffer::ToJsBuffer; -pub use magic::bytestring::ByteString; -pub use magic::detached_buffer::DetachedBuffer; -pub use magic::string_or_buffer::StringOrBuffer; -pub use magic::u16string::U16String; -pub use magic::ExternalPointer; -pub use magic::Global; -pub use magic::Value; -pub use ser::to_v8; -pub use ser::Serializer; -pub use serializable::Serializable; -pub use serializable::SerializablePkg; diff --git a/serde_v8/magic/any_value.rs b/serde_v8/magic/any_value.rs deleted file mode 100644 index df85f90d8b..0000000000 --- a/serde_v8/magic/any_value.rs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use super::buffer::JsBuffer; -use super::transl8::FromV8; -use super::transl8::ToV8; -use crate::magic::transl8::impl_magic; -use crate::Error; -use crate::ToJsBuffer; -use num_bigint::BigInt; - -/// An untagged enum type that can be any of number, string, bool, bigint, or -/// buffer. -#[derive(Debug)] -pub enum AnyValue { - RustBuffer(ToJsBuffer), - V8Buffer(JsBuffer), - String(String), - Number(f64), - BigInt(BigInt), - Bool(bool), -} - -impl_magic!(AnyValue); - -impl ToV8 for AnyValue { - fn to_v8<'a>( - &mut self, - scope: &mut v8::HandleScope<'a>, - ) -> Result, crate::Error> { - match self { - Self::RustBuffer(buf) => crate::to_v8(scope, buf), - Self::V8Buffer(_) => unreachable!(), - Self::String(s) => crate::to_v8(scope, s), - Self::Number(num) => crate::to_v8(scope, num), - Self::BigInt(bigint) => { - crate::to_v8(scope, crate::BigInt::from(bigint.clone())) - } - Self::Bool(b) => crate::to_v8(scope, b), - } - } -} - -impl FromV8 for AnyValue { - fn from_v8( - scope: &mut v8::HandleScope, - value: v8::Local, - ) -> Result { - if value.is_string() { - let string = crate::from_v8(scope, value)?; - Ok(AnyValue::String(string)) - } else if value.is_number() { - let string = crate::from_v8(scope, value)?; - Ok(AnyValue::Number(string)) - } else if value.is_big_int() { - let bigint = crate::BigInt::from_v8(scope, value)?; - Ok(AnyValue::BigInt(bigint.into())) - } else if value.is_array_buffer_view() { - let buf = JsBuffer::from_v8(scope, value)?; - Ok(AnyValue::V8Buffer(buf)) - } else if value.is_boolean() { - let string = crate::from_v8(scope, value)?; - Ok(AnyValue::Bool(string)) - } else { - Err(Error::Message( - "expected string, number, bigint, ArrayBufferView, boolean".into(), - )) - } - } -} diff --git a/serde_v8/magic/bigint.rs b/serde_v8/magic/bigint.rs deleted file mode 100644 index 330803daf8..0000000000 --- a/serde_v8/magic/bigint.rs +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use smallvec::smallvec; -use smallvec::SmallVec; - -use super::transl8::FromV8; -use super::transl8::ToV8; -use crate::error::value_to_type_str; -use crate::magic::transl8::impl_magic; -use crate::Error; - -#[derive( - PartialEq, - Eq, - Clone, - Debug, - Default, - derive_more::Deref, - derive_more::DerefMut, - derive_more::AsRef, - derive_more::AsMut, -)] -#[as_mut(forward)] -#[as_ref(forward)] -pub struct BigInt(num_bigint::BigInt); -impl_magic!(BigInt); - -impl ToV8 for BigInt { - fn to_v8<'a>( - &mut self, - scope: &mut v8::HandleScope<'a>, - ) -> Result, crate::Error> { - let (sign, words) = self.0.to_u64_digits(); - let sign_bit = sign == num_bigint::Sign::Minus; - let v = v8::BigInt::new_from_words(scope, sign_bit, &words).unwrap(); - Ok(v.into()) - } -} - -impl FromV8 for BigInt { - fn from_v8( - _scope: &mut v8::HandleScope, - value: v8::Local, - ) -> Result { - let v8bigint = v8::Local::::try_from(value) - .map_err(|_| Error::ExpectedBigInt(value_to_type_str(value)))?; - let word_count = v8bigint.word_count(); - let mut words: SmallVec<[u64; 1]> = smallvec![0u64; word_count]; - let (sign_bit, _words) = v8bigint.to_words_array(&mut words); - let sign = match sign_bit { - true => num_bigint::Sign::Minus, - false => num_bigint::Sign::Plus, - }; - // SAFETY: Because the alignment of u64 is 8, the alignment of u32 is 4, and - // the size of u64 is 8, the size of u32 is 4, the alignment of u32 is a - // factor of the alignment of u64, and the size of u32 is a factor of the - // size of u64, we can safely transmute the slice of u64 to a slice of u32. - let (prefix, slice, suffix) = unsafe { words.align_to::() }; - assert!(prefix.is_empty()); - assert!(suffix.is_empty()); - assert_eq!(slice.len(), words.len() * 2); - let big_int = num_bigint::BigInt::from_slice(sign, slice); - Ok(Self(big_int)) - } -} - -impl From for BigInt { - fn from(big_int: num_bigint::BigInt) -> Self { - Self(big_int) - } -} - -impl From for num_bigint::BigInt { - fn from(big_int: BigInt) -> Self { - big_int.0 - } -} diff --git a/serde_v8/magic/buffer.rs b/serde_v8/magic/buffer.rs deleted file mode 100644 index 032a3be33d..0000000000 --- a/serde_v8/magic/buffer.rs +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use std::fmt::Debug; -use std::ops::Deref; -use std::ops::DerefMut; - -use super::transl8::FromV8; -use super::transl8::ToV8; -use super::v8slice::V8Slice; -use crate::magic::transl8::impl_magic; - -pub struct JsBuffer(V8Slice); - -impl_magic!(JsBuffer); - -impl Debug for JsBuffer { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_list().entries(self.0.as_ref().iter()).finish() - } -} - -impl Clone for JsBuffer { - fn clone(&self) -> Self { - Self(self.0.clone()) - } -} - -impl AsRef<[u8]> for JsBuffer { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl AsMut<[u8]> for JsBuffer { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } -} - -impl Deref for JsBuffer { - type Target = [u8]; - fn deref(&self) -> &[u8] { - &self.0 - } -} - -impl DerefMut for JsBuffer { - fn deref_mut(&mut self) -> &mut [u8] { - &mut self.0 - } -} - -impl FromV8 for JsBuffer { - fn from_v8( - scope: &mut v8::HandleScope, - value: v8::Local, - ) -> Result { - Ok(Self(V8Slice::from_v8(scope, value)?)) - } -} - -impl From for bytes::Bytes { - fn from(zbuf: JsBuffer) -> bytes::Bytes { - zbuf.0.into() - } -} - -// NOTE(bartlomieju): we use Option here, because `to_v8()` uses `&mut self` -// instead of `self` which is dictated by the `serde` API. -#[derive(Debug)] -pub struct ToJsBuffer(Option>); - -impl_magic!(ToJsBuffer); - -impl ToJsBuffer { - pub fn empty() -> Self { - ToJsBuffer(Some(vec![0_u8; 0].into_boxed_slice())) - } -} - -impl From> for ToJsBuffer { - fn from(buf: Box<[u8]>) -> Self { - ToJsBuffer(Some(buf)) - } -} - -impl From> for ToJsBuffer { - fn from(vec: Vec) -> Self { - vec.into_boxed_slice().into() - } -} - -impl ToV8 for ToJsBuffer { - fn to_v8<'a>( - &mut self, - scope: &mut v8::HandleScope<'a>, - ) -> Result, crate::Error> { - let buf: Box<[u8]> = self.0.take().expect("RustToV8Buf was empty"); - - if buf.is_empty() { - let ab = v8::ArrayBuffer::new(scope, 0); - return Ok( - v8::Uint8Array::new(scope, ab, 0, 0) - .expect("Failed to create Uint8Array") - .into(), - ); - } - let buf_len: usize = buf.len(); - let backing_store = - v8::ArrayBuffer::new_backing_store_from_boxed_slice(buf); - let backing_store_shared = backing_store.make_shared(); - let ab = v8::ArrayBuffer::with_backing_store(scope, &backing_store_shared); - Ok( - v8::Uint8Array::new(scope, ab, 0, buf_len) - .expect("Failed to create Uint8Array") - .into(), - ) - } -} diff --git a/serde_v8/magic/bytestring.rs b/serde_v8/magic/bytestring.rs deleted file mode 100644 index 3baa704e5f..0000000000 --- a/serde_v8/magic/bytestring.rs +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use super::transl8::FromV8; -use super::transl8::ToV8; -use crate::error::value_to_type_str; -use crate::magic::transl8::impl_magic; -use crate::Error; -use smallvec::SmallVec; -use std::mem::size_of; - -const USIZE2X: usize = size_of::() * 2; - -#[derive( - PartialEq, - Eq, - Clone, - Debug, - Default, - derive_more::Deref, - derive_more::DerefMut, - derive_more::AsRef, - derive_more::AsMut, -)] -#[as_mut(forward)] -#[as_ref(forward)] -pub struct ByteString(SmallVec<[u8; USIZE2X]>); -impl_magic!(ByteString); - -// const-assert that Vec and SmallVec<[u8; size_of::() * 2]> have a same size. -// Note from https://docs.rs/smallvec/latest/smallvec/#union - -// smallvec can still be larger than Vec if the inline buffer is -// larger than two machine words. -const _: () = - assert!(size_of::>() == size_of::>()); - -impl ToV8 for ByteString { - fn to_v8<'a>( - &mut self, - scope: &mut v8::HandleScope<'a>, - ) -> Result, crate::Error> { - let v = - v8::String::new_from_one_byte(scope, self, v8::NewStringType::Normal) - .unwrap(); - Ok(v.into()) - } -} - -impl FromV8 for ByteString { - fn from_v8( - scope: &mut v8::HandleScope, - value: v8::Local, - ) -> Result { - let v8str = v8::Local::::try_from(value) - .map_err(|_| Error::ExpectedString(value_to_type_str(value)))?; - if !v8str.contains_only_onebyte() { - return Err(Error::ExpectedLatin1); - } - let len = v8str.length(); - let mut buffer = SmallVec::with_capacity(len); - #[allow(clippy::uninit_vec)] - // SAFETY: we set length == capacity (see previous line), - // before immediately writing into that buffer and sanity check with an assert - unsafe { - buffer.set_len(len); - let written = v8str.write_one_byte( - scope, - &mut buffer, - 0, - v8::WriteOptions::NO_NULL_TERMINATION, - ); - assert!(written == len); - } - Ok(Self(buffer)) - } -} - -// smallvec does not impl From/Into traits -// like Vec does. So here we are. - -impl From> for ByteString { - fn from(vec: Vec) -> Self { - ByteString(SmallVec::from_vec(vec)) - } -} - -#[allow(clippy::from_over_into)] -impl Into> for ByteString { - fn into(self) -> Vec { - self.0.into_vec() - } -} - -impl From<&[u8]> for ByteString { - fn from(s: &[u8]) -> Self { - ByteString(SmallVec::from_slice(s)) - } -} - -impl From<&str> for ByteString { - fn from(s: &str) -> Self { - let v: Vec = s.into(); - ByteString::from(v) - } -} - -impl From for ByteString { - fn from(s: String) -> Self { - ByteString::from(s.into_bytes()) - } -} diff --git a/serde_v8/magic/detached_buffer.rs b/serde_v8/magic/detached_buffer.rs deleted file mode 100644 index bc4b3de677..0000000000 --- a/serde_v8/magic/detached_buffer.rs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use core::ops::Range; -use std::ops::Deref; -use std::ops::DerefMut; - -use super::transl8::FromV8; -use super::transl8::ToV8; -use super::v8slice::to_ranged_buffer; -use super::v8slice::V8Slice; -use crate::error::value_to_type_str; -use crate::magic::transl8::impl_magic; - -// A buffer that detaches when deserialized from JS -pub struct DetachedBuffer(V8Slice); -impl_magic!(DetachedBuffer); - -impl AsRef<[u8]> for DetachedBuffer { - fn as_ref(&self) -> &[u8] { - self.0.as_ref() - } -} - -impl AsMut<[u8]> for DetachedBuffer { - fn as_mut(&mut self) -> &mut [u8] { - self.0.as_mut() - } -} - -impl Deref for DetachedBuffer { - type Target = [u8]; - fn deref(&self) -> &[u8] { - self.0.deref() - } -} - -impl DerefMut for DetachedBuffer { - fn deref_mut(&mut self) -> &mut [u8] { - self.0.deref_mut() - } -} - -impl ToV8 for DetachedBuffer { - fn to_v8<'a>( - &mut self, - scope: &mut v8::HandleScope<'a>, - ) -> Result, crate::Error> { - let buffer = v8::ArrayBuffer::with_backing_store(scope, &self.0.store); - let Range { start, end } = self.0.range; - let (off, len) = (start, end - start); - let v = v8::Uint8Array::new(scope, buffer, off, len).unwrap(); - Ok(v.into()) - } -} - -impl FromV8 for DetachedBuffer { - fn from_v8( - scope: &mut v8::HandleScope, - value: v8::Local, - ) -> Result { - let (b, range) = to_ranged_buffer(scope, value) - .map_err(|_| crate::Error::ExpectedBuffer(value_to_type_str(value)))?; - if !b.is_detachable() { - return Err(crate::Error::ExpectedDetachable(value_to_type_str(value))); - } - let store = b.get_backing_store(); - b.detach(None); // Detach - Ok(Self(V8Slice { store, range })) - } -} diff --git a/serde_v8/magic/external_pointer.rs b/serde_v8/magic/external_pointer.rs deleted file mode 100644 index e22e41a010..0000000000 --- a/serde_v8/magic/external_pointer.rs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use std::ffi::c_void; - -use crate::error::value_to_type_str; - -use super::transl8::impl_magic; -use super::transl8::FromV8; -use super::transl8::ToV8; - -pub struct ExternalPointer(*mut c_void); - -// SAFETY: Nonblocking FFI is user controller and we must trust user to have it right. -unsafe impl Send for ExternalPointer {} -// SAFETY: Nonblocking FFI is user controller and we must trust user to have it right. -unsafe impl Sync for ExternalPointer {} - -impl_magic!(ExternalPointer); - -impl ToV8 for ExternalPointer { - fn to_v8<'a>( - &mut self, - scope: &mut v8::HandleScope<'a>, - ) -> Result, crate::Error> { - if self.0.is_null() { - Ok(v8::null(scope).into()) - } else { - Ok(v8::External::new(scope, self.0).into()) - } - } -} - -impl FromV8 for ExternalPointer { - fn from_v8( - _scope: &mut v8::HandleScope, - value: v8::Local, - ) -> Result { - if value.is_null() { - Ok(ExternalPointer(std::ptr::null_mut())) - } else if let Ok(external) = v8::Local::::try_from(value) { - Ok(ExternalPointer(external.value())) - } else { - Err(crate::Error::ExpectedExternal(value_to_type_str(value))) - } - } -} - -impl From<*mut c_void> for ExternalPointer { - fn from(value: *mut c_void) -> Self { - ExternalPointer(value) - } -} - -impl From<*const c_void> for ExternalPointer { - fn from(value: *const c_void) -> Self { - ExternalPointer(value as *mut c_void) - } -} diff --git a/serde_v8/magic/global.rs b/serde_v8/magic/global.rs deleted file mode 100644 index 52b316fa51..0000000000 --- a/serde_v8/magic/global.rs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use crate::magic::transl8::impl_magic; -use crate::magic::transl8::FromV8; -use crate::magic::transl8::ToV8; - -pub struct Global { - pub v8_value: v8::Global, -} -impl_magic!(Global); - -impl From> for Global { - fn from(v8_value: v8::Global) -> Self { - Self { v8_value } - } -} - -impl From for v8::Global { - fn from(v: Global) -> Self { - v.v8_value - } -} - -impl ToV8 for Global { - fn to_v8<'a>( - &mut self, - scope: &mut v8::HandleScope<'a>, - ) -> Result, crate::Error> { - Ok(v8::Local::new(scope, self.v8_value.clone())) - } -} - -impl FromV8 for Global { - fn from_v8( - scope: &mut v8::HandleScope, - value: v8::Local, - ) -> Result { - let global = v8::Global::new(scope, value); - Ok(global.into()) - } -} diff --git a/serde_v8/magic/mod.rs b/serde_v8/magic/mod.rs deleted file mode 100644 index 3e984527dd..0000000000 --- a/serde_v8/magic/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -pub mod any_value; -pub mod bigint; -pub mod buffer; -pub mod bytestring; -pub mod detached_buffer; -mod external_pointer; -mod global; -pub(super) mod rawbytes; -pub mod string_or_buffer; -pub mod transl8; -pub mod u16string; -pub mod v8slice; -mod value; -pub use external_pointer::ExternalPointer; -pub use global::Global; -pub use value::Value; diff --git a/serde_v8/magic/rawbytes.rs b/serde_v8/magic/rawbytes.rs deleted file mode 100644 index 2703c77566..0000000000 --- a/serde_v8/magic/rawbytes.rs +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -pub(crate) type AtomicPtr = *mut T; -#[allow(unused)] -pub(crate) struct RawBytes { - ptr: *const u8, - len: usize, - // inlined "trait object" - data: AtomicPtr<()>, - vtable: &'static Vtable, -} - -impl RawBytes { - pub fn new_raw( - ptr: *const u8, - len: usize, - data: AtomicPtr<()>, - vtable: &'static Vtable, - ) -> bytes::Bytes { - RawBytes { - ptr, - len, - data, - vtable, - } - .into() - } -} - -// Validate some bytes::Bytes layout assumptions at compile time. -const _: () = { - assert!( - core::mem::size_of::() == core::mem::size_of::(), - ); - assert!( - core::mem::align_of::() == core::mem::align_of::(), - ); -}; - -#[allow(unused)] -pub(crate) struct Vtable { - /// fn(data, ptr, len) - pub clone: unsafe fn(&AtomicPtr<()>, *const u8, usize) -> bytes::Bytes, - /// fn(data, ptr, len) - /// - /// takes `Bytes` to value - pub to_vec: unsafe fn(&AtomicPtr<()>, *const u8, usize) -> Vec, - /// fn(data, ptr, len) - pub drop: unsafe fn(&mut AtomicPtr<()>, *const u8, usize), -} - -impl From for bytes::Bytes { - fn from(b: RawBytes) -> Self { - // SAFETY: RawBytes has the same layout as bytes::Bytes - // this is tested below, both are composed of usize-d ptrs/values - // thus aren't currently subject to rust's field re-ordering to minimize padding - unsafe { std::mem::transmute(b) } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::mem; - - const HELLO: &str = "hello"; - - // ===== impl StaticVtable ===== - - const STATIC_VTABLE: Vtable = Vtable { - clone: static_clone, - drop: static_drop, - to_vec: static_to_vec, - }; - - unsafe fn static_clone( - _: &AtomicPtr<()>, - ptr: *const u8, - len: usize, - ) -> bytes::Bytes { - from_static(std::slice::from_raw_parts(ptr, len)).into() - } - - unsafe fn static_to_vec( - _: &AtomicPtr<()>, - ptr: *const u8, - len: usize, - ) -> Vec { - let slice = std::slice::from_raw_parts(ptr, len); - slice.to_vec() - } - - unsafe fn static_drop(_: &mut AtomicPtr<()>, _: *const u8, _: usize) { - // nothing to drop for &'static [u8] - } - - fn from_static(bytes: &'static [u8]) -> RawBytes { - RawBytes { - ptr: bytes.as_ptr(), - len: bytes.len(), - data: std::ptr::null_mut(), - vtable: &STATIC_VTABLE, - } - } - - #[test] - fn bytes_identity() { - let b1: bytes::Bytes = from_static(HELLO.as_bytes()).into(); - let b2 = bytes::Bytes::from_static(HELLO.as_bytes()); - assert_eq!(b1, b2); // Values are equal - } - - #[test] - fn bytes_layout() { - let u1: [usize; 4] = - // SAFETY: ensuring layout is the same - unsafe { mem::transmute(from_static(HELLO.as_bytes())) }; - let u2: [usize; 4] = - // SAFETY: ensuring layout is the same - unsafe { mem::transmute(bytes::Bytes::from_static(HELLO.as_bytes())) }; - assert_eq!(u1[..3], u2[..3]); // Struct bytes are equal besides Vtables - } -} diff --git a/serde_v8/magic/string_or_buffer.rs b/serde_v8/magic/string_or_buffer.rs deleted file mode 100644 index 986f7d32ad..0000000000 --- a/serde_v8/magic/string_or_buffer.rs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use super::buffer::JsBuffer; -use super::transl8::FromV8; -use crate::error::value_to_type_str; -use crate::magic::transl8::impl_magic; -use crate::Error; -use std::ops::Deref; - -#[derive(Debug)] -pub enum StringOrBuffer { - Buffer(JsBuffer), - String(String), -} - -impl_magic!(StringOrBuffer); - -impl Deref for StringOrBuffer { - type Target = [u8]; - fn deref(&self) -> &Self::Target { - match self { - Self::Buffer(b) => b.as_ref(), - Self::String(s) => s.as_bytes(), - } - } -} - -impl<'a> TryFrom<&'a StringOrBuffer> for &'a str { - type Error = std::str::Utf8Error; - fn try_from(value: &'a StringOrBuffer) -> Result { - match value { - StringOrBuffer::String(s) => Ok(s.as_str()), - StringOrBuffer::Buffer(b) => std::str::from_utf8(b.as_ref()), - } - } -} - -impl FromV8 for StringOrBuffer { - fn from_v8( - scope: &mut v8::HandleScope, - value: v8::Local, - ) -> Result { - if let Ok(buf) = JsBuffer::from_v8(scope, value) { - return Ok(Self::Buffer(buf)); - } else if let Ok(s) = crate::from_v8(scope, value) { - return Ok(Self::String(s)); - } - Err(Error::ExpectedBuffer(value_to_type_str(value))) - } -} - -impl From for bytes::Bytes { - fn from(sob: StringOrBuffer) -> Self { - match sob { - StringOrBuffer::Buffer(b) => b.into(), - StringOrBuffer::String(s) => s.into_bytes().into(), - } - } -} diff --git a/serde_v8/magic/transl8.rs b/serde_v8/magic/transl8.rs deleted file mode 100644 index 3a8d0c358f..0000000000 --- a/serde_v8/magic/transl8.rs +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -//! Transerialization extends the set of serde-compatible types (for given de/serializers). -//! By "hackishly" transmuting references across serde boundaries as u64s. -//! Type-safety is enforced using special struct names for each "magic type". -//! Memory-safety relies on transerialized values being "pinned" during de/serialization. - -pub(crate) const MAGIC_FIELD: &str = "$__v8_magic_field"; - -pub(crate) trait MagicType { - const NAME: &'static str; - const MAGIC_NAME: &'static str; -} - -pub(crate) trait ToV8 { - fn to_v8<'a>( - &mut self, - scope: &mut v8::HandleScope<'a>, - ) -> Result, crate::Error>; -} - -pub(crate) trait FromV8: Sized { - fn from_v8( - scope: &mut v8::HandleScope, - value: v8::Local, - ) -> Result; -} - -pub(crate) fn magic_serialize( - serializer: S, - x: &T, -) -> Result -where - S: serde::Serializer, - T: MagicType, -{ - use serde::ser::SerializeStruct; - - let mut s = serializer.serialize_struct(T::MAGIC_NAME, 1)?; - let ptr = opaque_send(x); - s.serialize_field(MAGIC_FIELD, &ptr)?; - s.end() -} - -pub(crate) fn magic_deserialize<'de, T, D>( - deserializer: D, -) -> Result -where - D: serde::Deserializer<'de>, - T: MagicType, -{ - struct ValueVisitor { - p1: std::marker::PhantomData, - } - - impl<'de, T: MagicType> serde::de::Visitor<'de> for ValueVisitor { - type Value = T; - - fn expecting( - &self, - formatter: &mut std::fmt::Formatter, - ) -> std::fmt::Result { - formatter.write_str("a ")?; - formatter.write_str(T::NAME) - } - - fn visit_u64(self, ptr: u64) -> Result - where - E: serde::de::Error, - { - // SAFETY: opaque ptr originates from visit_magic, which forgets ownership so we can take it - Ok(unsafe { opaque_take(ptr) }) - } - } - - deserializer.deserialize_struct( - T::MAGIC_NAME, - &[MAGIC_FIELD], - ValueVisitor:: { - p1: std::marker::PhantomData, - }, - ) -} - -pub(crate) fn visit_magic<'de, T, V, E>(visitor: V, x: T) -> Result -where - V: serde::de::Visitor<'de>, - E: serde::de::Error, -{ - let y = visitor.visit_u64::(opaque_send(&x)); - std::mem::forget(x); - y -} - -/// Constructs an "opaque" ptr from a reference to transerialize -pub(crate) fn opaque_send(x: &T) -> u64 { - (x as *const T) as u64 -} - -/// Copies an "opaque" ptr from a reference to an opaque ptr (transerialized) -/// NOTE: ptr-to-ptr, extra indirection -pub(crate) unsafe fn opaque_recv(ptr: &T) -> u64 { - *(ptr as *const T as *const u64) -} - -/// Transmutes an "opaque" ptr back into a reference -pub(crate) unsafe fn opaque_deref_mut<'a, T>(ptr: u64) -> &'a mut T { - std::mem::transmute(ptr as usize) -} - -/// Transmutes & copies the value from the "opaque" ptr -/// NOTE: takes ownership & requires other end to forget its ownership -pub(crate) unsafe fn opaque_take(ptr: u64) -> T { - std::mem::transmute_copy::(std::mem::transmute(ptr as usize)) -} - -macro_rules! impl_magic { - ($t:ty) => { - impl crate::magic::transl8::MagicType for $t { - const NAME: &'static str = stringify!($t); - const MAGIC_NAME: &'static str = concat!("$__v8_magic_", stringify!($t)); - } - - impl serde::Serialize for $t { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - crate::magic::transl8::magic_serialize(serializer, self) - } - } - - impl<'de> serde::Deserialize<'de> for $t { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - crate::magic::transl8::magic_deserialize(deserializer) - } - } - }; -} -pub(crate) use impl_magic; - -macro_rules! impl_wrapper { - ($i:item) => { - #[derive( - PartialEq, - Eq, - Clone, - Debug, - Default, - derive_more::Deref, - derive_more::DerefMut, - derive_more::AsRef, - derive_more::AsMut, - derive_more::From, - )] - #[as_mut(forward)] - #[as_ref(forward)] - #[from(forward)] - $i - }; -} -pub(crate) use impl_wrapper; diff --git a/serde_v8/magic/u16string.rs b/serde_v8/magic/u16string.rs deleted file mode 100644 index 04d742da96..0000000000 --- a/serde_v8/magic/u16string.rs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use crate::error::value_to_type_str; -use crate::Error; - -use super::transl8::impl_magic; -use super::transl8::impl_wrapper; -use super::transl8::FromV8; -use super::transl8::ToV8; - -impl_wrapper!( - pub struct U16String(Vec); -); -impl_magic!(U16String); - -impl ToV8 for U16String { - fn to_v8<'a>( - &mut self, - scope: &mut v8::HandleScope<'a>, - ) -> Result, crate::Error> { - let maybe_v = - v8::String::new_from_two_byte(scope, self, v8::NewStringType::Normal); - - // 'new_from_two_byte' can return 'None' if buffer length > kMaxLength. - if let Some(v) = maybe_v { - Ok(v.into()) - } else { - Err(Error::Message(String::from( - "Cannot allocate String from UTF-16: buffer exceeds maximum length.", - ))) - } - } -} - -impl FromV8 for U16String { - fn from_v8( - scope: &mut v8::HandleScope, - value: v8::Local, - ) -> Result { - let v8str = v8::Local::::try_from(value) - .map_err(|_| Error::ExpectedString(value_to_type_str(value)))?; - let len = v8str.length(); - let mut buffer = Vec::with_capacity(len); - #[allow(clippy::uninit_vec)] - // SAFETY: we set length == capacity (see previous line), - // before immediately writing into that buffer and sanity check with an assert - unsafe { - buffer.set_len(len); - let written = v8str.write( - scope, - &mut buffer, - 0, - v8::WriteOptions::NO_NULL_TERMINATION, - ); - assert!(written == len); - } - Ok(buffer.into()) - } -} diff --git a/serde_v8/magic/v8slice.rs b/serde_v8/magic/v8slice.rs deleted file mode 100644 index 2b103f1c96..0000000000 --- a/serde_v8/magic/v8slice.rs +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use std::ops::Deref; -use std::ops::DerefMut; -use std::ops::Range; -use std::rc::Rc; - -use crate::error::value_to_type_str; - -use super::rawbytes; -use super::transl8::FromV8; - -/// A V8Slice encapsulates a slice that's been borrowed from a JavaScript -/// ArrayBuffer object. JavaScript objects can normally be garbage collected, -/// but the existence of a V8Slice inhibits this until it is dropped. It -/// behaves much like an Arc<[u8]>. -/// -/// # Cloning -/// Cloning a V8Slice does not clone the contents of the buffer, -/// it creates a new reference to that buffer. -/// -/// To actually clone the contents of the buffer do -/// `let copy = Vec::from(&*zero_copy_buf);` -#[derive(Clone)] -pub struct V8Slice { - pub(crate) store: v8::SharedRef, - pub(crate) range: Range, -} - -// SAFETY: unsafe trait must have unsafe implementation -unsafe impl Send for V8Slice {} - -impl V8Slice { - fn as_slice(&self) -> &[u8] { - // SAFETY: v8::SharedRef is similar to Arc<[u8]>, - // it points to a fixed continuous slice of bytes on the heap. - // We assume it's initialized and thus safe to read (though may not contain meaningful data) - unsafe { &*(&self.store[self.range.clone()] as *const _ as *const [u8]) } - } - - fn as_slice_mut(&mut self) -> &mut [u8] { - #[allow(clippy::cast_ref_to_mut)] - // SAFETY: v8::SharedRef is similar to Arc<[u8]>, - // it points to a fixed continuous slice of bytes on the heap. - // It's safe-ish to mutate concurrently because it can not be - // shrunk/grown/moved/reallocated, thus avoiding dangling refs (unlike a Vec). - // Concurrent writes can't lead to meaningful structural invalidation - // since we treat them as opaque buffers / "bags of bytes", - // concurrent mutation is simply an accepted fact of life. - // And in practice V8Slices also do not have overallping read/write phases. - // TLDR: permissive interior mutability on slices of bytes is "fine" - unsafe { - &mut *(&self.store[self.range.clone()] as *const _ as *mut [u8]) - } - } -} - -pub(crate) fn to_ranged_buffer<'s>( - scope: &mut v8::HandleScope<'s>, - value: v8::Local, -) -> Result<(v8::Local<'s, v8::ArrayBuffer>, Range), v8::DataError> { - if let Ok(view) = v8::Local::::try_from(value) { - let (offset, len) = (view.byte_offset(), view.byte_length()); - let buffer = view.buffer(scope).ok_or(v8::DataError::NoData { - expected: "view to have a buffer", - })?; - let buffer = v8::Local::new(scope, buffer); // recreate handle to avoid lifetime issues - return Ok((buffer, offset..offset + len)); - } - let b: v8::Local = value.try_into()?; - let b = v8::Local::new(scope, b); // recreate handle to avoid lifetime issues - Ok((b, 0..b.byte_length())) -} - -impl FromV8 for V8Slice { - fn from_v8( - scope: &mut v8::HandleScope, - value: v8::Local, - ) -> Result { - match to_ranged_buffer(scope, value) { - Ok((b, range)) => { - let store = b.get_backing_store(); - if store.is_resizable_by_user_javascript() { - Err(crate::Error::ResizableBackingStoreNotSupported) - } else if store.is_shared() { - Err(crate::Error::ExpectedBuffer(value_to_type_str(value))) - } else { - Ok(V8Slice { store, range }) - } - } - Err(_) => Err(crate::Error::ExpectedBuffer(value_to_type_str(value))), - } - } -} - -impl Deref for V8Slice { - type Target = [u8]; - fn deref(&self) -> &[u8] { - self.as_slice() - } -} - -impl DerefMut for V8Slice { - fn deref_mut(&mut self) -> &mut [u8] { - self.as_slice_mut() - } -} - -impl AsRef<[u8]> for V8Slice { - fn as_ref(&self) -> &[u8] { - self.as_slice() - } -} - -impl AsMut<[u8]> for V8Slice { - fn as_mut(&mut self) -> &mut [u8] { - self.as_slice_mut() - } -} - -// Implement V8Slice -> bytes::Bytes -impl V8Slice { - fn rc_into_byte_parts(self: Rc) -> (*const u8, usize, *mut V8Slice) { - let (ptr, len) = { - let slice = self.as_ref(); - (slice.as_ptr(), slice.len()) - }; - let rc_raw = Rc::into_raw(self); - let data = rc_raw as *mut V8Slice; - (ptr, len, data) - } -} - -impl From for bytes::Bytes { - fn from(v8slice: V8Slice) -> Self { - let (ptr, len, data) = Rc::new(v8slice).rc_into_byte_parts(); - rawbytes::RawBytes::new_raw(ptr, len, data.cast(), &V8SLICE_VTABLE) - } -} - -// NOTE: in the limit we could avoid extra-indirection and use the C++ shared_ptr -// but we can't store both the underlying data ptr & ctrl ptr ... so instead we -// use a shared rust ptr (Rc/Arc) that itself controls the C++ shared_ptr -const V8SLICE_VTABLE: rawbytes::Vtable = rawbytes::Vtable { - clone: v8slice_clone, - drop: v8slice_drop, - to_vec: v8slice_to_vec, -}; - -unsafe fn v8slice_clone( - data: &rawbytes::AtomicPtr<()>, - ptr: *const u8, - len: usize, -) -> bytes::Bytes { - let rc = Rc::from_raw(*data as *const V8Slice); - let (_, _, data) = rc.clone().rc_into_byte_parts(); - std::mem::forget(rc); - // NOTE: `bytes::Bytes` does bounds checking so we trust its ptr, len inputs - // and must use them to allow cloning Bytes it has sliced - rawbytes::RawBytes::new_raw(ptr, len, data.cast(), &V8SLICE_VTABLE) -} - -unsafe fn v8slice_to_vec( - data: &rawbytes::AtomicPtr<()>, - ptr: *const u8, - len: usize, -) -> Vec { - let rc = Rc::from_raw(*data as *const V8Slice); - std::mem::forget(rc); - // NOTE: `bytes::Bytes` does bounds checking so we trust its ptr, len inputs - // and must use them to allow cloning Bytes it has sliced - Vec::from_raw_parts(ptr as _, len, len) -} - -unsafe fn v8slice_drop( - data: &mut rawbytes::AtomicPtr<()>, - _: *const u8, - _: usize, -) { - drop(Rc::from_raw(*data as *const V8Slice)) -} diff --git a/serde_v8/magic/value.rs b/serde_v8/magic/value.rs deleted file mode 100644 index 0333d75bcb..0000000000 --- a/serde_v8/magic/value.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -use crate::magic::transl8::impl_magic; -use crate::magic::transl8::FromV8; -use crate::magic::transl8::ToV8; -use std::mem::transmute; - -/// serde_v8::Value allows passing through `v8::Value`s untouched -/// when de/serializing & allows mixing rust & v8 values in structs, tuples... -// -// SAFETY: caveat emptor, the rust-compiler can no longer link lifetimes to their -// original scope, you must take special care in ensuring your handles don't outlive their scope -pub struct Value<'s> { - pub v8_value: v8::Local<'s, v8::Value>, -} -impl_magic!(Value<'_>); - -impl<'s> From> for Value<'s> { - fn from(v8_value: v8::Local<'s, v8::Value>) -> Self { - Self { v8_value } - } -} - -impl<'s> From> for v8::Local<'s, v8::Value> { - fn from(v: Value<'s>) -> Self { - v.v8_value - } -} - -impl ToV8 for Value<'_> { - fn to_v8<'a>( - &mut self, - _scope: &mut v8::HandleScope<'a>, - ) -> Result, crate::Error> { - // SAFETY: not fully safe, since lifetimes are detached from original scope - Ok(unsafe { transmute(self.v8_value) }) - } -} - -impl FromV8 for Value<'_> { - fn from_v8( - _scope: &mut v8::HandleScope, - value: v8::Local, - ) -> Result { - // SAFETY: not fully safe, since lifetimes are detached from original scope - Ok(unsafe { transmute::(value.into()) }) - } -} diff --git a/serde_v8/payload.rs b/serde_v8/payload.rs deleted file mode 100644 index b396ad01d7..0000000000 --- a/serde_v8/payload.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -// TODO: maybe add a Payload type that holds scope & v8::Value -// so it can implement Deserialize by itself - -// Classifies v8::Values into sub-types -#[derive(Debug)] -pub enum ValueType { - Null, - Bool, - Number, - BigInt, - String, - Array, - ArrayBuffer, - ArrayBufferView, - Object, -} - -impl ValueType { - pub fn from_v8(v: v8::Local) -> ValueType { - if v.is_boolean() { - return Self::Bool; - } else if v.is_number() { - return Self::Number; - } else if v.is_string() { - return Self::String; - } else if v.is_array() { - return Self::Array; - } else if v.is_big_int() { - return Self::BigInt; - } else if v.is_array_buffer() { - return Self::ArrayBuffer; - } else if v.is_array_buffer_view() { - return Self::ArrayBufferView; - } else if v.is_object() { - return Self::Object; - } else if v.is_null_or_undefined() { - return Self::Null; - } - panic!("serde_v8: unknown ValueType for v8::Value") - } -} diff --git a/serde_v8/ser.rs b/serde_v8/ser.rs deleted file mode 100644 index 51625f2305..0000000000 --- a/serde_v8/ser.rs +++ /dev/null @@ -1,647 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use serde::ser; -use serde::ser::Serialize; - -use std::cell::RefCell; -use std::ops::DerefMut; - -use crate::error::Error; -use crate::error::Result; -use crate::keys::v8_struct_key; -use crate::magic; -use crate::magic::transl8::opaque_deref_mut; -use crate::magic::transl8::opaque_recv; -use crate::magic::transl8::MagicType; -use crate::magic::transl8::ToV8; -use crate::magic::transl8::MAGIC_FIELD; -use crate::AnyValue; -use crate::BigInt; -use crate::ByteString; -use crate::DetachedBuffer; -use crate::ExternalPointer; -use crate::ToJsBuffer; -use crate::U16String; - -type JsValue<'s> = v8::Local<'s, v8::Value>; -type JsResult<'s> = Result>; - -type ScopePtr<'a, 'b, 'c> = &'c RefCell<&'b mut v8::HandleScope<'a>>; - -pub fn to_v8<'a, T>(scope: &mut v8::HandleScope<'a>, input: T) -> JsResult<'a> -where - T: Serialize, -{ - let scopeptr = RefCell::new(scope); - let serializer = Serializer::new(&scopeptr); - - input.serialize(serializer) -} - -/// Wraps other serializers into an enum tagged variant form. -/// Uses {"Variant": ...payload...} for compatibility with serde-json. -pub struct VariantSerializer<'a, 'b, 'c, S> { - inner: S, - scope: ScopePtr<'a, 'b, 'c>, - variant: &'static str, -} - -impl<'a, 'b, 'c, S> VariantSerializer<'a, 'b, 'c, S> { - pub fn new( - scope: ScopePtr<'a, 'b, 'c>, - variant: &'static str, - inner: S, - ) -> Self { - Self { - inner, - scope, - variant, - } - } - - fn end(self, inner: impl FnOnce(S) -> JsResult<'a>) -> JsResult<'a> { - let value = inner(self.inner)?; - let scope = &mut *self.scope.borrow_mut(); - let null = v8::null(scope).into(); - let key = v8_struct_key(scope, self.variant).into(); - let obj = - v8::Object::with_prototype_and_properties(scope, null, &[key], &[value]); - Ok(obj.into()) - } -} - -impl<'a, 'b, 'c, S> ser::SerializeTupleVariant - for VariantSerializer<'a, 'b, 'c, S> -where - S: ser::SerializeTupleStruct, Error = Error>, -{ - type Ok = JsValue<'a>; - type Error = Error; - - fn serialize_field( - &mut self, - value: &T, - ) -> Result<()> { - self.inner.serialize_field(value) - } - - fn end(self) -> JsResult<'a> { - self.end(S::end) - } -} - -impl<'a, 'b, 'c, S> ser::SerializeStructVariant - for VariantSerializer<'a, 'b, 'c, S> -where - S: ser::SerializeStruct, Error = Error>, -{ - type Ok = JsValue<'a>; - type Error = Error; - - fn serialize_field( - &mut self, - key: &'static str, - value: &T, - ) -> Result<()> { - self.inner.serialize_field(key, value) - } - - fn end(self) -> JsResult<'a> { - self.end(S::end) - } -} - -pub struct ArraySerializer<'a, 'b, 'c> { - pending: Vec>, - scope: ScopePtr<'a, 'b, 'c>, -} - -impl<'a, 'b, 'c> ArraySerializer<'a, 'b, 'c> { - pub fn new(scope: ScopePtr<'a, 'b, 'c>, len: Option) -> Self { - let pending = match len { - Some(len) => Vec::with_capacity(len), - None => vec![], - }; - Self { pending, scope } - } -} - -impl<'a, 'b, 'c> ser::SerializeSeq for ArraySerializer<'a, 'b, 'c> { - type Ok = JsValue<'a>; - type Error = Error; - - fn serialize_element( - &mut self, - value: &T, - ) -> Result<()> { - let x = value.serialize(Serializer::new(self.scope))?; - self.pending.push(x); - Ok(()) - } - - fn end(self) -> JsResult<'a> { - let elements = self.pending.iter().as_slice(); - let scope = &mut *self.scope.borrow_mut(); - let arr = v8::Array::new_with_elements(scope, elements); - Ok(arr.into()) - } -} - -impl<'a, 'b, 'c> ser::SerializeTuple for ArraySerializer<'a, 'b, 'c> { - type Ok = JsValue<'a>; - type Error = Error; - - fn serialize_element( - &mut self, - value: &T, - ) -> Result<()> { - ser::SerializeSeq::serialize_element(self, value) - } - - fn end(self) -> JsResult<'a> { - ser::SerializeSeq::end(self) - } -} - -impl<'a, 'b, 'c> ser::SerializeTupleStruct for ArraySerializer<'a, 'b, 'c> { - type Ok = JsValue<'a>; - type Error = Error; - - fn serialize_field( - &mut self, - value: &T, - ) -> Result<()> { - ser::SerializeTuple::serialize_element(self, value) - } - - fn end(self) -> JsResult<'a> { - ser::SerializeTuple::end(self) - } -} - -pub struct ObjectSerializer<'a, 'b, 'c> { - scope: ScopePtr<'a, 'b, 'c>, - keys: Vec>, - values: Vec>, -} - -impl<'a, 'b, 'c> ObjectSerializer<'a, 'b, 'c> { - pub fn new(scope: ScopePtr<'a, 'b, 'c>, len: usize) -> Self { - let keys = Vec::with_capacity(len); - let values = Vec::with_capacity(len); - Self { - scope, - keys, - values, - } - } -} - -impl<'a, 'b, 'c> ser::SerializeStruct for ObjectSerializer<'a, 'b, 'c> { - type Ok = JsValue<'a>; - type Error = Error; - - fn serialize_field( - &mut self, - key: &'static str, - value: &T, - ) -> Result<()> { - let value = value.serialize(Serializer::new(self.scope))?; - let scope = &mut *self.scope.borrow_mut(); - let key = v8_struct_key(scope, key).into(); - self.keys.push(key); - self.values.push(value); - Ok(()) - } - - fn end(self) -> JsResult<'a> { - let scope = &mut *self.scope.borrow_mut(); - let null = v8::null(scope); - let obj = v8::Object::with_prototype_and_properties( - scope, - null.into(), - &self.keys[..], - &self.values[..], - ); - Ok(obj.into()) - } -} - -pub struct MagicalSerializer<'a, 'b, 'c, T> { - scope: ScopePtr<'a, 'b, 'c>, - opaque: u64, - p1: std::marker::PhantomData, -} - -impl<'a, 'b, 'c, T> MagicalSerializer<'a, 'b, 'c, T> { - pub fn new(scope: ScopePtr<'a, 'b, 'c>) -> MagicalSerializer<'a, 'b, 'c, T> { - Self { - scope, - opaque: 0, - p1: std::marker::PhantomData:: {}, - } - } -} - -impl<'a, 'b, 'c, T: MagicType + ToV8> ser::SerializeStruct - for MagicalSerializer<'a, 'b, 'c, T> -{ - type Ok = JsValue<'a>; - type Error = Error; - - fn serialize_field( - &mut self, - key: &'static str, - value: &U, - ) -> Result<()> { - assert_eq!(key, MAGIC_FIELD); - let ptr: &U = value; - // SAFETY: MagicalSerializer only ever receives single field u64s, - // type-safety is ensured by MAGIC_NAME checks in `serialize_struct()` - self.opaque = unsafe { opaque_recv(ptr) }; - Ok(()) - } - - fn end(self) -> JsResult<'a> { - // SAFETY: transerialization assumptions imply `T` is still alive. - let x: &mut T = unsafe { opaque_deref_mut(self.opaque) }; - let scope = &mut *self.scope.borrow_mut(); - x.to_v8(scope) - } -} - -// Dispatches between magic and regular struct serializers -pub enum StructSerializers<'a, 'b, 'c> { - ExternalPointer(MagicalSerializer<'a, 'b, 'c, magic::ExternalPointer>), - Magic(MagicalSerializer<'a, 'b, 'c, magic::Value<'a>>), - RustToV8Buf(MagicalSerializer<'a, 'b, 'c, ToJsBuffer>), - MagicAnyValue(MagicalSerializer<'a, 'b, 'c, AnyValue>), - MagicDetached(MagicalSerializer<'a, 'b, 'c, DetachedBuffer>), - MagicByteString(MagicalSerializer<'a, 'b, 'c, ByteString>), - MagicU16String(MagicalSerializer<'a, 'b, 'c, U16String>), - MagicBigInt(MagicalSerializer<'a, 'b, 'c, BigInt>), - Regular(ObjectSerializer<'a, 'b, 'c>), -} - -impl<'a, 'b, 'c> ser::SerializeStruct for StructSerializers<'a, 'b, 'c> { - type Ok = JsValue<'a>; - type Error = Error; - - fn serialize_field( - &mut self, - key: &'static str, - value: &T, - ) -> Result<()> { - match self { - StructSerializers::ExternalPointer(s) => s.serialize_field(key, value), - StructSerializers::Magic(s) => s.serialize_field(key, value), - StructSerializers::RustToV8Buf(s) => s.serialize_field(key, value), - StructSerializers::MagicAnyValue(s) => s.serialize_field(key, value), - StructSerializers::MagicDetached(s) => s.serialize_field(key, value), - StructSerializers::MagicByteString(s) => s.serialize_field(key, value), - StructSerializers::MagicU16String(s) => s.serialize_field(key, value), - StructSerializers::MagicBigInt(s) => s.serialize_field(key, value), - StructSerializers::Regular(s) => s.serialize_field(key, value), - } - } - - fn end(self) -> JsResult<'a> { - match self { - StructSerializers::ExternalPointer(s) => s.end(), - StructSerializers::Magic(s) => s.end(), - StructSerializers::RustToV8Buf(s) => s.end(), - StructSerializers::MagicAnyValue(s) => s.end(), - StructSerializers::MagicDetached(s) => s.end(), - StructSerializers::MagicByteString(s) => s.end(), - StructSerializers::MagicU16String(s) => s.end(), - StructSerializers::MagicBigInt(s) => s.end(), - StructSerializers::Regular(s) => s.end(), - } - } -} - -// Serializes to JS Objects, NOT JS Maps ... -pub struct MapSerializer<'a, 'b, 'c> { - scope: ScopePtr<'a, 'b, 'c>, - keys: Vec>, - values: Vec>, -} - -impl<'a, 'b, 'c> MapSerializer<'a, 'b, 'c> { - pub fn new(scope: ScopePtr<'a, 'b, 'c>, len: Option) -> Self { - let keys = Vec::with_capacity(len.unwrap_or_default()); - let values = Vec::with_capacity(len.unwrap_or_default()); - Self { - scope, - keys, - values, - } - } -} - -impl<'a, 'b, 'c> ser::SerializeMap for MapSerializer<'a, 'b, 'c> { - type Ok = JsValue<'a>; - type Error = Error; - - fn serialize_key(&mut self, key: &T) -> Result<()> { - let key = key.serialize(Serializer::new(self.scope))?; - self.keys.push(key.try_into().map_err(|_| { - Error::Message("Serialized Maps expect String keys".into()) - })?); - Ok(()) - } - - fn serialize_value( - &mut self, - value: &T, - ) -> Result<()> { - let v8_value = value.serialize(Serializer::new(self.scope))?; - self.values.push(v8_value); - Ok(()) - } - - fn end(self) -> JsResult<'a> { - debug_assert!(self.keys.len() == self.values.len()); - let scope = &mut *self.scope.borrow_mut(); - let null = v8::null(scope).into(); - let obj = v8::Object::with_prototype_and_properties( - scope, - null, - &self.keys[..], - &self.values[..], - ); - Ok(obj.into()) - } -} - -pub struct Serializer<'a, 'b, 'c> { - scope: ScopePtr<'a, 'b, 'c>, -} - -impl<'a, 'b, 'c> Serializer<'a, 'b, 'c> { - pub fn new(scope: ScopePtr<'a, 'b, 'c>) -> Self { - Serializer { scope } - } -} - -macro_rules! forward_to { - ($($name:ident($ty:ty, $to:ident, $lt:lifetime);)*) => { - $(fn $name(self, v: $ty) -> JsResult<$lt> { - self.$to(v as _) - })* - }; -} - -pub(crate) const MAX_SAFE_INTEGER: i64 = (1 << 53) - 1; -pub(crate) const MIN_SAFE_INTEGER: i64 = -MAX_SAFE_INTEGER; - -impl<'a, 'b, 'c> ser::Serializer for Serializer<'a, 'b, 'c> { - type Ok = v8::Local<'a, v8::Value>; - type Error = Error; - - type SerializeSeq = ArraySerializer<'a, 'b, 'c>; - type SerializeTuple = ArraySerializer<'a, 'b, 'c>; - type SerializeTupleStruct = ArraySerializer<'a, 'b, 'c>; - type SerializeTupleVariant = - VariantSerializer<'a, 'b, 'c, ArraySerializer<'a, 'b, 'c>>; - type SerializeMap = MapSerializer<'a, 'b, 'c>; - type SerializeStruct = StructSerializers<'a, 'b, 'c>; - type SerializeStructVariant = - VariantSerializer<'a, 'b, 'c, StructSerializers<'a, 'b, 'c>>; - - forward_to! { - serialize_i8(i8, serialize_i32, 'a); - serialize_i16(i16, serialize_i32, 'a); - - serialize_u8(u8, serialize_u32, 'a); - serialize_u16(u16, serialize_u32, 'a); - - serialize_f32(f32, serialize_f64, 'a); - } - - fn serialize_i32(self, v: i32) -> JsResult<'a> { - Ok(v8::Integer::new(&mut self.scope.borrow_mut(), v).into()) - } - - fn serialize_u32(self, v: u32) -> JsResult<'a> { - Ok(v8::Integer::new_from_unsigned(&mut self.scope.borrow_mut(), v).into()) - } - - fn serialize_i64(self, v: i64) -> JsResult<'a> { - let s = &mut self.scope.borrow_mut(); - // If i64 can fit in max safe integer bounds then serialize as v8::Number - // otherwise serialize as v8::BigInt - if (MIN_SAFE_INTEGER..=MAX_SAFE_INTEGER).contains(&v) { - Ok(v8::Number::new(s, v as _).into()) - } else { - Ok(v8::BigInt::new_from_i64(s, v).into()) - } - } - - fn serialize_u64(self, v: u64) -> JsResult<'a> { - let s = &mut self.scope.borrow_mut(); - // If u64 can fit in max safe integer bounds then serialize as v8::Number - // otherwise serialize as v8::BigInt - if v <= (MAX_SAFE_INTEGER as u64) { - Ok(v8::Number::new(s, v as _).into()) - } else { - Ok(v8::BigInt::new_from_u64(s, v).into()) - } - } - - fn serialize_f64(self, v: f64) -> JsResult<'a> { - let scope = &mut self.scope.borrow_mut(); - Ok(v8::Number::new(scope.deref_mut(), v).into()) - } - - fn serialize_bool(self, v: bool) -> JsResult<'a> { - Ok(v8::Boolean::new(&mut *self.scope.borrow_mut(), v).into()) - } - - fn serialize_char(self, v: char) -> JsResult<'a> { - self.serialize_str(&v.to_string()) - } - - fn serialize_str(self, v: &str) -> JsResult<'a> { - let maybe_str = v8::String::new(&mut self.scope.borrow_mut(), v); - - // v8 string can return 'None' if buffer length > kMaxLength. - if let Some(str) = maybe_str { - Ok(str.into()) - } else { - Err(Error::Message(String::from( - "Cannot allocate String: buffer exceeds maximum length.", - ))) - } - } - - fn serialize_bytes(self, v: &[u8]) -> JsResult<'a> { - Ok(slice_to_uint8array(&mut self.scope.borrow_mut(), v).into()) - } - - fn serialize_none(self) -> JsResult<'a> { - Ok(v8::null(&mut *self.scope.borrow_mut()).into()) - } - - fn serialize_some(self, value: &T) -> JsResult<'a> { - value.serialize(self) - } - - fn serialize_unit(self) -> JsResult<'a> { - Ok(v8::null(&mut *self.scope.borrow_mut()).into()) - } - - fn serialize_unit_struct(self, _name: &'static str) -> JsResult<'a> { - Ok(v8::null(&mut *self.scope.borrow_mut()).into()) - } - - /// For compatibility with serde-json, serialises unit variants as "Variant" strings. - fn serialize_unit_variant( - self, - _name: &'static str, - _variant_index: u32, - variant: &'static str, - ) -> JsResult<'a> { - Ok(v8_struct_key(&mut self.scope.borrow_mut(), variant).into()) - } - - fn serialize_newtype_struct( - self, - _name: &'static str, - value: &T, - ) -> JsResult<'a> { - value.serialize(self) - } - - fn serialize_newtype_variant( - self, - _name: &'static str, - _variant_index: u32, - variant: &'static str, - value: &T, - ) -> JsResult<'a> { - let scope = self.scope; - let x = self.serialize_newtype_struct(variant, value)?; - VariantSerializer::new(scope, variant, x).end(Ok) - } - - /// Serialises any Rust iterable into a JS Array - fn serialize_seq(self, len: Option) -> Result { - Ok(ArraySerializer::new(self.scope, len)) - } - - fn serialize_tuple(self, len: usize) -> Result { - self.serialize_seq(Some(len)) - } - - fn serialize_tuple_struct( - self, - _name: &'static str, - len: usize, - ) -> Result { - self.serialize_tuple(len) - } - - fn serialize_tuple_variant( - self, - _name: &'static str, - _variant_index: u32, - variant: &'static str, - len: usize, - ) -> Result { - Ok(VariantSerializer::new( - self.scope, - variant, - self.serialize_tuple_struct(variant, len)?, - )) - } - - fn serialize_map(self, len: Option) -> Result { - // Serializes a rust Map (e.g: BTreeMap, HashMap) to a v8 Object - // TODO: consider allowing serializing to v8 Maps (e.g: via a magic type) - // since they're lighter and better suited for K/V data - // and maybe restrict keys (e.g: strings and numbers) - Ok(MapSerializer::new(self.scope, len)) - } - - /// Serialises Rust typed structs into plain JS objects. - fn serialize_struct( - self, - name: &'static str, - len: usize, - ) -> Result { - match name { - magic::ExternalPointer::MAGIC_NAME => { - let m = MagicalSerializer::::new(self.scope); - Ok(StructSerializers::ExternalPointer(m)) - } - ByteString::MAGIC_NAME => { - let m = MagicalSerializer::::new(self.scope); - Ok(StructSerializers::MagicByteString(m)) - } - U16String::MAGIC_NAME => { - let m = MagicalSerializer::::new(self.scope); - Ok(StructSerializers::MagicU16String(m)) - } - ToJsBuffer::MAGIC_NAME => { - let m = MagicalSerializer::::new(self.scope); - Ok(StructSerializers::RustToV8Buf(m)) - } - AnyValue::MAGIC_NAME => { - let m = MagicalSerializer::::new(self.scope); - Ok(StructSerializers::MagicAnyValue(m)) - } - DetachedBuffer::MAGIC_NAME => { - let m = MagicalSerializer::::new(self.scope); - Ok(StructSerializers::MagicDetached(m)) - } - BigInt::MAGIC_NAME => { - let m = MagicalSerializer::::new(self.scope); - Ok(StructSerializers::MagicBigInt(m)) - } - magic::Value::MAGIC_NAME => { - let m = MagicalSerializer::>::new(self.scope); - Ok(StructSerializers::Magic(m)) - } - _ => { - // Regular structs - let o = ObjectSerializer::new(self.scope, len); - Ok(StructSerializers::Regular(o)) - } - } - } - - fn serialize_struct_variant( - self, - _name: &'static str, - _variant_index: u32, - variant: &'static str, - len: usize, - ) -> Result { - let scope = self.scope; - let x = self.serialize_struct(variant, len)?; - Ok(VariantSerializer::new(scope, variant, x)) - } -} - -pub fn slice_to_uint8array<'a>( - scope: &mut v8::HandleScope<'a>, - buf: &[u8], -) -> v8::Local<'a, v8::Uint8Array> { - let buffer = if buf.is_empty() { - v8::ArrayBuffer::new(scope, 0) - } else { - let store: v8::UniqueRef<_> = - v8::ArrayBuffer::new_backing_store(scope, buf.len()); - // SAFETY: raw memory copy into the v8 ArrayBuffer allocated above - unsafe { - std::ptr::copy_nonoverlapping( - buf.as_ptr(), - store.data().unwrap().as_ptr() as *mut u8, - buf.len(), - ) - } - v8::ArrayBuffer::with_backing_store(scope, &store.make_shared()) - }; - v8::Uint8Array::new(scope, buffer, 0, buf.len()) - .expect("Failed to create UintArray8") -} diff --git a/serde_v8/serializable.rs b/serde_v8/serializable.rs deleted file mode 100644 index 7380ab5a70..0000000000 --- a/serde_v8/serializable.rs +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use std::any::TypeId; -use std::mem::transmute_copy; - -use crate::BigInt; -use crate::ByteString; -use crate::ToJsBuffer; -use crate::U16String; - -/// Serializable exists to allow boxing values as "objects" to be serialized later, -/// this is particularly useful for async op-responses. This trait is a more efficient -/// replacement for erased-serde that makes less allocations, since it's specific to serde_v8 -/// (and thus doesn't have to have generic outputs, etc...) -pub trait Serializable { - fn to_v8<'a>( - &mut self, - scope: &mut v8::HandleScope<'a>, - ) -> Result, crate::Error>; -} - -/// Allows all implementors of `serde::Serialize` to implement Serializable -impl Serializable for T { - fn to_v8<'a>( - &mut self, - scope: &mut v8::HandleScope<'a>, - ) -> Result, crate::Error> { - crate::to_v8(scope, self) - } -} - -/// SerializablePkg exists to provide a fast path for op returns, -/// allowing them to avoid boxing primitives (ints/floats/bool/unit/...) -pub enum SerializablePkg { - Primitive(Primitive), - Serializable(Box), -} - -impl SerializablePkg { - pub fn to_v8<'a>( - &mut self, - scope: &mut v8::HandleScope<'a>, - ) -> Result, crate::Error> { - match self { - Self::Primitive(x) => crate::to_v8(scope, x), - Self::Serializable(x) => x.to_v8(scope), - } - } -} - -/// Primitive serves as a lightweight serializable wrapper around primitives -/// so that we can use them for async values -pub enum Primitive { - Unit, - Bool(bool), - Int8(i8), - Int16(i16), - Int32(i32), - Int64(i64), - UInt8(u8), - UInt16(u16), - UInt32(u32), - UInt64(u64), - Float32(f32), - Float64(f64), - String(String), - RustToV8Buf(ToJsBuffer), - ByteString(ByteString), - U16String(U16String), - BigInt(BigInt), -} - -impl serde::Serialize for Primitive { - fn serialize(&self, s: S) -> Result - where - S: serde::Serializer, - { - match self { - Self::Unit => ().serialize(s), - Self::Bool(x) => x.serialize(s), - Self::Int8(x) => x.serialize(s), - Self::Int16(x) => x.serialize(s), - Self::Int32(x) => x.serialize(s), - Self::Int64(x) => x.serialize(s), - Self::UInt8(x) => x.serialize(s), - Self::UInt16(x) => x.serialize(s), - Self::UInt32(x) => x.serialize(s), - Self::UInt64(x) => x.serialize(s), - Self::Float32(x) => x.serialize(s), - Self::Float64(x) => x.serialize(s), - Self::String(x) => x.serialize(s), - Self::RustToV8Buf(x) => x.serialize(s), - Self::ByteString(x) => x.serialize(s), - Self::U16String(x) => x.serialize(s), - Self::BigInt(x) => x.serialize(s), - } - } -} - -impl From for SerializablePkg { - fn from(x: T) -> Self { - #[inline(always)] - fn tc(src: T) -> U { - // SAFETY: the caller has ensured via the TypeId that the T and U types - // are the same. - let x = unsafe { transmute_copy(&src) }; - std::mem::forget(src); - x - } - - let tid = TypeId::of::(); - if tid == TypeId::of::<()>() { - Self::Primitive(Primitive::Unit) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::Bool(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::Int8(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::Int16(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::Int32(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::Int64(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::UInt8(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::UInt16(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::UInt32(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::UInt64(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::Float32(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::Float64(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::String(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::RustToV8Buf(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::ByteString(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::U16String(tc(x))) - } else if tid == TypeId::of::() { - Self::Primitive(Primitive::BigInt(tc(x))) - } else { - Self::Serializable(Box::new(x)) - } - } -} diff --git a/serde_v8/tests/de.rs b/serde_v8/tests/de.rs deleted file mode 100644 index 2edfe1bc62..0000000000 --- a/serde_v8/tests/de.rs +++ /dev/null @@ -1,613 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use serde::Deserialize; -use serde::Deserializer; - -use serde_v8::utils::js_exec; -use serde_v8::utils::v8_do; -use serde_v8::BigInt; -use serde_v8::ByteString; -use serde_v8::Error; -use serde_v8::JsBuffer; -use serde_v8::U16String; - -#[derive(Debug, Deserialize, PartialEq)] -struct MathOp { - pub a: u64, - pub b: u64, - pub operator: Option, -} - -#[derive(Debug, PartialEq, Deserialize)] -enum EnumUnit { - A, - B, - C, -} - -#[derive(Debug, PartialEq, Deserialize)] -enum EnumPayloads { - UInt(u64), - Int(i64), - Float(f64), - Point { x: i64, y: i64 }, - Tuple(bool, i64, ()), -} - -fn dedo( - code: &str, - f: impl FnOnce(&mut v8::HandleScope, v8::Local), -) { - v8_do(|| { - let isolate = &mut v8::Isolate::new(v8::CreateParams::default()); - let handle_scope = &mut v8::HandleScope::new(isolate); - let context = v8::Context::new(handle_scope); - let scope = &mut v8::ContextScope::new(handle_scope, context); - let v = js_exec(scope, code); - - f(scope, v); - }) -} - -macro_rules! decheck { - ($fn_name:ident, $t:ty, $src:expr, $x:ident, $check:expr) => { - #[test] - fn $fn_name() { - #[allow(clippy::bool_assert_comparison)] - dedo($src, |scope, v| { - let rt = serde_v8::from_v8(scope, v); - assert!(rt.is_ok(), "from_v8(\"{}\"): {:?}", $src, rt.err()); - let $x: $t = rt.unwrap(); - $check - }); - } - }; -} - -macro_rules! detest { - ($fn_name:ident, $t:ty, $src:expr, $rust:expr) => { - decheck!($fn_name, $t, $src, t, assert_eq!(t, $rust)); - }; -} - -macro_rules! defail { - ($fn_name:ident, $t:ty, $src:expr, $failcase:expr) => { - #[test] - fn $fn_name() { - #[allow(clippy::bool_assert_comparison)] - dedo($src, |scope, v| { - let rt: serde_v8::Result<$t> = serde_v8::from_v8(scope, v); - let rtstr = format!("{:?}", rt); - let failed_as_expected = $failcase(rt); - assert!( - failed_as_expected, - "expected failure on deserialize(\"{}\"), got: {}", - $src, rtstr - ); - }); - } - }; -} - -detest!(de_option_some, Option, "true", Some(true)); -detest!(de_option_null, Option, "null", None); -detest!(de_option_undefined, Option, "undefined", None); -detest!(de_unit_null, (), "null", ()); -detest!(de_unit_undefined, (), "undefined", ()); -detest!(de_bool, bool, "true", true); -detest!(de_char, char, "'é'", 'é'); -detest!(de_u64, u64, "32", 32); -detest!(de_string, String, "'Hello'", "Hello".to_owned()); -detest!(de_vec_empty, Vec, "[]", vec![0; 0]); -detest!(de_vec_u64, Vec, "[1,2,3,4,5]", vec![1, 2, 3, 4, 5]); -detest!( - de_vec_str, - Vec, - "['hello', 'world']", - vec!["hello".to_owned(), "world".to_owned()] -); -detest!( - de_tuple, - (u64, bool, ()), - "[123, true, null]", - (123, true, ()) -); -defail!( - de_tuple_wrong_len_short, - (u64, bool, ()), - "[123, true]", - |e| e == Err(Error::LengthMismatch(2, 3)) -); -defail!( - de_tuple_wrong_len_long, - (u64, bool, ()), - "[123, true, null, 'extra']", - |e| e == Err(Error::LengthMismatch(4, 3)) -); -detest!( - de_mathop, - MathOp, - "({a: 1, b: 3, c: 'ignored'})", - MathOp { - a: 1, - b: 3, - operator: None - } -); - -// Unit enums -detest!(de_enum_unit_a, EnumUnit, "'A'", EnumUnit::A); -detest!(de_enum_unit_so_a, EnumUnit, "new String('A')", EnumUnit::A); -detest!(de_enum_unit_b, EnumUnit, "'B'", EnumUnit::B); -detest!(de_enum_unit_so_b, EnumUnit, "new String('B')", EnumUnit::B); -detest!(de_enum_unit_c, EnumUnit, "'C'", EnumUnit::C); -detest!(de_enum_unit_so_c, EnumUnit, "new String('C')", EnumUnit::C); - -// Enums with payloads (tuples & struct) -detest!( - de_enum_payload_int, - EnumPayloads, - "({ Int: -123 })", - EnumPayloads::Int(-123) -); -detest!( - de_enum_payload_uint, - EnumPayloads, - "({ UInt: 123 })", - EnumPayloads::UInt(123) -); -detest!( - de_enum_payload_float, - EnumPayloads, - "({ Float: 1.23 })", - EnumPayloads::Float(1.23) -); -detest!( - de_enum_payload_point, - EnumPayloads, - "({ Point: { x: 1, y: 2 } })", - EnumPayloads::Point { x: 1, y: 2 } -); -detest!( - de_enum_payload_tuple, - EnumPayloads, - "({ Tuple: [true, 123, null ] })", - EnumPayloads::Tuple(true, 123, ()) -); - -#[test] -fn de_f64() { - dedo("12345.0", |scope, v| { - let x: f64 = serde_v8::from_v8(scope, v).unwrap(); - assert!((x - 12345.0).abs() < f64::EPSILON); - }); -} - -#[test] -fn de_map() { - use std::collections::HashMap; - - dedo("({a: 1, b: 2, c: 3})", |scope, v| { - let map: HashMap = serde_v8::from_v8(scope, v).unwrap(); - assert_eq!(map.get("a").cloned(), Some(1)); - assert_eq!(map.get("b").cloned(), Some(2)); - assert_eq!(map.get("c").cloned(), Some(3)); - assert_eq!(map.get("nada"), None); - }) -} - -#[test] -fn de_obj_with_numeric_keys() { - dedo( - r#"({ - lines: { - 100: { - unit: "m" - }, - 200: { - unit: "cm" - } - } -})"#, - |scope, v| { - let json: serde_json::Value = serde_v8::from_v8(scope, v).unwrap(); - assert_eq!( - json.to_string(), - r#"{"lines":{"100":{"unit":"m"},"200":{"unit":"cm"}}}"# - ); - }, - ) -} - -#[test] -fn de_string_or_buffer() { - dedo("'hello'", |scope, v| { - let sob: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap(); - assert_eq!(sob.as_ref(), &[0x68, 0x65, 0x6C, 0x6C, 0x6F]); - }); - - dedo("new Uint8Array([97])", |scope, v| { - let sob: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap(); - assert_eq!(sob.as_ref(), &[97]); - }); - - dedo("new Uint8Array([128])", |scope, v| { - let sob: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap(); - assert_eq!(sob.as_ref(), &[128]); - }); - - dedo( - "(Uint8Array.from([0x68, 0x65, 0x6C, 0x6C, 0x6F]))", - |scope, v| { - let sob: serde_v8::StringOrBuffer = serde_v8::from_v8(scope, v).unwrap(); - assert_eq!(sob.as_ref(), &[0x68, 0x65, 0x6C, 0x6C, 0x6F]); - }, - ); -} - -#[test] -fn de_buffers() { - // ArrayBufferView - dedo("new Uint8Array([97])", |scope, v| { - let buf: JsBuffer = serde_v8::from_v8(scope, v).unwrap(); - assert_eq!(&*buf, &[97]); - }); - - // ArrayBuffer - dedo("(new Uint8Array([97])).buffer", |scope, v| { - let buf: JsBuffer = serde_v8::from_v8(scope, v).unwrap(); - assert_eq!(&*buf, &[97]); - }); - - dedo( - "(Uint8Array.from([0x68, 0x65, 0x6C, 0x6C, 0x6F]))", - |scope, v| { - let buf: JsBuffer = serde_v8::from_v8(scope, v).unwrap(); - assert_eq!(&*buf, &[0x68, 0x65, 0x6C, 0x6C, 0x6F]); - }, - ); - - dedo("(new ArrayBuffer(4))", |scope, v| { - let buf: JsBuffer = serde_v8::from_v8(scope, v).unwrap(); - assert_eq!(&*buf, &[0x0, 0x0, 0x0, 0x0]); - }); - - dedo("(new ArrayBuffer(8, { maxByteLength: 16}))", |scope, v| { - let result: Result = serde_v8::from_v8(scope, v); - matches!(result, Err(Error::ResizableBackingStoreNotSupported)); - }); -} - -// Structs -#[derive(Debug, PartialEq, Deserialize)] -struct StructUnit; - -#[derive(Debug, PartialEq)] -struct StructPayload { - a: u64, - b: u64, -} - -struct StructVisitor; - -impl<'de> serde::de::Visitor<'de> for StructVisitor { - type Value = StructPayload; - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("struct StructPayload") - } - fn visit_map(self, mut map: A) -> Result - where - A: serde::de::MapAccess<'de>, - { - let mut payload = StructPayload { a: 0, b: 0 }; - while let Some(key) = map.next_key::()? { - match key.as_ref() { - "a" => payload.a = map.next_value()?, - "b" => payload.b = map.next_value()?, - f => panic!("Unknown field {f}"), - } - } - Ok(payload) - } -} - -detest!(de_unit_struct, StructUnit, "'StructUnit'", StructUnit); - -#[test] -fn de_struct() { - dedo("({ a: 1, b: 2 })", |scope, v| { - let mut de = serde_v8::Deserializer::new(scope, v, None); - let payload = de - .deserialize_struct("StructPayload", &[], StructVisitor) - .unwrap(); - assert_eq!(payload, StructPayload { a: 1, b: 2 }) - }) -} - -#[test] -fn de_struct_hint() { - dedo("({ a: 1, b: 2 })", |scope, v| { - let mut de = serde_v8::Deserializer::new(scope, v, None); - let payload = de - .deserialize_struct("StructPayload", &["a", "b"], StructVisitor) - .unwrap(); - assert_eq!(payload, StructPayload { a: 1, b: 2 }) - }) -} - -//// -// JSON tests: serde_json::Value compatibility -//// - -detest!( - de_json_null, - serde_json::Value, - "null", - serde_json::Value::Null -); -detest!( - de_json_bool, - serde_json::Value, - "true", - serde_json::Value::Bool(true) -); -detest!( - de_json_int, - serde_json::Value, - "123", - serde_json::Value::Number(serde_json::Number::from(123)) -); -detest!( - de_json_float, - serde_json::Value, - "123.45", - serde_json::Value::Number(serde_json::Number::from_f64(123.45).unwrap()) -); -detest!( - de_json_string, - serde_json::Value, - "'Hello'", - serde_json::Value::String("Hello".to_string()) -); -detest!( - de_json_vec_string, - serde_json::Value, - "['Hello', 'World']", - serde_json::Value::Array(vec![ - serde_json::Value::String("Hello".to_string()), - serde_json::Value::String("World".to_string()) - ]) -); -detest!( - de_json_tuple, - serde_json::Value, - "[true, 'World', 123.45, null]", - serde_json::Value::Array(vec![ - serde_json::Value::Bool(true), - serde_json::Value::String("World".to_string()), - serde_json::Value::Number(serde_json::Number::from_f64(123.45).unwrap()), - serde_json::Value::Null, - ]) -); -detest!( - de_json_object, - serde_json::Value, - "({a: 1, b: 'hello', c: true})", - serde_json::json!({ - "a": 1, - "b": "hello", - "c": true, - }) -); -detest!( - de_json_object_from_map, - serde_json::Value, - "(new Map([['a', 1], ['b', 'hello'], ['c', true]]))", - serde_json::json!({ - "a": 1, - "b": "hello", - "c": true, - }) -); -// TODO: this is not optimal, ideally we'd get an array of [1,2,3] instead. -// Fixing that will require exposing Set::AsArray in the v8 bindings. -detest!( - de_json_object_from_set, - serde_json::Value, - "(new Set([1, 2, 3]))", - serde_json::json!({}) -); - -defail!(defail_struct, MathOp, "123", |e| e - == Err(Error::ExpectedObject("Number"))); - -#[derive(Eq, PartialEq, Debug, Deserialize)] -pub struct SomeThing { - pub a: String, - #[serde(default)] - pub b: String, -} -detest!( - de_struct_defaults, - SomeThing, - "({ a: 'hello' })", - SomeThing { - a: "hello".into(), - b: "".into() - } -); - -detest!(de_bstr, ByteString, "'hello'", "hello".into()); -defail!(defail_bstr, ByteString, "'👋bye'", |e| e - == Err(Error::ExpectedLatin1)); - -#[derive(Eq, PartialEq, Debug, Deserialize)] -pub struct StructWithBytes { - #[serde(with = "serde_bytes")] - a: Vec, - #[serde(with = "serde_bytes")] - b: Vec, - #[serde(with = "serde_bytes")] - c: Vec, -} -detest!( - de_struct_with_bytes, - StructWithBytes, - "({ a: new Uint8Array([1, 2]), b: (new Uint8Array([3 , 4])).buffer, c: (new Uint32Array([0])).buffer})", - StructWithBytes { - a: vec![1, 2], - b: vec![3, 4], - c: vec![0, 0, 0, 0], - } -); -detest!( - de_u16str, - U16String, - "'hello'", - "hello".encode_utf16().collect::>().into() -); -detest!( - de_u16str_non_latin1, - U16String, - "'👋bye'", - "👋bye".encode_utf16().collect::>().into() -); - -// NaN -detest!(de_nan_u8, u8, "NaN", 0); -detest!(de_nan_u16, u16, "NaN", 0); -detest!(de_nan_u32, u32, "NaN", 0); -detest!(de_nan_u64, u64, "NaN", 0); -detest!(de_nan_i8, i8, "NaN", 0); -detest!(de_nan_i16, i16, "NaN", 0); -detest!(de_nan_i32, i32, "NaN", 0); -detest!(de_nan_i64, i64, "NaN", 0); -decheck!(de_nan_f32, f32, "NaN", t, assert!(t.is_nan())); -decheck!(de_nan_f64, f64, "NaN", t, assert!(t.is_nan())); - -// Infinity -detest!(de_inf_u8, u8, "Infinity", u8::MAX); -detest!(de_inf_u16, u16, "Infinity", u16::MAX); -detest!(de_inf_u32, u32, "Infinity", u32::MAX); -detest!(de_inf_u64, u64, "Infinity", u64::MAX); -detest!(de_inf_i8, i8, "Infinity", i8::MAX); -detest!(de_inf_i16, i16, "Infinity", i16::MAX); -detest!(de_inf_i32, i32, "Infinity", i32::MAX); -detest!(de_inf_i64, i64, "Infinity", i64::MAX); -detest!(de_inf_f32, f32, "Infinity", f32::INFINITY); -detest!(de_inf_f64, f64, "Infinity", f64::INFINITY); - -// -Infinity -detest!(de_neg_inf_u8, u8, "-Infinity", u8::MIN); -detest!(de_neg_inf_u16, u16, "-Infinity", u16::MIN); -detest!(de_neg_inf_u32, u32, "-Infinity", u32::MIN); -detest!(de_neg_inf_u64, u64, "-Infinity", u64::MIN); -detest!(de_neg_inf_i8, i8, "-Infinity", i8::MIN); -detest!(de_neg_inf_i16, i16, "-Infinity", i16::MIN); -detest!(de_neg_inf_i32, i32, "-Infinity", i32::MIN); -detest!(de_neg_inf_i64, i64, "-Infinity", i64::MIN); -detest!(de_neg_inf_f32, f32, "-Infinity", f32::NEG_INFINITY); -detest!(de_neg_inf_f64, f64, "-Infinity", f64::NEG_INFINITY); - -// BigInt to f32/f64 max/min -detest!( - de_bigint_f64_max, - f64, - "BigInt(1.7976931348623157e+308)", - f64::MAX -); -detest!( - de_bigint_f64_min, - f64, - "BigInt(-1.7976931348623157e+308)", - f64::MIN -); -detest!(de_bigint_f32_max, f32, "BigInt(3.40282347e38)", f32::MAX); -detest!(de_bigint_f32_min, f32, "BigInt(-3.40282347e38)", f32::MIN); -// BigInt to f32/f64 saturating to inf -detest!( - de_bigint_f64_inf, - f64, - "(BigInt(1.7976931348623157e+308)*BigInt(100))", - f64::INFINITY -); -detest!( - de_bigint_f64_neg_inf, - f64, - "(BigInt(-1.7976931348623157e+308)*BigInt(100))", - f64::NEG_INFINITY -); - -detest!( - de_bigint_f32_inf, - f32, - "BigInt(1.7976931348623157e+308)", - f32::INFINITY -); -detest!( - de_bigint_f32_neg_inf, - f32, - "BigInt(-1.7976931348623157e+308)", - f32::NEG_INFINITY -); - -// BigInt to BigInt -detest!( - de_bigint_var_u8, - BigInt, - "255n", - num_bigint::BigInt::from(255u8).into() -); -detest!( - de_bigint_var_i8, - BigInt, - "-128n", - num_bigint::BigInt::from(-128i8).into() -); -detest!( - de_bigint_var_u16, - BigInt, - "65535n", - num_bigint::BigInt::from(65535u16).into() -); -detest!( - de_bigint_var_i16, - BigInt, - "-32768n", - num_bigint::BigInt::from(-32768i16).into() -); -detest!( - de_bigint_var_u32, - BigInt, - "4294967295n", - num_bigint::BigInt::from(4294967295u32).into() -); -detest!( - de_bigint_var_i32, - BigInt, - "-2147483648n", - num_bigint::BigInt::from(-2147483648i32).into() -); -detest!( - de_bigint_var_u64, - BigInt, - "18446744073709551615n", - num_bigint::BigInt::from(18446744073709551615u64).into() -); -detest!( - de_bigint_var_i64, - BigInt, - "-9223372036854775808n", - num_bigint::BigInt::from(-9223372036854775808i64).into() -); -detest!( - de_bigint_var_u128, - BigInt, - "340282366920938463463374607431768211455n", - num_bigint::BigInt::from(340282366920938463463374607431768211455u128).into() -); -detest!( - de_bigint_var_i128, - BigInt, - "-170141183460469231731687303715884105728n", - num_bigint::BigInt::from(-170141183460469231731687303715884105728i128).into() -); diff --git a/serde_v8/tests/magic.rs b/serde_v8/tests/magic.rs deleted file mode 100644 index e3ed1d330d..0000000000 --- a/serde_v8/tests/magic.rs +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use serde::Deserialize; -use serde::Serialize; - -use serde_v8::utils::js_exec; -use serde_v8::utils::v8_do; -use serde_v8::Result; - -#[derive(Deserialize)] -struct MagicOp<'s> { - #[allow(unused)] - pub a: u64, - #[allow(unused)] - pub b: u64, - pub c: serde_v8::Value<'s>, - #[allow(unused)] - pub operator: Option, -} - -#[derive(Serialize)] -struct MagicContainer<'s> { - pub magic: bool, - pub contains: serde_v8::Value<'s>, -} - -#[test] -fn magic_basic() { - v8_do(|| { - let isolate = &mut v8::Isolate::new(v8::CreateParams::default()); - let handle_scope = &mut v8::HandleScope::new(isolate); - let context = v8::Context::new(handle_scope); - let scope = &mut v8::ContextScope::new(handle_scope, context); - - // Decode - let v = js_exec(scope, "({a: 1, b: 3, c: 'abracadabra'})"); - let mop: MagicOp = serde_v8::from_v8(scope, v).unwrap(); - // Check string - let v8_value: v8::Local = mop.c.into(); - let vs = v8::Local::::try_from(v8_value).unwrap(); - let s = vs.to_rust_string_lossy(scope); - assert_eq!(s, "abracadabra"); - - // Encode - let container = MagicContainer { - magic: true, - contains: v.into(), - }; - let vc = serde_v8::to_v8(scope, container).unwrap(); - // JSON stringify & check - let json = v8::json::stringify(scope, vc).unwrap(); - let s2 = json.to_rust_string_lossy(scope); - assert_eq!( - s2, - r#"{"magic":true,"contains":{"a":1,"b":3,"c":"abracadabra"}}"# - ); - }) -} - -#[test] -fn magic_buffer() { - v8_do(|| { - // Init isolate - let isolate = &mut v8::Isolate::new(v8::CreateParams::default()); - let handle_scope = &mut v8::HandleScope::new(isolate); - let context = v8::Context::new(handle_scope); - let scope = &mut v8::ContextScope::new(handle_scope, context); - let global = context.global(scope); - - // Simple buffer - let v8_array = js_exec(scope, "new Uint8Array([1,2,3,4,5])"); - let zbuf: serde_v8::JsBuffer = serde_v8::from_v8(scope, v8_array).unwrap(); - assert_eq!(&*zbuf, &[1, 2, 3, 4, 5]); - - // Multi buffers - let v8_arrays = - js_exec(scope, "[new Uint8Array([1,2]), new Uint8Array([3,4,5])]"); - let (z1, z2): (serde_v8::JsBuffer, serde_v8::JsBuffer) = - serde_v8::from_v8(scope, v8_arrays).unwrap(); - assert_eq!(&*z1, &[1, 2]); - assert_eq!(&*z2, &[3, 4, 5]); - - // Wrapped in option, like our current op-ABI - let v8_array = js_exec(scope, "new Uint8Array([1,2,3,4,5])"); - let zbuf: Option = - serde_v8::from_v8(scope, v8_array).unwrap(); - assert_eq!(&*zbuf.unwrap(), &[1, 2, 3, 4, 5]); - - // Observe mutation in JS - let v8_array = js_exec(scope, "new Uint8Array([1,2,3,4,5])"); - let mut zbuf: serde_v8::JsBuffer = - serde_v8::from_v8(scope, v8_array).unwrap(); - let key = serde_v8::to_v8(scope, "t1").unwrap(); - global.set(scope, key, v8_array); - (&mut *zbuf)[2] = 42; - let eq = js_exec(scope, "t1[2] === 42"); - assert!(eq.is_true()); - - // Shared buffers - let v8_array = - js_exec(scope, "new Uint8Array(new SharedArrayBuffer([1,2,3,4,5]))"); - let zbuf: Result = serde_v8::from_v8(scope, v8_array); - assert!(zbuf.is_err()); - - // Serialization - let buf: Vec = vec![1, 2, 3, 99, 5]; - let zbuf: serde_v8::ToJsBuffer = buf.into(); - let v8_value = serde_v8::to_v8(scope, zbuf).unwrap(); - let key = serde_v8::to_v8(scope, "t2").unwrap(); - global.set(scope, key, v8_value); - let eq = js_exec(scope, "t2[3] === 99"); - assert!(eq.is_true()); - - // Composite Serialization - #[derive(serde::Serialize)] - struct Wrapper { - a: serde_v8::ToJsBuffer, - b: serde_v8::ToJsBuffer, - } - let buf1: Vec = vec![1, 2, 33, 4, 5]; - let buf2: Vec = vec![5, 4, 3, 2, 11]; - let wrapped = Wrapper { - a: buf1.into(), - b: buf2.into(), - }; - let v8_value = serde_v8::to_v8(scope, wrapped).unwrap(); - let key = serde_v8::to_v8(scope, "t3").unwrap(); - global.set(scope, key, v8_value); - let eq = js_exec(scope, "t3.a[2] === 33"); - assert!(eq.is_true()); - let eq = js_exec(scope, "t3.b[4] === 11"); - assert!(eq.is_true()); - - // JsBuffer as bytes::Bytes - let v8_array = js_exec(scope, "new Uint8Array([1,2,3,4,5])"); - let zbuf: serde_v8::JsBuffer = serde_v8::from_v8(scope, v8_array).unwrap(); - let buf: bytes::Bytes = zbuf.into(); - assert_eq!(buf, bytes::Bytes::from_static(&[1, 2, 3, 4, 5])); - assert_eq!(buf, bytes::Bytes::from_static(&[1, 2, 3, 4, 5])); - assert_eq!(buf.slice(0..2), bytes::Bytes::from_static(&[1, 2])); - assert_eq!(buf.slice(2..), bytes::Bytes::from_static(&[3, 4, 5])); - // We're specifically testing that slices are preserved post-clone - #[allow(clippy::redundant_clone)] - let buf2 = buf.slice(2..).clone(); - assert_eq!(buf2, bytes::Bytes::from_static(&[3, 4, 5])); - }) -} - -#[test] -fn magic_byte_string() { - v8_do(|| { - // Init isolate - let isolate = &mut v8::Isolate::new(v8::CreateParams::default()); - let handle_scope = &mut v8::HandleScope::new(isolate); - let context = v8::Context::new(handle_scope); - let scope = &mut v8::ContextScope::new(handle_scope, context); - let global = context.global(scope); - - // JS string to ByteString - let v8_string = js_exec(scope, "'test \\0\\t\\n\\r\\x7F\\x80áþÆñ'"); - let rust_reflex: serde_v8::ByteString = - serde_v8::from_v8(scope, v8_string).unwrap(); - assert_eq!( - rust_reflex.as_slice(), - b"test \0\t\n\r\x7F\x80\xE1\xFE\xC6\xF1" - ); - - // Non-Latin-1 characters - let v8_string = js_exec(scope, "'日本語'"); - let rust_reflex: Result = - serde_v8::from_v8(scope, v8_string); - assert!(rust_reflex.is_err()); - - // Windows-1252 characters that aren't Latin-1 - let v8_string = js_exec(scope, "'œ'"); - let rust_reflex: Result = - serde_v8::from_v8(scope, v8_string); - assert!(rust_reflex.is_err()); - - // ByteString to JS string - let expected = "a\x00sf:~\x7Fá\u{009C}þ\u{008A}"; - let buf: Vec = b"a\x00sf:~\x7F\xE1\x9C\xFE\x8A".as_ref().into(); - let zbuf = serde_v8::ByteString::from(buf); - let v8_value = serde_v8::to_v8(scope, zbuf).unwrap(); - let key = serde_v8::to_v8(scope, "actual").unwrap(); - global.set(scope, key, v8_value); - let v8_value_expected = serde_v8::to_v8(scope, expected).unwrap(); - let key_expected = serde_v8::to_v8(scope, "expected").unwrap(); - global.set(scope, key_expected, v8_value_expected); - let eq = js_exec(scope, "actual === expected"); - assert!(eq.is_true()); - }) -} diff --git a/serde_v8/tests/ser.rs b/serde_v8/tests/ser.rs deleted file mode 100644 index b61a758f91..0000000000 --- a/serde_v8/tests/ser.rs +++ /dev/null @@ -1,270 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use serde::Serialize; -use serde_json::json; -use serde_v8::utils::js_exec; -use serde_v8::utils::v8_do; -use serde_v8::BigInt; - -#[derive(Debug, Serialize, PartialEq)] -struct MathOp { - pub a: u64, - pub b: u64, - pub operator: Option, -} - -// Utility JS code (obj equality, etc...) -const JS_UTILS: &str = r#" -// Shallow obj equality (don't use deep objs for now) -function objEqual(a, b) { - const ka = Object.keys(a); - const kb = Object.keys(b); - return ka.length === kb.length && ka.every(k => a[k] === b[k]); -} - -function arrEqual(a, b) { - return a.length === b.length && a.every((v, i) => v === b[i]); -} -"#; -const JS_POLLUTE: &str = r#" -Object.defineProperty(Array.prototype, "0", { - set: function (v) { - throw new Error("Polluted Array 0 set"); - }, -}); - -Object.defineProperty(Object.prototype, "a", { - set: (v) => { - throw new Error("Polluted Object 'a' set"); - } -}); -"#; - -fn sercheck(val: T, code: &str, pollute: bool) -> bool { - let mut equal = false; - - v8_do(|| { - // Setup isolate - let isolate = &mut v8::Isolate::new(v8::CreateParams::default()); - let handle_scope = &mut v8::HandleScope::new(isolate); - let context = v8::Context::new(handle_scope); - let scope = &mut v8::ContextScope::new(handle_scope, context); - - // Load util functions - js_exec(scope, JS_UTILS); - if pollute { - js_exec(scope, JS_POLLUTE); - } - // TryCatch scope (to catch pollution exceptions) - let scope = &mut v8::TryCatch::new(scope); - - // Set value as "x" in global scope - let global = context.global(scope); - let v8_key = serde_v8::to_v8(scope, "x").unwrap(); - let v8_val = serde_v8::to_v8(scope, val).unwrap(); - global.set(scope, v8_key, v8_val); - - // Pollution check - if let Some(message) = scope.message() { - let msg = message.get(scope).to_rust_string_lossy(scope); - panic!("JS Exception: {msg}"); - } - - // Execute equality check in JS (e.g: x == ...) - let v = js_exec(scope, code); - // Cast to bool - equal = serde_v8::from_v8(scope, v).unwrap(); - }); - - equal -} - -macro_rules! sertest { - ($fn_name:ident, $rust:expr, $src:expr) => { - #[test] - fn $fn_name() { - assert!( - sercheck($rust, $src, false), - "Expected: {} where x={:?}", - $src, - $rust, - ); - } - }; -} - -macro_rules! sertest_polluted { - ($fn_name:ident, $rust:expr, $src:expr) => { - #[test] - fn $fn_name() { - assert!( - sercheck($rust, $src, true), - "Expected: {} where x={:?}", - $src, - $rust, - ); - } - }; -} - -sertest!(ser_char, 'é', "x === 'é'"); -sertest!(ser_option_some, Some(true), "x === true"); -sertest!(ser_option_null, None as Option, "x === null"); -sertest!(ser_unit_null, (), "x === null"); -sertest!(ser_bool, true, "x === true"); -sertest!(ser_u64, 9007199254740991_u64, "x === 9007199254740991"); -sertest!(ser_big_int, 9007199254740992_i64, "x === 9007199254740992n"); -sertest!( - ser_neg_big_int, - -9007199254740992_i64, - "x === -9007199254740992n" -); -sertest!(ser_f64, 12345.0, "x === 12345.0"); -sertest!(ser_string, "Hello", "x === 'Hello'"); -sertest!(ser_bytes, b"\x01\x02\x03", "arrEqual(x, [1, 2, 3])"); -sertest!(ser_vec_u64, vec![1, 2, 3, 4, 5], "arrEqual(x, [1,2,3,4,5])"); -sertest!( - ser_vec_string, - vec!["hello", "world"], - "arrEqual(x, ['hello', 'world'])" -); -sertest!(ser_tuple, (123, true, ()), "arrEqual(x, [123, true, null])"); -sertest!( - ser_mathop, - MathOp { - a: 1, - b: 3, - operator: None - }, - "objEqual(x, {a: 1, b: 3, operator: null})" -); - -sertest!( - ser_bigint_u8, - BigInt::from(num_bigint::BigInt::from(255_u8)), - "x === 255n" -); -sertest!( - ser_bigint_i8, - BigInt::from(num_bigint::BigInt::from(-128_i8)), - "x === -128n" -); -sertest!( - ser_bigint_u16, - BigInt::from(num_bigint::BigInt::from(65535_u16)), - "x === 65535n" -); -sertest!( - ser_bigint_i16, - BigInt::from(num_bigint::BigInt::from(-32768_i16)), - "x === -32768n" -); -sertest!( - ser_bigint_u32, - BigInt::from(num_bigint::BigInt::from(4294967295_u32)), - "x === 4294967295n" -); -sertest!( - ser_bigint_i32, - BigInt::from(num_bigint::BigInt::from(-2147483648_i32)), - "x === -2147483648n" -); -sertest!( - ser_bigint_u64, - BigInt::from(num_bigint::BigInt::from(9007199254740991_u64)), - "x === 9007199254740991n" -); -sertest!( - ser_bigint_i64, - BigInt::from(num_bigint::BigInt::from(-9007199254740991_i64)), - "x === -9007199254740991n" -); -sertest!( - ser_bigint_u128, - BigInt::from(num_bigint::BigInt::from( - 340282366920938463463374607431768211455_u128 - )), - "x === 340282366920938463463374607431768211455n" -); -sertest!( - ser_bigint_i128, - BigInt::from(num_bigint::BigInt::from( - -170141183460469231731687303715884105728_i128 - )), - "x === -170141183460469231731687303715884105728n" -); - -sertest!( - ser_map, - { - let map: std::collections::BTreeMap<&str, u32> = - vec![("a", 1), ("b", 2), ("c", 3)].drain(..).collect(); - map - }, - "objEqual(x, {a: 1, b: 2, c: 3})" -); - -//// -// JSON tests: json!() compatibility -//// -sertest!(ser_json_bool, json!(true), "x === true"); -sertest!(ser_json_null, json!(null), "x === null"); -sertest!( - ser_json_int, - json!(9007199254740991_u64), - "x === 9007199254740991" -); -sertest!( - ser_json_big_int, - json!(9007199254740992_i64), - "x === 9007199254740992n" -); -sertest!( - ser_json_neg_big_int, - json!(-9007199254740992_i64), - "x === -9007199254740992n" -); -sertest!(ser_json_f64, json!(123.45), "x === 123.45"); -sertest!(ser_json_string, json!("Hello World"), "x === 'Hello World'"); -sertest!(ser_json_obj_empty, json!({}), "objEqual(x, {})"); -sertest!( - ser_json_obj, - json!({"a": 1, "b": 2, "c": true}), - "objEqual(x, {a: 1, b: 2, c: true})" -); -sertest!( - ser_json_vec_int, - json!([1, 2, 3, 4, 5]), - "arrEqual(x, [1,2,3,4,5])" -); -sertest!( - ser_json_vec_string, - json!(["Goodbye", "Dinosaurs 👋☄️"]), - "arrEqual(x, ['Goodbye', 'Dinosaurs 👋☄️'])" -); -sertest!( - ser_json_tuple, - json!([true, 42, "nabla"]), - "arrEqual(x, [true, 42, 'nabla'])" -); - -//// -// Pollution tests -//// - -sertest_polluted!( - ser_polluted_obj, - MathOp { - a: 1, - b: 2, - operator: None - }, - "objEqual(x, { a: 1, b: 2, operator: null })" -); - -sertest_polluted!( - ser_polluted_tuple, - (true, 123, false), - "arrEqual(x, [true, 123, false])" -); - -sertest_polluted!(ser_polluted_vec, vec![1, 2, 3], "arrEqual(x, [1, 2, 3])"); diff --git a/serde_v8/utils.rs b/serde_v8/utils.rs deleted file mode 100644 index 6a97324004..0000000000 --- a/serde_v8/utils.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use std::sync::Once; - -pub fn js_exec<'s>( - scope: &mut v8::HandleScope<'s>, - src: &str, -) -> v8::Local<'s, v8::Value> { - let code = v8::String::new(scope, src).unwrap(); - let script = v8::Script::compile(scope, code, None).unwrap(); - script.run(scope).unwrap() -} - -pub fn v8_init() { - let platform = v8::new_default_platform(0, false).make_shared(); - v8::V8::initialize_platform(platform); - v8::V8::initialize(); -} - -pub fn v8_shutdown() { - // SAFETY: this is safe, because all isolates have been shut down already. - unsafe { - v8::V8::dispose(); - } - v8::V8::dispose_platform(); -} - -pub fn v8_do(f: impl FnOnce()) { - static V8_INIT: Once = Once::new(); - V8_INIT.call_once(|| { - v8_init(); - }); - f(); - // v8_shutdown(); -}