Skip to content

Conversation

@patrickdet
Copy link
Contributor

This PR implements WebAssembly Component Model resources in Wasmex, enabling WASM components to maintain their own internal state across multiple calls, essentially turning them into stateful applications running in a sandbox. This PR only implements guest resources. While host resources are possible in the Wasi component model, I would assume they are less common. We can add that later if needed.

This fundamentally changes what's possible with WebAssembly in Elixir: instead of just calling stateless functions, you're now interacting with persistent, sandboxed applications that maintain their own state, manage their own resources, and expose rich APIs through resource methods, all while maintaining WebAssembly's security guarantees.

It looks like this:

# WIT: counter.wit
  # resource counter {
  #   constructor(initial: u32);
  #   increment: func() -> u32;
  #   get-value: func() -> u32;
  # }

  # Generate Elixir client from WIT
  defmodule MyApp.Counter do
    use Wasmex.Components.GuestResource,
      wit: "counter.wit",
      resource: "counter"
  end

  # Use it
  {:ok, store} = Wasmex.Components.Store.new()
  {:ok, component} = Wasmex.Components.Component.new(store, wasm_bytes)
  {:ok, instance} = Wasmex.Components.Instance.new(store, component, %{})

  # Create and use resource
  {:ok, counter} = MyApp.Counter.start_link(instance, [0])
  {:ok, 1} = MyApp.Counter.increment(counter)
  {:ok, 1} = MyApp.Counter.get_value(counter)

  # Works with OTP supervisors
  children = [{MyApp.Counter, [instance, [42]]}]
  Supervisor.start_link(children, strategy: :one_for_one)

Disclaimer:
It was a quite a bit of work. Claude Code was used extensively.

I have just enabled the GH actions for now so I can see the run status on my branch. We can change that before merging.

Implements comprehensive support for WebAssembly Component Model guest resources,
enabling Elixir processes to interact with WASM resources through idiomatic OTP patterns.

## Key Features

### Guest Resource Support (WASM → Elixir)
- Complete implementation of guest resources from WASM components
- Automatic GenServer code generation from WIT files
- Each resource becomes a proper OTP process with supervision support
- Full method discovery and type-safe function generation

### Code Generation Approach
- Parse WIT files at compile time to generate resource clients
- Each generated module is a complete GenServer implementation
- Methods automatically converted to Elixir function names (kebab-case → snake_case)
- Proper argument handling and return value mapping

### Native Integration
- Rust NIFs for resource creation and method calls
- Automatic resource handle management
- Proper lifecycle handling with Erlang garbage collection
- Thread-safe operations across the BEAM

## Implementation Details

- Added Wasmex.Components.GuestResource macro for code generation
- WIT parser extracts resource definitions, methods, and types
- Generated modules work seamlessly with OTP supervisors
- No special supervisor needed - standard Supervisor/DynamicSupervisor work perfectly
- Comprehensive test coverage (28 focused tests)

## Example Usage

```elixir
# Define a resource client from WIT
defmodule MyApp.Counter do
  use Wasmex.Components.GuestResource,
    wit: "path/to/counter.wit",
    resource: "counter"
end

# Use it like any OTP process
{:ok, counter} = MyApp.Counter.start_link(instance, [42])
{:ok, 43} = MyApp.Counter.increment(counter)
{:ok, 0} = MyApp.Counter.reset(counter, 0)

# Works with supervisors
children = [
  {MyApp.Counter, [instance, [100]]}
]
Supervisor.start_link(children, strategy: :one_for_one)
```

## Testing
- Edge cases: GC behavior, concurrent access, constructor validation
- OTP integration: Supervision, restart strategies, fault isolation
- Instance lifecycle: Resources keep instances alive via reference counting
- 72% reduction in test code while maintaining excellent coverage

## Cleanup
- Removed non-functional host resource infrastructure (1,772 lines)
- Consolidated tests from 44 to 28 (more focused, less duplication)
- Removed unnecessary GuestResourceSupervisor wrapper
- Fixed module naming conventions across test suite

This provides a solid foundation for WebAssembly Component Model resources in Elixir,
following OTP principles and Elixir idioms throughout.
Remove tests that rely on unpredictable garbage collection behavior:
- Store garbage collection test was flaky and not testing real behavior
- Memory leak test was unreliable due to BEAM memory management complexity
- Both tests added no real value for validating functionality
@tessi
Copy link
Owner

tessi commented Sep 8, 2025

hey @patrickdet! as before, huge thanks for this contribution. The LLM did again add a lot of unrelated changes. However, some I really like :D
Before going too deep on this PR review, I would go and pick some of these changes into main if you don't mind. My strategy is to review this massive 9k+ LOC PR by divide and conquer ⚔️

@tessi
Copy link
Owner

tessi commented Sep 8, 2025

I have just enabled the GH actions for now so I can see the run status on my branch. We can change that before merging.

yes, let's do that. I wish I would be smart enough to figure out GH actions triggers so that they build nicely for your branches but not twice on PR pushes. I tried (also using GitHubs concurrency feature) but failed. Since tweaking CI is not my favourite hobby, I'd just like to keep the currently working config :)

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.

2 participants