summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Mastracci <matthew@mastracci.com>2023-06-24 13:54:10 +0200
committerGitHub <noreply@github.com>2023-06-24 13:54:10 +0200
commit65d9bfb53361bfce6dc594c6a9df92c017dea6cb (patch)
tree63886c7225b52444165be3abd53c4e745ca77512
parent8d6dbda90ec0593f3f6e10c6696e320bdff7daa9 (diff)
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<integer> parameters only, and allows us to start working on converting ops and adding features.
-rwxr-xr-x.github/workflows/ci.generate.ts2
-rw-r--r--.github/workflows/ci.yml8
-rw-r--r--Cargo.lock259
-rw-r--r--Cargo.toml5
-rw-r--r--core/lib.rs9
-rw-r--r--core/ops_builtin.rs3
-rw-r--r--core/runtime/ops.rs137
-rw-r--r--ext/http/http_next.rs5
-rw-r--r--ops/Cargo.toml6
-rw-r--r--ops/lib.rs22
-rw-r--r--ops/op2/dispatch_fast.rs193
-rw-r--r--ops/op2/dispatch_slow.rs220
-rw-r--r--ops/op2/generator_state.rs32
-rw-r--r--ops/op2/mod.rs287
-rw-r--r--ops/op2/signature.rs516
-rw-r--r--ops/op2/test_cases/sync/add.out54
-rw-r--r--ops/op2/test_cases/sync/add.rs6
-rw-r--r--ops/op2/test_cases/sync/add_options.out44
-rw-r--r--ops/op2/test_cases/sync/add_options.rs6
-rw-r--r--ops/op2/test_cases/sync/doc_comment.out35
-rw-r--r--ops/op2/test_cases/sync/doc_comment.rs5
-rw-r--r--ops/op2/test_cases/sync/smi.out52
-rw-r--r--ops/op2/test_cases/sync/smi.rs4
23 files changed, 1798 insertions, 112 deletions
diff --git a/.github/workflows/ci.generate.ts b/.github/workflows/ci.generate.ts
index 5ca7b6c17..a98eacfa7 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 3171439ff..46471dc13 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 d18d32ee5..fd5d8d411 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",
]
@@ -872,6 +872,29 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -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",
]
@@ -4614,6 +4643,28 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -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 befd1c94e..be20e29a0 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 1f069e661..9a960e93a 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 2334c6918..20afa210b 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 1a67a1d66..fb71de8cb 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<v8::Global<v8::Value>, 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<dyn std::error::Error>> {
+ 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>) -> u32 {
+ a + b.unwrap_or(100)
+ }
+
+ #[tokio::test]
+ pub async fn test_op_add_option() -> Result<(), Box<dyn std::error::Error>> {
+ 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 89506b47d..92afe1456 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 f142e4449..f9951f4e6 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 d7c8b0640..bd8ff9caf 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 000000000..79b8d141b
--- /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<v8::Value>"),
+ 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<Option<(TokenStream, TokenStream)>, 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::<Vec<_>>();
+ 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 000000000..dd47b2017
--- /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<TokenStream, V8MappingError> {
+ 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<TokenStream, V8MappingError> {
+ 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<TokenStream, V8MappingError> {
+ 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<TokenStream, V8MappingError> {
+ 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<TokenStream, V8MappingError> {
+ 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<TokenStream, V8MappingError> {
+ 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<TokenStream, V8MappingError> {
+ 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 000000000..741d4f7f3
--- /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<Ident>,
+ /// 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 000000000..73a457f25
--- /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<Ident>) -> Result<Self, Op2Error> {
+ 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<Self, Op2Error> {
+ 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<TokenStream, Op2Error> {
+ let func = parse2::<ItemFn>(item)?;
+ let config = MacroConfig::from_tokens(attr)?;
+ generate_op2(config, func)
+}
+
+fn generate_op2(
+ config: MacroConfig,
+ func: ItemFn,
+) -> Result<TokenStream, Op2Error> {
+ // 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::<Vec<_>>();
+
+ 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::<Path>("crate::deno_core")
+ } else {
+ syn2::parse_str::<Path>("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::<File>(&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 000000000..6158b2a55
--- /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<Arg>,
+ pub names: Vec<String>,
+ 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<AttributeModifier>,
+}
+
+fn stringify_token(tokens: impl ToTokens) -> String {
+ tokens
+ .into_token_stream()
+ .into_iter()
+ .map(|s| s.to_string())
+ .collect::<Vec<_>>()
+ .join("")
+}
+
+pub fn parse_signature(
+ attributes: Vec<Attribute>,
+ signature: Signature,
+) -> Result<ParsedSignature, SignatureError> {
+ 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<Attributes, ArgError> {
+ let attrs = attributes
+ .iter()
+ .filter_map(parse_attribute)
+ .collect::<Vec<_>>();
+
+ 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<AttributeModifier> {
+ 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<RetVal, ArgError> {
+ 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<Value>, like io::Result and other specialty result types
+ ($($_package:ident ::)* Result < $ty:ty >) => {
+ Ok(RetVal::Result(parse_type(attrs, &ty)?))
+ }
+ // x::y::Result<Value, Error>
+ ($($_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<Arg, ArgError> {
+ 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<V8Arg, ArgError> {
+ 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<Special, ArgError> {
+ match parse_type(attrs, ty)? {
+ Arg::Special(special) => Ok(special),
+ _ => Err(ArgError::InvalidType(stringify_token(ty))),
+ }
+}
+
+fn parse_type(attrs: Attributes, ty: &Type) -> Result<Arg, ArgError> {
+ 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<Arg, ArgError> {
+ 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::<ItemFn>(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<package::SerdeReturnType, Error>,
+ (SerdeV8("package::SerdeInputType")) -> Result(SerdeV8("package::SerdeReturnType"))
+ );
+ test!(
+ fn op_local(input: v8::Local<v8::String>) -> Result<v8::Local<v8::String>, 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<Option<u32>, AnyError>,
+ (Ref(Mut, OpState)) -> Result(OptionNumeric(u32))
+ );
+ test!(
+ fn op_ffi_read_f64(state: &mut OpState, ptr: * mut c_void, offset: isize) -> Result <f64, AnyError>,
+ (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::<ReturnType>("-> 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 000000000..a7269c5cf
--- /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<deno_core::v8::Object>,
+ 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 000000000..74dbb1893
--- /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 000000000..682a77309
--- /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>) -> 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 000000000..a5f2c8f4a
--- /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>) -> 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 000000000..bd0d0b21f
--- /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<deno_core::v8::Object>) -> () {
+ 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 000000000..b729a64bd
--- /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 000000000..e6c1bc1e3
--- /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<deno_core::v8::Object>,
+ 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 000000000..a5a441845
--- /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 {}