From 65d9bfb53361bfce6dc594c6a9df92c017dea6cb Mon Sep 17 00:00:00 2001 From: Matt Mastracci Date: Sat, 24 Jun 2023 13:54:10 +0200 Subject: [PATCH] refactor(ops): Adding op2 macro and implementing in a couple of places (#19534) This is a new op system that will eventually replace `#[op]`. Features - More maintainable, generally less-coupled code - More modern Rust proc-macro libraries - Enforces correct `fast` labelling for fast ops, allowing for visual scanning of fast ops - Explicit marking of `#[string]`, `#[serde]` and `#[smi]` parameters. This first version of op2 supports integer and Option parameters only, and allows us to start working on converting ops and adding features. --- .github/workflows/ci.generate.ts | 2 +- .github/workflows/ci.yml | 8 +- Cargo.lock | 259 +++++++----- Cargo.toml | 5 + core/lib.rs | 9 + core/ops_builtin.rs | 3 +- core/runtime/ops.rs | 137 +++++++ ext/http/http_next.rs | 5 +- ops/Cargo.toml | 6 + ops/lib.rs | 22 + ops/op2/dispatch_fast.rs | 193 +++++++++ ops/op2/dispatch_slow.rs | 220 ++++++++++ ops/op2/generator_state.rs | 32 ++ ops/op2/mod.rs | 287 +++++++++++++ ops/op2/signature.rs | 516 ++++++++++++++++++++++++ ops/op2/test_cases/sync/add.out | 54 +++ ops/op2/test_cases/sync/add.rs | 6 + ops/op2/test_cases/sync/add_options.out | 44 ++ ops/op2/test_cases/sync/add_options.rs | 6 + ops/op2/test_cases/sync/doc_comment.out | 35 ++ ops/op2/test_cases/sync/doc_comment.rs | 5 + ops/op2/test_cases/sync/smi.out | 52 +++ ops/op2/test_cases/sync/smi.rs | 4 + 23 files changed, 1798 insertions(+), 112 deletions(-) create mode 100644 ops/op2/dispatch_fast.rs create mode 100644 ops/op2/dispatch_slow.rs create mode 100644 ops/op2/generator_state.rs create mode 100644 ops/op2/mod.rs create mode 100644 ops/op2/signature.rs create mode 100644 ops/op2/test_cases/sync/add.out create mode 100644 ops/op2/test_cases/sync/add.rs create mode 100644 ops/op2/test_cases/sync/add_options.out create mode 100644 ops/op2/test_cases/sync/add_options.rs create mode 100644 ops/op2/test_cases/sync/doc_comment.out create mode 100644 ops/op2/test_cases/sync/doc_comment.rs create mode 100644 ops/op2/test_cases/sync/smi.out create mode 100644 ops/op2/test_cases/sync/smi.rs diff --git a/.github/workflows/ci.generate.ts b/.github/workflows/ci.generate.ts index 5ca7b6c178..a98eacfa72 100755 --- a/.github/workflows/ci.generate.ts +++ b/.github/workflows/ci.generate.ts @@ -5,7 +5,7 @@ import * as yaml from "https://deno.land/std@0.173.0/encoding/yaml.ts"; // Bump this number when you want to purge the cache. // Note: the tools/release/01_bump_crate_versions.ts script will update this version // automatically via regex, so ensure that this line maintains this format. -const cacheVersion = 40; +const cacheVersion = 41; const Runners = (() => { const ubuntuRunner = "ubuntu-22.04"; diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3171439ff8..46471dc139 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -298,8 +298,8 @@ jobs: path: |- ~/.cargo/registry/index ~/.cargo/registry/cache - key: '40-cargo-home-${{ matrix.os }}-${{ hashFiles(''Cargo.lock'') }}' - restore-keys: '40-cargo-home-${{ matrix.os }}' + key: '41-cargo-home-${{ matrix.os }}-${{ hashFiles(''Cargo.lock'') }}' + restore-keys: '41-cargo-home-${{ matrix.os }}' if: '!(github.event_name == ''pull_request'' && matrix.skip_pr)' - name: Restore cache build output (PR) uses: actions/cache/restore@v3 @@ -311,7 +311,7 @@ jobs: !./target/*/*.zip !./target/*/*.tar.gz key: never_saved - restore-keys: '40-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ matrix.job }}-' + restore-keys: '41-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ matrix.job }}-' - name: Apply and update mtime cache if: '!(github.event_name == ''pull_request'' && matrix.skip_pr) && (!startsWith(github.ref, ''refs/tags/''))' uses: ./.github/mtime_cache @@ -592,7 +592,7 @@ jobs: !./target/*/gn_out !./target/*/*.zip !./target/*/*.tar.gz - key: '40-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}' + key: '41-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}' publish-canary: name: publish canary runs-on: ubuntu-22.04 diff --git a/Cargo.lock b/Cargo.lock index d18d32ee52..fd5d8d4116 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -191,8 +191,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", "synstructure", ] @@ -203,8 +203,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -215,8 +215,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c704e2f6ee1a98223f5a7629a6ef0f3decb3b552ed282889dc957edff98ce1e6" dependencies = [ "pmutil", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "swc_macros_common", "syn 1.0.109", ] @@ -266,8 +266,8 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4655ae1a7b0cdf149156f780c5bf3f1352bc53cbd9e0a361a7ef7b22947e965" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -277,9 +277,9 @@ version = "0.1.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", - "syn 2.0.13", + "proc-macro2 1.0.60", + "quote 1.0.28", + "syn 2.0.18", ] [[package]] @@ -300,8 +300,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7862e21c893d65a1650125d157eaeec691439379a1cee17ee49031b79236ada4" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -716,7 +716,7 @@ version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ - "quote 1.0.26", + "quote 1.0.28", "syn 1.0.109", ] @@ -871,6 +871,29 @@ dependencies = [ "zstd", ] +[[package]] +name = "deno-proc-macro-rules" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c65c2ffdafc1564565200967edc4851c7b55422d3913466688907efd05ea26f" +dependencies = [ + "deno-proc-macro-rules-macros", + "proc-macro2 1.0.60", + "syn 2.0.18", +] + +[[package]] +name = "deno-proc-macro-rules-macros" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3047b312b7451e3190865713a4dd6e1f821aed614ada219766ebc3024a690435" +dependencies = [ + "once_cell", + "proc-macro2 1.0.60", + "quote 1.0.28", + "syn 2.0.18", +] + [[package]] name = "deno_ast" version = "0.27.0" @@ -1317,18 +1340,24 @@ dependencies = [ name = "deno_ops" version = "0.69.0" dependencies = [ + "deno-proc-macro-rules", "lazy-regex", "once_cell", "pmutil", "pretty_assertions", "prettyplease", "proc-macro-crate", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "regex", + "strum", + "strum_macros", "syn 1.0.109", + "syn 2.0.18", "testing_macros", + "thiserror", "trybuild", + "v8", ] [[package]] @@ -1537,8 +1566,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "rustc_version 0.4.0", "syn 1.0.109", ] @@ -1585,8 +1614,8 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -1727,8 +1756,8 @@ dependencies = [ "byteorder", "lazy_static", "proc-macro-error", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -1848,8 +1877,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" dependencies = [ "heck", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -2030,8 +2059,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479cde5eb168cf5a056dd98f311cbfab7494c216394e4fb9eba0336827a8db93" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -2078,7 +2107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d449976075322384507443937df2f1d5577afbf4282f12a5a66ef29fa3e6307" dependencies = [ "pmutil", - "proc-macro2 1.0.56", + "proc-macro2 1.0.60", "swc_macros_common", "syn 1.0.109", ] @@ -2167,9 +2196,9 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", - "syn 2.0.13", + "proc-macro2 1.0.60", + "quote 1.0.28", + "syn 2.0.18", ] [[package]] @@ -2646,8 +2675,8 @@ checksum = "8a7d079e129b77477a49c5c4f1cfe9ce6c2c909ef52520693e8e811a714c7b20" dependencies = [ "Inflector", "pmutil", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -2761,8 +2790,8 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8edfc11b8f56ce85e207e62ea21557cfa09bb24a8f6b04ae181b086ff8611c22" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "regex", "syn 1.0.109", ] @@ -3085,8 +3114,8 @@ dependencies = [ name = "napi_sym" version = "0.39.0" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "serde", "serde_json", "syn 1.0.109", @@ -3511,8 +3540,8 @@ dependencies = [ "phf_generator", "phf_shared", "proc-macro-hack", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -3540,8 +3569,8 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -3601,8 +3630,8 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3894e5d549cccbe44afecf72922f277f603cd4bb0219c8342631ef18fffbe004" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -3648,7 +3677,7 @@ version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" dependencies = [ - "proc-macro2 1.0.56", + "proc-macro2 1.0.60", "syn 1.0.109", ] @@ -3678,8 +3707,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", "version_check", ] @@ -3690,8 +3719,8 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "version_check", ] @@ -3712,9 +3741,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" dependencies = [ "unicode-ident", ] @@ -3766,11 +3795,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.26" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ - "proc-macro2 1.0.56", + "proc-macro2 1.0.60", ] [[package]] @@ -4117,8 +4146,8 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "107c3d5d7f370ac09efa62a78375f94d94b8a33c61d8c278b96683fb4dbf2d8d" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -4314,9 +4343,9 @@ version = "1.0.157" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78997f4555c22a7971214540c4a661291970619afd56de19f77e0de86296e1e5" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", - "syn 2.0.13", + "proc-macro2 1.0.60", + "quote 1.0.28", + "syn 2.0.18", ] [[package]] @@ -4337,8 +4366,8 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -4590,8 +4619,8 @@ checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" dependencies = [ "phf_generator", "phf_shared", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", ] [[package]] @@ -4601,8 +4630,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0090512bdfee4b56d82480d66c0fd8a6f53f0fe0f97e075e949b252acdd482e0" dependencies = [ "pmutil", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "swc_macros_common", "syn 1.0.109", ] @@ -4613,6 +4642,28 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck", + "proc-macro2 1.0.60", + "quote 1.0.28", + "rustversion", + "syn 1.0.109", +] + [[package]] name = "subtle" version = "2.4.1" @@ -4711,8 +4762,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dadb9998d4f5fc36ef558ed5a092579441579ee8c6fcce84a5228cca9df4004" dependencies = [ "pmutil", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "swc_macros_common", "syn 1.0.109", ] @@ -4760,8 +4811,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4ee0caee1018808d94ecd09490cb7affd3d504b19aa11c49238f5fc4b54901" dependencies = [ "pmutil", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "swc_macros_common", "syn 1.0.109", ] @@ -4856,8 +4907,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "984d5ac69b681fc5438f9abf82b0fda34fe04e119bc75f8213b7e01128c7c9a2" dependencies = [ "pmutil", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "swc_macros_common", "syn 1.0.109", ] @@ -4987,8 +5038,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c20468634668c2bbab581947bb8c75c97158d5a6959f4ba33df20983b20b4f6" dependencies = [ "pmutil", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -5024,8 +5075,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e582c3e3c2269238524923781df5be49e011dbe29cf7683a2215d600a562ea6" dependencies = [ "pmutil", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -5047,8 +5098,8 @@ checksum = "4cfc226380ba54a5feed2c12f3ccd33f1ae8e959160290e5d2d9b4e918b6472a" dependencies = [ "Inflector", "pmutil", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "swc_macros_common", "syn 1.0.109", ] @@ -5070,19 +5121,19 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.13" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "unicode-ident", ] @@ -5092,8 +5143,8 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", "unicode-xid 0.2.4", ] @@ -5195,8 +5246,8 @@ dependencies = [ "glob", "once_cell", "pmutil", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "regex", "relative-path", "syn 1.0.109", @@ -5232,9 +5283,9 @@ version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", - "syn 2.0.13", + "proc-macro2 1.0.60", + "quote 1.0.28", + "syn 2.0.18", ] [[package]] @@ -5304,9 +5355,9 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", - "syn 2.0.13", + "proc-macro2 1.0.60", + "quote 1.0.28", + "syn 2.0.18", ] [[package]] @@ -5443,8 +5494,8 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ebd99eec668d0a450c177acbc4d05e0d0d13b1f8d3db13cd706c52cbec4ac04" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -5472,8 +5523,8 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -5840,8 +5891,8 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", ] [[package]] @@ -5896,8 +5947,8 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", "wasm-bindgen-shared", ] @@ -5920,7 +5971,7 @@ version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ - "quote 1.0.26", + "quote 1.0.28", "wasm-bindgen-macro-support", ] @@ -5930,8 +5981,8 @@ version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", @@ -6265,8 +6316,8 @@ version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", "synstructure", ] diff --git a/Cargo.toml b/Cargo.toml index befd1c94e5..be20e29a02 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -131,6 +131,8 @@ 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" @@ -155,6 +157,9 @@ hkdf = "0.12.3" proc-macro2 = "1" quote = "1" syn = { version = "1", features = ["full", "extra-traits"] } +syn2 = { package = "syn", version = "=2.0.18", features = ["full", "extra-traits"] } +# Temporary fork while we wait for a more modern version to be published +deno-proc-macro-rules = "0.3.2" # unix nix = "=0.24.2" diff --git a/core/lib.rs b/core/lib.rs index 1f069e6610..9a960e93ab 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -41,6 +41,7 @@ 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; @@ -130,6 +131,7 @@ pub fn v8_version() -> &'static str { pub mod _ops { pub use super::error::throw_type_error; pub use super::error_codes::get_error_code; + pub use super::extensions::OpDecl; pub use super::ops::to_op_result; pub use super::ops::OpCtx; pub use super::ops::OpResult; @@ -139,10 +141,17 @@ pub mod _ops { 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_u32; pub use super::runtime::V8_WRAPPER_OBJECT_INDEX; pub use super::runtime::V8_WRAPPER_TYPE_INDEX; } +pub(crate) mod deno_core { + pub(crate) use crate::_ops; + pub(crate) use crate::v8; +} + // TODO(mmastrac): Temporary while we move code around pub mod snapshot_util { pub use crate::runtime::create_snapshot; diff --git a/core/ops_builtin.rs b/core/ops_builtin.rs index 2334c69180..20afa210b9 100644 --- a/core/ops_builtin.rs +++ b/core/ops_builtin.rs @@ -11,6 +11,7 @@ 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; @@ -95,7 +96,7 @@ pub fn op_resources(state: &mut OpState) -> Vec<(ResourceId, String)> { .collect() } -#[op(fast)] +#[op2(core, fast)] fn op_add(a: i32, b: i32) -> i32 { a + b } diff --git a/core/runtime/ops.rs b/core/runtime/ops.rs index 1a67a1d668..fb71de8cbb 100644 --- a/core/runtime/ops.rs +++ b/core/runtime/ops.rs @@ -145,3 +145,140 @@ pub fn queue_async_op<'s>( .spawn(unsafe { crate::task::MaskFutureAsSend::new(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 +} + +#[cfg(test)] +mod tests { + use crate::FastString; + use crate::JsRuntime; + use crate::RuntimeOptions; + use deno_ops::op2; + + crate::extension!(testing, ops = [op_test_add, op_test_add_option]); + + /// Run a test for a single op. + fn run_test( + op: &'static str, + test: &'static str, + f: impl FnOnce(Result<&v8::Value, anyhow::Error>, &mut v8::HandleScope), + ) { + let mut runtime = JsRuntime::new(RuntimeOptions { + extensions: vec![testing::init_ops_and_esm()], + ..Default::default() + }); + let value: Result, anyhow::Error> = runtime + .execute_script( + "", + FastString::Owned( + format!("const {{ {op} }} = Deno.core.ensureFastOps(); {test}") + .into(), + ), + ); + let mut scope: v8::HandleScope = + // SAFETY: transmute for test (this lifetime should be safe for this purpose) + unsafe { std::mem::transmute(runtime.handle_scope()) }; + match value { + Ok(value) => { + let value = value.open(&mut scope); + f(Ok(value), &mut scope) + } + Err(err) => f(Err(err), &mut scope), + } + } + + #[op2(core, fast)] + pub fn op_test_add(a: u32, b: u32) -> u32 { + a + b + } + + #[tokio::test] + pub async fn test_op_add() -> Result<(), Box> { + run_test("op_test_add", "op_test_add(1, 11)", |value, scope| { + assert_eq!(value.unwrap().int32_value(scope), Some(12)); + }); + Ok(()) + } + + #[op2(core)] + pub fn op_test_add_option(a: u32, b: Option) -> u32 { + a + b.unwrap_or(100) + } + + #[tokio::test] + pub async fn test_op_add_option() -> Result<(), Box> { + run_test( + "op_test_add_option", + "op_test_add_option(1, 11)", + |value, scope| { + assert_eq!(value.unwrap().int32_value(scope), Some(12)); + }, + ); + run_test( + "op_test_add_option", + "op_test_add_option(1, null)", + |value, scope| { + assert_eq!(value.unwrap().int32_value(scope), Some(101)); + }, + ); + Ok(()) + } +} diff --git a/ext/http/http_next.rs b/ext/http/http_next.rs index 89506b47d7..92afe14565 100644 --- a/ext/http/http_next.rs +++ b/ext/http/http_next.rs @@ -20,6 +20,7 @@ use cache_control::CacheControl; use deno_core::error::AnyError; use deno_core::futures::TryFutureExt; use deno_core::op; +use deno_core::op2; use deno_core::serde_v8; use deno_core::serde_v8::from_v8; use deno_core::task::spawn; @@ -208,8 +209,8 @@ pub async fn op_http_upgrade_websocket_next( ws_create_server_stream(&mut state.borrow_mut(), stream, bytes) } -#[op(fast)] -pub fn op_http_set_promise_complete(slab_id: SlabId, status: u16) { +#[op2(fast)] +pub fn op_http_set_promise_complete(#[smi] slab_id: SlabId, status: u16) { let mut http = slab_get(slab_id); // The Javascript code will never provide a status that is invalid here (see 23_response.js) *http.response().status_mut() = StatusCode::from_u16(status).unwrap(); diff --git a/ops/Cargo.toml b/ops/Cargo.toml index f142e4449b..f9951f4e69 100644 --- a/ops/Cargo.toml +++ b/ops/Cargo.toml @@ -15,6 +15,7 @@ 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" @@ -22,7 +23,12 @@ 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 diff --git a/ops/lib.rs b/ops/lib.rs index d7c8b06402..bd8ff9caf8 100644 --- a/ops/lib.rs +++ b/ops/lib.rs @@ -8,6 +8,7 @@ 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; @@ -22,6 +23,7 @@ use syn::LifetimeDef; mod attrs; mod deno; mod fast_call; +mod op2; mod optimizer; const SCOPE_LIFETIME: &str = "'scope"; @@ -235,6 +237,26 @@ pub fn op(attr: TokenStream, item: TokenStream) -> TokenStream { 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, diff --git a/ops/op2/dispatch_fast.rs b/ops/op2/dispatch_fast.rs new file mode 100644 index 0000000000..79b8d141b4 --- /dev/null +++ b/ops/op2/dispatch_fast.rs @@ -0,0 +1,193 @@ +// 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::V8MappingError; +use proc_macro2::TokenStream; +use quote::format_ident; +use quote::quote; + +#[allow(unused)] +#[derive(Debug, Default, PartialEq, Clone)] +pub(crate) enum FastValue { + #[default] + Void, + Bool, + U32, + I32, + U64, + I64, + F32, + F64, + Pointer, + V8Value, + Uint8Array, + Uint32Array, + Float64Array, + SeqOneByteString, +} + +impl FastValue { + /// Quote fast value type. + fn quote_rust_type(&self) -> TokenStream { + match self { + FastValue::Void => quote!(()), + FastValue::Bool => quote!(bool), + FastValue::U32 => quote!(u32), + FastValue::I32 => quote!(i32), + FastValue::U64 => quote!(u64), + FastValue::I64 => quote!(i64), + FastValue::F32 => quote!(f32), + FastValue::F64 => quote!(f64), + FastValue::Pointer => quote!(*mut ::std::ffi::c_void), + FastValue::V8Value => unimplemented!("v8::Local"), + FastValue::Uint8Array + | FastValue::Uint32Array + | FastValue::Float64Array + | FastValue::SeqOneByteString => unreachable!(), + } + } + + /// Quote fast value type's variant. + fn quote_ctype(&self) -> TokenStream { + match &self { + FastValue::Void => quote!(CType::Void), + FastValue::Bool => quote!(CType::Bool), + FastValue::U32 => quote!(CType::Uint32), + FastValue::I32 => quote!(CType::Int32), + FastValue::U64 => quote!(CType::Uint64), + FastValue::I64 => quote!(CType::Int64), + FastValue::F32 => quote!(CType::Float32), + FastValue::F64 => quote!(CType::Float64), + FastValue::Pointer => quote!(CType::Pointer), + FastValue::V8Value => quote!(CType::V8Value), + FastValue::Uint8Array => unreachable!(), + FastValue::Uint32Array => unreachable!(), + FastValue::Float64Array => unreachable!(), + FastValue::SeqOneByteString => quote!(CType::SeqOneByteString), + } + } + + /// Quote fast value type's variant. + fn quote_type(&self) -> TokenStream { + match &self { + FastValue::Void => quote!(Type::Void), + FastValue::Bool => quote!(Type::Bool), + FastValue::U32 => quote!(Type::Uint32), + FastValue::I32 => quote!(Type::Int32), + FastValue::U64 => quote!(Type::Uint64), + FastValue::I64 => quote!(Type::Int64), + FastValue::F32 => quote!(Type::Float32), + FastValue::F64 => quote!(Type::Float64), + FastValue::Pointer => quote!(Type::Pointer), + FastValue::V8Value => quote!(Type::V8Value), + FastValue::Uint8Array => quote!(Type::TypedArray(CType::Uint8)), + FastValue::Uint32Array => quote!(Type::TypedArray(CType::Uint32)), + FastValue::Float64Array => quote!(Type::TypedArray(CType::Float64)), + FastValue::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 fv = match arg { + Arg::OptionNumeric(_) | Arg::SerdeV8(_) => return Ok(None), + Arg::Numeric(NumericArg::bool) => FastValue::Bool, + Arg::Numeric(NumericArg::u32) + | Arg::Numeric(NumericArg::u16) + | Arg::Numeric(NumericArg::u8) => FastValue::U32, + Arg::Numeric(NumericArg::i32) + | Arg::Numeric(NumericArg::i16) + | Arg::Numeric(NumericArg::i8) + | Arg::Numeric(NumericArg::__SMI__) => FastValue::I32, + Arg::Numeric(NumericArg::u64) | Arg::Numeric(NumericArg::usize) => { + FastValue::U64 + } + Arg::Numeric(NumericArg::i64) | Arg::Numeric(NumericArg::isize) => { + FastValue::I64 + } + _ => { + return Err(V8MappingError::NoMapping("a fast argument", arg.clone())) + } + }; + inputs.push(fv); + } + + let ret_val = match &signature.ret_val { + RetVal::Infallible(arg) => arg, + RetVal::Result(arg) => arg, + }; + + let output = match ret_val { + Arg::OptionNumeric(_) | Arg::SerdeV8(_) => return Ok(None), + Arg::Void => FastValue::Void, + Arg::Numeric(NumericArg::bool) => FastValue::Bool, + Arg::Numeric(NumericArg::u32) + | Arg::Numeric(NumericArg::u16) + | Arg::Numeric(NumericArg::u8) => FastValue::U32, + Arg::Numeric(NumericArg::i32) + | Arg::Numeric(NumericArg::i16) + | Arg::Numeric(NumericArg::i8) => FastValue::I32, + Arg::Numeric(NumericArg::u64) | Arg::Numeric(NumericArg::usize) => { + FastValue::U64 + } + Arg::Numeric(NumericArg::i64) | Arg::Numeric(NumericArg::isize) => { + FastValue::I64 + } + Arg::Special(_) => return Ok(None), + _ => { + return Err(V8MappingError::NoMapping( + "a fast return value", + ret_val.clone(), + )) + } + }; + + let GeneratorState { + fast_function, + deno_core, + .. + } = &generator_state; + + let input_types = inputs.iter().map(|fv| fv.quote_type()); + 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( + &[ #( #input_types ),* ], + #output_type, + Self::#fast_function as *const ::std::ffi::c_void + ) + }; + + let output_type = output.quote_rust_type(); + let names = &inputs + .iter() + .enumerate() + .map(|(i, _)| format_ident!("arg{i}")) + .collect::>(); + let types = inputs.iter().map(|rv| rv.quote_rust_type()); + + let fast_fn = quote!( + fn #fast_function( + _: #deno_core::v8::Local<#deno_core::v8::Object>, + #( #names: #types, )* + ) -> #output_type { + #( + let #names = #names as _; + )* + Self::call(#(#names),*) + } + ); + + Ok(Some((fast_definition, fast_fn))) +} diff --git a/ops/op2/dispatch_slow.rs b/ops/op2/dispatch_slow.rs new file mode 100644 index 0000000000..dd47b2017b --- /dev/null +++ b/ops/op2/dispatch_slow.rs @@ -0,0 +1,220 @@ +// 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::TokenStream; +use quote::quote; + +pub fn generate_dispatch_slow( + generator_state: &mut GeneratorState, + signature: &ParsedSignature, +) -> Result { + let mut output = TokenStream::new(); + 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 GeneratorState { + deno_core, + scope, + fn_args, + retval, + info, + slow_function, + .. + } = &generator_state; + + let with_scope = if generator_state.needs_scope { + quote!(let #scope = &mut unsafe { #deno_core::v8::CallbackScope::new(&*#info) };) + } else { + quote!() + }; + + let with_retval = if generator_state.needs_retval { + quote!(let mut #retval = #deno_core::v8::ReturnValue::from_function_callback_info(unsafe { &*#info });) + } else { + quote!() + }; + + let with_args = if generator_state.needs_args { + quote!(let #fn_args = #deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe { &*#info });) + } else { + quote!() + }; + + Ok(quote! { + pub extern "C" fn #slow_function(#info: *const #deno_core::v8::FunctionCallbackInfo) { + #with_scope + #with_retval + #with_args + + #output + }}) +} + +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, .. + } = &mut generator_state; + let arg_ident = args.get_mut(index).expect("Argument at index was missing"); + + 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) => { + quote! { + let #arg_ident = #arg_ident.to_rust_string_lossy(); + } + } + Arg::Special(Special::RefStr) => { + quote! { + let #arg_ident = #arg_ident.to_rust_string_lossy(); + } + } + _ => 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 { + result, + retval, + needs_retval, + .. + } = generator_state; + + let res = match ret_type { + 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);) + } + _ => { + return Err(V8MappingError::NoMapping( + "a slow return value", + ret_type.clone(), + )) + } + }; + + Ok(res) +} + +pub fn return_value_result( + generator_state: &mut GeneratorState, + ret_type: &Arg, +) -> Result { + let infallible = return_value_infallible(generator_state, ret_type)?; + let GeneratorState { result, .. } = &generator_state; + + let tokens = quote!( + let result = match ret_type { + Ok(#result) => { + #infallible, + } + Err(err) => { + return; + } + } + ); + Ok(tokens) +} diff --git a/ops/op2/generator_state.rs b/ops/op2/generator_state.rs new file mode 100644 index 0000000000..741d4f7f33 --- /dev/null +++ b/ops/op2/generator_state.rs @@ -0,0 +1,32 @@ +// 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 `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, +} diff --git a/ops/op2/mod.rs b/ops/op2/mod.rs new file mode 100644 index 0000000000..73a457f25c --- /dev/null +++ b/ops/op2/mod.rs @@ -0,0 +1,287 @@ +// 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::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)] +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.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 slow_function = Ident::new("slow_function", Span::call_site()); + let fast_function = Ident::new("fast_function", Span::call_site()); + + let deno_core = if config.core { + syn2::parse_str::("crate::deno_core") + } 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, + deno_core, + result, + retval, + needs_args, + slow_function, + fast_function, + needs_retval: false, + needs_scope: false, + }; + + let name = func.sig.ident; + let slow_fn = generate_dispatch_slow(&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; + + Ok(quote! { + #[allow(non_camel_case_types)] + #vis struct #name { + } + + impl #name { + 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, + } + } + + #slow_fn + #fast_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 new file mode 100644 index 0000000000..6158b2a550 --- /dev/null +++ b/ops/op2/signature.rs @@ -0,0 +1,516 @@ +// 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 strum::IntoEnumIterator; +use strum::IntoStaticStr; +use strum_macros::EnumIter; +use strum_macros::EnumString; +use syn2::Attribute; +use syn2::FnArg; +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, + 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 { + pub args: Vec, + pub names: Vec, + pub ret_val: RetVal, +} + +#[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), +} + +#[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))?, + ); + } + Ok(ParsedSignature { + args, + names, + ret_val: parse_return(parse_attributes(&attributes)?, &signature.output)?, + }) +} + +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 :: 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 { + ( $(# [ $fn_attr:ident ])? fn $name:ident ( $( $(# [ $attr:ident ])? $ident:ident : $ty:ty ),* ) $(-> $(# [ $ret_attr:ident ])? $ret:ty)?, ( $( $arg_res:expr ),* ) -> $ret_res:expr ) => { + #[test] + fn $name() { + test( + stringify!($( #[$fn_attr] )? fn op( $( $( #[$attr] )? $ident : $ty ),* ) $(-> $( #[$ret_attr] )? $ret)? {}), + stringify!($($arg_res),*), + stringify!($ret_res) + ); + } + }; + } + + fn test(op: &str, args_expected: &str, return_expected: &str) { + 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(|_| { + panic!("Failed to successfully parse signature from {op}") + }); + + assert_eq!( + args_expected, + format!("{:?}", sig.args).trim_matches(|c| c == '[' || c == ']') + ); + assert_eq!(return_expected, format!("{:?}", sig.ret_val)); + } + + 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 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 new file mode 100644 index 0000000000..a7269c5cf2 --- /dev/null +++ b/ops/op2/test_cases/sync/add.out @@ -0,0 +1,54 @@ +#[allow(non_camel_case_types)] +struct op_add {} +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::slow_function 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::Uint32, Type::Uint32], + CType::Uint32, + Self::fast_function as *const ::std::ffi::c_void, + ) + }), + is_async: false, + is_unstable: false, + is_v8: false, + arg_count: 2usize as u8, + } + } + pub extern "C" fn slow_function(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); + } + fn fast_function( + _: deno_core::v8::Local, + arg0: u32, + arg1: u32, + ) -> u32 { + let arg0 = arg0 as _; + let arg1 = arg1 as _; + Self::call(arg0, arg1) + } + #[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 new file mode 100644 index 0000000000..74dbb18934 --- /dev/null +++ b/ops/op2/test_cases/sync/add.rs @@ -0,0 +1,6 @@ +// 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 new file mode 100644 index 0000000000..682a773095 --- /dev/null +++ b/ops/op2/test_cases/sync/add_options.out @@ -0,0 +1,44 @@ +#[allow(non_camel_case_types)] +pub struct op_test_add_option {} +impl op_test_add_option { + pub const fn name() -> &'static str { + stringify!(op_test_add_option) + } + pub const fn decl() -> crate::deno_core::_ops::OpDecl { + crate::deno_core::_ops::OpDecl { + name: stringify!(op_test_add_option), + v8_fn_ptr: Self::slow_function as _, + enabled: true, + fast_fn: None, + is_async: false, + is_unstable: false, + is_v8: false, + arg_count: 2usize as u8, + } + } + pub extern "C" fn slow_function( + info: *const crate::deno_core::v8::FunctionCallbackInfo, + ) { + let mut rv = crate::deno_core::v8::ReturnValue::from_function_callback_info(unsafe { + &*info + }); + let args = crate::deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe { + &*info + }); + let arg0 = args.get(0usize as i32); + let arg0 = crate::deno_core::_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::deno_core::_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 new file mode 100644 index 0000000000..a5f2c8f4a7 --- /dev/null +++ b/ops/op2/test_cases/sync/add_options.rs @@ -0,0 +1,6 @@ +// 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 new file mode 100644 index 0000000000..bd0d0b21fa --- /dev/null +++ b/ops/op2/test_cases/sync/doc_comment.out @@ -0,0 +1,35 @@ +#[allow(non_camel_case_types)] +pub struct op_has_doc_comment {} +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::slow_function 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( + &[], + CType::Void, + Self::fast_function as *const ::std::ffi::c_void, + ) + }), + is_async: false, + is_unstable: false, + is_v8: false, + arg_count: 0usize as u8, + } + } + pub extern "C" fn slow_function(info: *const deno_core::v8::FunctionCallbackInfo) { + let result = Self::call(); + } + fn fast_function(_: deno_core::v8::Local) -> () { + 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 new file mode 100644 index 0000000000..b729a64bd7 --- /dev/null +++ b/ops/op2/test_cases/sync/doc_comment.rs @@ -0,0 +1,5 @@ +// 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/smi.out b/ops/op2/test_cases/sync/smi.out new file mode 100644 index 0000000000..e6c1bc1e3f --- /dev/null +++ b/ops/op2/test_cases/sync/smi.out @@ -0,0 +1,52 @@ +#[allow(non_camel_case_types)] +struct op_add {} +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::slow_function 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::Int32, Type::Uint32], + CType::Uint32, + Self::fast_function as *const ::std::ffi::c_void, + ) + }), + is_async: false, + is_unstable: false, + is_v8: false, + arg_count: 2usize as u8, + } + } + pub extern "C" fn slow_function(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); + } + fn fast_function( + _: deno_core::v8::Local, + arg0: i32, + arg1: u32, + ) -> u32 { + let arg0 = arg0 as _; + let arg1 = arg1 as _; + Self::call(arg0, arg1) + } + #[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 new file mode 100644 index 0000000000..a5a441845f --- /dev/null +++ b/ops/op2/test_cases/sync/smi.rs @@ -0,0 +1,4 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +#[op2(fast)] +fn op_add(#[smi] id: ResourceId, extra: u16) -> u32 {}