wasm-tools-go icon indicating copy to clipboard operation
wasm-tools-go copied to clipboard

proposal: generate export bindings

Open ydnar opened this issue 11 months ago • 0 comments

This document describes a design and mechanism for generated Go bindings for Component Model exports.

Background

The Component Model describes a mechanism for a WebAssembly program to export functions and methods that adhere to a specific WIT world (an API contract).

WIT worlds can export whole interfaces (e.g. wasi:cli/command) or individual functions. A WIT interface contains freestanding functions and types, which can include resource types with methods.

Exports are currently declared in a WIT file with a world that has one or more export directives, which export either an entire WIT interface or an individual function. This document presumes the existence of //go:wasmexport or similar (e.g. //export in TinyGo) to expose exported symbols in the resulting WebAssembly binary.

The design proposed here is similar to the approach taken by wit-bindgen, which generates Go bindings on top of Cgo suitable for TinyGo.

TODO: add more context

Proposed Design

For each exported function and method, wit-bindgen-go would generate a corresponding Go function with the relevant //go:wasmexport directive. The generated function would call a user-provided function. If the user-provided function is undefined (nil), the program will panic.

The expectation is that user code would import a generated Go package and call an Export() function or similar to register its implementation of the required interface(s).

Exported Interfaces

For each WIT interface, create a Go package-level exports interface type named Exports, containing one Go method for each freestanding WIT function in the WIT interface, a Go method to map resource handles to a Go interface (more below), and any resource constructor(s) or static function(s) if present.

// Exports represents the exported interface "wasi:filesystem/types".
type Exports interface {
    // freestanding functions
    // resource types
    // resource constructors and static methods
}

For each resource type defined in the WIT interface, an additional Go resource interface would be defined:

// DescriptorInterface represents the exported resource "wasi:filesystem/types#descriptor".
type DescriptorInterface interface {
    // resource methods
    // TODO: ResourceDrop, ResourceRep, and other administrative functions
}

For each resource type, a Go method is added to the exports interface with the name of the resource type, to translate an opaque handle value (i32) to the resource interface, along with constructor(s) and static methods:

// Exports represents the exported interface "wasi:filesystem/types".
type Exports interface {
    // Descriptor maps a handle to an exported DescriptorInterface.
    Descriptor(handle Descriptor) DescriptorInterface

    // NewDescriptor represents the exported constructor for `wasi:filesystem/types#descriptor".
    NewDescriptor() Descriptor
}

Usage

In order for a Go program to satisfy the runtime requirements of its component exports, user code must implement the exports interface by creating a exports implementation type with methods that match the exports interface:

package main

import "wasi/filesystem/types"

func init() {
    types.Export(&FilesystemExports{})
}

type FilesystemExports struct{}

func (e *FilesystemExports) Descriptor(handle types.Descriptor) DescriptorInterface {
    // TODO: use Component Model resource rep to return pointer
}

func (e *FilesystemExports) NewDescriptor() types.Descriptor {
    // ...
}

type Descriptor struct { /* ... */ }

func (d *Descriptor) Destructor() { /* ... */ }

ydnar avatar Mar 08 '24 17:03 ydnar