wit-dylib 0.240.0

Generate an dynamic wasm library from a WIT world.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
// Generic implementation of a WIT world with a static set of functions.
//
// This is the header file for the dynamic library generated by
// `wasm-tools component wit-dylib`. This header describes in-memory data
// structures that are generated as well as functions that the dynamic library
// is expected to export.
//
// At a high level a `wit_t` provides type information during component
// initialization and then `wit_dylib_export_call` is used as the entrypoint to
// invoke functions. Various other `wit_dylib_*` intrinsics will receive indices
// relative to the original `wit_t` value.

#ifndef WIT_INTERPRETER_H
#define WIT_INTERPRETER_H

#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>

typedef uint32_t wit_type_t;

#define WIT_TYPE_KIND(ty) ((ty) & 0xff)
#define WIT_TYPE_INDEX(ty) ((ty) >> 8)

#define WIT_TYPE_U8 0
#define WIT_TYPE_U16 1
#define WIT_TYPE_U32 2
#define WIT_TYPE_U64 3
#define WIT_TYPE_S8 4
#define WIT_TYPE_S16 5
#define WIT_TYPE_S32 6
#define WIT_TYPE_S64 7
#define WIT_TYPE_BOOL 8
#define WIT_TYPE_CHAR 9
#define WIT_TYPE_F32 10
#define WIT_TYPE_F64 11
#define WIT_TYPE_STRING 12
#define WIT_TYPE_ERROR_CONTEXT 13
#define WIT_TYPE_RECORD 14
#define WIT_TYPE_OWN 15
#define WIT_TYPE_BORROW 16
#define WIT_TYPE_FLAGS 17
#define WIT_TYPE_TUPLE 18
#define WIT_TYPE_VARIANT 19
#define WIT_TYPE_ENUM 20
#define WIT_TYPE_OPTION 21
#define WIT_TYPE_RESULT 22
#define WIT_TYPE_LIST 23
#define WIT_TYPE_FIXED_SIZE_LIST 24
#define WIT_TYPE_FUTURE 25
#define WIT_TYPE_STREAM 26
#define WIT_TYPE_ALIAS 27
#define WIT_TYPE_EMPTY 0xff

typedef void(*wit_import_fn_t)(void* cx);
typedef uint32_t(*wit_import_async_fn_t)(void* cx, void *abi_area);
typedef void(*wit_import_async_lift_fn_t)(void* cx, void *abi_area);
typedef void(*wit_export_task_return_fn_t)(void* cx);

typedef struct wit_func {
     const char *interface;
     const char *name;

     // If this is an imported function, and the function is imported for
     // synchronous invocation, this field will be non-null.
     //
     // This function pointer takes a single `void*` argument which is the
     // context for the call. The context will be passed to various
     // `wit_dylib_*` intrinsics below for pushing/popping values from the stack
     // contained within `cx`.
     //
     // If this function is an exported function, or it's imported as an async
     // function, then this field will be null.
     wit_import_fn_t impl;

     // If this is an imported function and the function is imported for
     // asynchronous invocation these two fields will be non-null.
     //
     // The `async_impl` field is a function pointer that starts the async
     // import. This function call takes a `void *cx` just like `impl` above,
     // and it must stay alive for the entire invocation of the async imported
     // function. The second parameter of `async_impl` is an in-memory
     // allocation that must be of `async_abi_area_size` bytes aligned to
     // `async_abi_area_align`. This must also live for the duration of the
     // entire call and will be used to store canonical ABI values/results.
     // The return value from `async_impl` is the component-model status code
     // for the import's return value.
     //
     // The `async_lift_impl` field can be used once the component model
     // indicates that the function call is complete. The two parameters to
     // `async_lift_impl` as the same as `async_impl`. The `async_lift_impl`
     // function will use `wit_dylib_push_*` to translate from the canonical ABI
     // onto the stack within `cx`.
     //
     // These two fields are null for exported functions or sync imported
     // functions.
     wit_import_async_fn_t async_impl;
     wit_import_async_lift_fn_t async_lift_impl;

     // For exported functions which are exported as `async` this is the
     // `task.return` intrinsic to invoke.
     //
     // This function takes a single parameter which is a `void *cx` which is
     // used when passing to `wit_dylib_pop_*` functions. This must be used
     // to indicate the return value of an async function.
     wit_export_task_return_fn_t task_return;

     size_t nparams;
     const wit_type_t *params;
     wit_type_t result;

     // Only meaningful if `async_impl` is non-null, otherwise
     // `async_abi_area_{size,align}` are set to zero.
     size_t async_abi_area_size;
     size_t async_abi_area_align;
} wit_func_t;

typedef void(*wit_resource_drop_t)(uint32_t);
typedef uint32_t(*wit_resource_new_t)(size_t);
typedef size_t(*wit_resource_rep_t)(uint32_t);

typedef struct wit_resource {
     const char *interface;
     const char *name;
     wit_resource_drop_t drop;
     wit_resource_new_t new; // nullable
     wit_resource_rep_t rep; // nullable
} wit_resource_t;

typedef struct wit_field {
     const char *name;
     wit_type_t ty;
} wit_field_t;

typedef struct wit_record {
     const char *interface;
     const char *name;
     size_t nfields;
     const wit_field_t *fields;
} wit_record_t;

typedef struct wit_flags {
     const char *interface;
     const char *name;
     size_t nnames;
     const char **names;
} wit_flags_t;

typedef struct wit_tuple {
     const char *interface;
     const char *name;
     size_t ntypes;
     const wit_type_t *types;
} wit_tuple_t;

typedef struct wit_case {
     const char *name;
     wit_type_t ty;
} wit_case_t;

typedef struct wit_variant {
     const char *interface;
     const char *name;
     size_t ncases;
     const wit_case_t *cases;
} wit_variant_t;

typedef struct wit_enum {
     const char *interface;
     const char *name;
     size_t nnames;
     const char **names;
} wit_enum_t;

typedef struct wit_option {
     const char *interface;
     const char *name;
     wit_type_t ty;
} wit_option_t;

typedef struct wit_result {
     const char *interface;
     const char *name;
     wit_type_t ok;
     wit_type_t err;
} wit_result_t;

typedef struct wit_list {
     const char *interface;
     const char *name;
     wit_type_t ty;
} wit_list_t;

typedef struct wit_fixed_size_list {
     const char *interface;
     const char *name;
     size_t size;
     wit_type_t ty;
} wit_fixed_size_list_t;

typedef struct wit_future {
     const char *interface;
     const char *name;
     wit_type_t ty;
     // TODO: include future-related intrinsics for reading/writing
} wit_future_t;

typedef struct wit_stream {
     const char *interface;
     const char *name;
     wit_type_t ty;
     // TODO: include stream-related intrinsics for reading/writing
} wit_stream_t;

typedef struct wit_alias {
     const char *interface;
     const char *name;
     wit_type_t ty;
} wit_alias_t;

#define WIT_V0 0

typedef struct wit {
     uint32_t version; // `WIT_V*`

     size_t num_funcs;
     const wit_func_t *funcs;
     size_t num_resources;
     const wit_resource_t *resources;
     size_t num_records;
     const wit_record_t *records;
     size_t num_flags;
     const wit_flags_t *flags;
     size_t num_tuples;
     const wit_tuple_t *tuples;
     size_t num_variants;
     const wit_variant_t *variants;
     size_t num_enums;
     const wit_enum_t *enums;
     size_t num_options;
     const wit_option_t *options;
     size_t num_results;
     const wit_result_t *results;
     size_t num_lists;
     const wit_list_t *lists;
     size_t num_fixed_size_lists;
     const wit_fixed_size_list_t *fixed_size_lists;
     size_t num_futures;
     const wit_future_t *futures;
     size_t num_streams;
     const wit_stream_t *streams;
     size_t num_aliases;
     const wit_alias_t *aliases;
} wit_t;

// Invoked during `__wasm_call_ctors` with an in-memory `wit_t` data structure.
//
// The pointer provided lives for the lifetime of the entire program so it's
// safe to store this pointer.
void wit_dylib_initialize(const wit_t* wit);

// Generic byte deallocation function.
//
// This function will deallocate the `ptr` provided which was previously
// allocated with `cabi_realloc` which has `byte_size` bytes and `align`
// alignment.
//
// Note that if `defer` is set to `true` then this deallocation should happen
// when `cx` is deallocated, not right now. If `defer` is set to `false` then
// the deallocation can happen right now. The `defer` flag will be set
// when a list is translated into the canonical ABI format when passing to an
// import call or returning from an export. In this situation the deallocation
// needs to happen after the ABI value is read, such as after the import call
// or during post-return of the export.
void wit_dylib_dealloc_bytes(void *cx, void *ptr, size_t byte_size, size_t align, bool defer);

// Entrypoints for WIT exports.
//
// When an exported WIT function is called first `wit_dylib_export_start` will
// be invoked with `which` as an index into the `wit_t` provided as part of
// `wit_dylib_initialize`. The returned pointer is then passed as a contextual
// argument to everything below.
//
// The `wit_dylib_export_call` function is invoked once arguments have all
// been pushed into `cx`. The top of the stack of `cx` is the last argument of
// the function invocation. Once the call returns the result is pulled out of
// the stack of `cx` through the `*_pop_*` functions below.
//
// The `post-return` function will invoke `wit_dylib_export_finish` to clean up
// any allocations or such.
void *wit_dylib_export_start(size_t which);
void wit_dylib_export_call(void *cx, size_t which);
void wit_dylib_export_finish(void *cx, size_t which);

// Entrypoint for WIT resource destructors.
//
// The `ty` poitns to `wit->resources` and `handle` is the value being
// destroyed.
void wit_dylib_resource_dtor(size_t ty, size_t handle);

// =============================================================================
// Converting between WIT and language types.
//
// The functions below are used for converting a component model WIT value to a
// language's specific representation of a value. This is modeled as a
// stack-machine of sorts within a `cx` argument passed around to all functions.
// For example all "push" functions take a WIT value and push the
// language-specific representation onto `cx`'s internal stack. Composite types
// such as records will both pop and push to the stack.

void wit_dylib_push_bool(void *cx, bool val);
void wit_dylib_push_char(void *cx, uint32_t val);
void wit_dylib_push_u8(void *cx, uint8_t val);
void wit_dylib_push_s8(void *cx, int8_t val);
void wit_dylib_push_u16(void *cx, uint16_t val);
void wit_dylib_push_s16(void *cx, int16_t val);
void wit_dylib_push_u32(void *cx, uint32_t val);
void wit_dylib_push_s32(void *cx, int32_t val);
void wit_dylib_push_u64(void *cx, uint64_t val);
void wit_dylib_push_s64(void *cx, int64_t val);
void wit_dylib_push_f32(void *cx, float val);
void wit_dylib_push_f64(void *cx, double val);
void wit_dylib_push_flags(void *cx, size_t ty, uint32_t flags);
void wit_dylib_push_enum(void *cx, size_t ty, uint32_t enum_);
void wit_dylib_push_borrow(void *cx, size_t ty, uint32_t handle);
void wit_dylib_push_own(void *cx, size_t ty, uint32_t handle);
void wit_dylib_push_future(void *cx, size_t ty, uint32_t handle);
void wit_dylib_push_stream(void *cx, size_t ty, uint32_t handle);
// Note that `bytes` and `len` are allocated by `cabi_realloc` and thus this
// function is required to take ownership of the values.
void wit_dylib_push_string(void *cx, uint8_t *bytes, size_t len);
// Records and tuples pop fields from the stack of `cx`. The top entry of the
// stack is the last field, the next entry is the next-to-last field, and so on.
void wit_dylib_push_record(void *cx, size_t ty);
void wit_dylib_push_tuple(void *cx, size_t ty);
// Variants (and options/results) have their payload, if necessary, on the
// stack. If the `discr` case has a payload this needs to be popped. The end
// result of this, the final language value, is pushed to the stack.
void wit_dylib_push_option(void *cx, size_t ty, uint32_t discr);
void wit_dylib_push_result(void *cx, size_t ty, uint32_t discr);
void wit_dylib_push_variant(void *cx, size_t ty, uint32_t discr);
// If this function returns 0 then it means that `bytes`/`len` need to be pushed
// one-by-one. If a true value is returned then it's assume that `bytes` and
// `len` is now owned by the engine.
//
// Note that `bytes` was allocated with `cabi_realloc` and thus represents an
// owned allocation. This function takes ownership if a nonzero value is
// returned, otherwise the generated bindings will clean it up.
//
// If this function returns false then a list with `len` capacity should be
// pushed to the stack of `cx`. In this situation `wit_dylib_list_append` will
// be called element-by-element to pop an element from the stack and then push
// it onto the list which is then at the top of the stack.
bool wit_dylib_push_list(void *cx, size_t ty, uint8_t *bytes, size_t len);
void wit_dylib_list_append(void *cx, size_t ty);

uint8_t wit_dylib_pop_u8(void *cx);
uint16_t wit_dylib_pop_u16(void *cx);
uint32_t wit_dylib_pop_u32(void *cx);
uint64_t wit_dylib_pop_u64(void *cx);
int8_t wit_dylib_pop_s8(void *cx);
int16_t wit_dylib_pop_s16(void *cx);
int32_t wit_dylib_pop_s32(void *cx);
int64_t wit_dylib_pop_s64(void *cx);
float wit_dylib_pop_f32(void *cx);
double wit_dylib_pop_f64(void *cx);
bool wit_dylib_pop_bool(void *cx);
uint32_t wit_dylib_pop_char(void *cx);
uint32_t wit_dylib_pop_borrow(void *cx, size_t ty);
uint32_t wit_dylib_pop_own(void *cx, size_t ty);
uint32_t wit_dylib_pop_enum(void *cx, size_t ty);
uint32_t wit_dylib_pop_flags(void *cx, size_t ty);
uint32_t wit_dylib_pop_future(void *cx, size_t ty);
uint32_t wit_dylib_pop_stream(void *cx, size_t ty);
// When popping a string from the stack the `ptr` argument should be set to the
// location of the string in memory, and the `size_t` return value is the byte
// length of the string.
size_t wit_dylib_pop_string(void *cx, char **ptr);
// When popping a variant from the stack the langauge value is first removed.
// The discriminant of the value is returned, and if there is a payload for the
// case then it's pushed back onto the stack.
uint32_t wit_dylib_pop_option(void *cx, size_t type_index);
uint32_t wit_dylib_pop_result(void *cx, size_t type_index);
uint32_t wit_dylib_pop_variant(void *cx, size_t type_index);
// When a record or tuple is popped that means that the language's
// representation is being destructured. This pops a language value from the
// stack and then pushes the fields back to the stack. The last field should be
// pushed first meaning that the top entry of the stack after this is the first
// field of the record or tuple.
void wit_dylib_pop_record(void *cx, size_t ty);
void wit_dylib_pop_tuple(void *cx, size_t ty);
// When a list is popped from the stack the engine returns the pointer/length
// through this intrinsic.
//
// Note that `ptr` must be written in this function, and `NULL` has a special
// value. Regardless the return value of this function is the element length of
// the list that was popped from the stack.
//
// If this function returns a non-null pointer through the `ptr` field that
// means that the data is already in canonical ABI format (for example
// `list<u8>` is just a sequential list of bytes). In this situation the
// list should be popped from the stack and popping will continue with any
// further values from here.
//
// If this function returns a null pointer through the `ptr` field then it
// means that the data for this list is not in the canonical ABI format. That
// means that it's required to translate elements one-by-one. In this situation
// the list is popped from the stack and an iterator over the list is pushed
// to the stack.  Calls to `wit_dylib_pop_iter_next` are used to then extract
// a single element from the iterator and push it onto the stack. Once
// translation of the list is finished `wit_dylib_pop_iter` will be used to
// remove the iterator from the stack.
size_t wit_dylib_pop_list(void *cx, size_t ty, void **ptr);
void wit_dylib_pop_iter_next(void *cx, size_t ty);
void wit_dylib_pop_iter(void *cx, size_t ty);

#endif