// 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_LENGTH_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_import_func {
     const char *interface;
     const char *name;

     // If this 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 imported as an async function, then this field will
     // be null.
     wit_import_fn_t impl;

     // If this 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 sync imported functions.
     wit_import_async_fn_t async_impl;
     wit_import_async_lift_fn_t async_lift_impl;

     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_import_func_t;

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

     // 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;
} wit_export_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_length_list {
     const char *interface;
     const char *name;
     size_t size;
     wit_type_t ty;
} wit_fixed_length_list_t;

typedef void(*wit_lift_fn_t)(void* cx, const void *buffer);
typedef void(*wit_lower_fn_t)(void* cx, void *buffer);

typedef uint64_t(*wit_future_new_fn_t)();
typedef uint32_t(*wit_future_read_fn_t)(uint32_t future, void *buffer);
typedef uint32_t(*wit_future_write_fn_t)(uint32_t future, const void *buffer);
typedef uint32_t(*wit_future_cancel_read_fn_t)(uint32_t future);
typedef uint32_t(*wit_future_cancel_write_fn_t)(uint32_t future);
typedef void(*wit_future_drop_readable_fn_t)(uint32_t future);
typedef void(*wit_future_drop_writable_fn_t)(uint32_t future);

typedef struct wit_future {
     const char *interface;
     const char *name;
     wit_type_t ty;
     wit_future_new_fn_t new;
     wit_future_read_fn_t read;
     wit_future_write_fn_t write;
     wit_future_cancel_read_fn_t cancel_read;
     wit_future_cancel_write_fn_t cancel_write;
     wit_future_drop_readable_fn_t drop_readable;
     wit_future_drop_writable_fn_t drop_writable;
     wit_lift_fn_t lift;
     wit_lower_fn_t lower;
     size_t abi_payload_size;
     size_t abi_payload_align;
} wit_future_t;

typedef uint64_t(*wit_stream_new_fn_t)();
typedef uint32_t(*wit_stream_read_fn_t)(uint32_t stream, void *buffer, size_t count);
typedef uint32_t(*wit_stream_write_fn_t)(uint32_t stream, const void *buffer, size_t count);
typedef uint32_t(*wit_stream_cancel_read_fn_t)(uint32_t stream);
typedef uint32_t(*wit_stream_cancel_write_fn_t)(uint32_t stream);
typedef void(*wit_stream_drop_writable_fn_t)(uint32_t stream);
typedef void(*wit_stream_drop_readable_fn_t)(uint32_t stream);

typedef struct wit_stream {
     const char *interface;
     const char *name;
     wit_type_t ty;
     wit_stream_new_fn_t new;
     wit_stream_read_fn_t read;
     wit_stream_write_fn_t write;
     wit_stream_cancel_read_fn_t cancel_read;
     wit_stream_cancel_write_fn_t cancel_write;
     wit_stream_drop_readable_fn_t drop_readable;
     wit_stream_drop_writable_fn_t drop_writable;
     wit_lift_fn_t lift;
     wit_lower_fn_t lower;
     size_t abi_payload_size;
     size_t abi_payload_align;
} wit_stream_t;

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

#define WIT_CURRENT_VERSION 2

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

     size_t num_import_funcs;
     const wit_import_func_t *import_funcs;
     size_t num_export_funcs;
     const wit_export_func_t *export_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_length_lists;
     const wit_fixed_length_list_t *fixed_length_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` points 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 language 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
