From dcd59e097415ae9927f57b0b192ca9889ea23a1a Mon Sep 17 00:00:00 2001 From: Matt Mastracci Date: Sun, 30 Apr 2023 10:50:24 +0200 Subject: [PATCH] perf(core): async op pseudo-codegen and performance work (#18887) Performance: ``` async_ops.js: 760k -> 1030k (!) async_ops_deferred.js: 730k -> 770k Deno.serve bench: 118k -> 124k WS test w/ third_party/prebuilt/mac/load_test 100 localhost 8000 0 0: unchanged Startup time: approx 0.5ms slower (13.7 -> 14.2ms) ``` --- cli/bench/async_ops.js | 4 +- cli/bench/async_ops_deferred.js | 4 +- cli/tests/unit/metrics_test.ts | 10 +- cli/tests/unit/opcall_test.ts | 33 +- cli/tsc/compiler.d.ts | 2 + core/01_core.js | 469 ++++++++++++++++-- core/bindings.js | 49 ++ core/bindings.rs | 210 ++++---- core/extensions.rs | 1 + core/lib.deno_core.d.ts | 8 +- core/ops_builtin.rs | 18 + core/runtime.rs | 30 -- ext/http/00_serve.js | 81 ++- ops/lib.rs | 24 +- ops/optimizer_tests/async_nop.out | 1 + ops/optimizer_tests/async_result.out | 1 + ops/optimizer_tests/callback_options.out | 1 + ops/optimizer_tests/cow_str.out | 1 + ops/optimizer_tests/f64_slice.out | 1 + ops/optimizer_tests/incompatible_1.out | 1 + ops/optimizer_tests/issue16934.out | 1 + ops/optimizer_tests/issue16934_fast.out | 1 + .../op_blob_revoke_object_url.out | 1 + ops/optimizer_tests/op_ffi_ptr_value.out | 1 + ops/optimizer_tests/op_print.out | 1 + ops/optimizer_tests/op_state.out | 1 + ops/optimizer_tests/op_state_basic1.out | 1 + ops/optimizer_tests/op_state_generics.out | 1 + ops/optimizer_tests/op_state_result.out | 1 + ops/optimizer_tests/op_state_warning.out | 1 + .../op_state_with_transforms.out | 1 + ops/optimizer_tests/opstate_with_arity.out | 1 + ops/optimizer_tests/option_arg.out | 1 + ops/optimizer_tests/owned_string.out | 1 + .../param_mut_binding_warning.out | 1 + ops/optimizer_tests/raw_ptr.out | 1 + ops/optimizer_tests/serde_v8_value.out | 1 + ops/optimizer_tests/strings.out | 1 + ops/optimizer_tests/strings_result.out | 1 + ops/optimizer_tests/u64_result.out | 1 + ops/optimizer_tests/uint8array.out | 1 + ops/optimizer_tests/unit_result.out | 1 + ops/optimizer_tests/unit_result2.out | 1 + ops/optimizer_tests/unit_ret.out | 1 + ops/optimizer_tests/wasm_op.out | 1 + 45 files changed, 737 insertions(+), 237 deletions(-) create mode 100644 core/bindings.js diff --git a/cli/bench/async_ops.js b/cli/bench/async_ops.js index fc04942be0..f6c1465d2b 100644 --- a/cli/bench/async_ops.js +++ b/cli/bench/async_ops.js @@ -17,4 +17,6 @@ async function bench(fun) { } const core = Deno[Deno.internal].core; -bench(() => core.opAsync("op_void_async")); +const ops = core.ops; +const opVoidAsync = ops.op_void_async; +bench(() => opVoidAsync()); diff --git a/cli/bench/async_ops_deferred.js b/cli/bench/async_ops_deferred.js index 7a816cf954..2751ad2261 100644 --- a/cli/bench/async_ops_deferred.js +++ b/cli/bench/async_ops_deferred.js @@ -17,4 +17,6 @@ async function bench(fun) { } const core = Deno[Deno.internal].core; -bench(() => core.opAsync("op_void_async_deferred")); +const ops = core.ops; +const opVoidAsyncDeferred = ops.op_void_async_deferred; +bench(() => opVoidAsyncDeferred()); diff --git a/cli/tests/unit/metrics_test.ts b/cli/tests/unit/metrics_test.ts index df2f1b2be5..5fdfebc85b 100644 --- a/cli/tests/unit/metrics_test.ts +++ b/cli/tests/unit/metrics_test.ts @@ -80,12 +80,14 @@ Deno.test(function metricsForOpCrates() { // Test that op_names == Objects.keys(Deno[Deno.internal].core.ops) // since building the per-op metrics depends on op_names being complete Deno.test(function opNamesMatch() { + // @ts-ignore: Deno[Deno.internal].core allowed + const ops = Object.keys(Deno[Deno.internal].core.ops); + // @ts-ignore: Deno[Deno.internal].core allowed + ops.concat(Object.keys(Deno[Deno.internal].core.asyncOps)); + assertEquals( // @ts-ignore: Deno[Deno.internal].core allowed Deno[Deno.internal].core.opNames().sort(), - // @ts-ignore: Deno[Deno.internal].core allowed - Object.keys(Deno[Deno.internal].core.ops).sort().filter((name) => - name !== "asyncOpsInfo" - ), + ops.sort().filter((name) => name !== "asyncOpsInfo"), ); }); diff --git a/cli/tests/unit/opcall_test.ts b/cli/tests/unit/opcall_test.ts index 8985c97801..3b37f8c097 100644 --- a/cli/tests/unit/opcall_test.ts +++ b/cli/tests/unit/opcall_test.ts @@ -1,20 +1,18 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { assertEquals } from "https://deno.land/std@v0.42.0/testing/asserts.ts"; import { assert, assertStringIncludes, unreachable } from "./test_util.ts"; Deno.test(async function sendAsyncStackTrace() { - const buf = new Uint8Array(10); - const rid = 10; try { - await Deno.read(rid, buf); + await core.ops.op_error_async(); unreachable(); } catch (error) { assert(error instanceof Error); const s = error.stack?.toString(); assert(s); - console.log(s); assertStringIncludes(s, "opcall_test.ts"); - assertStringIncludes(s, "read"); + assertStringIncludes(s, "sendAsyncStackTrace"); assert( !s.includes("ext:core"), "opcall stack traces should NOT include ext:core internals such as unwrapOpResult", @@ -22,6 +20,31 @@ Deno.test(async function sendAsyncStackTrace() { } }); +Deno.test(async function sendAsyncStackTraceDeferred() { + try { + await core.ops.op_error_async_deferred(); + unreachable(); + } catch (error) { + assert(error instanceof Error); + const s = error.stack?.toString(); + assert(s); + assertStringIncludes(s, "opcall_test.ts"); + assertStringIncludes(s, "sendAsyncStackTraceDeferred"); + assert( + !s.includes("ext:core"), + "opcall stack traces should NOT include ext:core internals such as unwrapOpResult", + ); + } +}); + +Deno.test(function syncAdd() { + assertEquals(30, core.ops.op_add(10, 20)); +}); + +Deno.test(async function asyncAdd() { + assertEquals(30, await core.ops.op_add_async(10, 20)); +}); + // @ts-ignore This is not publicly typed namespace, but it's there for sure. const core = Deno[Deno.internal].core; diff --git a/cli/tsc/compiler.d.ts b/cli/tsc/compiler.d.ts index b59f6dca81..66c0946972 100644 --- a/cli/tsc/compiler.d.ts +++ b/cli/tsc/compiler.d.ts @@ -46,6 +46,8 @@ declare global { encode(value: string): Uint8Array; // deno-lint-ignore no-explicit-any ops: Record any>; + // deno-lint-ignore no-explicit-any + asyncOps: Record any>; print(msg: string, stderr: boolean): void; registerErrorClass( name: string, diff --git a/core/01_core.js b/core/01_core.js index a8bdeb2a86..3972dec333 100644 --- a/core/01_core.js +++ b/core/01_core.js @@ -16,11 +16,15 @@ ObjectAssign, ObjectFreeze, ObjectFromEntries, + ObjectKeys, Promise, + PromiseReject, + PromiseResolve, PromisePrototypeThen, RangeError, ReferenceError, ReflectHas, + ReflectApply, SafeArrayIterator, SafeMap, SafePromisePrototypeFinally, @@ -32,7 +36,7 @@ TypeError, URIError, } = window.__bootstrap.primordials; - const { ops } = window.Deno.core; + const { ops, asyncOps } = window.Deno.core; const build = { target: "unknown", @@ -85,6 +89,17 @@ 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 @@ -208,7 +223,29 @@ return error; } - function unwrapOpResult(res) { + 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; @@ -221,59 +258,359 @@ err.code = res.code; } // Strip unwrapOpResult() and errorBuilder() calls from stack trace - ErrorCaptureStackTrace(err, unwrapOpResult); - throw err; + ErrorCaptureStackTrace(err, hideFunction); + return PromiseReject(err); } - return res; + 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 + })`, + ); + } + return (ops[opName] = fn); } function opAsync2(name, arg0, arg1) { const id = nextPromiseId++; - let promise = PromisePrototypeThen(setPromise(id), unwrapOpResult); - let maybeResult; try { - maybeResult = ops[name](id, arg0, arg1); - } catch (err) { - // Cleanup the just-created promise - getPromise(id); - if (!ReflectHas(ops, name)) { - throw new TypeError(`${name} is not a registered op`); + const maybeResult = asyncOps[name](id, arg0, arg1); + if (maybeResult !== undefined) { + movePromise(id); + return unwrapOpResultNewPromise(id, maybeResult, opAsync2); } - // Rethrow the error - throw err; + } catch (err) { + movePromise(id); + if (!ReflectHas(asyncOps, name)) { + return PromiseReject(new TypeError(`${name} is not a registered op`)); + } + ErrorCaptureStackTrace(err, opAsync2); + return PromiseReject(err); } + let promise = PromisePrototypeThen( + setPromise(id), + unwrapOpError(eventLoopTick), + ); promise = handleOpCallTracing(name, id, promise); promise[promiseIdSymbol] = id; - if (typeof maybeResult !== "undefined") { - const promise = getPromise(id); - promise.resolve(maybeResult); - } - return promise; } function opAsync(name, ...args) { const id = nextPromiseId++; - let promise = PromisePrototypeThen(setPromise(id), unwrapOpResult); - let maybeResult; try { - maybeResult = ops[name](id, ...new SafeArrayIterator(args)); - } catch (err) { - // Cleanup the just-created promise - getPromise(id); - if (!ReflectHas(ops, name)) { - throw new TypeError(`${name} is not a registered op`); + const maybeResult = asyncOps[name](id, ...new SafeArrayIterator(args)); + if (maybeResult !== undefined) { + movePromise(id); + return unwrapOpResultNewPromise(id, maybeResult, opAsync); } - // Rethrow the error - throw err; + } 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; - if (typeof maybeResult !== "undefined") { - const promise = getPromise(id); - promise.resolve(maybeResult); - } - return promise; } @@ -439,8 +776,52 @@ ); } + // Eagerly initialize ops for snapshot purposes + for (const opName of new SafeArrayIterator(ObjectKeys(asyncOps))) { + setUpAsyncStub(opName); + } + + function generateAsyncOpHandler(/* opNames... */) { + const fastOps = {}; + for (const opName of new SafeArrayIterator(arguments)) { + if (ops[opName] === undefined) { + throw new Error(`Unknown or disabled op '${opName}'`); + } + if (asyncOps[opName] !== undefined) { + fastOps[opName] = setUpAsyncStub(opName); + } else { + fastOps[opName] = ops[opName]; + } + } + return fastOps; + } + + 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, + } = generateAsyncOpHandler( + "op_close", + "op_try_close", + "op_read", + "op_read_all", + "op_write", + "op_write_all", + "op_read_sync", + "op_write_sync", + "op_shutdown", + ); + // Extra Deno.core.* exports const core = ObjectAssign(globalThis.Deno.core, { + asyncStub, + generateAsyncOpHandler, opAsync, opAsync2, resources, @@ -460,15 +841,15 @@ unrefOp, setReportExceptionCallback, setPromiseHooks, - close: (rid) => ops.op_close(rid), - tryClose: (rid) => ops.op_try_close(rid), - read: opAsync.bind(null, "op_read"), - readAll: opAsync.bind(null, "op_read_all"), - write: opAsync.bind(null, "op_write"), - writeAll: opAsync.bind(null, "op_write_all"), - readSync: (rid, buffer) => ops.op_read_sync(rid, buffer), - writeSync: (rid, buffer) => ops.op_write_sync(rid, buffer), - shutdown: opAsync.bind(null, "op_shutdown"), + close, + tryClose, + read, + readAll, + write, + writeAll, + readSync, + writeSync, + shutdown, print: (msg, isErr) => ops.op_print(msg, isErr), setMacrotaskCallback, setNextTickCallback, diff --git a/core/bindings.js b/core/bindings.js new file mode 100644 index 0000000000..c7d7af30ce --- /dev/null +++ b/core/bindings.js @@ -0,0 +1,49 @@ +// 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; + core.ops[opName] = 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); + }; + } 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/bindings.rs b/core/bindings.rs index 95e78b6cd3..2d9c914619 100644 --- a/core/bindings.rs +++ b/core/bindings.rs @@ -1,9 +1,9 @@ // 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 log::debug; use v8::MapFnTo; use crate::error::is_instance_of_error; @@ -98,6 +98,23 @@ pub fn module_origin<'a>( ) } +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, ()>, op_ctxs: &[OpCtx], @@ -108,135 +125,92 @@ pub(crate) fn initialize_context<'s>( let scope = &mut v8::ContextScope::new(scope, context); - 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 ops_str = v8::String::new_external_onebyte_static(scope, b"ops").unwrap(); + 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 !snapshot_options.loaded() { + _ = writeln!(codegen, "Deno.__op__console(callConsole, console);"); + } + for op_ctx in op_ctxs { + if op_ctx.decl.enabled { + // If we're loading from a snapshot, we can skip registration for most ops + if matches!(snapshot_options, SnapshotOptions::Load) + && !op_ctx.decl.force_registration + { + continue; + } + _ = 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 ops_obj = if snapshot_options.loaded() { - // Snapshot already registered `Deno.core.ops` but - // extensions may provide ops that aren't part of the snapshot. - // Grab the Deno.core.ops object & init it - 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 ops_obj: v8::Local = core_obj - .get(scope, ops_str.into()) - .expect("Deno.core.ops to exist") - .try_into() - .unwrap(); - ops_obj + 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 snapshot_options.loaded() { + op_fn.call(scope, recv.into(), &[op_fns.into()]); } else { - // globalThis.Deno = { core: { } }; - let deno_obj = v8::Object::new(scope); - global.set(scope, deno_str.into(), deno_obj.into()); - - let core_obj = v8::Object::new(scope); - deno_obj.set(scope, core_str.into(), core_obj.into()); - // Bind functions to Deno.core.* - set_func(scope, core_obj, "callConsole", call_console); + 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_str = - v8::String::new_external_onebyte_static(scope, b"console").unwrap(); - let console_obj = extra_binding_obj.get(scope, console_str.into()).unwrap(); - core_obj.set(scope, console_str.into(), console_obj); + let console_obj: v8::Local = get( + scope, + extra_binding_obj, + b"console", + "ExtrasBindingObject.console", + ); - // Bind functions to Deno.core.ops.* - let ops_obj = v8::Object::new(scope); - core_obj.set(scope, ops_str.into(), ops_obj.into()); - ops_obj - }; - - if matches!(snapshot_options, SnapshotOptions::Load) { - // Only register ops that have `force_registration` flag set to true, - // the remaining ones should already be in the snapshot. Ignore ops that - // are disabled. - for op_ctx in op_ctxs { - if op_ctx.decl.enabled { - if op_ctx.decl.force_registration { - add_op_to_deno_core_ops(scope, ops_obj, op_ctx); - } - } else { - delete_op_from_deno_core_ops(scope, ops_obj, op_ctx) - } - } - } else if matches!(snapshot_options, SnapshotOptions::CreateFromExisting) { - // Register all enabled ops, probing for which ones are already registered. - for op_ctx in op_ctxs { - let key = v8::String::new_external_onebyte_static( - scope, - op_ctx.decl.name.as_bytes(), - ) - .unwrap(); - - if op_ctx.decl.enabled { - if ops_obj.get(scope, key.into()).is_some() { - continue; - } - add_op_to_deno_core_ops(scope, ops_obj, op_ctx); - } else { - delete_op_from_deno_core_ops(scope, ops_obj, op_ctx) - } - } - } else { - // In other cases register all ops enabled unconditionally. - for op_ctx in op_ctxs { - if op_ctx.decl.enabled { - add_op_to_deno_core_ops(scope, ops_obj, op_ctx); - } - } + op_fn.call( + scope, + recv.into(), + &[op_fns.into(), call_console_fn.into(), console_obj.into()], + ); } context } -fn set_func( - scope: &mut v8::HandleScope<'_>, - obj: v8::Local, - name: &'static str, - callback: impl v8::MapFnTo, -) { - let key = - v8::String::new_external_onebyte_static(scope, name.as_bytes()).unwrap(); - let val = v8::Function::new(scope, callback).unwrap(); - val.set_name(key); - obj.set(scope, key.into(), val.into()); -} - -fn delete_op_from_deno_core_ops( - scope: &mut v8::HandleScope<'_>, - obj: v8::Local, +fn op_ctx_function<'s>( + scope: &mut v8::HandleScope<'s>, op_ctx: &OpCtx, -) { - let key = - v8::String::new_external_onebyte_static(scope, op_ctx.decl.name.as_bytes()) - .unwrap(); - obj.delete(scope, key.into()); -} - -fn add_op_to_deno_core_ops( - scope: &mut v8::HandleScope<'_>, - obj: v8::Local, - op_ctx: &OpCtx, -) { +) -> v8::Local<'s, v8::Function> { let op_ctx_ptr = op_ctx as *const OpCtx as *const c_void; - let key = - v8::String::new_external_onebyte_static(scope, op_ctx.decl.name.as_bytes()) - .unwrap(); let external = v8::External::new(scope, op_ctx_ptr as *mut c_void); - let builder = v8::FunctionTemplate::builder_raw(op_ctx.decl.v8_fn_ptr) - .data(external.into()); + 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 templ = if let Some(fast_function) = &op_ctx.decl.fast_fn { builder.build_fast( @@ -249,9 +223,7 @@ fn add_op_to_deno_core_ops( } else { builder.build(scope) }; - let val = templ.get_function(scope).unwrap(); - val.set_name(key); - obj.set(scope, key.into(), val.into()); + templ.get_function(scope).unwrap() } pub extern "C" fn wasm_async_resolve_promise_callback( diff --git a/core/extensions.rs b/core/extensions.rs index a0f99c92b0..a8b52eb3b6 100644 --- a/core/extensions.rs +++ b/core/extensions.rs @@ -73,6 +73,7 @@ pub struct OpDecl { pub is_unstable: bool, pub is_v8: bool, pub force_registration: bool, + pub arg_count: u8, pub fast_fn: Option, } diff --git a/core/lib.deno_core.d.ts b/core/lib.deno_core.d.ts index 7f3ea2a191..fc78658294 100644 --- a/core/lib.deno_core.d.ts +++ b/core/lib.deno_core.d.ts @@ -23,10 +23,16 @@ declare namespace Deno { /** * List of all registered ops, in the form of a map that maps op - * name to internal numerical op id. + * 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. diff --git a/core/ops_builtin.rs b/core/ops_builtin.rs index 0c071a9186..70f478acd9 100644 --- a/core/ops_builtin.rs +++ b/core/ops_builtin.rs @@ -27,9 +27,12 @@ crate::extension!( 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, @@ -96,12 +99,27 @@ 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() {} diff --git a/core/runtime.rs b/core/runtime.rs index d88ddccacb..e6c365e420 100644 --- a/core/runtime.rs +++ b/core/runtime.rs @@ -3737,21 +3737,6 @@ assertEquals(1, notify_return_value); }) } - #[test] - fn test_core_js_stack_frame() { - let mut runtime = JsRuntime::new(RuntimeOptions::default()); - // Call non-existent op so we get error from `core.js` - let error = runtime - .execute_script_static( - "core_js_stack_frame.js", - "Deno.core.opAsync('non_existent');", - ) - .unwrap_err(); - let error_string = error.to_string(); - // Test that the script specifier is a URL: `ext:`. - assert!(error_string.contains("ext:core/01_core.js")); - } - #[test] fn test_v8_platform() { let options = RuntimeOptions { @@ -4721,21 +4706,6 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", { .is_ok()); } - #[test] - fn test_non_existent_async_op_error() { - // Verify that "resizable ArrayBuffer" is disabled - let mut runtime = JsRuntime::new(Default::default()); - let err = runtime - .execute_script_static( - "test_rab.js", - r#"Deno.core.opAsync("this_op_doesnt_exist");"#, - ) - .unwrap_err(); - assert!(err - .to_string() - .contains("this_op_doesnt_exist is not a registered op")); - } - #[tokio::test] async fn cant_load_internal_module_when_snapshot_is_loaded_and_not_snapshotting( ) { diff --git a/ext/http/00_serve.js b/ext/http/00_serve.js index 0b2c605388..33742e122c 100644 --- a/ext/http/00_serve.js +++ b/ext/http/00_serve.js @@ -1,4 +1,5 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// deno-lint-ignore-file camelcase const core = globalThis.Deno.core; const primordials = globalThis.__bootstrap.primordials; const internals = globalThis.__bootstrap.internals; @@ -46,6 +47,39 @@ const { Uint8Array, } = primordials; +const { + op_http_wait, + op_upgrade, + op_get_request_headers, + op_get_request_method_and_url, + op_read_request_body, + op_serve_http, + op_set_promise_complete, + op_set_response_body_bytes, + op_set_response_body_resource, + op_set_response_body_stream, + op_set_response_body_text, + op_set_response_header, + op_set_response_headers, + op_upgrade_raw, + op_ws_server_create, +} = Deno.core.generateAsyncOpHandler( + "op_http_wait", + "op_upgrade", + "op_get_request_headers", + "op_get_request_method_and_url", + "op_read_request_body", + "op_serve_http", + "op_set_promise_complete", + "op_set_response_body_bytes", + "op_set_response_body_resource", + "op_set_response_body_stream", + "op_set_response_body_text", + "op_set_response_header", + "op_set_response_headers", + "op_upgrade_raw", + "op_ws_server_create", +); const _upgraded = Symbol("_upgraded"); function internalServerError() { @@ -143,7 +177,7 @@ class InnerRequest { this.#upgraded = () => {}; - const upgradeRid = core.ops.op_upgrade_raw(slabId); + const upgradeRid = op_upgrade_raw(slabId); const conn = new TcpConn( upgradeRid, @@ -174,12 +208,11 @@ class InnerRequest { (async () => { try { // Returns the connection and extra bytes, which we can pass directly to op_ws_server_create - const upgrade = await core.opAsync2( - "op_upgrade", + const upgrade = await op_upgrade( slabId, response.headerList, ); - const wsRid = core.ops.op_ws_server_create(upgrade[0], upgrade[1]); + const wsRid = op_ws_server_create(upgrade[0], upgrade[1]); // We have to wait for the go-ahead signal await goAhead; @@ -214,7 +247,7 @@ class InnerRequest { } // TODO(mmastrac): This is quite slow as we're serializing a large number of values. We may want to consider // splitting this up into multiple ops. - this.#methodAndUri = core.ops.op_get_request_method_and_url(this.#slabId); + this.#methodAndUri = op_get_request_method_and_url(this.#slabId); } const path = this.#methodAndUri[2]; @@ -249,7 +282,7 @@ class InnerRequest { if (this.#slabId === undefined) { throw new TypeError("request closed"); } - this.#methodAndUri = core.ops.op_get_request_method_and_url(this.#slabId); + this.#methodAndUri = op_get_request_method_and_url(this.#slabId); } return { transport: "tcp", @@ -263,7 +296,7 @@ class InnerRequest { if (this.#slabId === undefined) { throw new TypeError("request closed"); } - this.#methodAndUri = core.ops.op_get_request_method_and_url(this.#slabId); + this.#methodAndUri = op_get_request_method_and_url(this.#slabId); } return this.#methodAndUri[0]; } @@ -281,7 +314,7 @@ class InnerRequest { this.#body = null; return null; } - this.#streamRid = core.ops.op_read_request_body(this.#slabId); + this.#streamRid = op_read_request_body(this.#slabId); this.#body = new InnerBody(readableStreamForRid(this.#streamRid, false)); return this.#body; } @@ -290,7 +323,7 @@ class InnerRequest { if (this.#slabId === undefined) { throw new TypeError("request closed"); } - return core.ops.op_get_request_headers(this.#slabId); + return op_get_request_headers(this.#slabId); } get slabId() { @@ -331,12 +364,12 @@ function fastSyncResponseOrStream(req, respBody) { const body = stream.body; if (ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, body)) { - core.ops.op_set_response_body_bytes(req, body); + op_set_response_body_bytes(req, body); return null; } if (typeof body === "string") { - core.ops.op_set_response_body_text(req, body); + op_set_response_body_text(req, body); return null; } @@ -346,7 +379,7 @@ function fastSyncResponseOrStream(req, respBody) { } const resourceBacking = getReadableStreamResourceBacking(stream); if (resourceBacking) { - core.ops.op_set_response_body_resource( + op_set_response_body_resource( req, resourceBacking.rid, resourceBacking.autoClose, @@ -382,9 +415,9 @@ async function asyncResponse(responseBodies, req, status, stream) { // and we race it. let timeoutPromise; timeout = setTimeout(() => { - responseRid = core.ops.op_set_response_body_stream(req); + responseRid = op_set_response_body_stream(req); SetPrototypeAdd(responseBodies, responseRid); - core.ops.op_set_promise_complete(req, status); + op_set_promise_complete(req, status); timeoutPromise = core.writeAll(responseRid, value1); }, 250); const { value: value2, done: done2 } = await reader.read(); @@ -409,13 +442,13 @@ async function asyncResponse(responseBodies, req, status, stream) { // Reader will be closed by finally block // No response stream closed = true; - core.ops.op_set_response_body_bytes(req, value1); + op_set_response_body_bytes(req, value1); return; } - responseRid = core.ops.op_set_response_body_stream(req); + responseRid = op_set_response_body_stream(req); SetPrototypeAdd(responseBodies, responseRid); - core.ops.op_set_promise_complete(req, status); + op_set_promise_complete(req, status); // Write our first packet await core.writeAll(responseRid, value1); } @@ -447,7 +480,7 @@ async function asyncResponse(responseBodies, req, status, stream) { core.tryClose(responseRid); SetPrototypeDelete(responseBodies, responseRid); } else { - core.ops.op_set_promise_complete(req, status); + op_set_promise_complete(req, status); } } } @@ -511,9 +544,9 @@ function mapToCallback(responseBodies, context, signal, callback, onError) { const headers = inner.headerList; if (headers && headers.length > 0) { if (headers.length == 1) { - core.ops.op_set_response_header(req, headers[0][0], headers[0][1]); + op_set_response_header(req, headers[0][0], headers[0][1]); } else { - core.ops.op_set_response_headers(req, headers); + op_set_response_headers(req, headers); } } @@ -523,7 +556,7 @@ function mapToCallback(responseBodies, context, signal, callback, onError) { // Handle the stream asynchronously await asyncResponse(responseBodies, req, status, stream); } else { - core.ops.op_set_promise_complete(req, status); + op_set_promise_complete(req, status); } innerRequest?.close(); @@ -591,13 +624,13 @@ async function serve(arg1, arg2) { listenOpts.alpnProtocols = ["h2", "http/1.1"]; const listener = Deno.listenTls(listenOpts); listenOpts.port = listener.addr.port; - context.initialize(core.ops.op_serve_http( + context.initialize(op_serve_http( listener.rid, )); } else { const listener = Deno.listen(listenOpts); listenOpts.port = listener.addr.port; - context.initialize(core.ops.op_serve_http( + context.initialize(op_serve_http( listener.rid, )); } @@ -624,7 +657,7 @@ async function serve(arg1, arg2) { const rid = context.serverRid; let req; try { - req = await core.opAsync2("op_http_wait", rid); + req = await op_http_wait(rid); } catch (error) { if (ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error)) { break; diff --git a/ops/lib.rs b/ops/lib.rs index 5a192537fd..d4fa0bb824 100644 --- a/ops/lib.rs +++ b/ops/lib.rs @@ -144,6 +144,8 @@ impl Op { is_unstable: #is_unstable, is_v8: #is_v8, force_registration: false, + // TODO(mmastrac) + arg_count: 0, } } @@ -158,8 +160,8 @@ impl Op { let has_fallible_fast_call = active && optimizer.returns_result; - let v8_body = if is_async { - let deferred = attrs.deferred; + let (v8_body, arg_count) = if is_async { + let deferred: bool = attrs.deferred; codegen_v8_async( &core, &item, @@ -205,6 +207,7 @@ impl Op { is_unstable: #is_unstable, is_v8: #is_v8, force_registration: false, + arg_count: #arg_count as u8, } } @@ -241,7 +244,7 @@ fn codegen_v8_async( margs: Attributes, asyncness: bool, deferred: bool, -) -> TokenStream2 { +) -> (TokenStream2, usize) { let Attributes { is_v8, .. } = margs; let special_args = f .sig @@ -309,7 +312,7 @@ fn codegen_v8_async( } }; - quote! { + 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 { @@ -336,7 +339,10 @@ fn codegen_v8_async( 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 { @@ -373,7 +379,7 @@ fn codegen_v8_sync( f: &syn::ItemFn, margs: Attributes, has_fallible_fast_call: bool, -) -> TokenStream2 { +) -> (TokenStream2, usize) { let Attributes { is_v8, .. } = margs; let special_args = f .sig @@ -404,7 +410,7 @@ fn codegen_v8_sync( quote! {} }; - 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() @@ -421,7 +427,9 @@ fn codegen_v8_sync( op_state.tracker.track_sync(ctx.id); #ret - } + }; + + (token_stream, f.sig.inputs.len() - rust_i0) } /// (full declarations, idents, v8 argument count) diff --git a/ops/optimizer_tests/async_nop.out b/ops/optimizer_tests/async_nop.out index d267338258..3765e611a8 100644 --- a/ops/optimizer_tests/async_nop.out +++ b/ops/optimizer_tests/async_nop.out @@ -41,6 +41,7 @@ impl op_void_async { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 1usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/async_result.out b/ops/optimizer_tests/async_result.out index 4494bf22ae..ca6d13c2e8 100644 --- a/ops/optimizer_tests/async_result.out +++ b/ops/optimizer_tests/async_result.out @@ -41,6 +41,7 @@ impl op_async_result { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 2usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/callback_options.out b/ops/optimizer_tests/callback_options.out index e892e01189..656124a807 100644 --- a/ops/optimizer_tests/callback_options.out +++ b/ops/optimizer_tests/callback_options.out @@ -41,6 +41,7 @@ impl op_fallback { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 1usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/cow_str.out b/ops/optimizer_tests/cow_str.out index dc909da819..ebb2108a21 100644 --- a/ops/optimizer_tests/cow_str.out +++ b/ops/optimizer_tests/cow_str.out @@ -41,6 +41,7 @@ impl op_cow_str { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 1usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/f64_slice.out b/ops/optimizer_tests/f64_slice.out index 3e8ef07d85..811aee288f 100644 --- a/ops/optimizer_tests/f64_slice.out +++ b/ops/optimizer_tests/f64_slice.out @@ -41,6 +41,7 @@ impl op_f64_buf { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 1usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/incompatible_1.out b/ops/optimizer_tests/incompatible_1.out index 5104fb5e46..59eb600bc6 100644 --- a/ops/optimizer_tests/incompatible_1.out +++ b/ops/optimizer_tests/incompatible_1.out @@ -31,6 +31,7 @@ impl op_sync_serialize_object_with_numbers_as_keys { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 1usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/issue16934.out b/ops/optimizer_tests/issue16934.out index e92510038c..35bd383390 100644 --- a/ops/optimizer_tests/issue16934.out +++ b/ops/optimizer_tests/issue16934.out @@ -31,6 +31,7 @@ impl send_stdin { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 2usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/issue16934_fast.out b/ops/optimizer_tests/issue16934_fast.out index 2a16d1b626..1291f9cabf 100644 --- a/ops/optimizer_tests/issue16934_fast.out +++ b/ops/optimizer_tests/issue16934_fast.out @@ -31,6 +31,7 @@ impl send_stdin { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 2usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/op_blob_revoke_object_url.out b/ops/optimizer_tests/op_blob_revoke_object_url.out index 4eda692240..1a10a2b0a8 100644 --- a/ops/optimizer_tests/op_blob_revoke_object_url.out +++ b/ops/optimizer_tests/op_blob_revoke_object_url.out @@ -31,6 +31,7 @@ impl op_blob_revoke_object_url { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 1usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/op_ffi_ptr_value.out b/ops/optimizer_tests/op_ffi_ptr_value.out index 3fee00cff8..f3da0dfce5 100644 --- a/ops/optimizer_tests/op_ffi_ptr_value.out +++ b/ops/optimizer_tests/op_ffi_ptr_value.out @@ -41,6 +41,7 @@ impl op_ffi_ptr_value { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 2usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/op_print.out b/ops/optimizer_tests/op_print.out index 7bf5457d78..e0fecd6b29 100644 --- a/ops/optimizer_tests/op_print.out +++ b/ops/optimizer_tests/op_print.out @@ -31,6 +31,7 @@ impl op_print { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 2usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/op_state.out b/ops/optimizer_tests/op_state.out index cebb1e25c7..300dd6fc2f 100644 --- a/ops/optimizer_tests/op_state.out +++ b/ops/optimizer_tests/op_state.out @@ -41,6 +41,7 @@ impl op_set_exit_code { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 1usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/op_state_basic1.out b/ops/optimizer_tests/op_state_basic1.out index d8278daca6..2452e886c0 100644 --- a/ops/optimizer_tests/op_state_basic1.out +++ b/ops/optimizer_tests/op_state_basic1.out @@ -41,6 +41,7 @@ impl foo { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 2usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/op_state_generics.out b/ops/optimizer_tests/op_state_generics.out index 631a2142f7..3faaa4bf16 100644 --- a/ops/optimizer_tests/op_state_generics.out +++ b/ops/optimizer_tests/op_state_generics.out @@ -47,6 +47,7 @@ impl op_foo { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 0usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/op_state_result.out b/ops/optimizer_tests/op_state_result.out index d03ffd5a61..137eeeac04 100644 --- a/ops/optimizer_tests/op_state_result.out +++ b/ops/optimizer_tests/op_state_result.out @@ -41,6 +41,7 @@ impl foo { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 2usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/op_state_warning.out b/ops/optimizer_tests/op_state_warning.out index 5548dc134e..ce677f0fa8 100644 --- a/ops/optimizer_tests/op_state_warning.out +++ b/ops/optimizer_tests/op_state_warning.out @@ -41,6 +41,7 @@ impl op_listen { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 0usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/op_state_with_transforms.out b/ops/optimizer_tests/op_state_with_transforms.out index ad4e5335a8..4347f63e45 100644 --- a/ops/optimizer_tests/op_state_with_transforms.out +++ b/ops/optimizer_tests/op_state_with_transforms.out @@ -47,6 +47,7 @@ impl op_now { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 1usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/opstate_with_arity.out b/ops/optimizer_tests/opstate_with_arity.out index 037774c255..a1ae081270 100644 --- a/ops/optimizer_tests/opstate_with_arity.out +++ b/ops/optimizer_tests/opstate_with_arity.out @@ -41,6 +41,7 @@ impl op_add_4 { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 4usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/option_arg.out b/ops/optimizer_tests/option_arg.out index 39d47562b8..adfc8da19d 100644 --- a/ops/optimizer_tests/option_arg.out +++ b/ops/optimizer_tests/option_arg.out @@ -31,6 +31,7 @@ impl op_try_close { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 1usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/owned_string.out b/ops/optimizer_tests/owned_string.out index f8b195b2fb..d8c0842ac5 100644 --- a/ops/optimizer_tests/owned_string.out +++ b/ops/optimizer_tests/owned_string.out @@ -41,6 +41,7 @@ impl op_string_length { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 1usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/param_mut_binding_warning.out b/ops/optimizer_tests/param_mut_binding_warning.out index 98dc6b2b91..e99606b377 100644 --- a/ops/optimizer_tests/param_mut_binding_warning.out +++ b/ops/optimizer_tests/param_mut_binding_warning.out @@ -31,6 +31,7 @@ impl op_read_sync { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 2usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/raw_ptr.out b/ops/optimizer_tests/raw_ptr.out index 678ce50152..3eefb5e7f4 100644 --- a/ops/optimizer_tests/raw_ptr.out +++ b/ops/optimizer_tests/raw_ptr.out @@ -52,6 +52,7 @@ impl op_ffi_ptr_of { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 2usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/serde_v8_value.out b/ops/optimizer_tests/serde_v8_value.out index d0f8dacdfc..867d89e43c 100644 --- a/ops/optimizer_tests/serde_v8_value.out +++ b/ops/optimizer_tests/serde_v8_value.out @@ -41,6 +41,7 @@ impl op_is_proxy { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 1usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/strings.out b/ops/optimizer_tests/strings.out index 3238bfc427..523736d70e 100644 --- a/ops/optimizer_tests/strings.out +++ b/ops/optimizer_tests/strings.out @@ -41,6 +41,7 @@ impl op_string_length { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 1usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/strings_result.out b/ops/optimizer_tests/strings_result.out index 8b2e2acef2..aae8b356bc 100644 --- a/ops/optimizer_tests/strings_result.out +++ b/ops/optimizer_tests/strings_result.out @@ -31,6 +31,7 @@ impl op_string_length { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 1usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/u64_result.out b/ops/optimizer_tests/u64_result.out index 02d25686a8..a0d7465125 100644 --- a/ops/optimizer_tests/u64_result.out +++ b/ops/optimizer_tests/u64_result.out @@ -31,6 +31,7 @@ impl op_bench_now { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 0usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/uint8array.out b/ops/optimizer_tests/uint8array.out index 93fa40e1f2..124f2ac576 100644 --- a/ops/optimizer_tests/uint8array.out +++ b/ops/optimizer_tests/uint8array.out @@ -41,6 +41,7 @@ impl op_import_spki_x25519 { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 2usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/unit_result.out b/ops/optimizer_tests/unit_result.out index 354a2e3b94..9a46ee0874 100644 --- a/ops/optimizer_tests/unit_result.out +++ b/ops/optimizer_tests/unit_result.out @@ -41,6 +41,7 @@ impl op_unit_result { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 0usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/unit_result2.out b/ops/optimizer_tests/unit_result2.out index 721229121b..c2e6708a03 100644 --- a/ops/optimizer_tests/unit_result2.out +++ b/ops/optimizer_tests/unit_result2.out @@ -41,6 +41,7 @@ impl op_set_nodelay { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 2usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/unit_ret.out b/ops/optimizer_tests/unit_ret.out index 7d0f63dc88..538674068e 100644 --- a/ops/optimizer_tests/unit_ret.out +++ b/ops/optimizer_tests/unit_ret.out @@ -41,6 +41,7 @@ impl op_unit { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 0usize as u8, } } #[inline] diff --git a/ops/optimizer_tests/wasm_op.out b/ops/optimizer_tests/wasm_op.out index 0196f45481..cc8e3b8472 100644 --- a/ops/optimizer_tests/wasm_op.out +++ b/ops/optimizer_tests/wasm_op.out @@ -41,6 +41,7 @@ impl op_wasm { is_unstable: false, is_v8: false, force_registration: false, + arg_count: 1usize as u8, } } #[inline]