From 9eeeadd69a78f46ae9885183bc87770f6a0426f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Wed, 23 Sep 2026 02:10:07 +0200 Subject: [PATCH 1/2] fix(ioredis): wire extended command dispatch --- .../perry-api-manifest/src/entries/part_1.rs | 7 +++ .../lower_call/native_module_rooting_tests.rs | 36 +++++++++++ .../src/lower_call/native_table/databases.rs | 63 +++++++++++++++++++ docs/src/api/reference.md | 9 ++- scripts/native_result_ledger.py | 9 ++- scripts/native_result_ledger.tsv | 7 +++ 6 files changed, 128 insertions(+), 3 deletions(-) diff --git a/crates/perry-api-manifest/src/entries/part_1.rs b/crates/perry-api-manifest/src/entries/part_1.rs index 90cc633587..a24e857a47 100644 --- a/crates/perry-api-manifest/src/entries/part_1.rs +++ b/crates/perry-api-manifest/src/entries/part_1.rs @@ -22,12 +22,19 @@ pub(crate) const API_MANIFEST_PART_1: &[ApiEntry] = &[ TypeSpec::Any, ), method("ioredis", "set", true, None), + method("ioredis", "setex", true, None), method("ioredis", "get", true, None), method("ioredis", "del", true, None), method("ioredis", "exists", true, None), method("ioredis", "incr", true, None), method("ioredis", "decr", true, None), method("ioredis", "expire", true, None), + method("ioredis", "ping", true, None), + method("ioredis", "hget", true, None), + method("ioredis", "hset", true, None), + method("ioredis", "hdel", true, None), + method("ioredis", "hlen", true, None), + method("ioredis", "hgetall", true, None), method("ioredis", "quit", true, None), // v0.5.707 closes-#605: NATIVE_MODULE_TABLE added connect/disconnect rows // when normalizing the `redis` npm package alias to ioredis dispatch. diff --git a/crates/perry-codegen/src/lower_call/native_module_rooting_tests.rs b/crates/perry-codegen/src/lower_call/native_module_rooting_tests.rs index f5c6b52b52..4092720cf2 100644 --- a/crates/perry-codegen/src/lower_call/native_module_rooting_tests.rs +++ b/crates/perry-codegen/src/lower_call/native_module_rooting_tests.rs @@ -115,3 +115,39 @@ fn x509_zero_argument_method_call_uses_invoking_dispatch() { "the zero-argument property-read fallback returns a bound method closure:\n{ir}" ); } + +#[test] +fn ioredis_extended_commands_emit_their_runtime_calls() { + let string = || Expr::String("value".to_string()); + let cases = [ + ( + "setex", + "js_ioredis_setex", + vec![string(), Expr::Number(30.0), string()], + ), + ("ping", "js_ioredis_ping", vec![]), + ("hget", "js_ioredis_hget", vec![string(), string()]), + ( + "hset", + "js_ioredis_hset", + vec![string(), string(), string()], + ), + ("hdel", "js_ioredis_hdel", vec![string(), string()]), + ("hlen", "js_ioredis_hlen", vec![string()]), + ("hgetall", "js_ioredis_hgetall", vec![string()]), + ]; + + for (method, runtime, args) in cases { + let module_ir = + compile_native_instance_call("ioredis", None, Some(Expr::Number(1.0)), method, args); + let ir = build_function_ir(&module_ir); + assert!( + ir.contains(&format!("call i64 @{runtime}(")), + "ioredis.{method} must emit {runtime}:\n{ir}" + ); + assert!( + !ir.contains("call double @js_native_call_method("), + "ioredis.{method} must not fall through to dynamic dispatch:\n{ir}" + ); + } +} diff --git a/crates/perry-codegen/src/lower_call/native_table/databases.rs b/crates/perry-codegen/src/lower_call/native_table/databases.rs index 0c9bf178b6..b9f738c4b5 100644 --- a/crates/perry-codegen/src/lower_call/native_table/databases.rs +++ b/crates/perry-codegen/src/lower_call/native_table/databases.rs @@ -30,6 +30,15 @@ pub(super) const DATABASES_ROWS: &[NativeModSig] = &[ args: &[NA_STR, NA_STR], ret: NR_GCPTR, }, + NativeModSig { + module: "ioredis", + has_receiver: true, + method: "setex", + class_filter: None, + runtime: "js_ioredis_setex", + args: &[NA_STR, NA_F64, NA_STR], + ret: NR_GCPTR, + }, NativeModSig { module: "ioredis", has_receiver: true, @@ -84,6 +93,60 @@ pub(super) const DATABASES_ROWS: &[NativeModSig] = &[ args: &[NA_STR, NA_F64], ret: NR_GCPTR, }, + NativeModSig { + module: "ioredis", + has_receiver: true, + method: "ping", + class_filter: None, + runtime: "js_ioredis_ping", + args: &[], + ret: NR_GCPTR, + }, + NativeModSig { + module: "ioredis", + has_receiver: true, + method: "hget", + class_filter: None, + runtime: "js_ioredis_hget", + args: &[NA_STR, NA_STR], + ret: NR_GCPTR, + }, + NativeModSig { + module: "ioredis", + has_receiver: true, + method: "hset", + class_filter: None, + runtime: "js_ioredis_hset", + args: &[NA_STR, NA_STR, NA_STR], + ret: NR_GCPTR, + }, + NativeModSig { + module: "ioredis", + has_receiver: true, + method: "hdel", + class_filter: None, + runtime: "js_ioredis_hdel", + args: &[NA_STR, NA_STR], + ret: NR_GCPTR, + }, + NativeModSig { + module: "ioredis", + has_receiver: true, + method: "hlen", + class_filter: None, + runtime: "js_ioredis_hlen", + args: &[NA_STR], + ret: NR_GCPTR, + }, + NativeModSig { + module: "ioredis", + has_receiver: true, + method: "hgetall", + class_filter: None, + runtime: "js_ioredis_hgetall", + args: &[NA_STR], + ret: NR_GCPTR, + }, NativeModSig { module: "ioredis", has_receiver: true, diff --git a/docs/src/api/reference.md b/docs/src/api/reference.md index dc78ca41cd..6b4d049e78 100644 --- a/docs/src/api/reference.md +++ b/docs/src/api/reference.md @@ -2,7 +2,7 @@ This page is auto-generated from Perry's compile-time API manifest (`perry-api-manifest::API_MANIFEST`). It is the source of truth for what `perry compile` accepts; references to symbols not listed here produce `R005 UnimplementedApi` (issue #463). Stubs (#464) are flagged ⚠ — they link cleanly but no-op at runtime on the chosen target. -Total: 2835 entries across 117 modules. +Total: 2842 entries across 117 modules. ## Modules @@ -1831,9 +1831,16 @@ Total: 2835 entries across 117 modules. - `exists` — instance - `expire` — instance - `get` — instance +- `hdel` — instance +- `hget` — instance +- `hgetall` — instance +- `hlen` — instance +- `hset` — instance - `incr` — instance +- `ping` — instance - `quit` — instance - `set` — instance +- `setex` — instance ## `iovalkey` diff --git a/scripts/native_result_ledger.py b/scripts/native_result_ledger.py index 476dfa220e..cffb5aee3f 100644 --- a/scripts/native_result_ledger.py +++ b/scripts/native_result_ledger.py @@ -71,8 +71,13 @@ # specifiers, so rows fall by twice the provider count). 349 -> 314 rows and # 300 -> 279 providers; each figure is what the script reports on the # resolved tree, not arithmetic (#10739). -EXPECTED_ROWS = 314 -EXPECTED_PROVIDERS = 279 +# +# +7 rows / +7 providers (#11068): the previously unreachable ioredis +# `setex`, `ping`, and hash-command methods now have native-table rows. Each +# provider returns its newly allocated Promise pointer, matching the existing +# ioredis command rows. 314 -> 321 rows and 279 -> 286 providers. +EXPECTED_ROWS = 321 +EXPECTED_PROVIDERS = 286 KINDS = { "NR_GCPTR", "NR_NULLABLE_GCPTR", diff --git a/scripts/native_result_ledger.tsv b/scripts/native_result_ledger.tsv index 1e68c88490..136475b863 100644 --- a/scripts/native_result_ledger.tsv +++ b/scripts/native_result_ledger.tsv @@ -89,10 +89,17 @@ js_ioredis_del NR_GCPTR crates/perry-ext-ioredis/src/lib.rs *mut Promise js_ioredis_exists NR_GCPTR crates/perry-ext-ioredis/src/lib.rs *mut Promise js_ioredis_expire NR_GCPTR crates/perry-ext-ioredis/src/lib.rs *mut Promise js_ioredis_get NR_GCPTR crates/perry-ext-ioredis/src/lib.rs *mut Promise +js_ioredis_hdel NR_GCPTR crates/perry-ext-ioredis/src/lib.rs *mut Promise +js_ioredis_hget NR_GCPTR crates/perry-ext-ioredis/src/lib.rs *mut Promise +js_ioredis_hgetall NR_GCPTR crates/perry-ext-ioredis/src/lib.rs *mut Promise +js_ioredis_hlen NR_GCPTR crates/perry-ext-ioredis/src/lib.rs *mut Promise +js_ioredis_hset NR_GCPTR crates/perry-ext-ioredis/src/lib.rs *mut Promise js_ioredis_incr NR_GCPTR crates/perry-ext-ioredis/src/lib.rs *mut Promise js_ioredis_new NR_HANDLE_ID crates/perry-ext-ioredis/src/lib.rs Handle +js_ioredis_ping NR_GCPTR crates/perry-ext-ioredis/src/lib.rs *mut Promise js_ioredis_quit NR_GCPTR crates/perry-ext-ioredis/src/lib.rs *mut Promise js_ioredis_set NR_GCPTR crates/perry-ext-ioredis/src/lib.rs *mut Promise +js_ioredis_setex NR_GCPTR crates/perry-ext-ioredis/src/lib.rs *mut Promise js_lodash_chunk NR_GCPTR crates/perry-stdlib/src/lodash.rs *mut ArrayHeader js_lodash_compact NR_GCPTR crates/perry-stdlib/src/lodash.rs *mut ArrayHeader js_lodash_drop NR_GCPTR crates/perry-stdlib/src/lodash.rs *mut ArrayHeader From 9202c7e33a5e0284fe57fc248859c6a1249f2b28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Wed, 23 Sep 2026 02:11:14 +0200 Subject: [PATCH 2/2] docs(changelog): record ioredis command dispatch fix --- changelog.d/11068-ioredis-extended-commands.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelog.d/11068-ioredis-extended-commands.md diff --git a/changelog.d/11068-ioredis-extended-commands.md b/changelog.d/11068-ioredis-extended-commands.md new file mode 100644 index 0000000000..0b9c1f1d9d --- /dev/null +++ b/changelog.d/11068-ioredis-extended-commands.md @@ -0,0 +1,3 @@ +### Fixed + +- Perry now compiles ioredis `setex`, `ping`, `hget`, `hset`, `hdel`, `hlen`, and `hgetall` calls to their native runtime implementations instead of falling through to undefined dynamic dispatch.