Skip to content

Runtime merge callback issues#1942

Draft
cursor[bot] wants to merge 1 commit intofeat/sync-013-wasm-merge-callbackfrom
cursor/runtime-merge-callback-issues-5074
Draft

Runtime merge callback issues#1942
cursor[bot] wants to merge 1 commit intofeat/sync-013-wasm-merge-callbackfrom
cursor/runtime-merge-callback-issues-5074

Conversation

@cursor
Copy link
Contributor

@cursor cursor bot commented Feb 11, 2026

Fixes for WASM Merge Callback and Type Registry

Description

This PR addresses several critical and quality-of-life issues related to WASM merge callbacks and the merge type registry.

  1. Empty WASM imports cause instance creation to always fail: Fixed WASM instance creation failure by providing stub imports for all declared host functions, ensuring compliance with the WASM spec.
  2. Hardcoded memory offset corrupts WASM module state: Addressed WASM memory corruption by moving merge data allocation to the second WASM memory page (offset 65536), avoiding overlap with the module's static data, stack, and heap.
  3. Duplicated merge function logic across two methods: Refactored call_merge_function and call_custom_merge_function into a shared execute_merge helper to eliminate code duplication and improve maintainability.
  4. Timeout field stored but never used in merge operations: Removed the unused timeout field and related API from RuntimeMergeCallback to prevent misleading users about timeout functionality, as it's a future async enhancement.
  5. Type name lookup skips normalization applied during registration: Corrected type name lookup in MergeRegistry and the global try_merge_by_type_name function to normalize input type names, matching the normalization applied during registration and ensuring successful lookups for fully-qualified names.

Test plan

All existing unit and integration tests were run and passed successfully after these changes. The fixes primarily address correctness issues in the WASM runtime and registry logic, which are covered by existing test infrastructure. No new end-to-end tests were added as part of this fix, but the existing ones should now pass reliably.

Documentation update

The removal of the with_timeout method from RuntimeMergeCallback and the clarified behavior of type name normalization in MergeRegistry (and its global functions) should be reflected in any relevant public or internal documentation.


- Fix empty WASM imports by creating stub imports for all host functions
  declared by the module (bug 1). The WASM spec requires all declared
  imports to be provided at instantiation time.

- Fix hardcoded memory offset by using 65536 (start of second page)
  instead of 1024 to avoid corrupting static data, stack, and heap
  metadata in the first page (bug 2).

- Refactor duplicated merge function logic into shared execute_merge
  helper to reduce code duplication and ensure consistent behavior
  (bug 3).

- Remove unused timeout field and related DEFAULT_MERGE_TIMEOUT constant
  and with_timeout method per no-dead-code guidelines (bug 4).

- Add type name normalization in lookup functions to match the
  normalization applied during registration. Both MergeRegistry and
  global try_merge_by_type_name now apply rsplit('::') normalization
  so 'my_app::MyState' will match registered 'MyState' (bug 5).
@cursor
Copy link
Contributor Author

cursor bot commented Feb 11, 2026

Cursor Agent can help with this pull request. Just @cursor in comments and I'll start working on changes in this branch.
Learn more about Cursor Agents

@github-actions
Copy link

Your PR title does not adhere to the Conventional Commits convention:

<type>(<scope>): <subject>

Common errors to avoid:

  1. The title must be in lower case.
  2. Allowed type values are: build, ci, docs, feat, fix, perf, refactor, test.

Copy link

@meroreviewer meroreviewer bot left a comment

Choose a reason for hiding this comment

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

🤖 AI Code Reviewer

Reviewed by 1 agents | Quality score: 33% | Review time: 89.6s

🟡 1 warnings, 💡 2 suggestions, 📝 1 nitpicks. See inline comments.


🤖 Generated by AI Code Reviewer | Review ID: review-7bcdd3f7

// Merge functions shouldn't call host functions, so this is safe
let results_arity = func_type.results().len();
let stub = Function::new(store, func_type, move |_args| {
// Return default values (zeros) for all result types
Copy link

Choose a reason for hiding this comment

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

🟡 Stub functions return I64 regardless of declared type

The stub always returns Value::I64(0) but the actual function signature may expect different types (i32, f32, f64), which could cause a type mismatch error at runtime if a merge function accidentally calls a host function.

Suggested fix:

Match on `func_type.results()` to return the correct `Value` variant for each expected return type (e.g., `Value::I32(0)`, `Value::F64(0.0)`).

for import in self.module.imports() {
if import.module() == "env" {
if let wasmer::ExternType::Function(func_type) = import.ty() {
// Create a stub function that returns default values
Copy link

Choose a reason for hiding this comment

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

💡 Stub invocation silently returns zeros instead of signaling a bug

If a merge function erroneously calls a host function, the stub silently returns zeros which could cause subtle data corruption; adding a tracing::warn! or panic would make such bugs immediately visible during development.

Suggested fix:

Add `tracing::warn!("Merge function called host import '{}' - this indicates a bug", import.name());` inside the stub closure before returning.

let (type_name_ptr, type_name_len) =
type_name_info.expect("type_name_info must be Some for custom merge");

let merge_fn = instance
Copy link

Choose a reason for hiding this comment

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

💡 expect() in closure could propagate cleaner error

Using .expect() inside the closure could panic; while logically safe given the calling code, returning a WasmMergeError would be more consistent with the rest of the error handling.

Suggested fix:

Replace `.expect(...)` with `.ok_or_else(|| WasmMergeError::MergeFailed("internal: missing type_name_info".into()))?`

remote_data,
None,
|instance, store, local_ptr, local_len, remote_ptr, remote_len, _type_name_info| {
let merge_fn = instance
Copy link

Choose a reason for hiding this comment

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

📝 Nit: Unnecessary clone of already-owned String

export_name is already String from to_owned() at line 654; using clone() in the error path allocates again when the string could be moved.

Suggested fix:

Consider restructuring to avoid the clone, or acknowledge this is acceptable for the error path.

@github-actions
Copy link

This pull request has been automatically marked as stale. If this pull request is still relevant, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize reviewing it yet. Your contribution is very much appreciated.

@github-actions github-actions bot added the Stale label Feb 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant