From 66c1d4e2cd013c49cdffd6af213fe5bd0b6a2a50 Mon Sep 17 00:00:00 2001 From: Tyler Rockwood Date: Mon, 15 Jan 2024 13:30:20 -0600 Subject: [PATCH 1/4] Expose component-model to c-api Step 1 : * Component/Linker * Value * Calls into the component --- crates/c-api/Cargo.toml | 1 + crates/c-api/artifact/Cargo.toml | 2 + crates/c-api/build.rs | 1 + crates/c-api/cmake/features.cmake | 1 + crates/c-api/include/wasmtime/component.h | 199 +++++ crates/c-api/include/wasmtime/conf.h.in | 1 + crates/c-api/src/async.rs | 10 +- crates/c-api/src/component.rs | 729 ++++++++++++++++++ crates/c-api/src/lib.rs | 18 + crates/c-api/src/vec.rs | 11 + .../wasmtime/src/runtime/component/types.rs | 3 +- 11 files changed, 968 insertions(+), 8 deletions(-) create mode 100644 crates/c-api/include/wasmtime/component.h create mode 100644 crates/c-api/src/component.rs diff --git a/crates/c-api/Cargo.toml b/crates/c-api/Cargo.toml index 84b7d45ede1e..6917d6bdcdee 100644 --- a/crates/c-api/Cargo.toml +++ b/crates/c-api/Cargo.toml @@ -49,6 +49,7 @@ logging = ['dep:env_logger'] disable-logging = ["log/max_level_off", "tracing/max_level_off"] coredump = ["wasmtime/coredump"] addr2line = ["wasmtime/addr2line"] +component-model = ["wasmtime/component-model", "cranelift"] demangle = ["wasmtime/demangle"] threads = ["wasmtime/threads"] gc = ["wasmtime/gc"] diff --git a/crates/c-api/artifact/Cargo.toml b/crates/c-api/artifact/Cargo.toml index 1eb6659ec01b..afc0222deb82 100644 --- a/crates/c-api/artifact/Cargo.toml +++ b/crates/c-api/artifact/Cargo.toml @@ -38,6 +38,7 @@ default = [ 'gc-null', 'cranelift', 'winch', + 'component-model', # ... if you add a line above this be sure to change the other locations # marked WASMTIME_FEATURE_LIST ] @@ -52,6 +53,7 @@ coredump = ["wasmtime-c-api/coredump"] addr2line = ["wasmtime-c-api/addr2line"] demangle = ["wasmtime-c-api/demangle"] wat = ["wasmtime-c-api/wat"] +component-model = ["wasmtime-c-api/component-model"] threads = ["wasmtime-c-api/threads"] gc = ["wasmtime-c-api/gc"] gc-drc = ["wasmtime-c-api/gc-drc"] diff --git a/crates/c-api/build.rs b/crates/c-api/build.rs index b500fd1bf4d1..d1b24f1c7ee7 100644 --- a/crates/c-api/build.rs +++ b/crates/c-api/build.rs @@ -19,6 +19,7 @@ const FEATURES: &[&str] = &[ "GC_NULL", "CRANELIFT", "WINCH", + "COMPONENT_MODEL", ]; // ... if you add a line above this be sure to change the other locations // marked WASMTIME_FEATURE_LIST diff --git a/crates/c-api/cmake/features.cmake b/crates/c-api/cmake/features.cmake index 385c139659e1..cb8079446fc8 100644 --- a/crates/c-api/cmake/features.cmake +++ b/crates/c-api/cmake/features.cmake @@ -43,5 +43,6 @@ feature(gc-null ON) feature(async ON) feature(cranelift ON) feature(winch ON) +feature(component-model ON) # ... if you add a line above this be sure to change the other locations # marked WASMTIME_FEATURE_LIST diff --git a/crates/c-api/include/wasmtime/component.h b/crates/c-api/include/wasmtime/component.h new file mode 100644 index 000000000000..99caaaed18ab --- /dev/null +++ b/crates/c-api/include/wasmtime/component.h @@ -0,0 +1,199 @@ +/** + * The component model + * + * TODO: Write some more documentation here like in the Rust API. + * + */ + +#ifndef WASMTIME_COMPONENT_H +#define WASMTIME_COMPONENT_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Whether or not to enable support for the component model in + * Wasmtime. + * + * For more information see the Rust documentation at + * https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.wasm_component_model + */ +WASMTIME_CONFIG_PROP(void, component_model, bool) + +// The tag part of wasmtime_component_val_t that specifies what variant is +// populated in wasmtime_component_val_payload_t. +typedef uint8_t wasmtime_component_kind_t; + +#define WASMTIME_COMPONENT_KIND_BOOL 0 +#define WASMTIME_COMPONENT_KIND_S8 1 +#define WASMTIME_COMPONENT_KIND_U8 2 +#define WASMTIME_COMPONENT_KIND_S16 3 +#define WASMTIME_COMPONENT_KIND_U16 4 +#define WASMTIME_COMPONENT_KIND_S32 5 +#define WASMTIME_COMPONENT_KIND_U32 6 +#define WASMTIME_COMPONENT_KIND_S64 7 +#define WASMTIME_COMPONENT_KIND_U64 8 +#define WASMTIME_COMPONENT_KIND_F32 9 +#define WASMTIME_COMPONENT_KIND_F64 10 +#define WASMTIME_COMPONENT_KIND_CHAR 11 +#define WASMTIME_COMPONENT_KIND_STRING 12 +#define WASMTIME_COMPONENT_KIND_LIST 13 +#define WASMTIME_COMPONENT_KIND_RECORD 14 +#define WASMTIME_COMPONENT_KIND_TUPLE 15 +#define WASMTIME_COMPONENT_KIND_VARIANT 16 +#define WASMTIME_COMPONENT_KIND_ENUM 17 +#define WASMTIME_COMPONENT_KIND_OPTION 18 +#define WASMTIME_COMPONENT_KIND_RESULT 19 +#define WASMTIME_COMPONENT_KIND_FLAGS 20 + +typedef struct wasmtime_component_val_t wasmtime_component_val_t; +typedef struct wasmtime_component_val_record_field_t wasmtime_component_val_record_field_t; + +#define WASMTIME_COMPONENT_DECLARE_VEC(name, element) \ + typedef struct wasmtime_component_##name##_t { \ + size_t size; \ + element *data; \ + } wasmtime_component_##name##_t; \ + \ + WASM_API_EXTERN void wasmtime_component_##name##_new_empty( \ + wasmtime_component_##name##_t *out); \ + WASM_API_EXTERN void wasmtime_component_##name##_new_uninitialized( \ + wasmtime_component_##name##_t *out, size_t); \ + WASM_API_EXTERN void wasmtime_component_##name##_copy( \ + wasmtime_component_##name##_t *out, \ + const wasmtime_component_##name##_t *); \ + WASM_API_EXTERN void wasmtime_component_##name##_delete( \ + wasmtime_component_##name##_t *); + +// in C, an array type needs a complete element type, we need to defer xxx_new +#define WASMTIME_COMPONENT_DECLARE_VEC_NEW(name, element) \ + WASM_API_EXTERN void wasmtime_component_##name##_new( \ + wasmtime_component_##name##_t *out, size_t, element const[]); + +/// \brief A vector of values. +WASMTIME_COMPONENT_DECLARE_VEC(val_vec, wasmtime_component_val_t); + +/// \brief A tuple of named fields. +WASMTIME_COMPONENT_DECLARE_VEC(val_record, wasmtime_component_val_record_field_t); + +/// \brief A variable sized bitset. +WASMTIME_COMPONENT_DECLARE_VEC(val_flags, uint32_t); +WASMTIME_COMPONENT_DECLARE_VEC_NEW(val_flags, uint32_t); + +#undef WASMTIME_COMPONENT_DECLARE_VEC + +// A variant contains the discriminant index and an optional value that is held. +typedef struct wasmtime_component_val_variant_t { + uint32_t discriminant; + wasmtime_component_val_t *val; +} wasmtime_component_val_variant_t; + +// A result is an either type holding a value and a bit if is it an ok or error +// variant. +typedef struct wasmtime_component_val_result_t { + wasmtime_component_val_t *val; + bool error; +} wasmtime_component_val_result_t; + +// Which value within an enumeration is selected. +typedef struct wasmtime_component_val_enum_t { + uint32_t discriminant; +} wasmtime_component_val_enum_t; + +typedef union wasmtime_component_val_payload_t { + bool boolean; + int8_t s8; + uint8_t u8; + int16_t s16; + uint16_t u16; + int32_t s32; + uint32_t u32; + int64_t s64; + uint64_t u64; + float f32; + double f64; + uint8_t character; + wasm_name_t string; + wasmtime_component_val_vec_t list; + wasmtime_component_val_record_t record; + wasmtime_component_val_vec_t tuple; + wasmtime_component_val_variant_t variant; + wasmtime_component_val_enum_t enumeration; + wasmtime_component_val_t *option; + wasmtime_component_val_result_t result; + wasmtime_component_val_flags_t flags; +} wasmtime_component_val_payload_t; + +// The tagged union for a value within the component model. +typedef struct wasmtime_component_val_t { + wasmtime_component_kind_t kind; + wasmtime_component_val_payload_t payload; +} wasmtime_component_val_t; + +WASMTIME_COMPONENT_DECLARE_VEC_NEW(val_vec, wasmtime_component_val_t); + +// A record is a series of named fields, which are values with a string name. +typedef struct wasmtime_component_val_record_field_t { + wasm_name_t name; + wasmtime_component_val_t val; +} wasmtime_component_val_record_field_t; + +WASMTIME_COMPONENT_DECLARE_VEC_NEW(val_record, wasmtime_component_val_record_field_t); + +// Set a value within this bitset. +// +// If this bit set is too small to hold a value at `index` it will be resized. +void wasmtime_component_val_flags_set(wasmtime_component_val_flags_t *flags, uint32_t index, bool enabled); + +// Test if this bitset holds a value at `index`. +bool wasmtime_component_val_flags_test(const wasmtime_component_val_flags_t* flags, uint32_t index); + +wasmtime_component_val_t* wasmtime_component_val_new(); + +void wasmtime_component_val_delete(wasmtime_component_val_t* val); + +typedef struct wasmtime_component_t wasmtime_component_t; + +wasmtime_error_t * +wasmtime_component_from_binary(const wasm_engine_t *engine, const uint8_t *buf, size_t len, + wasmtime_component_t **component_out); + +void wasmtime_component_delete(wasmtime_component_t *component); + +typedef struct wasmtime_component_linker_t wasmtime_component_linker_t; + +wasmtime_component_linker_t *wasmtime_component_linker_new(const wasm_engine_t *engine); + +void wasmtime_component_linker_delete(wasmtime_component_linker_t *linker); + +typedef struct wasmtime_component_instance_t wasmtime_component_instance_t; + +// declaration from store.h +typedef struct wasmtime_context wasmtime_context_t; + +wasmtime_error_t *wasmtime_component_linker_instantiate( + const wasmtime_component_linker_t *linker, wasmtime_context_t *context, + const wasmtime_component_t *component, wasmtime_component_instance_t **instance_out); + +typedef struct wasmtime_component_func_t wasmtime_component_func_t; + +bool wasmtime_component_instance_get_func( + const wasmtime_component_instance_t *instance, wasmtime_context_t *context, + const char *name, size_t name_len, wasmtime_component_func_t **item_out); + +wasmtime_error_t *wasmtime_component_func_call( + const wasmtime_component_func_t *func, wasmtime_context_t *context, + const wasmtime_component_val_t *params, size_t params_len, + wasmtime_component_val_t *results, size_t results_len, + wasm_trap_t **trap_out); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WASMTIME_COMPONENT_H diff --git a/crates/c-api/include/wasmtime/conf.h.in b/crates/c-api/include/wasmtime/conf.h.in index 3dff987acc4f..2288407cca41 100644 --- a/crates/c-api/include/wasmtime/conf.h.in +++ b/crates/c-api/include/wasmtime/conf.h.in @@ -25,6 +25,7 @@ #cmakedefine WASMTIME_FEATURE_ASYNC #cmakedefine WASMTIME_FEATURE_CRANELIFT #cmakedefine WASMTIME_FEATURE_WINCH +#cmakedefine WASMTIME_FEATURE_COMPONENT_MODEL // ... if you add a line above this be sure to change the other locations // marked WASMTIME_FEATURE_LIST diff --git a/crates/c-api/src/async.rs b/crates/c-api/src/async.rs index c6066cf5402d..4d96d5c03931 100644 --- a/crates/c-api/src/async.rs +++ b/crates/c-api/src/async.rs @@ -7,9 +7,7 @@ use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; use std::{ptr, str}; -use wasmtime::{ - AsContextMut, Func, Instance, Result, RootScope, StackCreator, StackMemory, Trap, Val, -}; +use wasmtime::{AsContextMut, Func, Instance, Result, RootScope, StackCreator, StackMemory, Val}; use crate::{ bad_utf8, handle_result, to_str, translate_args, wasm_config_t, wasm_functype_t, wasm_trap_t, @@ -211,10 +209,8 @@ fn handle_call_error( trap_ret: &mut *mut wasm_trap_t, err_ret: &mut *mut wasmtime_error_t, ) { - if err.is::() { - *trap_ret = Box::into_raw(Box::new(wasm_trap_t::new(err))); - } else { - *err_ret = Box::into_raw(Box::new(wasmtime_error_t::from(err))); + if let Some(err) = crate::handle_call_error(err, trap_ret) { + *err_ret = Box::into_raw(err); } } diff --git a/crates/c-api/src/component.rs b/crates/c-api/src/component.rs new file mode 100644 index 000000000000..85c2f1e9d174 --- /dev/null +++ b/crates/c-api/src/component.rs @@ -0,0 +1,729 @@ +use anyhow::{bail, ensure, Context, Result}; +use wasmtime::component::{Component, Func, Instance, Linker, Type, Val}; +use wasmtime::{AsContext, AsContextMut}; + +use crate::{ + declare_vecs, handle_call_error, handle_result, wasm_byte_vec_t, wasm_config_t, wasm_engine_t, + wasm_name_t, wasm_trap_t, wasmtime_error_t, WasmtimeStoreContextMut, WasmtimeStoreData, +}; +use std::collections::HashMap; +use std::{mem, mem::MaybeUninit, ptr, slice}; + +#[no_mangle] +pub extern "C" fn wasmtime_config_component_model_set(c: &mut wasm_config_t, enable: bool) { + c.config.wasm_component_model(enable); +} + +pub type wasmtime_component_string_t = wasm_byte_vec_t; + +#[repr(C)] +#[derive(Clone)] +pub struct wasmtime_component_val_record_field_t { + pub name: wasm_name_t, + pub val: wasmtime_component_val_t, +} + +impl Default for wasmtime_component_val_record_field_t { + fn default() -> Self { + Self { + name: Vec::new().into(), + val: Default::default(), + } + } +} + +declare_vecs! { + ( + name: wasmtime_component_val_vec_t, + ty: wasmtime_component_val_t, + new: wasmtime_component_val_vec_new, + empty: wasmtime_component_val_vec_new_empty, + uninit: wasmtime_component_val_vec_new_uninitialized, + copy: wasmtime_component_val_vec_copy, + delete: wasmtime_component_val_vec_delete, + ) + ( + name: wasmtime_component_val_record_t, + ty: wasmtime_component_val_record_field_t, + new: wasmtime_component_val_record_new, + empty: wasmtime_component_val_record_new_empty, + uninit: wasmtime_component_val_record_new_uninitialized, + copy: wasmtime_component_val_record_copy, + delete: wasmtime_component_val_record_delete, + ) + ( + name: wasmtime_component_val_flags_t, + ty: u32, + new: wasmtime_component_val_flags_new, + empty: wasmtime_component_val_flags_new_empty, + uninit: wasmtime_component_val_flags_new_uninitialized, + copy: wasmtime_component_val_flags_copy, + delete: wasmtime_component_val_flags_delete, + ) +} + +#[repr(C)] +#[derive(Clone)] +pub struct wasmtime_component_val_variant_t { + pub discriminant: u32, + pub val: Option>, +} + +#[repr(C)] +#[derive(Clone)] +pub struct wasmtime_component_val_result_t { + pub value: Option>, + pub error: bool, +} + +#[repr(C)] +#[derive(Clone)] +pub struct wasmtime_component_val_enum_t { + pub discriminant: u32, +} + +#[no_mangle] +pub extern "C" fn wasmtime_component_val_flags_set( + flags: &mut wasmtime_component_val_flags_t, + index: u32, + enabled: bool, +) { + let mut f = flags.take(); + let (idx, bit) = ((index / u32::BITS) as usize, index % u32::BITS); + if idx >= f.len() { + f.resize(idx + 1, Default::default()); + } + if enabled { + f[idx] |= 1 << (bit); + } else { + f[idx] &= !(1 << (bit)); + } + flags.set_buffer(f); +} + +#[no_mangle] +pub extern "C" fn wasmtime_component_val_flags_test( + flags: &wasmtime_component_val_flags_t, + index: u32, +) -> bool { + let flags = flags.as_slice(); + let (idx, bit) = ((index / u32::BITS) as usize, index % u32::BITS); + flags.get(idx).map(|v| v & (1 << bit) != 0).unwrap_or(false) +} + +#[repr(C, u8)] +#[derive(Clone)] +pub enum wasmtime_component_val_t { + Bool(bool), + S8(i8), + U8(u8), + S16(i16), + U16(u16), + S32(i32), + U32(u32), + S64(i64), + U64(u64), + F32(f32), + F64(f64), + Char(char), + String(wasmtime_component_string_t), + List(wasmtime_component_val_vec_t), + Record(wasmtime_component_val_record_t), + Tuple(wasmtime_component_val_vec_t), + Variant(wasmtime_component_val_variant_t), + Enum(wasmtime_component_val_enum_t), + Option(Option>), + Result(wasmtime_component_val_result_t), + Flags(wasmtime_component_val_flags_t), +} + +macro_rules! ensure_type { + ($ty:ident, $variant:pat) => { + ensure!( + matches!($ty, $variant), + "attempted to create a {} for a {}", + $ty.desc(), + stringify!($variant) + ); + }; +} + +// a c_api value and its associated Type (from the component model runtime) +struct TypedCVal<'a>(&'a wasmtime_component_val_t, &'a Type); + +impl TryFrom> for Val { + type Error = anyhow::Error; + fn try_from(value: TypedCVal) -> Result { + let (value, ty) = (value.0, value.1); + Ok(match value { + &wasmtime_component_val_t::Bool(b) => { + ensure_type!(ty, Type::Bool); + Val::Bool(b) + } + &wasmtime_component_val_t::S8(v) => { + ensure_type!(ty, Type::S8); + Val::S8(v) + } + &wasmtime_component_val_t::U8(v) => { + ensure_type!(ty, Type::U8); + Val::U8(v) + } + &wasmtime_component_val_t::S16(v) => { + ensure_type!(ty, Type::S16); + Val::S16(v) + } + &wasmtime_component_val_t::U16(v) => { + ensure_type!(ty, Type::U16); + Val::U16(v) + } + &wasmtime_component_val_t::S32(v) => { + ensure_type!(ty, Type::S32); + Val::S32(v) + } + &wasmtime_component_val_t::U32(v) => { + ensure_type!(ty, Type::U32); + Val::U32(v) + } + &wasmtime_component_val_t::S64(v) => { + ensure_type!(ty, Type::S64); + Val::S64(v) + } + &wasmtime_component_val_t::U64(v) => { + ensure_type!(ty, Type::U64); + Val::U64(v) + } + &wasmtime_component_val_t::F32(v) => { + ensure_type!(ty, Type::Float32); + Val::Float32(v) + } + &wasmtime_component_val_t::F64(v) => { + ensure_type!(ty, Type::Float64); + Val::Float64(v) + } + &wasmtime_component_val_t::Char(v) => { + ensure_type!(ty, Type::Char); + Val::Char(v) + } + wasmtime_component_val_t::String(v) => { + ensure_type!(ty, Type::String); + Val::String(String::from_utf8(v.as_slice().to_vec())?) + } + wasmtime_component_val_t::List(v) => { + if let Type::List(ty) = ty { + Val::List( + v.as_slice() + .iter() + .map(|v| TypedCVal(v, &ty.ty()).try_into()) + .collect::>>()?, + ) + } else { + bail!("attempted to create a list for a {}", ty.desc()); + } + } + wasmtime_component_val_t::Record(v) => { + if let Type::Record(ty) = ty { + let mut field_vals: HashMap<&[u8], &wasmtime_component_val_t> = + HashMap::from_iter( + v.as_slice().iter().map(|f| (f.name.as_slice(), &f.val)), + ); + let field_tys = ty.fields(); + Val::Record( + field_tys + .map(|tyf| { + if let Some(v) = field_vals.remove(tyf.name.as_bytes()) { + Ok((tyf.name.to_string(), TypedCVal(v, &tyf.ty).try_into()?)) + } else { + bail!("record missing field: {}", tyf.name); + } + }) + .collect::>>()?, + ) + } else { + bail!("attempted to create a record for a {}", ty.desc()); + } + } + wasmtime_component_val_t::Tuple(v) => { + if let Type::Tuple(ty) = ty { + Val::Tuple( + ty.types() + .zip(v.as_slice().iter()) + .map(|(ty, v)| TypedCVal(v, &ty).try_into()) + .collect::>>()?, + ) + } else { + bail!("attempted to create a tuple for a {}", ty.desc()); + } + } + wasmtime_component_val_t::Variant(v) => { + if let Type::Variant(ty) = ty { + let case = ty + .cases() + .nth(v.discriminant as usize) + .with_context(|| format!("missing variant {}", v.discriminant))?; + ensure!( + case.ty.is_some() == v.val.is_some(), + "variant type mismatch: {}", + case.ty.map(|ty| ty.desc()).unwrap_or("none") + ); + if let (Some(t), Some(v)) = (case.ty, &v.val) { + let v = TypedCVal(v.as_ref(), &t).try_into()?; + Val::Variant(case.name.to_string(), Some(Box::new(v))) + } else { + Val::Variant(case.name.to_string(), None) + } + } else { + bail!("attempted to create a variant for a {}", ty.desc()); + } + } + wasmtime_component_val_t::Enum(v) => { + if let Type::Enum(ty) = ty { + let name = ty + .names() + .nth(v.discriminant as usize) + .with_context(|| format!("missing enumeration {}", v.discriminant))?; + Val::Enum(name.to_string()) + } else { + bail!("attempted to create an enum for a {}", ty.desc()); + } + } + wasmtime_component_val_t::Option(v) => { + if let Type::Option(ty) = ty { + Val::Option(match v { + Some(v) => Some(Box::new(TypedCVal(v.as_ref(), &ty.ty()).try_into()?)), + None => None, + }) + } else { + bail!("attempted to create an option for a {}", ty.desc()); + } + } + wasmtime_component_val_t::Result(v) => { + if let Type::Result(ty) = ty { + if v.error { + match &v.value { + Some(v) => { + let ty = ty.err().context("expected err type")?; + Val::Result(Err(Some(Box::new( + TypedCVal(v.as_ref(), &ty).try_into()?, + )))) + } + None => { + ensure!(ty.err().is_none(), "expected no err type"); + Val::Result(Err(None)) + } + } + } else { + match &v.value { + Some(v) => { + let ty = ty.ok().context("expected ok type")?; + Val::Result(Ok(Some(Box::new( + TypedCVal(v.as_ref(), &ty).try_into()?, + )))) + } + None => { + ensure!(ty.ok().is_none(), "expected no ok type"); + Val::Result(Ok(None)) + } + } + } + } else { + bail!("attempted to create a result for a {}", ty.desc()); + } + } + wasmtime_component_val_t::Flags(flags) => { + if let Type::Flags(ty) = ty { + let mut set = Vec::new(); + for (idx, name) in ty.names().enumerate() { + if wasmtime_component_val_flags_test(&flags, idx as u32) { + set.push(name.to_string()); + } + } + Val::Flags(set) + } else { + bail!("attempted to create a flags for a {}", ty.desc()); + } + } + }) + } +} + +impl TryFrom<(&Val, &Type)> for wasmtime_component_val_t { + type Error = anyhow::Error; + + fn try_from((value, ty): (&Val, &Type)) -> Result { + Ok(match value { + Val::Bool(v) => wasmtime_component_val_t::Bool(*v), + Val::S8(v) => wasmtime_component_val_t::S8(*v), + Val::U8(v) => wasmtime_component_val_t::U8(*v), + Val::S16(v) => wasmtime_component_val_t::S16(*v), + Val::U16(v) => wasmtime_component_val_t::U16(*v), + Val::S32(v) => wasmtime_component_val_t::S32(*v), + Val::U32(v) => wasmtime_component_val_t::U32(*v), + Val::S64(v) => wasmtime_component_val_t::S64(*v), + Val::U64(v) => wasmtime_component_val_t::U64(*v), + Val::Float32(v) => wasmtime_component_val_t::F32(*v), + Val::Float64(v) => wasmtime_component_val_t::F64(*v), + Val::Char(v) => wasmtime_component_val_t::Char(*v), + Val::String(v) => wasmtime_component_val_t::String(v.clone().into_bytes().into()), + Val::List(v) => { + if let Type::List(ty) = ty { + let v = v + .iter() + .map(|v| (v, &ty.ty()).try_into()) + .collect::>>()?; + wasmtime_component_val_t::List(v.into()) + } else { + bail!("attempted to create a {} from a list", ty.desc()); + } + } + Val::Record(v) => { + if let Type::Record(ty) = ty { + let fields_types: HashMap = + HashMap::from_iter(ty.fields().map(|f| (f.name.to_string(), f.ty))); + let v = v + .iter() + .map(|(name, v)| { + if let Some(ty) = fields_types.get(name.as_str()) { + Ok(wasmtime_component_val_record_field_t { + name: name.clone().into_bytes().into(), + val: (v, ty).try_into()?, + }) + } else { + bail!("field {} not found in record type", name); + } + }) + .collect::>>()?; + wasmtime_component_val_t::Record(v.into()) + } else { + bail!("attempted to create a {} from a record", ty.desc()); + } + } + Val::Tuple(v) => { + if let Type::Tuple(ty) = ty { + let elem_types = ty.types().collect::>(); + if v.len() != elem_types.len() { + bail!( + "attempted to create a size {} tuple from a size {} tuple", + elem_types.len(), + v.len() + ); + } + let v = v + .iter() + .zip(elem_types.iter()) + .map(|v| v.try_into()) + .collect::>>()?; + wasmtime_component_val_t::Tuple(v.into()) + } else { + bail!("attempted to create a {} from a tuple", ty.desc()); + } + } + Val::Variant(discriminant, v) => { + if let Type::Variant(ty) = ty { + let (index, case) = ty + .cases() + .enumerate() + .find(|(_, v)| v.name == discriminant) + .map(|(idx, case)| (idx as u32, case)) + .context("expected valid discriminant")?; + let val = match v { + Some(v) => { + if let Some(ty) = &case.ty { + Some(Box::new((v.as_ref(), ty).try_into()?)) + } else { + bail!("attempted to create a None Variant from a Some variant"); + } + } + None => None, + }; + wasmtime_component_val_t::Variant(wasmtime_component_val_variant_t { + discriminant: index, + val, + }) + } else { + bail!("attempted to create a {} from a variant", ty.desc()); + } + } + Val::Enum(discriminant) => { + if let Type::Enum(ty) = ty { + let index = ty + .names() + .zip(0u32..) + .find(|(n, _)| *n == discriminant) + .map(|(_, idx)| idx) + .context("expected valid discriminant")?; + wasmtime_component_val_t::Enum(wasmtime_component_val_enum_t { + discriminant: index, + }) + } else { + bail!("attempted to create a {} from an enum", ty.desc()); + } + } + Val::Option(v) => { + if let Type::Option(ty) = ty { + wasmtime_component_val_t::Option(match v { + Some(v) => Some(Box::new((v.as_ref(), &ty.ty()).try_into()?)), + None => None, + }) + } else { + bail!("attempted to create a {} from an option", ty.desc()); + } + } + Val::Result(v) => { + if let Type::Result(ty) = ty { + let (error, value) = match v { + Err(v) => { + let value = match v { + Some(v) => { + if let Some(ty) = ty.err() { + Some(Box::new((v.as_ref(), &ty).try_into()?)) + } else { + bail!( + "attempted to create a None result from a Some result" + ); + } + } + None => None, + }; + (true, value) + } + Ok(v) => { + let value = match v { + Some(v) => { + if let Some(ty) = ty.ok() { + Some(Box::new((v.as_ref(), &ty).try_into()?)) + } else { + bail!( + "attempted to create a None result from a Some result" + ); + } + } + None => None, + }; + (false, value) + } + }; + wasmtime_component_val_t::Result(wasmtime_component_val_result_t { + value, + error, + }) + } else { + bail!("attempted to create a {} from a result", ty.desc()); + } + } + Val::Flags(v) => { + if let Type::Flags(ty) = ty { + let mapping: HashMap<_, _> = ty.names().zip(0u32..).collect(); + let mut flags: wasmtime_component_val_flags_t = Vec::new().into(); + for name in v { + let idx = mapping.get(name.as_str()).context("expected valid name")?; + wasmtime_component_val_flags_set(&mut flags, *idx, true); + } + wasmtime_component_val_t::Flags(flags) + } else { + bail!("attempted to create a {} from a flags", ty.desc()); + } + } + Val::Resource(_) => bail!("resource types are unimplemented"), + }) + } +} + +impl Default for wasmtime_component_val_t { + fn default() -> Self { + Self::Bool(false) + } +} + +#[no_mangle] +pub extern "C" fn wasmtime_component_val_new() -> Box { + Box::new(wasmtime_component_val_t::default()) +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_component_val_delete(_: Box) {} + +pub type wasmtime_component_kind_t = u8; +pub const WASMTIME_COMPONENT_KIND_BOOL: wasmtime_component_kind_t = 0; +pub const WASMTIME_COMPONENT_KIND_S8: wasmtime_component_kind_t = 1; +pub const WASMTIME_COMPONENT_KIND_U8: wasmtime_component_kind_t = 2; +pub const WASMTIME_COMPONENT_KIND_S16: wasmtime_component_kind_t = 3; +pub const WASMTIME_COMPONENT_KIND_U16: wasmtime_component_kind_t = 4; +pub const WASMTIME_COMPONENT_KIND_S32: wasmtime_component_kind_t = 5; +pub const WASMTIME_COMPONENT_KIND_U32: wasmtime_component_kind_t = 6; +pub const WASMTIME_COMPONENT_KIND_S64: wasmtime_component_kind_t = 7; +pub const WASMTIME_COMPONENT_KIND_U64: wasmtime_component_kind_t = 8; +pub const WASMTIME_COMPONENT_KIND_F32: wasmtime_component_kind_t = 9; +pub const WASMTIME_COMPONENT_KIND_F64: wasmtime_component_kind_t = 10; +pub const WASMTIME_COMPONENT_KIND_CHAR: wasmtime_component_kind_t = 11; +pub const WASMTIME_COMPONENT_KIND_STRING: wasmtime_component_kind_t = 12; +pub const WASMTIME_COMPONENT_KIND_LIST: wasmtime_component_kind_t = 13; +pub const WASMTIME_COMPONENT_KIND_RECORD: wasmtime_component_kind_t = 14; +pub const WASMTIME_COMPONENT_KIND_TUPLE: wasmtime_component_kind_t = 15; +pub const WASMTIME_COMPONENT_KIND_VARIANT: wasmtime_component_kind_t = 16; +pub const WASMTIME_COMPONENT_KIND_ENUM: wasmtime_component_kind_t = 17; +pub const WASMTIME_COMPONENT_KIND_OPTION: wasmtime_component_kind_t = 18; +pub const WASMTIME_COMPONENT_KIND_RESULT: wasmtime_component_kind_t = 19; +pub const WASMTIME_COMPONENT_KIND_FLAGS: wasmtime_component_kind_t = 20; + +#[repr(transparent)] +pub struct wasmtime_component_t { + component: Component, +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_component_from_binary( + engine: &wasm_engine_t, + bytes: *const u8, + len: usize, + out: &mut *mut wasmtime_component_t, +) -> Option> { + let bytes = crate::slice_from_raw_parts(bytes, len); + handle_result(Component::from_binary(&engine.engine, bytes), |component| { + *out = Box::into_raw(Box::new(wasmtime_component_t { component })); + }) +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_component_delete(_: Box) {} + +#[repr(C)] +pub struct wasmtime_component_linker_t { + linker: Linker, +} + +#[no_mangle] +pub extern "C" fn wasmtime_component_linker_new( + engine: &wasm_engine_t, +) -> Box { + Box::new(wasmtime_component_linker_t { + linker: Linker::new(&engine.engine), + }) +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_component_linker_delete(_: Box) {} + +#[no_mangle] +pub extern "C" fn wasmtime_component_linker_instantiate( + linker: &wasmtime_component_linker_t, + store: WasmtimeStoreContextMut<'_>, + component: &wasmtime_component_t, + out: &mut *mut wasmtime_component_instance_t, +) -> Option> { + match linker.linker.instantiate(store, &component.component) { + Ok(instance) => { + *out = Box::into_raw(Box::new(wasmtime_component_instance_t { instance })); + None + } + Err(e) => Some(Box::new(wasmtime_error_t::from(e))), + } +} + +#[repr(transparent)] +pub struct wasmtime_component_instance_t { + instance: Instance, +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_component_instance_get_func( + instance: &wasmtime_component_instance_t, + context: WasmtimeStoreContextMut<'_>, + name: *const u8, + len: usize, + item: &mut *mut wasmtime_component_func_t, +) -> bool { + let name = crate::slice_from_raw_parts(name, len); + let name = match std::str::from_utf8(name) { + Ok(name) => name, + Err(_) => return false, + }; + let func = instance.instance.get_func(context, name); + if let Some(func) = func { + *item = Box::into_raw(Box::new(wasmtime_component_func_t { func })); + } + func.is_some() +} + +#[repr(transparent)] +pub struct wasmtime_component_func_t { + func: Func, +} + +fn call_func( + func: &wasmtime_component_func_t, + mut context: WasmtimeStoreContextMut<'_>, + raw_params: &[wasmtime_component_val_t], + raw_results: &mut [wasmtime_component_val_t], +) -> Result<()> { + let params_types = func.func.params(context.as_context()); + if params_types.len() != raw_params.len() { + bail!( + "called with {} parameters instead of the expected {}", + raw_params.len(), + params_types.len() + ); + } + let results_types = func.func.results(context.as_context()); + if results_types.len() != raw_results.len() { + bail!( + "returns {} results instead of the expected {}", + raw_results.len(), + results_types.len() + ); + } + let params = func + .func + .params(context.as_context()) + .iter() + .zip(raw_params.iter()) + .map(|(ty, v)| TypedCVal(v, &ty.1).try_into()) + .collect::>>()?; + let mut results = vec![Val::Bool(false); raw_results.len()]; + func.func + .call(context.as_context_mut(), ¶ms, &mut results)?; + func.func.post_return(context)?; + for (i, (ty, r)) in results_types.iter().zip(results.iter()).enumerate() { + raw_results[i] = (r, ty).try_into()?; + } + Ok(()) +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_component_func_call( + func: &wasmtime_component_func_t, + context: WasmtimeStoreContextMut<'_>, + params: *const wasmtime_component_val_t, + params_len: usize, + results: *mut wasmtime_component_val_t, + results_len: usize, + out_trap: &mut *mut wasm_trap_t, +) -> Option> { + let raw_params = crate::slice_from_raw_parts(params, params_len); + let mut raw_results = crate::slice_from_raw_parts_mut(results, results_len); + match call_func(func, context, &raw_params, &mut raw_results) { + Ok(_) => None, + Err(e) => handle_call_error(e, out_trap), + } +} + +#[cfg(test)] +mod tests { + use crate::{ + wasmtime_component_val_flags_set, wasmtime_component_val_flags_t, + wasmtime_component_val_flags_test, + }; + + #[test] + fn bit_fiddling() { + let mut flags: wasmtime_component_val_flags_t = Vec::new().into(); + wasmtime_component_val_flags_set(&mut flags, 1, true); + assert!(wasmtime_component_val_flags_test(&flags, 1)); + assert!(!wasmtime_component_val_flags_test(&flags, 0)); + wasmtime_component_val_flags_set(&mut flags, 260, true); + assert!(wasmtime_component_val_flags_test(&flags, 260)); + assert!(!wasmtime_component_val_flags_test(&flags, 261)); + assert!(!wasmtime_component_val_flags_test(&flags, 259)); + assert!(wasmtime_component_val_flags_test(&flags, 1)); + assert!(!wasmtime_component_val_flags_test(&flags, 0)); + } +} diff --git a/crates/c-api/src/lib.rs b/crates/c-api/src/lib.rs index 9537407f2e6f..c5db0ddc8579 100644 --- a/crates/c-api/src/lib.rs +++ b/crates/c-api/src/lib.rs @@ -15,6 +15,7 @@ #![expect(non_camel_case_types, reason = "matching C style, not Rust")] pub use wasmtime; +use wasmtime::Trap; mod config; mod engine; @@ -70,6 +71,11 @@ mod wat2wasm; #[cfg(feature = "wat")] pub use crate::wat2wasm::*; +#[cfg(feature = "component-model")] +mod component; +#[cfg(feature = "component-model")] +pub use crate::component::*; + /// Initialize a `MaybeUninit` /// /// TODO: Replace calls to this function with @@ -121,6 +127,18 @@ unsafe fn slice_from_raw_parts_mut<'a, T>(ptr: *mut T, len: usize) -> &'a mut [T } } +pub(crate) fn handle_call_error( + err: wasmtime::Error, + trap_ret: &mut *mut wasm_trap_t, +) -> Option> { + if err.is::() { + *trap_ret = Box::into_raw(Box::new(wasm_trap_t::new(err))); + None + } else { + Some(Box::new(wasmtime_error_t::from(err))) + } +} + pub(crate) fn abort(name: &str) -> ! { eprintln!("`{name}` is not implemented"); std::process::abort(); diff --git a/crates/c-api/src/vec.rs b/crates/c-api/src/vec.rs index 7a5aae734ffa..b2765423699d 100644 --- a/crates/c-api/src/vec.rs +++ b/crates/c-api/src/vec.rs @@ -103,6 +103,15 @@ macro_rules! declare_vecs { } } + impl$(<$lt>)? Default for $name $(<$lt>)? { + fn default() -> Self { + Self { + size: 0, + data: ptr::null_mut() + } + } + } + #[no_mangle] pub extern "C" fn $empty(out: &mut $name) { out.size = 0; @@ -249,3 +258,5 @@ declare_vecs! { delete: wasm_extern_vec_delete, ) } + +pub(crate) use declare_vecs; diff --git a/crates/wasmtime/src/runtime/component/types.rs b/crates/wasmtime/src/runtime/component/types.rs index 0d63bd664625..20964dfb1651 100644 --- a/crates/wasmtime/src/runtime/component/types.rs +++ b/crates/wasmtime/src/runtime/component/types.rs @@ -663,7 +663,8 @@ impl Type { } } - fn desc(&self) -> &'static str { + /// Return a string description of this type + pub fn desc(&self) -> &'static str { match self { Type::Bool => "bool", Type::S8 => "s8", From e1d18808de690a51535ff35388ec9e9f4084ec79 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Lafay Date: Tue, 10 Dec 2024 12:08:16 +0100 Subject: [PATCH 2/4] Allow definition of host callbacks in c-api component-model --- crates/c-api/include/wasmtime/component.h | 62 ++ crates/c-api/src/component.rs | 913 +++++++++++++++++++++- 2 files changed, 970 insertions(+), 5 deletions(-) diff --git a/crates/c-api/include/wasmtime/component.h b/crates/c-api/include/wasmtime/component.h index 99caaaed18ab..c074da38a3e9 100644 --- a/crates/c-api/include/wasmtime/component.h +++ b/crates/c-api/include/wasmtime/component.h @@ -53,6 +53,8 @@ typedef uint8_t wasmtime_component_kind_t; typedef struct wasmtime_component_val_t wasmtime_component_val_t; typedef struct wasmtime_component_val_record_field_t wasmtime_component_val_record_field_t; +typedef struct wasmtime_component_type_t wasmtime_component_type_t; +typedef struct wasmtime_component_type_field_t wasmtime_component_type_field_t; #define WASMTIME_COMPONENT_DECLARE_VEC(name, element) \ typedef struct wasmtime_component_##name##_t { \ @@ -85,6 +87,16 @@ WASMTIME_COMPONENT_DECLARE_VEC(val_record, wasmtime_component_val_record_field_t WASMTIME_COMPONENT_DECLARE_VEC(val_flags, uint32_t); WASMTIME_COMPONENT_DECLARE_VEC_NEW(val_flags, uint32_t); +/// \brief A vector of types +WASMTIME_COMPONENT_DECLARE_VEC(type_vec, wasmtime_component_type_t); + +/// \brief A vector of field types +WASMTIME_COMPONENT_DECLARE_VEC(type_field_vec, wasmtime_component_type_field_t); + +/// \brief A vector of strings +WASMTIME_COMPONENT_DECLARE_VEC(string_vec, wasm_name_t); +WASMTIME_COMPONENT_DECLARE_VEC_NEW(string_vec, wasm_name_t); + #undef WASMTIME_COMPONENT_DECLARE_VEC // A variant contains the discriminant index and an optional value that is held. @@ -157,6 +169,43 @@ wasmtime_component_val_t* wasmtime_component_val_new(); void wasmtime_component_val_delete(wasmtime_component_val_t* val); +typedef struct wasmtime_component_type_field_t { + wasm_name_t name; + wasmtime_component_type_t* ty; +} wasmtime_component_type_field_t; + +WASMTIME_COMPONENT_DECLARE_VEC_NEW(type_field_vec, wasmtime_component_type_field_t); + +typedef struct wasmtime_component_type_result_t { + wasmtime_component_type_t* ok_ty; + wasmtime_component_type_t* err_ty; +} wasmtime_component_type_result_t; + +typedef union wasmtime_component_type_payload_t +{ + wasmtime_component_type_t* list; + wasmtime_component_type_field_vec_t record; + wasmtime_component_type_vec_t tuple; + wasmtime_component_type_field_vec_t variant; + wasmtime_component_string_vec_t enumeration; + wasmtime_component_type_t* option; + wasmtime_component_type_result_t result; + wasmtime_component_string_vec_t flags; +} wasmtime_component_type_payload_t; + +typedef struct wasmtime_component_type_t { + wasmtime_component_kind_t kind; + wasmtime_component_type_payload_t payload; +} wasmtime_component_type_t; + +WASMTIME_COMPONENT_DECLARE_VEC_NEW(type_vec, wasmtime_component_type_t); + +#undef WASMTIME_COMPONENT_DECLARE_VEC_NEW + +wasmtime_component_type_t* wasmtime_component_type_new(); + +void wasmtime_component_type_delete(wasmtime_component_type_t* ty); + typedef struct wasmtime_component_t wasmtime_component_t; wasmtime_error_t * @@ -176,6 +225,19 @@ typedef struct wasmtime_component_instance_t wasmtime_component_instance_t; // declaration from store.h typedef struct wasmtime_context wasmtime_context_t; +typedef wasm_trap_t *(*wasmtime_component_func_callback_t)( + void *env, wasmtime_context_t *context, const wasmtime_component_val_t *args, + size_t nargs, wasmtime_component_val_t *results, size_t nresults); + +wasmtime_error_t *wasmtime_component_linker_define_func( + wasmtime_component_linker_t *linker, const char *path, size_t path_len, + const char *name, size_t name_len, + wasmtime_component_type_t* params_types_buf, size_t params_types_len, + wasmtime_component_type_t* outputs_types_buf, size_t outputs_types_len, + wasmtime_component_func_callback_t cb, void *data, void (*finalizer)(void *)); + +wasmtime_error_t *wasmtime_component_linker_build(wasmtime_component_linker_t *linker); + wasmtime_error_t *wasmtime_component_linker_instantiate( const wasmtime_component_linker_t *linker, wasmtime_context_t *context, const wasmtime_component_t *component, wasmtime_component_instance_t **instance_out); diff --git a/crates/c-api/src/component.rs b/crates/c-api/src/component.rs index 85c2f1e9d174..eb723e2d4517 100644 --- a/crates/c-api/src/component.rs +++ b/crates/c-api/src/component.rs @@ -1,13 +1,16 @@ -use anyhow::{bail, ensure, Context, Result}; -use wasmtime::component::{Component, Func, Instance, Linker, Type, Val}; +use anyhow::{anyhow, bail, ensure, Context, Result}; +use wasmtime::component::{Component, Func, Instance, Linker, LinkerInstance, Type, Val}; use wasmtime::{AsContext, AsContextMut}; use crate::{ - declare_vecs, handle_call_error, handle_result, wasm_byte_vec_t, wasm_config_t, wasm_engine_t, - wasm_name_t, wasm_trap_t, wasmtime_error_t, WasmtimeStoreContextMut, WasmtimeStoreData, + bad_utf8, declare_vecs, handle_call_error, handle_result, to_str, wasm_byte_vec_t, + wasm_config_t, wasm_engine_t, wasm_name_t, wasm_trap_t, wasmtime_error_t, + WasmtimeStoreContextMut, WasmtimeStoreData, }; +use core::ffi::c_void; use std::collections::HashMap; -use std::{mem, mem::MaybeUninit, ptr, slice}; +use std::ops::Deref; +use std::{mem, mem::MaybeUninit, ptr, slice, str}; #[no_mangle] pub extern "C" fn wasmtime_config_component_model_set(c: &mut wasm_config_t, enable: bool) { @@ -346,6 +349,430 @@ impl TryFrom> for Val { } } +// a Val and its associated wasmtime_component_type_t (from the c_api) +struct CTypedVal<'a>(&'a Val, &'a CType); + +impl TryFrom> for wasmtime_component_val_t { + type Error = anyhow::Error; + fn try_from(value: CTypedVal) -> Result { + let (value, ty) = (value.0, value.1); + Ok(match value { + Val::Bool(v) => { + ensure_type!(ty, CType::Bool); + wasmtime_component_val_t::Bool(*v) + } + Val::S8(v) => { + ensure_type!(ty, CType::S8); + wasmtime_component_val_t::S8(*v) + } + Val::U8(v) => { + ensure_type!(ty, CType::U8); + wasmtime_component_val_t::U8(*v) + } + Val::S16(v) => { + ensure_type!(ty, CType::S16); + wasmtime_component_val_t::S16(*v) + } + Val::U16(v) => { + ensure_type!(ty, CType::U16); + wasmtime_component_val_t::U16(*v) + } + Val::S32(v) => { + ensure_type!(ty, CType::S32); + wasmtime_component_val_t::S32(*v) + } + Val::U32(v) => { + ensure_type!(ty, CType::U32); + wasmtime_component_val_t::U32(*v) + } + Val::S64(v) => { + ensure_type!(ty, CType::S64); + wasmtime_component_val_t::S64(*v) + } + Val::U64(v) => { + ensure_type!(ty, CType::U64); + wasmtime_component_val_t::U64(*v) + } + Val::Float32(v) => { + ensure_type!(ty, CType::F32); + wasmtime_component_val_t::F32(*v) + } + Val::Float64(v) => { + ensure_type!(ty, CType::F64); + wasmtime_component_val_t::F64(*v) + } + Val::Char(v) => { + ensure_type!(ty, CType::Char); + wasmtime_component_val_t::Char(*v) + } + Val::String(v) => { + ensure_type!(ty, CType::String); + wasmtime_component_val_t::String(v.clone().into_bytes().into()) + } + Val::List(vec) => { + if let CType::List(ty) = ty { + wasmtime_component_val_t::List( + vec.iter() + .map(|v| CTypedVal(v, ty.deref()).try_into()) + .collect::>>()? + .into(), + ) + } else { + bail!("attempted to create a List for a {}", ty.desc()); + } + } + Val::Record(vec) => { + if let CType::Record(ty) = ty { + let mut field_vals: HashMap<&str, &Val> = + HashMap::from_iter(vec.iter().map(|f| (f.0.as_str(), &f.1))); + + wasmtime_component_val_t::Record( + ty.iter() + .map(|(field_name, field_type)| { + match field_vals.remove(field_name.as_str()) { + Some(v) => Ok(wasmtime_component_val_record_field_t { + name: field_name.clone().into_bytes().into(), + val: CTypedVal(v, field_type).try_into()?, + }), + None => bail!("missing field {} in record", field_name), + } + }) + .collect::>>()? + .into(), + ) + } else { + bail!("attempted to create a Record for a {}", ty.desc()); + } + } + Val::Tuple(vec) => { + if let CType::Tuple(ty) = ty { + wasmtime_component_val_t::Tuple( + vec.iter() + .zip(ty.iter()) + .map(|(v, ty)| CTypedVal(v, ty).try_into()) + .collect::>>()? + .into(), + ) + } else { + bail!("attempted to create a Tuple for a {}", ty.desc()); + } + } + Val::Variant(case, val) => { + if let CType::Variant(ty) = ty { + let index = ty + .iter() + .position(|c| &c.0 == case) + .context(format!("case {case} not found in type"))?; + ensure!( + val.is_some() == ty[index].1.is_some(), + "mismatched variant case {} : value is {}, but type is {}", + case, + if val.is_some() { "some" } else { "none" }, + ty[index].1.as_ref().map(|ty| ty.desc()).unwrap_or("none") + ); + wasmtime_component_val_t::Variant(wasmtime_component_val_variant_t { + discriminant: index as u32, + val: match val { + Some(val) => Some(Box::new( + CTypedVal(val.deref(), ty[index].1.as_ref().unwrap()).try_into()?, + )), + None => None, + }, + }) + } else { + bail!("attempted to create a Variant for a {}", ty.desc()); + } + } + Val::Enum(v) => { + if let CType::Enum(ty) = ty { + let index = ty + .iter() + .position(|s| s == v) + .context(format!("enum value {v} not found in type"))?; + wasmtime_component_val_t::Enum(wasmtime_component_val_enum_t { + discriminant: index as u32, + }) + } else { + bail!("attempted to create a Enum for a {}", ty.desc()); + } + } + Val::Option(val) => { + if let CType::Option(ty) = ty { + wasmtime_component_val_t::Option(match val { + Some(val) => Some(Box::new(CTypedVal(val.deref(), ty.deref()).try_into()?)), + None => None, + }) + } else { + bail!("attempted to create a Option for a {}", ty.desc()); + } + } + Val::Result(val) => { + if let CType::Result(ok_type, err_type) = ty { + wasmtime_component_val_t::Result(match val { + Ok(Some(ok)) => { + let ok_type = ok_type + .as_ref() + .context("some ok result found instead of none")?; + wasmtime_component_val_result_t { + value: Some(Box::new( + CTypedVal(ok.deref(), ok_type.deref()).try_into()?, + )), + error: false, + } + } + Ok(None) => { + ensure!( + ok_type.is_none(), + "none ok result found instead of {}", + ok_type.as_ref().unwrap().desc() + ); + wasmtime_component_val_result_t { + value: None, + error: false, + } + } + Err(Some(err)) => { + let err_type = err_type + .as_ref() + .context("some err result found instead of none")?; + wasmtime_component_val_result_t { + value: Some(Box::new( + CTypedVal(err.deref(), err_type.deref()).try_into()?, + )), + error: true, + } + } + Err(None) => { + ensure!( + err_type.is_none(), + "none err result found instead of {}", + err_type.as_ref().unwrap().desc() + ); + wasmtime_component_val_result_t { + value: None, + error: true, + } + } + }) + } else { + bail!("attempted to create a Result for a {}", ty.desc()); + } + } + Val::Flags(vec) => { + if let CType::Flags(ty) = ty { + let mapping: HashMap<_, _> = ty.iter().zip(0u32..).collect(); + let mut flags: wasmtime_component_val_flags_t = Vec::new().into(); + for name in vec.iter() { + let idx = mapping.get(name).context("expected valid name")?; + wasmtime_component_val_flags_set(&mut flags, *idx, true); + } + wasmtime_component_val_t::Flags(flags) + } else { + bail!("attempted to create a Flags for a {}", ty.desc()); + } + } + Val::Resource(_) => bail!("resources not supported"), + }) + } +} + +// a wasmtime_component_val_t and its associated wasmtime_component_type_t +struct CTypedCVal<'a>(&'a wasmtime_component_val_t, &'a CType); + +impl TryFrom> for Val { + type Error = anyhow::Error; + fn try_from(value: CTypedCVal) -> Result { + let (value, ty) = (value.0, value.1); + Ok(match value { + &wasmtime_component_val_t::Bool(b) => { + ensure_type!(ty, CType::Bool); + Val::Bool(b) + } + &wasmtime_component_val_t::S8(v) => { + ensure_type!(ty, CType::S8); + Val::S8(v) + } + &wasmtime_component_val_t::U8(v) => { + ensure_type!(ty, CType::U8); + Val::U8(v) + } + &wasmtime_component_val_t::S16(v) => { + ensure_type!(ty, CType::S16); + Val::S16(v) + } + &wasmtime_component_val_t::U16(v) => { + ensure_type!(ty, CType::U16); + Val::U16(v) + } + &wasmtime_component_val_t::S32(v) => { + ensure_type!(ty, CType::S32); + Val::S32(v) + } + &wasmtime_component_val_t::U32(v) => { + ensure_type!(ty, CType::U32); + Val::U32(v) + } + &wasmtime_component_val_t::S64(v) => { + ensure_type!(ty, CType::S64); + Val::S64(v) + } + &wasmtime_component_val_t::U64(v) => { + ensure_type!(ty, CType::U64); + Val::U64(v) + } + &wasmtime_component_val_t::F32(v) => { + ensure_type!(ty, CType::F32); + Val::Float32(v) + } + &wasmtime_component_val_t::F64(v) => { + ensure_type!(ty, CType::F64); + Val::Float64(v) + } + &wasmtime_component_val_t::Char(v) => { + ensure_type!(ty, CType::Char); + Val::Char(v) + } + wasmtime_component_val_t::String(v) => { + ensure_type!(ty, CType::String); + Val::String(String::from_utf8(v.as_slice().to_vec())?) + } + wasmtime_component_val_t::List(v) => { + if let CType::List(ty) = ty { + Val::List( + v.as_slice() + .iter() + .map(|v| CTypedCVal(v, ty.deref()).try_into()) + .collect::>>()?, + ) + } else { + bail!("attempted to create a list for a {}", ty.desc()); + } + } + wasmtime_component_val_t::Record(v) => { + if let CType::Record(ty) = ty { + let mut field_vals: HashMap<&[u8], &wasmtime_component_val_t> = + HashMap::from_iter( + v.as_slice().iter().map(|f| (f.name.as_slice(), &f.val)), + ); + Val::Record( + ty.iter() + .map(|tyf| { + if let Some(v) = field_vals.remove(tyf.0.as_bytes()) { + Ok((tyf.0.clone(), CTypedCVal(v, &tyf.1).try_into()?)) + } else { + bail!("record missing field: {}", tyf.0); + } + }) + .collect::>>()?, + ) + } else { + bail!("attempted to create a record for a {}", ty.desc()); + } + } + wasmtime_component_val_t::Tuple(v) => { + if let CType::Tuple(ty) = ty { + Val::Tuple( + ty.iter() + .zip(v.as_slice().iter()) + .map(|(ty, v)| CTypedCVal(v, ty).try_into()) + .collect::>>()?, + ) + } else { + bail!("attempted to create a tuple for a {}", ty.desc()); + } + } + wasmtime_component_val_t::Variant(v) => { + if let CType::Variant(ty) = ty { + let index = v.discriminant as usize; + ensure!(index < ty.len(), "variant index outside range"); + let case = &ty[index]; + let case_name = case.0.clone(); + ensure!( + case.1.is_some() == v.val.is_some(), + "variant type mismatch for case {}: {} instead of {}", + case_name, + if v.val.is_some() { "some" } else { "none" }, + case.1.as_ref().map(|ty| ty.desc()).unwrap_or("none") + ); + if let (Some(t), Some(v)) = (&case.1, &v.val) { + let v = CTypedCVal(v.as_ref(), t.deref()).try_into()?; + Val::Variant(case_name, Some(Box::new(v))) + } else { + Val::Variant(case_name, None) + } + } else { + bail!("attempted to create a variant for a {}", ty.desc()); + } + } + wasmtime_component_val_t::Enum(v) => { + if let CType::Enum(ty) = ty { + let index = v.discriminant as usize; + ensure!(index < ty.as_slice().len(), "variant index outside range"); + Val::Enum(ty[index].clone()) + } else { + bail!("attempted to create an enum for a {}", ty.desc()); + } + } + wasmtime_component_val_t::Option(v) => { + if let CType::Option(ty) = ty { + Val::Option(match v { + Some(v) => Some(Box::new(CTypedCVal(v.as_ref(), ty.deref()).try_into()?)), + None => None, + }) + } else { + bail!("attempted to create an option for a {}", ty.desc()); + } + } + wasmtime_component_val_t::Result(v) => { + if let CType::Result(ok_ty, err_ty) = ty { + if v.error { + match &v.value { + Some(v) => { + let ty = err_ty.as_deref().context("expected err type")?; + Val::Result(Err(Some(Box::new( + CTypedCVal(v.as_ref(), ty).try_into()?, + )))) + } + None => { + ensure!(err_ty.is_none(), "expected no err type"); + Val::Result(Err(None)) + } + } + } else { + match &v.value { + Some(v) => { + let ty = ok_ty.as_deref().context("expected ok type")?; + Val::Result(Ok(Some(Box::new( + CTypedCVal(v.as_ref(), ty).try_into()?, + )))) + } + None => { + ensure!(ok_ty.is_none(), "expected no ok type"); + Val::Result(Ok(None)) + } + } + } + } else { + bail!("attempted to create a result for a {}", ty.desc()); + } + } + wasmtime_component_val_t::Flags(flags) => { + if let CType::Flags(ty) = ty { + let mut set = Vec::new(); + for (idx, name) in ty.iter().enumerate() { + if wasmtime_component_val_flags_test(&flags, idx as u32) { + set.push(name.clone()); + } + } + Val::Flags(set) + } else { + bail!("attempted to create a flags for a {}", ty.desc()); + } + } + }) + } +} + impl TryFrom<(&Val, &Type)> for wasmtime_component_val_t { type Error = anyhow::Error; @@ -565,6 +992,303 @@ pub const WASMTIME_COMPONENT_KIND_OPTION: wasmtime_component_kind_t = 18; pub const WASMTIME_COMPONENT_KIND_RESULT: wasmtime_component_kind_t = 19; pub const WASMTIME_COMPONENT_KIND_FLAGS: wasmtime_component_kind_t = 20; +#[repr(C)] +#[derive(Clone)] +pub struct wasmtime_component_type_field_t { + pub name: wasm_name_t, + pub ty: Option, +} + +impl Default for wasmtime_component_type_field_t { + fn default() -> Self { + Self { + name: Vec::new().into(), + ty: Default::default(), + } + } +} + +#[repr(C)] +#[derive(Clone)] +pub struct wasmtime_component_type_result_t { + pub ok_ty: Option>, + pub err_ty: Option>, +} + +declare_vecs! { + ( + name: wasmtime_component_type_vec_t, + ty: wasmtime_component_type_t, + new: wasmtime_component_type_vec_new, + empty: wasmtime_component_type_vec_new_empty, + uninit: wasmtime_component_type_vec_new_uninitialized, + copy: wasmtime_component_type_vec_copy, + delete: wasmtime_component_type_vec_delete, + ) + ( + name: wasmtime_component_type_field_vec_t, + ty: wasmtime_component_type_field_t, + new: wasmtime_component_type_field_vec_new, + empty: wasmtime_component_type_field_vec_new_empty, + uninit: wasmtime_component_type_field_vec_new_uninitialized, + copy: wasmtime_component_type_field_vec_copy, + delete: wasmtime_component_type_field_vec_delete, + ) + ( + name: wasmtime_component_string_vec_t, + ty: wasmtime_component_string_t, + new: wasmtime_component_string_vec_new, + empty: wasmtime_component_string_vec_new_empty, + uninit: wasmtime_component_string_vec_new_uninitialized, + copy: wasmtime_component_string_vec_copy, + delete: wasmtime_component_string_vec_delete, + ) +} + +#[repr(C, u8)] +#[derive(Clone)] +pub enum wasmtime_component_type_t { + Bool, + S8, + U8, + S16, + U16, + S32, + U32, + S64, + U64, + F32, + F64, + Char, + String, + List(Box), + Record(wasmtime_component_type_field_vec_t), + Tuple(wasmtime_component_type_vec_t), + Variant(wasmtime_component_type_field_vec_t), + Enum(wasmtime_component_string_vec_t), + Option(Box), + Result(wasmtime_component_type_result_t), + Flags(wasmtime_component_string_vec_t), +} + +impl Default for wasmtime_component_type_t { + fn default() -> Self { + Self::Bool + } +} + +#[no_mangle] +pub extern "C" fn wasmtime_component_type_new() -> Box { + Box::new(wasmtime_component_type_t::Bool) +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_component_type_delete(_: Box) {} + +#[derive(Clone)] +pub enum CType { + Bool, + S8, + U8, + S16, + U16, + S32, + U32, + S64, + U64, + F32, + F64, + Char, + String, + List(Box), + Record(Vec<(String, CType)>), + Tuple(Vec), + Variant(Vec<(String, Option>)>), + Enum(Vec), + Option(Box), + Result(Option>, Option>), + Flags(Vec), +} + +impl TryFrom<&wasmtime_component_type_t> for CType { + type Error = anyhow::Error; + + fn try_from(ty: &wasmtime_component_type_t) -> Result { + Ok(match ty { + wasmtime_component_type_t::Bool => CType::Bool, + wasmtime_component_type_t::S8 => CType::S8, + wasmtime_component_type_t::U8 => CType::U8, + wasmtime_component_type_t::S16 => CType::S16, + wasmtime_component_type_t::U16 => CType::U16, + wasmtime_component_type_t::S32 => CType::S32, + wasmtime_component_type_t::U32 => CType::U32, + wasmtime_component_type_t::S64 => CType::S64, + wasmtime_component_type_t::U64 => CType::U64, + wasmtime_component_type_t::F32 => CType::F32, + wasmtime_component_type_t::F64 => CType::F64, + wasmtime_component_type_t::Char => CType::Char, + wasmtime_component_type_t::String => CType::String, + wasmtime_component_type_t::List(ty) => CType::List(Box::new(ty.as_ref().try_into()?)), + wasmtime_component_type_t::Record(fields) => CType::Record( + fields + .as_slice() + .iter() + .map(|field| { + let field_name = String::from_utf8(field.name.as_slice().to_vec())?; + let field_type = match &field.ty { + Some(ty) => ty.try_into()?, + None => bail!("missing type of field {} in record", field_name), + }; + Ok((field_name, field_type)) + }) + .collect::>>()?, + ), + wasmtime_component_type_t::Tuple(types) => CType::Tuple( + types + .as_slice() + .iter() + .map(|ty| ty.try_into()) + .collect::>>()?, + ), + wasmtime_component_type_t::Variant(cases) => CType::Variant( + cases + .as_slice() + .iter() + .map(|case| { + let case_name = String::from_utf8(case.name.as_slice().to_vec())?; + let case_type = match &case.ty { + Some(ty) => Some(Box::new(ty.try_into()?)), + None => None, + }; + Ok((case_name, case_type)) + }) + .collect::>>()?, + ), + wasmtime_component_type_t::Enum(enums) => CType::Enum( + enums + .as_slice() + .iter() + .map(|s| Ok(String::from_utf8(s.as_slice().to_vec())?)) + .collect::>>()?, + ), + wasmtime_component_type_t::Option(ty) => { + CType::Option(Box::new(ty.as_ref().try_into()?)) + } + wasmtime_component_type_t::Result(wasmtime_component_type_result_t { + ok_ty, + err_ty, + }) => CType::Result( + match ok_ty { + Some(ty) => Some(Box::new(ty.as_ref().try_into()?)), + None => None, + }, + match err_ty { + Some(ty) => Some(Box::new(ty.as_ref().try_into()?)), + None => None, + }, + ), + wasmtime_component_type_t::Flags(flags) => CType::Flags( + flags + .as_slice() + .iter() + .map(|s| Ok(String::from_utf8(s.as_slice().to_vec())?)) + .collect::>>()?, + ), + }) + } +} + +impl CType { + /// Return a string description of this type + fn desc(&self) -> &'static str { + match self { + CType::Bool => "bool", + CType::S8 => "s8", + CType::U8 => "u8", + CType::S16 => "s16", + CType::U16 => "u16", + CType::S32 => "s32", + CType::U32 => "u32", + CType::S64 => "s64", + CType::U64 => "u64", + CType::F32 => "f32", + CType::F64 => "f64", + CType::Char => "char", + CType::String => "string", + CType::List(_) => "list", + CType::Record(_) => "record", + CType::Tuple(_) => "tuple", + CType::Variant(_) => "variant", + CType::Enum(_) => "enum", + CType::Option(_) => "option", + CType::Result(_, _) => "result", + CType::Flags(_) => "flags", + } + } + + fn default_cval(&self) -> wasmtime_component_val_t { + match self { + CType::Bool => wasmtime_component_val_t::Bool(false), + CType::S8 => wasmtime_component_val_t::S8(0), + CType::U8 => wasmtime_component_val_t::U8(0), + CType::S16 => wasmtime_component_val_t::S16(0), + CType::U16 => wasmtime_component_val_t::U16(0), + CType::S32 => wasmtime_component_val_t::S32(0), + CType::U32 => wasmtime_component_val_t::U32(0), + CType::S64 => wasmtime_component_val_t::S64(0), + CType::U64 => wasmtime_component_val_t::U64(0), + CType::F32 => wasmtime_component_val_t::F32(0.0), + CType::F64 => wasmtime_component_val_t::F64(0.0), + CType::Char => wasmtime_component_val_t::Char('\0'), + CType::String => { + wasmtime_component_val_t::String(wasmtime_component_string_t::default()) + } + CType::List(_) => { + wasmtime_component_val_t::List(wasmtime_component_val_vec_t::default()) + } + CType::Record(fields) => wasmtime_component_val_t::Record( + fields + .iter() + .map(|(name, ty)| wasmtime_component_val_record_field_t { + name: name.clone().into_bytes().into(), + val: ty.default_cval(), + }) + .collect::>() + .into(), + ), + CType::Tuple(tuple) => wasmtime_component_val_t::Tuple( + tuple + .iter() + .map(|ty| ty.default_cval()) + .collect::>() + .into(), + ), + CType::Variant(cases) => { + wasmtime_component_val_t::Variant(wasmtime_component_val_variant_t { + discriminant: 0, + val: match &cases[0].1 { + Some(ty) => Some(Box::new(ty.default_cval())), + None => None, + }, + }) + } + CType::Enum(_) => { + wasmtime_component_val_t::Enum(wasmtime_component_val_enum_t { discriminant: 0 }) + } + CType::Option(_) => wasmtime_component_val_t::Option(None), + CType::Result(_, _) => { + wasmtime_component_val_t::Result(wasmtime_component_val_result_t { + value: None, + error: false, + }) + } + CType::Flags(_) => { + wasmtime_component_val_t::Flags(wasmtime_component_val_flags_t::default()) + } + } + } +} + #[repr(transparent)] pub struct wasmtime_component_t { component: Component, @@ -586,9 +1310,30 @@ pub unsafe extern "C" fn wasmtime_component_from_binary( #[no_mangle] pub unsafe extern "C" fn wasmtime_component_delete(_: Box) {} +pub type wasmtime_component_func_callback_t = extern "C" fn( + *mut c_void, + WasmtimeStoreContextMut<'_>, + *const wasmtime_component_val_t, + usize, + *mut wasmtime_component_val_t, + usize, +) -> Option>; + +struct HostFuncDefinition { + path: Vec, + name: String, + params_types: Vec, + outputs_types: Vec, + callback: wasmtime_component_func_callback_t, + data: *mut c_void, + finalizer: Option, +} + #[repr(C)] pub struct wasmtime_component_linker_t { linker: Linker, + is_built: bool, + functions: Vec, } #[no_mangle] @@ -597,12 +1342,165 @@ pub extern "C" fn wasmtime_component_linker_new( ) -> Box { Box::new(wasmtime_component_linker_t { linker: Linker::new(&engine.engine), + is_built: false, + functions: Vec::new(), }) } #[no_mangle] pub unsafe extern "C" fn wasmtime_component_linker_delete(_: Box) {} +fn to_ctype_vec(buf: *mut wasmtime_component_type_t, len: usize) -> Result> { + if len == 0 { + return Ok(Vec::new()); + } + let v = unsafe { crate::slice_from_raw_parts(buf, len) }; + v.iter().map(|t| t.try_into()).collect::>>() +} + +#[no_mangle] +pub unsafe extern "C" fn wasmtime_component_linker_define_func( + linker: &mut wasmtime_component_linker_t, + path_buf: *const u8, + path_len: usize, + name_buf: *const u8, + name_len: usize, + params_types_buf: *mut wasmtime_component_type_t, + params_types_len: usize, + outputs_types_buf: *mut wasmtime_component_type_t, + outputs_types_len: usize, + callback: wasmtime_component_func_callback_t, + data: *mut c_void, + finalizer: Option, +) -> Option> { + let path = to_str!(path_buf, path_len) + .split('.') + .filter(|s| s.len() > 0) + .map(|s| s.to_string()) + .collect::>(); + let name = to_str!(name_buf, name_len).to_string(); + let params_types = match to_ctype_vec(params_types_buf, params_types_len) { + Err(err) => return Some(Box::new(wasmtime_error_t::from(err))), + Ok(p) => p, + }; + let outputs_types = match to_ctype_vec(outputs_types_buf, outputs_types_len) { + Err(err) => return Some(Box::new(wasmtime_error_t::from(err))), + Ok(p) => p, + }; + + linker.functions.push(HostFuncDefinition { + path, + name, + params_types, + outputs_types, + callback, + data, + finalizer, + }); + None +} + +fn build_closure( + function: &HostFuncDefinition, +) -> impl Fn(WasmtimeStoreContextMut<'_>, &[Val], &mut [Val]) -> Result<()> { + let func = function.callback; + let params_types = function.params_types.clone(); + let outputs_types = function.outputs_types.clone(); + let foreign = crate::ForeignData { + data: function.data, + finalizer: function.finalizer, + }; + move |context, parameters, outputs| { + let _ = &foreign; + let _ = ¶ms_types; + let _ = &outputs_types; + let mut params = Vec::new(); + for param in parameters.iter().zip(params_types.iter()) { + params.push(CTypedVal(param.0, param.1).try_into()?); + } + let mut outs = Vec::new(); + for output_type in outputs_types.iter() { + outs.push(output_type.default_cval()); + } + let res = func( + foreign.data, + context, + params.as_ptr(), + params.len(), + outs.as_mut_ptr(), + outs.len(), + ); + match res { + None => { + for (i, (output, output_type)) in outs.iter().zip(outputs_types.iter()).enumerate() + { + outputs[i] = CTypedCVal(output, output_type).try_into()?; + } + Ok(()) + } + Some(trap) => Err(trap.error), + } + } +} + +#[no_mangle] +pub extern "C" fn wasmtime_component_linker_build( + linker: &mut wasmtime_component_linker_t, +) -> Option> { + if linker.is_built { + return Some(Box::new(wasmtime_error_t::from(anyhow!( + "cannot build an already built linker" + )))); + } + + struct InstanceTree { + children: HashMap, + functions: Vec, + } + + impl InstanceTree { + fn insert(&mut self, depth: usize, function: HostFuncDefinition) { + if function.path.len() == depth { + self.functions.push(function); + } else { + let child = self + .children + .entry(function.path[depth].to_string()) + .or_insert_with(|| InstanceTree { + children: HashMap::new(), + functions: Vec::new(), + }); + child.insert(depth + 1, function); + } + } + fn build(&self, mut instance: LinkerInstance) -> Result<()> { + for function in self.functions.iter() { + instance.func_new(&function.name, build_closure(function))?; + } + for (name, child) in self.children.iter() { + let child_instance = instance.instance(&name)?; + child.build(child_instance)?; + } + Ok(()) + } + } + + let mut root = InstanceTree { + children: HashMap::new(), + functions: Vec::new(), + }; + for function in linker.functions.drain(..) { + root.insert(0, function); + } + match root.build(linker.linker.root()) { + Ok(()) => { + linker.is_built = true; + None + } + Err(err) => Some(Box::new(wasmtime_error_t::from(anyhow!(err)))), + } +} + #[no_mangle] pub extern "C" fn wasmtime_component_linker_instantiate( linker: &wasmtime_component_linker_t, @@ -610,6 +1508,11 @@ pub extern "C" fn wasmtime_component_linker_instantiate( component: &wasmtime_component_t, out: &mut *mut wasmtime_component_instance_t, ) -> Option> { + if !linker.is_built && !linker.functions.is_empty() { + return Some(Box::new(wasmtime_error_t::from(anyhow!( + "cannot instantiate with a linker not built" + )))); + } match linker.linker.instantiate(store, &component.component) { Ok(instance) => { *out = Box::into_raw(Box::new(wasmtime_component_instance_t { instance })); From 9dbce5c16fa8611b70760cd10314350523fa8f41 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Lafay Date: Tue, 10 Dec 2024 12:09:30 +0100 Subject: [PATCH 3/4] Add documentation and example --- crates/c-api/include/wasmtime/component.h | 320 +++++++++++++++++++++- examples/CMakeLists.txt | 1 + examples/component/convert.wit | 12 + examples/component/main.c | 203 ++++++++++++++ examples/component/main.rs | 29 +- examples/component/wasm/guest.rs | 13 +- 6 files changed, 557 insertions(+), 21 deletions(-) create mode 100644 examples/component/main.c diff --git a/crates/c-api/include/wasmtime/component.h b/crates/c-api/include/wasmtime/component.h index c074da38a3e9..4daf02a3b77f 100644 --- a/crates/c-api/include/wasmtime/component.h +++ b/crates/c-api/include/wasmtime/component.h @@ -1,7 +1,7 @@ /** * The component model * - * TODO: Write some more documentation here like in the Rust API. + * Wasmtime APIs for interacting with WebAssembly Component Model. * */ @@ -25,32 +25,59 @@ extern "C" { */ WASMTIME_CONFIG_PROP(void, component_model, bool) -// The tag part of wasmtime_component_val_t that specifies what variant is -// populated in wasmtime_component_val_payload_t. +/** + * \brief The tag part of #wasmtime_component_val_t or #wasmtime_component_type_t + * + * Specifies what variant is populated in #wasmtime_component_val_payload_t + * or #wasmtime_component_type_payload_t. + */ typedef uint8_t wasmtime_component_kind_t; +/// \brief Value of #wasmtime_component_kind_t indicating a boolean #define WASMTIME_COMPONENT_KIND_BOOL 0 +/// \brief Value of #wasmtime_component_kind_t indicating a signed 8-bit integer #define WASMTIME_COMPONENT_KIND_S8 1 +/// \brief Value of #wasmtime_component_kind_t indicating an unsigned 8-bit integer #define WASMTIME_COMPONENT_KIND_U8 2 +/// \brief Value of #wasmtime_component_kind_t indicating a signed 16-bit integer #define WASMTIME_COMPONENT_KIND_S16 3 +/// \brief Value of #wasmtime_component_kind_t indicating an unsigned 16-bit integer #define WASMTIME_COMPONENT_KIND_U16 4 +/// \brief Value of #wasmtime_component_kind_t indicating a signed 32-bit integer #define WASMTIME_COMPONENT_KIND_S32 5 +/// \brief Value of #wasmtime_component_kind_t indicating an unsigned 32-bit integer #define WASMTIME_COMPONENT_KIND_U32 6 +/// \brief Value of #wasmtime_component_kind_t indicating a signed 64-bit integer #define WASMTIME_COMPONENT_KIND_S64 7 +/// \brief Value of #wasmtime_component_kind_t indicating an unsigned 64-bit integer #define WASMTIME_COMPONENT_KIND_U64 8 +/// \brief Value of #wasmtime_component_kind_t indicating an 32-bit floating point number #define WASMTIME_COMPONENT_KIND_F32 9 +/// \brief Value of #wasmtime_component_kind_t indicating an 64-bit floating point number #define WASMTIME_COMPONENT_KIND_F64 10 +/// \brief Value of #wasmtime_component_kind_t indicating a unicode character #define WASMTIME_COMPONENT_KIND_CHAR 11 +/// \brief Value of #wasmtime_component_kind_t indicating a unicode string #define WASMTIME_COMPONENT_KIND_STRING 12 +/// \brief Value of #wasmtime_component_kind_t indicating a list #define WASMTIME_COMPONENT_KIND_LIST 13 +/// \brief Value of #wasmtime_component_kind_t indicating a record #define WASMTIME_COMPONENT_KIND_RECORD 14 +/// \brief Value of #wasmtime_component_kind_t indicating a tuple #define WASMTIME_COMPONENT_KIND_TUPLE 15 +/// \brief Value of #wasmtime_component_kind_t indicating a variant #define WASMTIME_COMPONENT_KIND_VARIANT 16 +/// \brief Value of #wasmtime_component_kind_t indicating an enum #define WASMTIME_COMPONENT_KIND_ENUM 17 +/// \brief Value of #wasmtime_component_kind_t indicating an option #define WASMTIME_COMPONENT_KIND_OPTION 18 +/// \brief Value of #wasmtime_component_kind_t indicating a result #define WASMTIME_COMPONENT_KIND_RESULT 19 +/// \brief Value of #wasmtime_component_kind_t indicating a set of flags #define WASMTIME_COMPONENT_KIND_FLAGS 20 + +// forward declarations typedef struct wasmtime_component_val_t wasmtime_component_val_t; typedef struct wasmtime_component_val_record_field_t wasmtime_component_val_record_field_t; typedef struct wasmtime_component_type_t wasmtime_component_type_t; @@ -99,102 +126,206 @@ WASMTIME_COMPONENT_DECLARE_VEC_NEW(string_vec, wasm_name_t); #undef WASMTIME_COMPONENT_DECLARE_VEC -// A variant contains the discriminant index and an optional value that is held. +/// \brief Representation of a variant value typedef struct wasmtime_component_val_variant_t { + /// \brief Discriminant indicating the index of the variant case of this value uint32_t discriminant; + /// \brief #wasmtime_component_val_t value of the variant case of this value if it has one wasmtime_component_val_t *val; } wasmtime_component_val_variant_t; -// A result is an either type holding a value and a bit if is it an ok or error -// variant. +/** + * \brief Representation of a result value + * + * A result is an either type holding an ok value or an error + */ typedef struct wasmtime_component_val_result_t { + /// \brief Value of the result, either of the ok type or of the error type, depending on #error wasmtime_component_val_t *val; + /// \brief Discriminant indicating if this result is an ok value or an error bool error; } wasmtime_component_val_result_t; -// Which value within an enumeration is selected. +/// \brief Representation of an enum value typedef struct wasmtime_component_val_enum_t { + /// \brief Discriminant indicating the index of this value in the enum uint32_t discriminant; } wasmtime_component_val_enum_t; +/** + * \brief Container for different kind of component model value data + * + * Setting any dynamic data to one of this field means that the payload now owns it + */ typedef union wasmtime_component_val_payload_t { + /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_BOOLEAN bool boolean; + /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_S8 int8_t s8; + /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_U8 uint8_t u8; + /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_S16 int16_t s16; + /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_U16 uint16_t u16; + /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_S32 int32_t s32; + /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_U32 uint32_t u32; + /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_S64 int64_t s64; + /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_U64 uint64_t u64; + /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_F32 float f32; + /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_F64 double f64; + /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_CHARACTER uint8_t character; + /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_STRING wasm_name_t string; + /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_LIST wasmtime_component_val_vec_t list; + /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_RECORD wasmtime_component_val_record_t record; + /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_TUPLE wasmtime_component_val_vec_t tuple; + /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_VARIANT wasmtime_component_val_variant_t variant; + /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_ENUMERATION wasmtime_component_val_enum_t enumeration; + /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_OPTION wasmtime_component_val_t *option; + /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_RESULT wasmtime_component_val_result_t result; + /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_FLAGS wasmtime_component_val_flags_t flags; } wasmtime_component_val_payload_t; -// The tagged union for a value within the component model. +/** + * \brief Representation of a component model value + * + * Many kind of values own data, so those need to be properly dropped in wasmtime, + * and therefore should not live on the stack. + */ typedef struct wasmtime_component_val_t { + /// \brief Discriminant indicating which field of #payload is valid wasmtime_component_kind_t kind; + /// \brief Container for the value data wasmtime_component_val_payload_t payload; } wasmtime_component_val_t; WASMTIME_COMPONENT_DECLARE_VEC_NEW(val_vec, wasmtime_component_val_t); -// A record is a series of named fields, which are values with a string name. +/// \brief Representation of record field. typedef struct wasmtime_component_val_record_field_t { + /// \brief Name of the field wasm_name_t name; + /// \brief Value of the field wasmtime_component_val_t val; } wasmtime_component_val_record_field_t; WASMTIME_COMPONENT_DECLARE_VEC_NEW(val_record, wasmtime_component_val_record_field_t); -// Set a value within this bitset. -// -// If this bit set is too small to hold a value at `index` it will be resized. +/** + * \brief Sets the value of a flag within within a #wasmtime_component_val_flags_t. + * + * If this bit set is too small to hold a value at `index` it will be resized. + + * \param flags the #wasmtime_component_val_flags_t to modify + * \param index the index of the flag to modify + * \param enabled the value to set the flag to + */ void wasmtime_component_val_flags_set(wasmtime_component_val_flags_t *flags, uint32_t index, bool enabled); -// Test if this bitset holds a value at `index`. +/** + * \brief Tests the value of a flag within within a #wasmtime_component_val_flags_t. + * + * If this bit set is too small to hold a value at `index` it will be resized. + + * \param flags the #wasmtime_component_val_flags_t to test + * \param index the index of the flag to test + * \return true if the flag is set, else false + */ bool wasmtime_component_val_flags_test(const wasmtime_component_val_flags_t* flags, uint32_t index); +/** + * \brief Creates a new #wasmtime_component_val_t + * + * This is usually used to create inner values, typically as part of an option or result. + * In this case, ownership is given out to the outer value. + * + * In case where a top level #wasmtime_component_val_t is created (for example to be passed directly + * to #wasmtime_component_func_call), then it should be deleted with #wasmtime_component_val_delete + * + * \return a pointer to the newly created #wasmtime_component_val_t + */ wasmtime_component_val_t* wasmtime_component_val_new(); +/** + * \brief Deletes a #wasmtime_component_val_t previously created by #wasmtime_component_val_new + * + * This should not be called if the value has been given out as part of an outer #wasmtime_component_val_t + * + * \param val the #wasmtime_component_val_t to delete + */ void wasmtime_component_val_delete(wasmtime_component_val_t* val); +/** + * \brief Representation of a field in a record type or a case in a variant type + */ typedef struct wasmtime_component_type_field_t { + /// \brief Name of the record field or variant case wasm_name_t name; + /// \brief Type of the record field or variant case (may be null for variant case) wasmtime_component_type_t* ty; } wasmtime_component_type_field_t; WASMTIME_COMPONENT_DECLARE_VEC_NEW(type_field_vec, wasmtime_component_type_field_t); +/** + * \brief Representation of a result type + */ typedef struct wasmtime_component_type_result_t { + /// \brief Type of the ok value (if there is one) wasmtime_component_type_t* ok_ty; + /// \brief Type of the error value (if there is one) wasmtime_component_type_t* err_ty; } wasmtime_component_type_result_t; +/** + * \brief Container for different kind of component model type data + */ typedef union wasmtime_component_type_payload_t { + /// \brief Field used if #wasmtime_component_type_t::kind is #WASMTIME_COMPONENT_KIND_LIST wasmtime_component_type_t* list; + /// \brief Field used if #wasmtime_component_type_t::kind is #WASMTIME_COMPONENT_KIND_RECORD wasmtime_component_type_field_vec_t record; + /// \brief Field used if #wasmtime_component_type_t::kind is #WASMTIME_COMPONENT_KIND_TUPLE wasmtime_component_type_vec_t tuple; + /// \brief Field used if #wasmtime_component_type_t::kind is #WASMTIME_COMPONENT_KIND_VARIANT wasmtime_component_type_field_vec_t variant; + /// \brief Field used if #wasmtime_component_type_t::kind is #WASMTIME_COMPONENT_KIND_ENUM wasmtime_component_string_vec_t enumeration; + /// \brief Field used if #wasmtime_component_type_t::kind is #WASMTIME_COMPONENT_KIND_OPTION wasmtime_component_type_t* option; + /// \brief Field used if #wasmtime_component_type_t::kind is #WASMTIME_COMPONENT_KIND_RESULT wasmtime_component_type_result_t result; + /// \brief Field used if #wasmtime_component_type_t::kind is #WASMTIME_COMPONENT_KIND_FLAGS wasmtime_component_string_vec_t flags; } wasmtime_component_type_payload_t; +/** + * \brief Representation of a component model type + * + * Many kind of types own data, so those need to be properly dropped in wasmtime, + * and therefore should not live on the stack. + */ typedef struct wasmtime_component_type_t { + /// \brief Discriminant indicating what kind of type it is, and which field of #payload is valid, if any wasmtime_component_kind_t kind; + /// \brief Container for the type data, if any wasmtime_component_type_payload_t payload; } wasmtime_component_type_t; @@ -202,33 +333,142 @@ WASMTIME_COMPONENT_DECLARE_VEC_NEW(type_vec, wasmtime_component_type_t); #undef WASMTIME_COMPONENT_DECLARE_VEC_NEW +/** + * \brief Creates a new #wasmtime_component_type_t + * + * This is usually used to create inner types, typically as part of an option or result. + * In this case, ownership is given out to the outer value. + * + * In case where a top level #wasmtime_component_type_t is created (for example to be passed directly to + * #wasmtime_component_linker_define_func), then it should be deleted with #wasmtime_component_type_delete + * + * \return a pointer to the newly created #wasmtime_component_type_t + */ wasmtime_component_type_t* wasmtime_component_type_new(); +/** + * \brief Deletes a #wasmtime_component_type_t previously created by #wasmtime_component_type_new + * + * This should not be called if the type has been given out as part of an outer #wasmtime_component_type_t + * + * \param val the #wasmtime_component_type_t to delete + */ void wasmtime_component_type_delete(wasmtime_component_type_t* ty); +/** + * \brief Representation of a component in the component model. + */ typedef struct wasmtime_component_t wasmtime_component_t; +/** + * \brief Compiles a WebAssembly binary into a #wasmtime_component_t + * + * This function will compile a WebAssembly binary into an owned #wasmtime_component_t. + * + * It requires a component binary, such as what is produced by Rust `cargo component` tooling. + * + * This function does not take ownership of any of its arguments, but the + * returned error and component are owned by the caller. + + * \param engine the #wasm_engine_t that will create the component + * \param buf the address of the buffer containing the WebAssembly binary + * \param len the length of the buffer containing the WebAssembly binary + * \param component_out on success, contains the address of the created component + * \return NULL on success, else a #wasmtime_error_t describing the error + */ wasmtime_error_t * wasmtime_component_from_binary(const wasm_engine_t *engine, const uint8_t *buf, size_t len, wasmtime_component_t **component_out); +/** + * \brief Deletes a #wasmtime_component_t created by #wasmtime_component_from_binary + * + * \param component the component to delete + */ void wasmtime_component_delete(wasmtime_component_t *component); +/** + * \brief Representation of a component linker + * + * This type corresponds to a `wasmtime::component::Linker`. + * + * Due to the interaction between `wasmtime::component::Linker` and + * `wasmtime::component::LinkerInstance`, the latter being more of a builder, + * it is expected to first define the host functions through calls to + * #wasmtime_component_linker_define_func, then call #wasmtime_component_linker_build + * to create and populate the root `wasmtime::component::LinkerInstance` and it's + * descendants (if any). + */ typedef struct wasmtime_component_linker_t wasmtime_component_linker_t; +/** + * \brief Creates a new #wasmtime_component_linker_t for the specified engine. + * + * \param engine the compilation environment and configuration + * \return a pointer to the newly created #wasmtime_component_linker_t + */ wasmtime_component_linker_t *wasmtime_component_linker_new(const wasm_engine_t *engine); +/** + * \brief Deletes a #wasmtime_component_linker_t created by #wasmtime_component_linker_new + * + * \param linker the #wasmtime_component_linker_t to delete + */ void wasmtime_component_linker_delete(wasmtime_component_linker_t *linker); +/// \brief Representation of a component instance typedef struct wasmtime_component_instance_t wasmtime_component_instance_t; // declaration from store.h typedef struct wasmtime_context wasmtime_context_t; +/** + * \brief Callback signature for #wasmtime_component_linker_define_func. + * + * This is the function signature for host functions that can be made accessible + * to WebAssembly components. The arguments to this function are: + * + * \param env a user-provided argument passed to #wasmtime_component_linker_define_func + * \param context a #wasmtime_context_t, the context of this call + * \param args the arguments provided to this function invocation + * \param nargs how many arguments are provided + * \param results where to write the results of this function + * \param nresults how many results must be produced + * + * Callbacks are guaranteed to get called with the right types of arguments, but + * they must produce the correct number and types of results. Failure to do so + * will cause traps to get raised on the wasm side. + * + * This callback can optionally return a #wasm_trap_t indicating that a trap + * should be raised in WebAssembly. It's expected that in this case the caller + * relinquishes ownership of the trap and it is passed back to the engine. + */ typedef wasm_trap_t *(*wasmtime_component_func_callback_t)( void *env, wasmtime_context_t *context, const wasmtime_component_val_t *args, size_t nargs, wasmtime_component_val_t *results, size_t nresults); +/** + * \brief Defines a host function in a linker + * + * Must be done before calling #wasmtime_component_linker_build + * + * Does not take ownership of the #wasmtime_component_type_t arguments. + * + * \param linker the #wasmtime_component_linker_t in which the function should be defined + * \param path the dot-separated path of the package where the function is defined + * \param path_len the byte length of `path` + * \param name the name of the function + * \param name_len the byte length of `name` + * \param params_types_buf a pointer to an array of #wasmtime_component_type_t describing the function's parameters + * \param params_types_len the length of `params_types_buf` + * \param outputs_types_buf a pointer to an array of #wasmtime_component_type_t describing the function's outputs + * \param outputs_types_len the length of `outputs_types_buf` + * \param cb a pointer to the actual functions, a #wasmtime_component_func_callback_t + * \param data the host-provided data to provide as the first argument to the callback + * \param finalizer an optional finalizer for the `data` argument. + * \return wasmtime_error_t* on success `NULL` is returned, otherwise an error is returned which + * describes why the definition failed. + */ wasmtime_error_t *wasmtime_component_linker_define_func( wasmtime_component_linker_t *linker, const char *path, size_t path_len, const char *name, size_t name_len, @@ -236,18 +476,72 @@ wasmtime_error_t *wasmtime_component_linker_define_func( wasmtime_component_type_t* outputs_types_buf, size_t outputs_types_len, wasmtime_component_func_callback_t cb, void *data, void (*finalizer)(void *)); +/** + * \brief Builds the linker, providing the host functions defined by calls to #wasmtime_component_linker_define_func + * + * \param linker the #wasmtime_component_linker_t to build + * \return wasmtime_error_t* On success `NULL` is returned, otherwise an error is returned which + * describes why the build failed. + */ wasmtime_error_t *wasmtime_component_linker_build(wasmtime_component_linker_t *linker); +/** + * \brief Instantiates a component instance in a given #wasmtime_context_t + * + * \param linker a #wasmtime_component_linker_t that will help provide host functions + * \param context the #wasmtime_context_t in which the instance should be created + * \param component the #wasmtime_component_t to instantiate + * \param instance_out on success, the instantiated #wasmtime_component_instance_t + * \return wasmtime_error_t* on success `NULL` is returned, otherwise an error is returned which + * describes why the build failed. + */ wasmtime_error_t *wasmtime_component_linker_instantiate( const wasmtime_component_linker_t *linker, wasmtime_context_t *context, const wasmtime_component_t *component, wasmtime_component_instance_t **instance_out); +/// \brief Representation of an exported function in Wasmtime component model. typedef struct wasmtime_component_func_t wasmtime_component_func_t; +/** + * \brief Looks for an exported function in the given component instance + * + * \param instance the #wasmtime_component_instance_t in which the function should be looked for + * \param context the #wasmtime_context_t that contains `instance` + * \param name the name of function to look for + * \param name_len the byte length of `name` + * \param item_out the wasmtime_component_func_t that was found, if any + * \return true if the function was found, else false + */ bool wasmtime_component_instance_get_func( const wasmtime_component_instance_t *instance, wasmtime_context_t *context, const char *name, size_t name_len, wasmtime_component_func_t **item_out); +/** + * \brief Calls an exported function of a component + * + * It is the responsibility of the caller to make sure that `params` has the expected + * length, and the correct types, else the call will error out. `results` must have the + * expected length, but the values will be written with the correct types. + * + * This can fail in two ways : either a non-NULL #wasmtime_error_t is returned, for example if the + * parameters are incorrect (and `trap_out` will be NULL), or the call may trap, in which case + * NULL is returned, but `trap_out` will be non-NULL. + * + * Does not take ownership of #wasmtime_component_val_t arguments. Gives ownership of + * #wasmtime_component_val_t results. As such, if those are data-owning values, they + * should be created and deleted through this api, either directly with #wasmtime_component_val_new, + * or through a #wasmtime_component_val_vec_t, using #wasmtime_component_val_vec_new and + * #wasmtime_component_val_vec_delete + * + * \param func the function to call, typically found with #wasmtime_component_instance_get_func + * \param context the #wasmtime_context_t that contains `func` + * \param params the parameters of `func`, as an array of #wasmtime_component_val_t + * \param params_len the length of `params` + * \param results the results of `func`, as an array of #wasmtime_component_val_t that will be written + * \param results_len the length of `results` + * \param trap_out NULL if the call completed successfully or couldn't be made, otherwise the trap that was raised + * \return wasmtime_error_t* NULL on success or a description of the error calling the function + */ wasmtime_error_t *wasmtime_component_func_call( const wasmtime_component_func_t *func, wasmtime_context_t *context, const wasmtime_component_val_t *params, size_t params_len, diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 84a02e46fb56..927b66d57867 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -58,6 +58,7 @@ create_target(multimemory multimemory.c) create_target(serialize serialize.c) create_target(threads threads.c) create_target(wasi wasi/main.c) +create_target(component component/main.c) # Add rust tests create_rust_test(anyref) diff --git a/examples/component/convert.wit b/examples/component/convert.wit index 52f7c012516a..a41d901eaeef 100644 --- a/examples/component/convert.wit +++ b/examples/component/convert.wit @@ -1,11 +1,23 @@ package local:demo; world convert { + variant temperature { + celsius(f32), + fahrenheit(f32), + } /// This interface needs to be provided by the host import host: interface { + enum binary-operation { + add, + multiply, + } /// Example function that does a simple a × b operation multiply: func(a: f32, b: f32) -> f32; + /// Example function that does a simple operation 'op' + apply: func(a: f32, b: f32, op: binary-operation) -> f32; } /// Exported function for computing: (°C × 9/5) + 32 = °F export convert-celsius-to-fahrenheit: func(x: f32) -> f32; + /// Exported function for converting from one temperature scale to the other + export convert: func(t: temperature) -> temperature; } diff --git a/examples/component/main.c b/examples/component/main.c new file mode 100644 index 000000000000..ffb54cca59b5 --- /dev/null +++ b/examples/component/main.c @@ -0,0 +1,203 @@ +#include +#include +#include +#include +#include +#include +#include + +const char* multiply_data = "hello multiply"; +const char* apply_data = "hello apply"; +const char* context_data = "context data"; + +static void exit_with_error(const char *message, wasmtime_error_t *error, + wasm_trap_t *trap); + +wasm_trap_t *mult( + void *env, wasmtime_context_t *context, const wasmtime_component_val_t *args, + size_t nargs, wasmtime_component_val_t *results, size_t nresults) +{ + assert(env == (void*)multiply_data); + const char* exec_env = (const char*)wasmtime_context_get_data(context); + assert(exec_env == context_data); + assert(nargs == 2); + assert(nresults == 1); + assert(args[0].kind == WASMTIME_COMPONENT_KIND_F32); + assert(args[1].kind == WASMTIME_COMPONENT_KIND_F32); + float res = args[0].payload.f32 * args[1].payload.f32; + results[0].kind = WASMTIME_COMPONENT_KIND_F32; + results[0].payload.f32 = res; + return NULL; +} + +wasm_trap_t *apply( + void *env, wasmtime_context_t *context, const wasmtime_component_val_t *args, + size_t nargs, wasmtime_component_val_t *results, size_t nresults) +{ + assert(env == (void*)apply_data); + const char* exec_env = (const char*)wasmtime_context_get_data(context); + assert(exec_env == context_data); + assert(nargs == 3); + assert(nresults == 1); + assert(args[0].kind == WASMTIME_COMPONENT_KIND_F32); + assert(args[1].kind == WASMTIME_COMPONENT_KIND_F32); + assert(args[2].kind == WASMTIME_COMPONENT_KIND_ENUM); + float res = args[2].payload.enumeration.discriminant == 0 ? + args[0].payload.f32 + args[1].payload.f32 : args[0].payload.f32 * args[1].payload.f32; + results[0].kind = WASMTIME_COMPONENT_KIND_F32; + results[0].payload.f32 = res; + return NULL; +} + +int main() { + wasm_engine_t *engine = wasm_engine_new(); + + // Create a component linker with host functions defined + wasmtime_component_linker_t *linker = wasmtime_component_linker_new(engine); + // `multiply` only uses types without additional owned data, that can live on the stack + wasmtime_component_type_t mult_param_types[2]; + mult_param_types[0].kind = WASMTIME_COMPONENT_KIND_F32; + mult_param_types[1].kind = WASMTIME_COMPONENT_KIND_F32; + wasmtime_component_type_t f32_result_type; + f32_result_type.kind = WASMTIME_COMPONENT_KIND_F32; + + wasmtime_error_t* error = wasmtime_component_linker_define_func( + linker, "host", 4, "multiply", 8, mult_param_types, 2, + &f32_result_type, 1, mult, (void*)multiply_data, NULL); + if (error) + exit_with_error("failed to define function multiply", error, NULL); + + // `apply` uses an enum, which needs additional data, use a wasmtime_component_type_vec_t + wasmtime_component_type_vec_t apply_param_types; + wasmtime_component_type_vec_new_uninitialized(&apply_param_types, 3); + apply_param_types.data[0].kind = WASMTIME_COMPONENT_KIND_F32; + apply_param_types.data[1].kind = WASMTIME_COMPONENT_KIND_F32; + apply_param_types.data[2].kind = WASMTIME_COMPONENT_KIND_ENUM; + wasmtime_component_string_vec_new_uninitialized(&apply_param_types.data[2].payload.enumeration, 2); + wasm_name_new_from_string(&apply_param_types.data[2].payload.enumeration.data[0], "add"); + wasm_name_new_from_string(&apply_param_types.data[2].payload.enumeration.data[1], "multiply"); + error = wasmtime_component_linker_define_func( + linker, "host", 4, "apply", 5, apply_param_types.data, apply_param_types.size, + &f32_result_type, 1, apply, (void*)apply_data, NULL); + if (error) + exit_with_error("failed to define function apply", error, NULL); + // deleting the vector also drops the full types hierarchy + wasmtime_component_type_vec_delete(&apply_param_types); + + error = wasmtime_component_linker_build(linker); + if (error) + exit_with_error("failed to build linker", error, NULL); + + // Load binary. + printf("Loading binary...\n"); + FILE *file = fopen("target/wasm32-unknown-unknown/debug/guest.wasm", "rb"); + if (!file) { + printf("> Error opening module!\n"); + return 1; + } + fseek(file, 0L, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0L, SEEK_SET); + wasm_byte_vec_t binary; + wasm_byte_vec_new_uninitialized(&binary, file_size); + if (fread(binary.data, file_size, 1, file) != 1) { + printf("> Error reading module!\n"); + return 1; + } + fclose(file); + + // Compile. + // Note that the binary should be a component (not a plain module), typically built by running + // `cargo component build -p example-component-wasm --target wasm32-unknown-unknown` + // this will therefore fail if only the README instructions are followed + printf("Compiling component...\n"); + wasmtime_component_t* component; + error = wasmtime_component_from_binary(engine, (uint8_t *)binary.data, binary.size, &component); + if (error) + exit_with_error("failed to build component", error, NULL); + wasm_byte_vec_delete(&binary); + + wasmtime_store_t *store = wasmtime_store_new(engine, (void*)context_data, NULL); + wasmtime_context_t *context = wasmtime_store_context(store); + + // Instantiate. + printf("Instantiating component...\n"); + wasmtime_component_instance_t *instance; + error = wasmtime_component_linker_instantiate(linker, context, component, &instance); + if (error) + exit_with_error("failed to instantiate component", error, NULL); + + // Lookup functions. + wasmtime_component_func_t* convert1; + const char* func_name1 = "convert-celsius-to-fahrenheit"; + bool ok = wasmtime_component_instance_get_func(instance, context, func_name1, strlen(func_name1), &convert1); + if (!ok) + exit_with_error("function convert-celsius-to-fahrenheit not found", NULL, NULL); + wasmtime_component_func_t* convert2; + const char* func_name2 = "convert"; + ok = wasmtime_component_instance_get_func(instance, context, func_name2, strlen(func_name2), &convert2); + if (!ok) + exit_with_error("function convert not found", NULL, NULL); + + // Call. + printf("Calling convert-celsius-to-fahrenheit...\n"); + wasmtime_component_val_t param_val, result_val; + param_val.kind = WASMTIME_COMPONENT_KIND_F32; + param_val.payload.f32 = 23.4f; + // will be written, but must be "droppable", i.e. without owned data + result_val.kind = WASMTIME_COMPONENT_KIND_BOOL; + wasm_trap_t* trap = NULL; + error = wasmtime_component_func_call(convert1, context, ¶m_val, 1, &result_val, 1, &trap); + if (error != NULL || trap != NULL) + exit_with_error("failed to call function convert-celsius-to-fahrenheit", error, trap); + + assert(result_val.kind == WASMTIME_COMPONENT_KIND_F32); + printf("23.4°C = %f°F\n", result_val.payload.f32); + + printf("Calling convert...\n"); + wasmtime_component_val_t* t = wasmtime_component_val_new(); + t->kind = WASMTIME_COMPONENT_KIND_VARIANT; + t->payload.variant.discriminant = 1; + t->payload.variant.val = wasmtime_component_val_new(); + t->payload.variant.val->kind = WASMTIME_COMPONENT_KIND_F32; + t->payload.variant.val->payload.f32 = 66.2f; + wasmtime_component_val_t* result = wasmtime_component_val_new(); + + error = wasmtime_component_func_call(convert2, context, t, 1, result, 1, &trap); + if (error != NULL || trap != NULL) + exit_with_error("failed to call function", error, trap); + wasmtime_component_val_delete(t); + + assert(result->kind == WASMTIME_COMPONENT_KIND_VARIANT); + assert(result->payload.variant.discriminant == 0); + assert(result->payload.variant.val != NULL); + assert(result->payload.variant.val->kind == WASMTIME_COMPONENT_KIND_F32); + printf("66.2°F = %f°C\n", result->payload.variant.val->payload.f32); + wasmtime_component_val_delete(result); + + wasmtime_store_delete(store); + + // Shut down. + printf("Shutting down...\n"); + wasmtime_component_delete(component); + wasmtime_component_linker_delete(linker); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} + +static void exit_with_error(const char *message, wasmtime_error_t *error, + wasm_trap_t *trap) { + fprintf(stderr, "error: %s\n", message); + wasm_byte_vec_t error_message; + if (error != NULL) { + wasmtime_error_message(error, &error_message); + } else { + wasm_trap_message(trap, &error_message); + } + fprintf(stderr, "%.*s\n", (int)error_message.size, error_message.data); + wasm_byte_vec_delete(&error_message); + exit(1); +} diff --git a/examples/component/main.rs b/examples/component/main.rs index c26e70b57607..784fc134bc32 100644 --- a/examples/component/main.rs +++ b/examples/component/main.rs @@ -16,6 +16,12 @@ impl host::Host for HostComponent { fn multiply(&mut self, a: f32, b: f32) -> f32 { a * b } + fn apply(&mut self, a: f32, b: f32, op: host::BinaryOperation) -> f32 { + match op { + host::BinaryOperation::Add => a + b, + host::BinaryOperation::Multiply => a * b, + } + } } struct MyState { @@ -30,10 +36,16 @@ struct MyState { /// /// In this example we convert the code here to simplify the testing process and build system. fn convert_to_component(path: impl AsRef) -> Result> { - let bytes = &fs::read(&path).context("failed to read input file")?; - wit_component::ComponentEncoder::default() - .module(&bytes)? - .encode() + let bytes = fs::read(&path).context("failed to read input file")?; + // allow direct use of a component : look at the layer field of the preamble, see + // https://github.com/WebAssembly/component-model/blob/main/design/mvp/Binary.md#component-definitions + if bytes.len() > 6 && bytes[6] == 1 { + Ok(bytes) + } else { + wit_component::ComponentEncoder::default() + .module(&bytes)? + .encode() + } } fn main() -> Result<()> { @@ -45,18 +57,21 @@ fn main() -> Result<()> { // model. let component = convert_to_component("target/wasm32-unknown-unknown/debug/guest.wasm")?; - // Create our component and call our generated host function. + // Create our component and call our generated host functions. let component = Component::from_binary(&engine, &component)?; + let mut linker = Linker::new(&engine); + host::add_to_linker(&mut linker, |state: &mut MyState| &mut state.host)?; + let mut store = Store::new( &engine, MyState { host: HostComponent {}, }, ); - let mut linker = Linker::new(&engine); - host::add_to_linker(&mut linker, |state: &mut MyState| &mut state.host)?; let convert = Convert::instantiate(&mut store, &component, &linker)?; let result = convert.call_convert_celsius_to_fahrenheit(&mut store, 23.4)?; println!("Converted to: {result:?}"); + let result = convert.call_convert(&mut store, Temperature::Fahrenheit(66.2))?; + println!("Converted to: {result:?}"); Ok(()) } diff --git a/examples/component/wasm/guest.rs b/examples/component/wasm/guest.rs index 8af47944470f..c91f258428ec 100644 --- a/examples/component/wasm/guest.rs +++ b/examples/component/wasm/guest.rs @@ -11,6 +11,17 @@ export!(GuestComponent); impl Guest for GuestComponent { fn convert_celsius_to_fahrenheit(x: f32) -> f32 { - host::multiply(x, 1.8) + 32.0 + host::apply( + host::apply(x, 1.8, host::BinaryOperation::Multiply), + 32.0, + host::BinaryOperation::Add, + ) + } + + fn convert(t: Temperature) -> Temperature { + match t { + Temperature::Celsius(t) => Temperature::Fahrenheit(host::multiply(t, 1.8) + 32.0), + Temperature::Fahrenheit(t) => Temperature::Celsius(host::multiply(t - 32.0, 5.0 / 9.0)), + } } } From f14d4579ceb535552e18df7c905a9bffae8eafd3 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Lafay Date: Fri, 13 Dec 2024 17:51:43 +0100 Subject: [PATCH 4/4] Fix CI --- crates/c-api/include/wasmtime/component.h | 440 +++++++++++++--------- crates/c-api/src/lib.rs | 3 +- examples/CMakeLists.txt | 3 + examples/component/main.c | 115 +++--- examples/component/main.rs | 18 +- 5 files changed, 344 insertions(+), 235 deletions(-) diff --git a/crates/c-api/include/wasmtime/component.h b/crates/c-api/include/wasmtime/component.h index 4daf02a3b77f..3d8922503fc3 100644 --- a/crates/c-api/include/wasmtime/component.h +++ b/crates/c-api/include/wasmtime/component.h @@ -26,60 +26,65 @@ extern "C" { WASMTIME_CONFIG_PROP(void, component_model, bool) /** - * \brief The tag part of #wasmtime_component_val_t or #wasmtime_component_type_t + * \brief The tag part of #wasmtime_component_val_t or + * #wasmtime_component_type_t * * Specifies what variant is populated in #wasmtime_component_val_payload_t * or #wasmtime_component_type_payload_t. + * + * Note that resources are currently not supported. */ typedef uint8_t wasmtime_component_kind_t; -/// \brief Value of #wasmtime_component_kind_t indicating a boolean +/// Value of #wasmtime_component_kind_t indicating a boolean #define WASMTIME_COMPONENT_KIND_BOOL 0 -/// \brief Value of #wasmtime_component_kind_t indicating a signed 8-bit integer +/// Value of #wasmtime_component_kind_t indicating a signed 8-bit integer #define WASMTIME_COMPONENT_KIND_S8 1 -/// \brief Value of #wasmtime_component_kind_t indicating an unsigned 8-bit integer +/// Value of #wasmtime_component_kind_t indicating an unsigned 8-bit integer #define WASMTIME_COMPONENT_KIND_U8 2 -/// \brief Value of #wasmtime_component_kind_t indicating a signed 16-bit integer +/// Value of #wasmtime_component_kind_t indicating a signed 16-bit integer #define WASMTIME_COMPONENT_KIND_S16 3 -/// \brief Value of #wasmtime_component_kind_t indicating an unsigned 16-bit integer +/// Value of #wasmtime_component_kind_t indicating an unsigned 16-bit integer #define WASMTIME_COMPONENT_KIND_U16 4 -/// \brief Value of #wasmtime_component_kind_t indicating a signed 32-bit integer +/// Value of #wasmtime_component_kind_t indicating a signed 32-bit integer #define WASMTIME_COMPONENT_KIND_S32 5 -/// \brief Value of #wasmtime_component_kind_t indicating an unsigned 32-bit integer +/// Value of #wasmtime_component_kind_t indicating an unsigned 32-bit integer #define WASMTIME_COMPONENT_KIND_U32 6 -/// \brief Value of #wasmtime_component_kind_t indicating a signed 64-bit integer +/// Value of #wasmtime_component_kind_t indicating a signed 64-bit integer #define WASMTIME_COMPONENT_KIND_S64 7 -/// \brief Value of #wasmtime_component_kind_t indicating an unsigned 64-bit integer +/// Value of #wasmtime_component_kind_t indicating an unsigned 64-bit integer #define WASMTIME_COMPONENT_KIND_U64 8 -/// \brief Value of #wasmtime_component_kind_t indicating an 32-bit floating point number +/// Value of #wasmtime_component_kind_t indicating a 32-bit floating point +/// number #define WASMTIME_COMPONENT_KIND_F32 9 -/// \brief Value of #wasmtime_component_kind_t indicating an 64-bit floating point number +/// Value of #wasmtime_component_kind_t indicating a 64-bit floating point +/// number #define WASMTIME_COMPONENT_KIND_F64 10 -/// \brief Value of #wasmtime_component_kind_t indicating a unicode character +/// Value of #wasmtime_component_kind_t indicating a unicode character #define WASMTIME_COMPONENT_KIND_CHAR 11 -/// \brief Value of #wasmtime_component_kind_t indicating a unicode string +/// Value of #wasmtime_component_kind_t indicating a unicode string #define WASMTIME_COMPONENT_KIND_STRING 12 -/// \brief Value of #wasmtime_component_kind_t indicating a list +/// Value of #wasmtime_component_kind_t indicating a list #define WASMTIME_COMPONENT_KIND_LIST 13 -/// \brief Value of #wasmtime_component_kind_t indicating a record +/// Value of #wasmtime_component_kind_t indicating a record #define WASMTIME_COMPONENT_KIND_RECORD 14 -/// \brief Value of #wasmtime_component_kind_t indicating a tuple +/// Value of #wasmtime_component_kind_t indicating a tuple #define WASMTIME_COMPONENT_KIND_TUPLE 15 -/// \brief Value of #wasmtime_component_kind_t indicating a variant +/// Value of #wasmtime_component_kind_t indicating a variant #define WASMTIME_COMPONENT_KIND_VARIANT 16 -/// \brief Value of #wasmtime_component_kind_t indicating an enum +/// Value of #wasmtime_component_kind_t indicating an enum #define WASMTIME_COMPONENT_KIND_ENUM 17 -/// \brief Value of #wasmtime_component_kind_t indicating an option +/// Value of #wasmtime_component_kind_t indicating an option #define WASMTIME_COMPONENT_KIND_OPTION 18 -/// \brief Value of #wasmtime_component_kind_t indicating a result +/// Value of #wasmtime_component_kind_t indicating a result #define WASMTIME_COMPONENT_KIND_RESULT 19 -/// \brief Value of #wasmtime_component_kind_t indicating a set of flags +/// Value of #wasmtime_component_kind_t indicating a set of flags #define WASMTIME_COMPONENT_KIND_FLAGS 20 - // forward declarations typedef struct wasmtime_component_val_t wasmtime_component_val_t; -typedef struct wasmtime_component_val_record_field_t wasmtime_component_val_record_field_t; +typedef struct wasmtime_component_val_record_field_t + wasmtime_component_val_record_field_t; typedef struct wasmtime_component_type_t wasmtime_component_type_t; typedef struct wasmtime_component_type_field_t wasmtime_component_type_field_t; @@ -104,33 +109,35 @@ typedef struct wasmtime_component_type_field_t wasmtime_component_type_field_t; WASM_API_EXTERN void wasmtime_component_##name##_new( \ wasmtime_component_##name##_t *out, size_t, element const[]); -/// \brief A vector of values. +/// A vector of values. WASMTIME_COMPONENT_DECLARE_VEC(val_vec, wasmtime_component_val_t); -/// \brief A tuple of named fields. -WASMTIME_COMPONENT_DECLARE_VEC(val_record, wasmtime_component_val_record_field_t); +/// A tuple of named fields. +WASMTIME_COMPONENT_DECLARE_VEC(val_record, + wasmtime_component_val_record_field_t); -/// \brief A variable sized bitset. +/// A variable sized bitset. WASMTIME_COMPONENT_DECLARE_VEC(val_flags, uint32_t); WASMTIME_COMPONENT_DECLARE_VEC_NEW(val_flags, uint32_t); -/// \brief A vector of types +/// A vector of types WASMTIME_COMPONENT_DECLARE_VEC(type_vec, wasmtime_component_type_t); -/// \brief A vector of field types +/// A vector of field types WASMTIME_COMPONENT_DECLARE_VEC(type_field_vec, wasmtime_component_type_field_t); -/// \brief A vector of strings +/// A vector of strings WASMTIME_COMPONENT_DECLARE_VEC(string_vec, wasm_name_t); WASMTIME_COMPONENT_DECLARE_VEC_NEW(string_vec, wasm_name_t); #undef WASMTIME_COMPONENT_DECLARE_VEC -/// \brief Representation of a variant value +/// Representation of a variant value typedef struct wasmtime_component_val_variant_t { - /// \brief Discriminant indicating the index of the variant case of this value + /// Discriminant indicating the index of the variant case of this value uint32_t discriminant; - /// \brief #wasmtime_component_val_t value of the variant case of this value if it has one + /// #wasmtime_component_val_t value of the variant case of this value if it + /// has one wasmtime_component_val_t *val; } wasmtime_component_val_variant_t; @@ -140,192 +147,226 @@ typedef struct wasmtime_component_val_variant_t { * A result is an either type holding an ok value or an error */ typedef struct wasmtime_component_val_result_t { - /// \brief Value of the result, either of the ok type or of the error type, depending on #error + /// Value of the result, either of the ok type or of the error type, depending + /// on #error wasmtime_component_val_t *val; - /// \brief Discriminant indicating if this result is an ok value or an error + /// Discriminant indicating if this result is an ok value or an error bool error; } wasmtime_component_val_result_t; -/// \brief Representation of an enum value +/// Representation of an enum value typedef struct wasmtime_component_val_enum_t { - /// \brief Discriminant indicating the index of this value in the enum + /// Discriminant indicating the index of this value in the enum uint32_t discriminant; } wasmtime_component_val_enum_t; /** * \brief Container for different kind of component model value data * - * Setting any dynamic data to one of this field means that the payload now owns it + * Setting dynamic data to one of this field means that the payload now owns it */ typedef union wasmtime_component_val_payload_t { - /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_BOOLEAN + /// Field used if #wasmtime_component_val_t::kind is + /// #WASMTIME_COMPONENT_KIND_BOOLEAN bool boolean; - /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_S8 + /// Field used if #wasmtime_component_val_t::kind is + /// #WASMTIME_COMPONENT_KIND_S8 int8_t s8; - /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_U8 + /// Field used if #wasmtime_component_val_t::kind is + /// #WASMTIME_COMPONENT_KIND_U8 uint8_t u8; - /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_S16 + /// Field used if #wasmtime_component_val_t::kind is + /// #WASMTIME_COMPONENT_KIND_S16 int16_t s16; - /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_U16 + /// Field used if #wasmtime_component_val_t::kind is + /// #WASMTIME_COMPONENT_KIND_U16 uint16_t u16; - /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_S32 + /// Field used if #wasmtime_component_val_t::kind is + /// #WASMTIME_COMPONENT_KIND_S32 int32_t s32; - /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_U32 + /// Field used if #wasmtime_component_val_t::kind is + /// #WASMTIME_COMPONENT_KIND_U32 uint32_t u32; - /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_S64 + /// Field used if #wasmtime_component_val_t::kind is + /// #WASMTIME_COMPONENT_KIND_S64 int64_t s64; - /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_U64 + /// Field used if #wasmtime_component_val_t::kind is + /// #WASMTIME_COMPONENT_KIND_U64 uint64_t u64; - /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_F32 + /// Field used if #wasmtime_component_val_t::kind is + /// #WASMTIME_COMPONENT_KIND_F32 float f32; - /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_F64 + /// Field used if #wasmtime_component_val_t::kind is + /// #WASMTIME_COMPONENT_KIND_F64 double f64; - /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_CHARACTER + /// Field used if #wasmtime_component_val_t::kind is + /// #WASMTIME_COMPONENT_KIND_CHARACTER uint8_t character; - /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_STRING + /// Field used if #wasmtime_component_val_t::kind is + /// #WASMTIME_COMPONENT_KIND_STRING wasm_name_t string; - /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_LIST + /// Field used if #wasmtime_component_val_t::kind is + /// #WASMTIME_COMPONENT_KIND_LIST wasmtime_component_val_vec_t list; - /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_RECORD + /// Field used if #wasmtime_component_val_t::kind is + /// #WASMTIME_COMPONENT_KIND_RECORD wasmtime_component_val_record_t record; - /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_TUPLE + /// Field used if #wasmtime_component_val_t::kind is + /// #WASMTIME_COMPONENT_KIND_TUPLE wasmtime_component_val_vec_t tuple; - /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_VARIANT + /// Field used if #wasmtime_component_val_t::kind is + /// #WASMTIME_COMPONENT_KIND_VARIANT wasmtime_component_val_variant_t variant; - /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_ENUMERATION + /// Field used if #wasmtime_component_val_t::kind is + /// #WASMTIME_COMPONENT_KIND_ENUMERATION wasmtime_component_val_enum_t enumeration; - /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_OPTION + /// Field used if #wasmtime_component_val_t::kind is + /// #WASMTIME_COMPONENT_KIND_OPTION wasmtime_component_val_t *option; - /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_RESULT + /// Field used if #wasmtime_component_val_t::kind is + /// #WASMTIME_COMPONENT_KIND_RESULT wasmtime_component_val_result_t result; - /// \brief Field used if #wasmtime_component_val_t::kind is #WASMTIME_COMPONENT_KIND_FLAGS + /// Field used if #wasmtime_component_val_t::kind is + /// #WASMTIME_COMPONENT_KIND_FLAGS wasmtime_component_val_flags_t flags; } wasmtime_component_val_payload_t; /** * \brief Representation of a component model value * - * Many kind of values own data, so those need to be properly dropped in wasmtime, - * and therefore should not live on the stack. + * Many kind of values own data, so those need to be properly dropped in + * wasmtime, and therefore should not live on the stack. */ typedef struct wasmtime_component_val_t { - /// \brief Discriminant indicating which field of #payload is valid + /// Discriminant indicating which field of #payload is valid wasmtime_component_kind_t kind; - /// \brief Container for the value data + /// Container for the value data wasmtime_component_val_payload_t payload; } wasmtime_component_val_t; WASMTIME_COMPONENT_DECLARE_VEC_NEW(val_vec, wasmtime_component_val_t); -/// \brief Representation of record field. +/// Representation of record field. typedef struct wasmtime_component_val_record_field_t { - /// \brief Name of the field + /// Name of the field wasm_name_t name; - /// \brief Value of the field + /// Value of the field wasmtime_component_val_t val; } wasmtime_component_val_record_field_t; -WASMTIME_COMPONENT_DECLARE_VEC_NEW(val_record, wasmtime_component_val_record_field_t); +WASMTIME_COMPONENT_DECLARE_VEC_NEW(val_record, + wasmtime_component_val_record_field_t); /** - * \brief Sets the value of a flag within within a #wasmtime_component_val_flags_t. + * \brief Sets the value of a flag within within a + #wasmtime_component_val_flags_t. * * If this bit set is too small to hold a value at `index` it will be resized. - + * * \param flags the #wasmtime_component_val_flags_t to modify * \param index the index of the flag to modify * \param enabled the value to set the flag to */ -void wasmtime_component_val_flags_set(wasmtime_component_val_flags_t *flags, uint32_t index, bool enabled); +void wasmtime_component_val_flags_set(wasmtime_component_val_flags_t *flags, + uint32_t index, bool enabled); /** - * \brief Tests the value of a flag within within a #wasmtime_component_val_flags_t. + * \brief Tests the value of a flag within within a + #wasmtime_component_val_flags_t. * * If this bit set is too small to hold a value at `index` it will be resized. - + * * \param flags the #wasmtime_component_val_flags_t to test * \param index the index of the flag to test + * * \return true if the flag is set, else false */ -bool wasmtime_component_val_flags_test(const wasmtime_component_val_flags_t* flags, uint32_t index); +bool wasmtime_component_val_flags_test( + const wasmtime_component_val_flags_t *flags, uint32_t index); /** * \brief Creates a new #wasmtime_component_val_t * - * This is usually used to create inner values, typically as part of an option or result. - * In this case, ownership is given out to the outer value. + * This is usually used to create inner values, typically as part of an option + * or result. In this case, ownership is given out to the outer value. * - * In case where a top level #wasmtime_component_val_t is created (for example to be passed directly - * to #wasmtime_component_func_call), then it should be deleted with #wasmtime_component_val_delete + * In case where a top level #wasmtime_component_val_t is created (for example + * to be passed directly to #wasmtime_component_func_call), then it should be + * deleted with #wasmtime_component_val_delete * * \return a pointer to the newly created #wasmtime_component_val_t */ -wasmtime_component_val_t* wasmtime_component_val_new(); +wasmtime_component_val_t *wasmtime_component_val_new(); /** - * \brief Deletes a #wasmtime_component_val_t previously created by #wasmtime_component_val_new + * \brief Deletes a #wasmtime_component_val_t previously created by + * #wasmtime_component_val_new * - * This should not be called if the value has been given out as part of an outer #wasmtime_component_val_t + * This should not be called if the value has been given out as part of an outer + * #wasmtime_component_val_t * * \param val the #wasmtime_component_val_t to delete */ -void wasmtime_component_val_delete(wasmtime_component_val_t* val); +void wasmtime_component_val_delete(wasmtime_component_val_t *val); -/** - * \brief Representation of a field in a record type or a case in a variant type - */ +/// Representation of a field in a record type or a case in a variant type typedef struct wasmtime_component_type_field_t { - /// \brief Name of the record field or variant case + /// Name of the record field or variant case wasm_name_t name; - /// \brief Type of the record field or variant case (may be null for variant case) - wasmtime_component_type_t* ty; + /// Type of the record field or variant case (may be null for variant case) + wasmtime_component_type_t *ty; } wasmtime_component_type_field_t; -WASMTIME_COMPONENT_DECLARE_VEC_NEW(type_field_vec, wasmtime_component_type_field_t); +WASMTIME_COMPONENT_DECLARE_VEC_NEW(type_field_vec, + wasmtime_component_type_field_t); -/** - * \brief Representation of a result type - */ +/// Representation of a result type typedef struct wasmtime_component_type_result_t { - /// \brief Type of the ok value (if there is one) - wasmtime_component_type_t* ok_ty; - /// \brief Type of the error value (if there is one) - wasmtime_component_type_t* err_ty; + /// Type of the ok value (if there is one) + wasmtime_component_type_t *ok_ty; + /// Type of the error value (if there is one) + wasmtime_component_type_t *err_ty; } wasmtime_component_type_result_t; -/** - * \brief Container for different kind of component model type data - */ -typedef union wasmtime_component_type_payload_t -{ - /// \brief Field used if #wasmtime_component_type_t::kind is #WASMTIME_COMPONENT_KIND_LIST - wasmtime_component_type_t* list; - /// \brief Field used if #wasmtime_component_type_t::kind is #WASMTIME_COMPONENT_KIND_RECORD +/// Container for different kind of component model type data +typedef union wasmtime_component_type_payload_t { + /// Field used if #wasmtime_component_type_t::kind is + /// #WASMTIME_COMPONENT_KIND_LIST + wasmtime_component_type_t *list; + /// Field used if #wasmtime_component_type_t::kind is + /// #WASMTIME_COMPONENT_KIND_RECORD wasmtime_component_type_field_vec_t record; - /// \brief Field used if #wasmtime_component_type_t::kind is #WASMTIME_COMPONENT_KIND_TUPLE + /// Field used if #wasmtime_component_type_t::kind is + /// #WASMTIME_COMPONENT_KIND_TUPLE wasmtime_component_type_vec_t tuple; - /// \brief Field used if #wasmtime_component_type_t::kind is #WASMTIME_COMPONENT_KIND_VARIANT + /// Field used if #wasmtime_component_type_t::kind is + /// #WASMTIME_COMPONENT_KIND_VARIANT wasmtime_component_type_field_vec_t variant; - /// \brief Field used if #wasmtime_component_type_t::kind is #WASMTIME_COMPONENT_KIND_ENUM + /// Field used if #wasmtime_component_type_t::kind is + /// #WASMTIME_COMPONENT_KIND_ENUM wasmtime_component_string_vec_t enumeration; - /// \brief Field used if #wasmtime_component_type_t::kind is #WASMTIME_COMPONENT_KIND_OPTION - wasmtime_component_type_t* option; - /// \brief Field used if #wasmtime_component_type_t::kind is #WASMTIME_COMPONENT_KIND_RESULT + /// Field used if #wasmtime_component_type_t::kind is + /// #WASMTIME_COMPONENT_KIND_OPTION + wasmtime_component_type_t *option; + /// Field used if #wasmtime_component_type_t::kind is + /// #WASMTIME_COMPONENT_KIND_RESULT wasmtime_component_type_result_t result; - /// \brief Field used if #wasmtime_component_type_t::kind is #WASMTIME_COMPONENT_KIND_FLAGS + /// Field used if #wasmtime_component_type_t::kind is + /// #WASMTIME_COMPONENT_KIND_FLAGS wasmtime_component_string_vec_t flags; } wasmtime_component_type_payload_t; /** * \brief Representation of a component model type * - * Many kind of types own data, so those need to be properly dropped in wasmtime, - * and therefore should not live on the stack. + * Many kind of types own data, so those need to be properly dropped in + * wasmtime, and therefore should not live on the stack. */ typedef struct wasmtime_component_type_t { - /// \brief Discriminant indicating what kind of type it is, and which field of #payload is valid, if any + /// Discriminant indicating what kind of type it is, and which field of + /// #payload is valid, if any wasmtime_component_kind_t kind; - /// \brief Container for the type data, if any + /// Container for the type data, if any wasmtime_component_type_payload_t payload; } wasmtime_component_type_t; @@ -336,36 +377,39 @@ WASMTIME_COMPONENT_DECLARE_VEC_NEW(type_vec, wasmtime_component_type_t); /** * \brief Creates a new #wasmtime_component_type_t * - * This is usually used to create inner types, typically as part of an option or result. - * In this case, ownership is given out to the outer value. + * This is usually used to create inner types, typically as part of an option or + * result. In this case, ownership is given out to the outer value. * - * In case where a top level #wasmtime_component_type_t is created (for example to be passed directly to - * #wasmtime_component_linker_define_func), then it should be deleted with #wasmtime_component_type_delete + * In case where a top level #wasmtime_component_type_t is created (for example + * to be passed directly to #wasmtime_component_linker_define_func), then it + * should be deleted with #wasmtime_component_type_delete * * \return a pointer to the newly created #wasmtime_component_type_t */ -wasmtime_component_type_t* wasmtime_component_type_new(); +wasmtime_component_type_t *wasmtime_component_type_new(); /** - * \brief Deletes a #wasmtime_component_type_t previously created by #wasmtime_component_type_new + * \brief Deletes a #wasmtime_component_type_t previously created by + * #wasmtime_component_type_new * - * This should not be called if the type has been given out as part of an outer #wasmtime_component_type_t + * This should not be called if the type has been given out as part of an outer + * #wasmtime_component_type_t * * \param val the #wasmtime_component_type_t to delete */ -void wasmtime_component_type_delete(wasmtime_component_type_t* ty); +void wasmtime_component_type_delete(wasmtime_component_type_t *ty); -/** - * \brief Representation of a component in the component model. - */ +/// Representation of a component in the component model. typedef struct wasmtime_component_t wasmtime_component_t; /** * \brief Compiles a WebAssembly binary into a #wasmtime_component_t * - * This function will compile a WebAssembly binary into an owned #wasmtime_component_t. + * This function will compile a WebAssembly binary into an owned + #wasmtime_component_t. * - * It requires a component binary, such as what is produced by Rust `cargo component` tooling. + * It requires a component binary, such as what is produced by Rust `cargo + component` tooling. * * This function does not take ownership of any of its arguments, but the * returned error and component are owned by the caller. @@ -373,15 +417,19 @@ typedef struct wasmtime_component_t wasmtime_component_t; * \param engine the #wasm_engine_t that will create the component * \param buf the address of the buffer containing the WebAssembly binary * \param len the length of the buffer containing the WebAssembly binary - * \param component_out on success, contains the address of the created component + * \param component_out on success, contains the address of the created + * component + * * \return NULL on success, else a #wasmtime_error_t describing the error */ wasmtime_error_t * -wasmtime_component_from_binary(const wasm_engine_t *engine, const uint8_t *buf, size_t len, +wasmtime_component_from_binary(const wasm_engine_t *engine, const uint8_t *buf, + size_t len, wasmtime_component_t **component_out); /** - * \brief Deletes a #wasmtime_component_t created by #wasmtime_component_from_binary + * \brief Deletes a #wasmtime_component_t created by + * #wasmtime_component_from_binary * * \param component the component to delete */ @@ -395,9 +443,9 @@ void wasmtime_component_delete(wasmtime_component_t *component); * Due to the interaction between `wasmtime::component::Linker` and * `wasmtime::component::LinkerInstance`, the latter being more of a builder, * it is expected to first define the host functions through calls to - * #wasmtime_component_linker_define_func, then call #wasmtime_component_linker_build - * to create and populate the root `wasmtime::component::LinkerInstance` and it's - * descendants (if any). + * #wasmtime_component_linker_define_func, then call + * #wasmtime_component_linker_build to create and populate the root + * `wasmtime::component::LinkerInstance` and it's descendants (if any). */ typedef struct wasmtime_component_linker_t wasmtime_component_linker_t; @@ -405,18 +453,21 @@ typedef struct wasmtime_component_linker_t wasmtime_component_linker_t; * \brief Creates a new #wasmtime_component_linker_t for the specified engine. * * \param engine the compilation environment and configuration + * * \return a pointer to the newly created #wasmtime_component_linker_t */ -wasmtime_component_linker_t *wasmtime_component_linker_new(const wasm_engine_t *engine); +wasmtime_component_linker_t * +wasmtime_component_linker_new(const wasm_engine_t *engine); /** - * \brief Deletes a #wasmtime_component_linker_t created by #wasmtime_component_linker_new + * \brief Deletes a #wasmtime_component_linker_t created by + * #wasmtime_component_linker_new * * \param linker the #wasmtime_component_linker_t to delete */ void wasmtime_component_linker_delete(wasmtime_component_linker_t *linker); -/// \brief Representation of a component instance +/// Representation of a component instance typedef struct wasmtime_component_instance_t wasmtime_component_instance_t; // declaration from store.h @@ -428,7 +479,8 @@ typedef struct wasmtime_context wasmtime_context_t; * This is the function signature for host functions that can be made accessible * to WebAssembly components. The arguments to this function are: * - * \param env a user-provided argument passed to #wasmtime_component_linker_define_func + * \param env a user-provided argument passed to + * #wasmtime_component_linker_define_func * \param context a #wasmtime_context_t, the context of this call * \param args the arguments provided to this function invocation * \param nargs how many arguments are provided @@ -444,8 +496,9 @@ typedef struct wasmtime_context wasmtime_context_t; * relinquishes ownership of the trap and it is passed back to the engine. */ typedef wasm_trap_t *(*wasmtime_component_func_callback_t)( - void *env, wasmtime_context_t *context, const wasmtime_component_val_t *args, - size_t nargs, wasmtime_component_val_t *results, size_t nresults); + void *env, wasmtime_context_t *context, + const wasmtime_component_val_t *args, size_t nargs, + wasmtime_component_val_t *results, size_t nresults); /** * \brief Defines a host function in a linker @@ -454,62 +507,80 @@ typedef wasm_trap_t *(*wasmtime_component_func_callback_t)( * * Does not take ownership of the #wasmtime_component_type_t arguments. * - * \param linker the #wasmtime_component_linker_t in which the function should be defined - * \param path the dot-separated path of the package where the function is defined + * \param linker the #wasmtime_component_linker_t in which the function should + * be defined + * \param path the dot-separated path of the package where the function is + * defined * \param path_len the byte length of `path` * \param name the name of the function * \param name_len the byte length of `name` - * \param params_types_buf a pointer to an array of #wasmtime_component_type_t describing the function's parameters + * \param params_types_buf a pointer to an array of #wasmtime_component_type_t + * describing the function's parameters * \param params_types_len the length of `params_types_buf` - * \param outputs_types_buf a pointer to an array of #wasmtime_component_type_t describing the function's outputs + * \param outputs_types_buf a pointer to an array of #wasmtime_component_type_t + * describing the function's outputs * \param outputs_types_len the length of `outputs_types_buf` - * \param cb a pointer to the actual functions, a #wasmtime_component_func_callback_t - * \param data the host-provided data to provide as the first argument to the callback + * \param cb a pointer to the actual functions, a + * #wasmtime_component_func_callback_t + * \param data the host-provided data to provide as the first argument to the + * callback * \param finalizer an optional finalizer for the `data` argument. - * \return wasmtime_error_t* on success `NULL` is returned, otherwise an error is returned which - * describes why the definition failed. + * + * \return wasmtime_error_t* on success `NULL` is returned, otherwise an error + * is returned which describes why the definition failed. */ wasmtime_error_t *wasmtime_component_linker_define_func( wasmtime_component_linker_t *linker, const char *path, size_t path_len, const char *name, size_t name_len, - wasmtime_component_type_t* params_types_buf, size_t params_types_len, - wasmtime_component_type_t* outputs_types_buf, size_t outputs_types_len, - wasmtime_component_func_callback_t cb, void *data, void (*finalizer)(void *)); + wasmtime_component_type_t *params_types_buf, size_t params_types_len, + wasmtime_component_type_t *outputs_types_buf, size_t outputs_types_len, + wasmtime_component_func_callback_t cb, void *data, + void (*finalizer)(void *)); /** - * \brief Builds the linker, providing the host functions defined by calls to #wasmtime_component_linker_define_func + * \brief Builds the linker, providing the host functions defined by calls to + * #wasmtime_component_linker_define_func * * \param linker the #wasmtime_component_linker_t to build - * \return wasmtime_error_t* On success `NULL` is returned, otherwise an error is returned which - * describes why the build failed. + * + * \return wasmtime_error_t* On success `NULL` is returned, otherwise an error + * is returned which describes why the build failed. */ -wasmtime_error_t *wasmtime_component_linker_build(wasmtime_component_linker_t *linker); +wasmtime_error_t * +wasmtime_component_linker_build(wasmtime_component_linker_t *linker); /** * \brief Instantiates a component instance in a given #wasmtime_context_t * - * \param linker a #wasmtime_component_linker_t that will help provide host functions - * \param context the #wasmtime_context_t in which the instance should be created + * \param linker a #wasmtime_component_linker_t that will help provide host + * functions + * \param context the #wasmtime_context_t in which the instance should be + * created * \param component the #wasmtime_component_t to instantiate - * \param instance_out on success, the instantiated #wasmtime_component_instance_t - * \return wasmtime_error_t* on success `NULL` is returned, otherwise an error is returned which - * describes why the build failed. + * \param instance_out on success, the instantiated + * #wasmtime_component_instance_t + * + * \return wasmtime_error_t* on success `NULL` is returned, otherwise an error + * is returned which describes why the build failed. */ wasmtime_error_t *wasmtime_component_linker_instantiate( const wasmtime_component_linker_t *linker, wasmtime_context_t *context, - const wasmtime_component_t *component, wasmtime_component_instance_t **instance_out); + const wasmtime_component_t *component, + wasmtime_component_instance_t **instance_out); -/// \brief Representation of an exported function in Wasmtime component model. +/// Representation of an exported function in Wasmtime component model. typedef struct wasmtime_component_func_t wasmtime_component_func_t; /** * \brief Looks for an exported function in the given component instance * - * \param instance the #wasmtime_component_instance_t in which the function should be looked for + * \param instance the #wasmtime_component_instance_t in which the function + * should be looked for * \param context the #wasmtime_context_t that contains `instance` * \param name the name of function to look for * \param name_len the byte length of `name` * \param item_out the wasmtime_component_func_t that was found, if any + * * \return true if the function was found, else false */ bool wasmtime_component_instance_get_func( @@ -519,28 +590,37 @@ bool wasmtime_component_instance_get_func( /** * \brief Calls an exported function of a component * - * It is the responsibility of the caller to make sure that `params` has the expected - * length, and the correct types, else the call will error out. `results` must have the - * expected length, but the values will be written with the correct types. - * - * This can fail in two ways : either a non-NULL #wasmtime_error_t is returned, for example if the - * parameters are incorrect (and `trap_out` will be NULL), or the call may trap, in which case - * NULL is returned, but `trap_out` will be non-NULL. - * - * Does not take ownership of #wasmtime_component_val_t arguments. Gives ownership of - * #wasmtime_component_val_t results. As such, if those are data-owning values, they - * should be created and deleted through this api, either directly with #wasmtime_component_val_new, - * or through a #wasmtime_component_val_vec_t, using #wasmtime_component_val_vec_new and + * It is the responsibility of the caller to make sure that `params` has the + * expected length, and the correct types, else the call will error out. + * `results` must have the expected length, but the values will be written with + * the correct types. + * + * This can fail in two ways : either a non-NULL #wasmtime_error_t is returned, + * for example if the parameters are incorrect (and `trap_out` will be NULL), or + * the call may trap, in which case NULL is returned, but `trap_out` will be + * non-NULL. + * + * Does not take ownership of #wasmtime_component_val_t arguments. Gives + * ownership of #wasmtime_component_val_t results. As such, if those are + * data-owning values, they should be created and deleted through this api, + * either directly with #wasmtime_component_val_new, or through a + * #wasmtime_component_val_vec_t, using #wasmtime_component_val_vec_new and * #wasmtime_component_val_vec_delete * - * \param func the function to call, typically found with #wasmtime_component_instance_get_func + * \param func the function to call, typically found with + * #wasmtime_component_instance_get_func * \param context the #wasmtime_context_t that contains `func` - * \param params the parameters of `func`, as an array of #wasmtime_component_val_t + * \param params the parameters of `func`, as an array of + * #wasmtime_component_val_t * \param params_len the length of `params` - * \param results the results of `func`, as an array of #wasmtime_component_val_t that will be written + * \param results the results of `func`, as an array of + * #wasmtime_component_val_t that will be written * \param results_len the length of `results` - * \param trap_out NULL if the call completed successfully or couldn't be made, otherwise the trap that was raised - * \return wasmtime_error_t* NULL on success or a description of the error calling the function + * \param trap_out NULL if the call completed successfully or couldn't be made, + * otherwise the trap that was raised + * + * \return wasmtime_error_t* NULL on success or a description of the error + * calling the function */ wasmtime_error_t *wasmtime_component_func_call( const wasmtime_component_func_t *func, wasmtime_context_t *context, diff --git a/crates/c-api/src/lib.rs b/crates/c-api/src/lib.rs index c5db0ddc8579..5b78fe15ad34 100644 --- a/crates/c-api/src/lib.rs +++ b/crates/c-api/src/lib.rs @@ -15,7 +15,6 @@ #![expect(non_camel_case_types, reason = "matching C style, not Rust")] pub use wasmtime; -use wasmtime::Trap; mod config; mod engine; @@ -127,10 +126,12 @@ unsafe fn slice_from_raw_parts_mut<'a, T>(ptr: *mut T, len: usize) -> &'a mut [T } } +#[cfg(any(feature = "async", feature = "component-model"))] pub(crate) fn handle_call_error( err: wasmtime::Error, trap_ret: &mut *mut wasm_trap_t, ) -> Option> { + use wasmtime::Trap; if err.is::() { *trap_ret = Box::into_raw(Box::new(wasm_trap_t::new(err))); None diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 927b66d57867..1f4860a17dc3 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -66,6 +66,9 @@ create_rust_wasm(fib-debug wasm32-unknown-unknown) create_rust_wasm(tokio wasm32-wasip1) create_rust_wasm(wasi wasm32-wasip1) create_rust_wasm(component wasm32-unknown-unknown) +# the C test for component needs a component binary, not a plain module +# this is done using the rust example to do the conversion +execute_process(COMMAND cargo run --example component copy WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/..) create_rust_test(epochs) create_rust_test(externref) create_rust_test(fib-debug) diff --git a/examples/component/main.c b/examples/component/main.c index ffb54cca59b5..7813e252a7ed 100644 --- a/examples/component/main.c +++ b/examples/component/main.c @@ -6,19 +6,18 @@ #include #include -const char* multiply_data = "hello multiply"; -const char* apply_data = "hello apply"; -const char* context_data = "context data"; +const char *multiply_data = "hello multiply"; +const char *apply_data = "hello apply"; +const char *context_data = "context data"; static void exit_with_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap); -wasm_trap_t *mult( - void *env, wasmtime_context_t *context, const wasmtime_component_val_t *args, - size_t nargs, wasmtime_component_val_t *results, size_t nresults) -{ - assert(env == (void*)multiply_data); - const char* exec_env = (const char*)wasmtime_context_get_data(context); +wasm_trap_t *mult(void *env, wasmtime_context_t *context, + const wasmtime_component_val_t *args, size_t nargs, + wasmtime_component_val_t *results, size_t nresults) { + assert(env == (void *)multiply_data); + const char *exec_env = (const char *)wasmtime_context_get_data(context); assert(exec_env == context_data); assert(nargs == 2); assert(nresults == 1); @@ -30,20 +29,20 @@ wasm_trap_t *mult( return NULL; } -wasm_trap_t *apply( - void *env, wasmtime_context_t *context, const wasmtime_component_val_t *args, - size_t nargs, wasmtime_component_val_t *results, size_t nresults) -{ - assert(env == (void*)apply_data); - const char* exec_env = (const char*)wasmtime_context_get_data(context); +wasm_trap_t *apply(void *env, wasmtime_context_t *context, + const wasmtime_component_val_t *args, size_t nargs, + wasmtime_component_val_t *results, size_t nresults) { + assert(env == (void *)apply_data); + const char *exec_env = (const char *)wasmtime_context_get_data(context); assert(exec_env == context_data); assert(nargs == 3); assert(nresults == 1); assert(args[0].kind == WASMTIME_COMPONENT_KIND_F32); assert(args[1].kind == WASMTIME_COMPONENT_KIND_F32); assert(args[2].kind == WASMTIME_COMPONENT_KIND_ENUM); - float res = args[2].payload.enumeration.discriminant == 0 ? - args[0].payload.f32 + args[1].payload.f32 : args[0].payload.f32 * args[1].payload.f32; + float res = args[2].payload.enumeration.discriminant == 0 + ? args[0].payload.f32 + args[1].payload.f32 + : args[0].payload.f32 * args[1].payload.f32; results[0].kind = WASMTIME_COMPONENT_KIND_F32; results[0].payload.f32 = res; return NULL; @@ -54,31 +53,37 @@ int main() { // Create a component linker with host functions defined wasmtime_component_linker_t *linker = wasmtime_component_linker_new(engine); - // `multiply` only uses types without additional owned data, that can live on the stack + // `multiply` only uses types without additional owned data, that can live on + // the stack wasmtime_component_type_t mult_param_types[2]; mult_param_types[0].kind = WASMTIME_COMPONENT_KIND_F32; mult_param_types[1].kind = WASMTIME_COMPONENT_KIND_F32; wasmtime_component_type_t f32_result_type; f32_result_type.kind = WASMTIME_COMPONENT_KIND_F32; - wasmtime_error_t* error = wasmtime_component_linker_define_func( - linker, "host", 4, "multiply", 8, mult_param_types, 2, - &f32_result_type, 1, mult, (void*)multiply_data, NULL); + wasmtime_error_t *error = wasmtime_component_linker_define_func( + linker, "host", 4, "multiply", 8, mult_param_types, 2, &f32_result_type, + 1, mult, (void *)multiply_data, NULL); if (error) exit_with_error("failed to define function multiply", error, NULL); - // `apply` uses an enum, which needs additional data, use a wasmtime_component_type_vec_t + // `apply` uses an enum, which needs additional data, use a + // wasmtime_component_type_vec_t wasmtime_component_type_vec_t apply_param_types; wasmtime_component_type_vec_new_uninitialized(&apply_param_types, 3); apply_param_types.data[0].kind = WASMTIME_COMPONENT_KIND_F32; apply_param_types.data[1].kind = WASMTIME_COMPONENT_KIND_F32; apply_param_types.data[2].kind = WASMTIME_COMPONENT_KIND_ENUM; - wasmtime_component_string_vec_new_uninitialized(&apply_param_types.data[2].payload.enumeration, 2); - wasm_name_new_from_string(&apply_param_types.data[2].payload.enumeration.data[0], "add"); - wasm_name_new_from_string(&apply_param_types.data[2].payload.enumeration.data[1], "multiply"); + wasmtime_component_string_vec_new_uninitialized( + &apply_param_types.data[2].payload.enumeration, 2); + wasm_name_new_from_string( + &apply_param_types.data[2].payload.enumeration.data[0], "add"); + wasm_name_new_from_string( + &apply_param_types.data[2].payload.enumeration.data[1], "multiply"); error = wasmtime_component_linker_define_func( - linker, "host", 4, "apply", 5, apply_param_types.data, apply_param_types.size, - &f32_result_type, 1, apply, (void*)apply_data, NULL); + linker, "host", 4, "apply", 5, apply_param_types.data, + apply_param_types.size, &f32_result_type, 1, apply, (void *)apply_data, + NULL); if (error) exit_with_error("failed to define function apply", error, NULL); // deleting the vector also drops the full types hierarchy @@ -90,9 +95,13 @@ int main() { // Load binary. printf("Loading binary...\n"); - FILE *file = fopen("target/wasm32-unknown-unknown/debug/guest.wasm", "rb"); + // Note that the binary should be a component (not a plain module), typically + // built by running `cargo component build -p example-component-wasm --target + // wasm32-unknown-unknown`, here created with a secondary usage of rust test + FILE *file = + fopen("target/wasm32-unknown-unknown/debug/guest-component.wasm", "rb"); if (!file) { - printf("> Error opening module!\n"); + printf("> Error opening component!\n"); return 1; } fseek(file, 0L, SEEK_END); @@ -101,41 +110,44 @@ int main() { wasm_byte_vec_t binary; wasm_byte_vec_new_uninitialized(&binary, file_size); if (fread(binary.data, file_size, 1, file) != 1) { - printf("> Error reading module!\n"); + printf("> Error reading component!\n"); return 1; } fclose(file); // Compile. - // Note that the binary should be a component (not a plain module), typically built by running - // `cargo component build -p example-component-wasm --target wasm32-unknown-unknown` - // this will therefore fail if only the README instructions are followed printf("Compiling component...\n"); - wasmtime_component_t* component; - error = wasmtime_component_from_binary(engine, (uint8_t *)binary.data, binary.size, &component); + wasmtime_component_t *component; + error = wasmtime_component_from_binary(engine, (uint8_t *)binary.data, + binary.size, &component); if (error) exit_with_error("failed to build component", error, NULL); wasm_byte_vec_delete(&binary); - wasmtime_store_t *store = wasmtime_store_new(engine, (void*)context_data, NULL); + wasmtime_store_t *store = + wasmtime_store_new(engine, (void *)context_data, NULL); wasmtime_context_t *context = wasmtime_store_context(store); // Instantiate. printf("Instantiating component...\n"); wasmtime_component_instance_t *instance; - error = wasmtime_component_linker_instantiate(linker, context, component, &instance); + error = wasmtime_component_linker_instantiate(linker, context, component, + &instance); if (error) exit_with_error("failed to instantiate component", error, NULL); // Lookup functions. - wasmtime_component_func_t* convert1; - const char* func_name1 = "convert-celsius-to-fahrenheit"; - bool ok = wasmtime_component_instance_get_func(instance, context, func_name1, strlen(func_name1), &convert1); + wasmtime_component_func_t *convert1; + const char *func_name1 = "convert-celsius-to-fahrenheit"; + bool ok = wasmtime_component_instance_get_func(instance, context, func_name1, + strlen(func_name1), &convert1); if (!ok) - exit_with_error("function convert-celsius-to-fahrenheit not found", NULL, NULL); - wasmtime_component_func_t* convert2; - const char* func_name2 = "convert"; - ok = wasmtime_component_instance_get_func(instance, context, func_name2, strlen(func_name2), &convert2); + exit_with_error("function convert-celsius-to-fahrenheit not found", NULL, + NULL); + wasmtime_component_func_t *convert2; + const char *func_name2 = "convert"; + ok = wasmtime_component_instance_get_func(instance, context, func_name2, + strlen(func_name2), &convert2); if (!ok) exit_with_error("function convert not found", NULL, NULL); @@ -146,24 +158,27 @@ int main() { param_val.payload.f32 = 23.4f; // will be written, but must be "droppable", i.e. without owned data result_val.kind = WASMTIME_COMPONENT_KIND_BOOL; - wasm_trap_t* trap = NULL; - error = wasmtime_component_func_call(convert1, context, ¶m_val, 1, &result_val, 1, &trap); + wasm_trap_t *trap = NULL; + error = wasmtime_component_func_call(convert1, context, ¶m_val, 1, + &result_val, 1, &trap); if (error != NULL || trap != NULL) - exit_with_error("failed to call function convert-celsius-to-fahrenheit", error, trap); + exit_with_error("failed to call function convert-celsius-to-fahrenheit", + error, trap); assert(result_val.kind == WASMTIME_COMPONENT_KIND_F32); printf("23.4°C = %f°F\n", result_val.payload.f32); printf("Calling convert...\n"); - wasmtime_component_val_t* t = wasmtime_component_val_new(); + wasmtime_component_val_t *t = wasmtime_component_val_new(); t->kind = WASMTIME_COMPONENT_KIND_VARIANT; t->payload.variant.discriminant = 1; t->payload.variant.val = wasmtime_component_val_new(); t->payload.variant.val->kind = WASMTIME_COMPONENT_KIND_F32; t->payload.variant.val->payload.f32 = 66.2f; - wasmtime_component_val_t* result = wasmtime_component_val_new(); + wasmtime_component_val_t *result = wasmtime_component_val_new(); - error = wasmtime_component_func_call(convert2, context, t, 1, result, 1, &trap); + error = + wasmtime_component_func_call(convert2, context, t, 1, result, 1, &trap); if (error != NULL || trap != NULL) exit_with_error("failed to call function", error, trap); wasmtime_component_val_delete(t); diff --git a/examples/component/main.rs b/examples/component/main.rs index 784fc134bc32..5bc346fa58f6 100644 --- a/examples/component/main.rs +++ b/examples/component/main.rs @@ -49,13 +49,23 @@ fn convert_to_component(path: impl AsRef) -> Result> { } fn main() -> Result<()> { - // Create an engine with the component model enabled (disabled by default). - let engine = Engine::new(Config::new().wasm_component_model(true))?; - // NOTE: The wasm32-unknown-unknown target is used here for simplicity, real world use cases // should probably use the wasm32-wasip1 target, and enable wasi preview2 within the component // model. - let component = convert_to_component("target/wasm32-unknown-unknown/debug/guest.wasm")?; + let module_path = "target/wasm32-unknown-unknown/debug/guest.wasm"; + let component = convert_to_component(module_path)?; + + if std::env::args().len() > 1 { + // if called with an argument, just write the component binary + // this is useful for the c-api test, which doesn't expose wit_component::ComponentEncoder + let component_path = module_path.replace("guest.wasm", "guest-component.wasm"); + println!("{} bytes written to {}", component.len(), component_path); + fs::write(&component_path, &component)?; + return Ok(()); + } + + // Create an engine with the component model enabled (disabled by default). + let engine = Engine::new(Config::new().wasm_component_model(true))?; // Create our component and call our generated host functions. let component = Component::from_binary(&engine, &component)?;