Skip to content

feat: add binary type support for host functions#40

Open
simongdavies wants to merge 4 commits intomainfrom
allow-binary-types-for-host-funcs
Open

feat: add binary type support for host functions#40
simongdavies wants to merge 4 commits intomainfrom
allow-binary-types-for-host-funcs

Conversation

@simongdavies
Copy link
Contributor

Adds Uint8Array/Buffer support for host function arguments and returns.

Architecture:

  • New hyperlight-js-common crate: shared wire-format constants, binary sidecar encode/decode, FnReturn enum, DecodeError type
  • Guest extracts Uint8Array from QuickJS VM into binary sidecar
  • Host dispatches via register() (typed serde) or register_js() (JS bridge)
  • NAPI layer creates native Node.js Buffers via C API (no base64)
  • Tagged return format (0x00=JSON, 0x01=binary) for return path

Key changes:

  • Single CallHostJsFunction entry point (removed legacy JSON-only path)
  • Native Buffer marshalling in NAPI (JsArg/JsReturn types)
  • Depth limits on all recursive JSON tree traversals (MAX_JSON_DEPTH=64)
  • Trailing data rejection in sidecar decoder
  • Typed register() rejects binary args with clear error message
  • Comprehensive test coverage (Rust unit + integration + JS vitest)
  • Updated README with Binary Data section and wire protocol docs
  • Updated CI publish order for new common crate

Closes #38

@simongdavies simongdavies added the kind/enhancement New feature or improvement label Mar 6, 2026
@simongdavies simongdavies force-pushed the allow-binary-types-for-host-funcs branch 4 times, most recently from db7d6ed to 4ba03cd Compare March 9, 2026 12:14
simongdavies added a commit to simongdavies/hyperlight-js that referenced this pull request Mar 9, 2026
…n path

QuickJS stores JSON-parsed numbers as doubles internally, so
value_to_json_with_binaries was emitting 42.0 instead of 42 for
whole numbers. This caused serde deserialization failures on the
host side when typed host functions expected i32/i64 args.

Fix: when a float has no fractional part and fits in i64, emit it
as an integer to match JSON.stringify behaviour.

Also adapts user_module_can_import_host_function test to use the
new typed register() API (register_raw was removed in PR hyperlight-dev#40).
@simongdavies simongdavies force-pushed the allow-binary-types-for-host-funcs branch 2 times, most recently from 9e70be3 to bfc51b5 Compare March 10, 2026 14:31
Copy link
Contributor

@dblnz dblnz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me, although I am not very familiar to the js-host-api side of things.

One thought I have is, it seems to me that this serialization/deserialization to/from guest would benefit from something similar to what we do in hyperlight-wasm with the WIT world bindings generation, right?
Practically this boils down to having shared types definitions between host and guest downstream.

);

// Calculate total size: 4 bytes for count + (4 bytes length + data) per blob
let total_size = 4 + blobs.iter().map(|b| 4 + b.as_ref().len()).sum::<usize>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since already using assert!, maybe use checked_add for total_size and assert for the result to be Some.

}

blobs.push(data[offset..offset + len].to_vec());
offset += len;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this can happen, but maybe use checked_add to avoid overflows?


/// Maximum recursion depth for JSON tree traversal in the guest runtime.
/// Matches the host-side limit in `hyperlight-js-common::MAX_JSON_DEPTH`.
const MAX_GUEST_JSON_DEPTH: usize = 64;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use the const in hyperlight-js-common directly?

Adds Uint8Array/Buffer support for host function arguments and returns.

Architecture:
- New hyperlight-js-common crate: shared wire-format constants, binary
  sidecar encode/decode, FnReturn enum, DecodeError type
- Guest extracts Uint8Array from QuickJS VM into binary sidecar
- Host dispatches via register() (typed serde) or register_js() (JS bridge)
- NAPI layer creates native Node.js Buffers via C API (no base64)
- Tagged return format (0x00=JSON, 0x01=binary) for return path

Key changes:
- Single CallHostJsFunction entry point (removed legacy JSON-only path)
- Native Buffer marshalling in NAPI (JsArg/JsReturn types)
- Depth limits on all recursive JSON tree traversals (MAX_JSON_DEPTH=64)
- Trailing data rejection in sidecar decoder
- Typed register() rejects binary args with clear error message
- Comprehensive test coverage (Rust unit + integration + JS vitest)
- Updated README with Binary Data section and wire protocol docs
- Updated CI publish order for new common crate

Signed-off-by: Simon Davies <simongdavies@users.noreply.github.com>
QuickJS stores JSON-parsed numbers as doubles internally, so
value_to_json_with_binaries was emitting 42.0 instead of 42 for
whole numbers. This caused serde deserialization failures on the
host side when typed host functions expected i32/i64 args.

Fix: when a float has no fractional part and fits in i64, emit it
as an integer to match JSON.stringify behaviour.

Also adds 7 numeric type tests covering i32, i64, f64, bool, mixed
types, negative integers, and zero — all passing through the binary
host function path with event data (JSON-parsed → float → host).
napi_get_buffer_info returns data=null with len=0 for empty buffers.
slice::from_raw_parts requires a non-null pointer even for zero-length
slices, which caused a panic when returning empty buffers from host
functions.

- Handle len=0 case specially by returning Vec::new() directly
- Add explicit null check with error for data=null with len > 0
- Add vitest for host returning Buffer.alloc(0) to guest

Signed-off-by: Simon Davies <simongdavies@users.noreply.github.com>
Signed-off-by: Simon Davies <simongdavies@users.noreply.github.com>
@simongdavies simongdavies force-pushed the allow-binary-types-for-host-funcs branch from b4e41a8 to 0ade7b4 Compare March 17, 2026 18:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/enhancement New feature or improvement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Native Binary Data Support for Host Functions

2 participants