Skip to content

feat: JS delegates with value-type parameters and return values - #16

Merged
triniwiz merged 1 commit into
mainfrom
feat/delegate-value-types
Sep 28, 2026
Merged

triniwiz merged 1 commit into
mainfrom
feat/delegate-value-types

Conversation

@triniwiz

Copy link
Copy Markdown
Member

JS functions passed as WinRT delegates (NSWinRT.asDelegate and delegate constructors) now work when the delegate's Invoke takes floating-point values or returns a value.

Problem

Every JS delegate shared one vtable whose Invoke takes up to three pointer-sized arguments and returns nothing. That covers event handlers ((sender, args)), but not a delegate such as Int64 F(Single, Single, Single, Single):

  • On x64 and ARM64, float arguments travel in vector registers, which an integer-typed Invoke never reads, so the JS function received garbage.
  • The return value goes through a trailing out-pointer, which was never written, so the native caller got whatever was in that slot.

Change

  • New vtable: delegates whose Invoke needs it get their own vtable. Its Invoke is a libffi closure built from the exact signature in metadata (runtime/src/delegate_invoke.rs). libffi reads each argument from wherever the ABI put it.
  • Return values: the JS function's result is converted to the declared return type. A BigInt keeps all 64 bits; numbers and booleans are converted. If the function throws, the call returns E_FAIL.
  • Everything else: other delegates keep the shared Invoke.
  • Engines: both the classic engine and the Node-API engine use it.

No API changes: asDelegate picks the right Invoke from the delegate's metadata.

Tests

cargo test -p runtime --lib --release:

  • New tests: unit tests for signature classification, typed argument reading and result conversion, plus interop tests covering a JS function behind Int64 F(Single, Single, Single, Single) and a check that plain event-handler delegates keep the shared Invoke. All pass.
  • Whole suite: 147 passed and 11 failed. The same 11 fail on main (the run_on_ui_thread, ui_dispatcher and a few XAML interop tests).

JS delegates shared one vtable whose Invoke takes up to three
pointer-sized arguments and returns nothing. That covers event handlers,
but a delegate such as Int64 F(Single, Single, Single, Single) got
garbage arguments (floats travel in vector registers on x64 and ARM64)
and its result was never written back.

Delegates whose Invoke takes floating-point values or returns a value now
get their own vtable, whose Invoke is a libffi closure with the exact
signature from metadata. The JS function's result is converted to the
declared return type, and a JS exception is reported as E_FAIL. Other
delegates keep the shared Invoke. Both the classic and the Node-API
engines use it.
@coderabbitai

coderabbitai Bot commented Sep 28, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 82883341-5a66-4b0f-b5be-f48843aee057

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@triniwiz
triniwiz merged commit 6061889 into main Sep 28, 2026
2 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant