mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-08 13:48:26 +00:00
Clean up WASI 'Wasm C API' API
This commit is contained in:
@@ -63,7 +63,8 @@ pub unsafe extern "C" fn wasi_state_builder_arg(
|
|||||||
state_builder.arg(arg_bytes);
|
state_builder.arg(arg_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// must be transparent
|
// NOTE: don't modify this type without updating all users of it. We rely on
|
||||||
|
// this struct being `repr(transparent)` with `Box<dyn WasiFile>` in the API.
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct wasi_file_handle_t {
|
pub struct wasi_file_handle_t {
|
||||||
inner: Box<dyn WasiFile>,
|
inner: Box<dyn WasiFile>,
|
||||||
@@ -100,7 +101,6 @@ pub unsafe extern "C" fn wasi_output_capturing_file_read(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: figure out ownership here
|
|
||||||
/// Override the Stdout that the WASI program will see.
|
/// Override the Stdout that the WASI program will see.
|
||||||
///
|
///
|
||||||
/// This function takes ownership of the `wasi_file_handle_t` passed in.
|
/// This function takes ownership of the `wasi_file_handle_t` passed in.
|
||||||
@@ -127,15 +127,11 @@ pub unsafe extern "C" fn wasi_state_builder_set_stderr(
|
|||||||
state_builder.stderr(stderr.inner);
|
state_builder.stderr(stderr.inner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: don't modify this type without updating all users of it. We rely on
|
||||||
|
// this struct being `repr(transparent)` with `WasiState` in the API.
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct wasi_state_t {
|
pub struct wasi_state_t {
|
||||||
inner: Box<WasiState>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct wasi_state_borrowed_t {
|
|
||||||
inner: WasiState,
|
inner: WasiState,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,26 +140,26 @@ pub struct wasi_state_borrowed_t {
|
|||||||
pub extern "C" fn wasi_state_builder_build(
|
pub extern "C" fn wasi_state_builder_build(
|
||||||
mut state_builder: Box<WasiStateBuilder>,
|
mut state_builder: Box<WasiStateBuilder>,
|
||||||
) -> Option<Box<wasi_state_t>> {
|
) -> Option<Box<wasi_state_t>> {
|
||||||
let inner = Box::new(c_try!(state_builder.build()));
|
let inner = c_try!(state_builder.build());
|
||||||
Some(Box::new(wasi_state_t { inner }))
|
Some(Box::new(wasi_state_t { inner }))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct wasi_env_t {
|
pub struct wasi_env_t {
|
||||||
inner: Box<WasiEnv>,
|
inner: WasiEnv,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes ownership over the `wasi_state_t`.
|
/// Takes ownership over the `wasi_state_t`.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn wasi_env_new(state: Box<wasi_state_t>) -> Box<wasi_env_t> {
|
pub extern "C" fn wasi_env_new(state: Box<wasi_state_t>) -> Box<wasi_env_t> {
|
||||||
Box::new(wasi_env_t {
|
Box::new(wasi_env_t {
|
||||||
inner: Box::new(WasiEnv::new(*state.inner)),
|
inner: WasiEnv::new(state.inner),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn wasi_env_delete(_state: Option<Box<wasi_state_t>>) {}
|
pub extern "C" fn wasi_env_delete(_state: Option<Box<wasi_env_t>>) {}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn wasi_env_set_memory(env: &mut wasi_env_t, memory: &wasm_memory_t) {
|
pub extern "C" fn wasi_env_set_memory(env: &mut wasi_env_t, memory: &wasm_memory_t) {
|
||||||
@@ -171,17 +167,19 @@ pub extern "C" fn wasi_env_set_memory(env: &mut wasi_env_t, memory: &wasm_memory
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn wasi_env_borrow_state(env: &wasi_env_t) -> &wasi_state_borrowed_t {
|
pub extern "C" fn wasi_env_borrow_state(env: &wasi_env_t) -> &wasi_state_t {
|
||||||
let state: &WasiState = &*env.inner.state();
|
let state: &WasiState = &*env.inner.state();
|
||||||
|
// This is correct because `wasi_state_t` is `repr(transparent)` to `WasiState`
|
||||||
unsafe { mem::transmute(state) }
|
unsafe { mem::transmute(state) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// returns a non-owning reference to stdout
|
/// returns a non-owning reference to stdout
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn wasi_state_get_stdout(
|
pub extern "C" fn wasi_state_get_stdout(
|
||||||
state: &wasi_state_borrowed_t,
|
state: &wasi_state_t,
|
||||||
) -> Option<&Option<wasi_file_handle_t>> {
|
) -> Option<&Option<wasi_file_handle_t>> {
|
||||||
let inner: &Option<Box<dyn WasiFile>> = c_try!(state.inner.fs.stdout());
|
let inner: &Option<Box<dyn WasiFile>> = c_try!(state.inner.fs.stdout());
|
||||||
|
// This is correct because `wasi_file_handle_t` is `repr(transparent)` to `Box<dyn WasiFile>`
|
||||||
let temp = unsafe { mem::transmute::<_, &'static Option<wasi_file_handle_t>>(inner) };
|
let temp = unsafe { mem::transmute::<_, &'static Option<wasi_file_handle_t>>(inner) };
|
||||||
Some(temp)
|
Some(temp)
|
||||||
}
|
}
|
||||||
@@ -241,7 +239,7 @@ pub unsafe extern "C" fn wasi_get_imports(
|
|||||||
//let version = c_try!(WasiVersion::try_from(version));
|
//let version = c_try!(WasiVersion::try_from(version));
|
||||||
let version = WasiVersion::try_from(version).ok()?;
|
let version = WasiVersion::try_from(version).ok()?;
|
||||||
|
|
||||||
let import_object = generate_import_object_from_env(store, (&*wasi_env.inner).clone(), version);
|
let import_object = generate_import_object_from_env(store, wasi_env.inner.clone(), version);
|
||||||
|
|
||||||
// TODO: this is very inefficient due to all the allocation required
|
// TODO: this is very inefficient due to all the allocation required
|
||||||
let mut extern_vec = vec![];
|
let mut extern_vec = vec![];
|
||||||
@@ -257,11 +255,3 @@ pub unsafe extern "C" fn wasi_get_imports(
|
|||||||
|
|
||||||
Some(extern_vec.into_boxed_slice())
|
Some(extern_vec.into_boxed_slice())
|
||||||
}
|
}
|
||||||
|
|
||||||
// get WASI import object
|
|
||||||
|
|
||||||
// TASKS TODO:
|
|
||||||
// - [x] clean up and simplify caputure code
|
|
||||||
// - [ ] generate header file for this new WASI API
|
|
||||||
// - [ ] get import objects working
|
|
||||||
// - [ ] finish C example
|
|
||||||
|
|||||||
5
lib/c-api/tests/.gitignore
vendored
5
lib/c-api/tests/.gitignore
vendored
@@ -31,4 +31,7 @@ test-wasi-import-object
|
|||||||
test-emscripten-import-object
|
test-emscripten-import-object
|
||||||
|
|
||||||
# ignore wasm-c-api binaries
|
# ignore wasm-c-api binaries
|
||||||
wasm-c-api-*
|
wasm-c-api-*
|
||||||
|
|
||||||
|
# Unignore files ending with `.c` (i.e. `wasm-c-api-wasi.c`)
|
||||||
|
!*.c
|
||||||
@@ -44,6 +44,7 @@ if (DEFINED EMSCRIPTEN_TESTS)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
include_directories(wasm-c-api/include)
|
include_directories(wasm-c-api/include)
|
||||||
|
include_directories(..)
|
||||||
|
|
||||||
|
|
||||||
find_library(
|
find_library(
|
||||||
|
|||||||
160
lib/c-api/tests/wasm-c-api-wasi.c
Normal file
160
lib/c-api/tests/wasm-c-api-wasi.c
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
//#include "wasm.h"
|
||||||
|
#include "wasmer_wasm.h"
|
||||||
|
|
||||||
|
#define own
|
||||||
|
|
||||||
|
// Use the last_error API to retrieve error messages
|
||||||
|
void print_wasmer_error()
|
||||||
|
{
|
||||||
|
int error_len = wasmer_last_error_length();
|
||||||
|
printf("Error len: `%d`\n", error_len);
|
||||||
|
char *error_str = malloc(error_len);
|
||||||
|
wasmer_last_error_message(error_str, error_len);
|
||||||
|
printf("Error str: `%s`\n", error_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, const char* argv[]) {
|
||||||
|
// Initialize.
|
||||||
|
printf("Initializing...\n");
|
||||||
|
wasm_engine_t* engine = wasm_engine_new();
|
||||||
|
wasm_store_t* store = wasm_store_new(engine);
|
||||||
|
|
||||||
|
// Load binary.
|
||||||
|
printf("Loading binary...\n");
|
||||||
|
FILE* file = fopen("assets/qjs.wasm", "r");
|
||||||
|
if (!file) {
|
||||||
|
printf("> Error loading 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 loading module!\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
// Compile.
|
||||||
|
printf("Compiling module...\n");
|
||||||
|
own wasm_module_t* module = wasm_module_new(store, &binary);
|
||||||
|
if (!module) {
|
||||||
|
printf("> Error compiling module!\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
wasm_byte_vec_delete(&binary);
|
||||||
|
|
||||||
|
printf("Setting up WASI...\n");
|
||||||
|
wasi_file_handle_t* stdout_capturer = wasi_output_capturing_file_new();
|
||||||
|
|
||||||
|
wasi_state_builder_t* wsb = wasi_state_builder_new("example_program");
|
||||||
|
// TODO: error checking
|
||||||
|
const char* js_string = "function greet(name) { return JSON.stringify('Hello, ' + name); }; print(greet('World'));";
|
||||||
|
wasi_state_builder_arg(wsb, "--eval");
|
||||||
|
wasi_state_builder_arg(wsb, js_string);
|
||||||
|
wasi_state_builder_set_stdout(wsb, stdout_capturer);
|
||||||
|
|
||||||
|
wasi_state_t* wasi_state = wasi_state_builder_build(wsb);
|
||||||
|
if (!wasi_state) {
|
||||||
|
printf("> Error building WASI state!\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
wasi_env_t* wasi_env = wasi_env_new(wasi_state);
|
||||||
|
if (!wasi_env) {
|
||||||
|
printf("> Error building WASI env!\n");
|
||||||
|
print_wasmer_error();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
wasi_version_t version = wasi_get_wasi_version(module);
|
||||||
|
|
||||||
|
// Instantiate.
|
||||||
|
printf("Instantiating module...\n");
|
||||||
|
const wasm_extern_t* const* imports = wasi_get_imports(store, module, wasi_env, version);
|
||||||
|
if (!imports) {
|
||||||
|
printf("> Error getting WASI imports!\n");
|
||||||
|
print_wasmer_error();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
own wasm_instance_t* instance =
|
||||||
|
wasm_instance_new(store, module, imports, NULL);
|
||||||
|
if (!instance) {
|
||||||
|
printf("> Error instantiating module!\n");
|
||||||
|
print_wasmer_error();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Extract export.
|
||||||
|
printf("Extracting export...\n");
|
||||||
|
own wasm_extern_vec_t exports;
|
||||||
|
wasm_instance_exports(instance, &exports);
|
||||||
|
if (exports.size == 0) {
|
||||||
|
printf("> Error accessing exports!\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "found %zu exports\n", exports.size);
|
||||||
|
|
||||||
|
printf("Getting memory...\n");
|
||||||
|
const wasm_memory_t* memory = wasm_extern_as_memory(exports.data[0]);
|
||||||
|
if (! memory) {
|
||||||
|
printf("Could not get memory!\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
wasi_env_set_memory(wasi_env, memory);
|
||||||
|
const wasm_func_t* run_func = wasm_extern_as_func(exports.data[1]);
|
||||||
|
if (run_func == NULL) {
|
||||||
|
printf("> Error accessing export!\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
wasm_module_delete(module);
|
||||||
|
wasm_instance_delete(instance);
|
||||||
|
|
||||||
|
// Call.
|
||||||
|
printf("Calling export...\n");
|
||||||
|
printf("Evaluating \"%s\"\n", js_string);
|
||||||
|
if (wasm_func_call(run_func, NULL, NULL)) {
|
||||||
|
printf("> Error calling function!\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int BUF_SIZE = 128;
|
||||||
|
char buffer[BUF_SIZE] = { };
|
||||||
|
wasi_state_t* wasi_state_ref = wasi_env_borrow_state(wasi_env);
|
||||||
|
wasi_file_handle_t* stdout_handle = wasi_state_get_stdout(wasi_state_ref);
|
||||||
|
if (!stdout_handle) {
|
||||||
|
printf("> Error getting stdout!\n");
|
||||||
|
print_wasmer_error();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
size_t result = BUF_SIZE;
|
||||||
|
for (size_t i = 0;
|
||||||
|
// TODO: this code is too clever, make the control flow more obvious here
|
||||||
|
result == BUF_SIZE &&
|
||||||
|
(result = wasi_output_capturing_file_read(stdout_handle, buffer, BUF_SIZE, i * BUF_SIZE));
|
||||||
|
++i) {
|
||||||
|
printf("%.*s", BUF_SIZE, buffer);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
wasm_extern_vec_delete(&exports);
|
||||||
|
|
||||||
|
// Shut down.
|
||||||
|
printf("Shutting down...\n");
|
||||||
|
wasi_env_delete(wasi_env);
|
||||||
|
wasm_store_delete(store);
|
||||||
|
wasm_engine_delete(engine);
|
||||||
|
|
||||||
|
// All done.
|
||||||
|
printf("Done.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
136
lib/c-api/wasmer_wasm.h
Normal file
136
lib/c-api/wasmer_wasm.h
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
// This header file is for Wasmer APIs intended to be used with the standard Wasm C API.
|
||||||
|
|
||||||
|
#ifndef WASMER_WASM_H
|
||||||
|
#define WASMER_WASM_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "wasm.h"
|
||||||
|
|
||||||
|
#define own
|
||||||
|
|
||||||
|
// In order to use WASI, we need a `wasi_env_t`, but first we need a `wasi_state_t`.
|
||||||
|
//
|
||||||
|
// We get a `wasi_state_t` by building it with the `wasi_state_builder_t`.
|
||||||
|
// Once we have a `wasi_state_t`, we can use `wasi_env_new` to create a `wasi_env_t`.
|
||||||
|
//
|
||||||
|
// Once we have a `wasi_env_t` we must:
|
||||||
|
// - set it up with `wasi_env_set_memory` to expose a memory to the WASI host functions
|
||||||
|
// - call `wasi_get_imports` to get an array of imports needed to instantiate the Wasm module.
|
||||||
|
|
||||||
|
// Used to build a `wasi_state_t`.
|
||||||
|
typedef struct wasi_state_builder_t wasi_state_builder_t;
|
||||||
|
// An opaque file handle to a WASI file.
|
||||||
|
typedef struct wasi_file_handle_t wasi_file_handle_t;
|
||||||
|
// The core WASI data structure, used to create a `wasi_env_t`.
|
||||||
|
typedef struct wasi_state_t wasi_state_t;
|
||||||
|
// This type is passed to the WASI host functions and owns a `wasi_state_t`.
|
||||||
|
typedef struct wasi_env_t wasi_env_t;
|
||||||
|
|
||||||
|
// The version of WASI to use.
|
||||||
|
typedef uint32_t wasi_version_t;
|
||||||
|
|
||||||
|
const uint32_t WASI_VERSION_LATEST = 0;
|
||||||
|
const uint32_t WASI_VERSION_SNAPSHOT0 = 1;
|
||||||
|
const uint32_t WASI_VERSION_SNAPSHOT1 = 2;
|
||||||
|
const uint32_t WASI_VERSION_INVALID = ~0;
|
||||||
|
|
||||||
|
|
||||||
|
// Create a `wasi_state_builder_t`.
|
||||||
|
//
|
||||||
|
// Takes as an argument the name of the Wasm program to execute (will show up
|
||||||
|
// as argv[0] to the Wasm program).
|
||||||
|
own wasi_state_builder_t* wasi_state_builder_new(const char* program_name);
|
||||||
|
|
||||||
|
// Add an argument to be passed to the Wasi program.
|
||||||
|
void wasi_state_builder_arg(wasi_state_builder_t*, const char* arg);
|
||||||
|
|
||||||
|
// Add an environment variable to be passed to the Wasi program.
|
||||||
|
void wasi_state_builder_env(wasi_state_builder_t*, const char* key, const char* value);
|
||||||
|
|
||||||
|
// Override `sdtout` with the given `wasi_file_handle_t`.
|
||||||
|
void wasi_state_builder_set_stdout(wasi_state_builder_t*, wasi_file_handle_t*);
|
||||||
|
|
||||||
|
// Consume the `wasi_state_builder_t` and get a `wasi_state_t`.
|
||||||
|
own wasi_state_t* wasi_state_builder_build(own wasi_state_builder_t*);
|
||||||
|
|
||||||
|
// Create a `wasi_env_t`.
|
||||||
|
own wasi_env_t* wasi_env_new(own wasi_state_t*);
|
||||||
|
|
||||||
|
// Delete the `wasi_env_t`, used to clean up all the resources used by WASI.
|
||||||
|
void wasi_env_delete(own wasi_env_t*);
|
||||||
|
|
||||||
|
// Get an array of imports that can be used to instantiate the given module.
|
||||||
|
own const wasm_extern_t* own const* wasi_get_imports(wasm_store_t*, wasm_module_t*, wasi_env_t*, wasi_version_t);
|
||||||
|
|
||||||
|
// TODO: investigate removing this part of the API
|
||||||
|
// TODO: investigate removing the wasi_version stuff from the API
|
||||||
|
// Set the memory in the `wasi_env_t` so that the WASI host functions can access WASI's memory.
|
||||||
|
void wasi_env_set_memory(wasi_env_t*, const wasm_memory_t*);
|
||||||
|
|
||||||
|
// Get temporary access to `wasi_state_t` owned by the given `wasi_env_t`.
|
||||||
|
wasi_state_t* wasi_env_borrow_state(const wasi_env_t*);
|
||||||
|
|
||||||
|
// Get the version of WASI needed by the given Wasm module.
|
||||||
|
wasi_version_t wasi_get_wasi_version(wasm_module_t*);
|
||||||
|
|
||||||
|
// TODO: consider using a circular buffer and making read a mutable operation to
|
||||||
|
// avoid wasted memory.
|
||||||
|
// Create a capturing WASI file. This file stores all data written to it.
|
||||||
|
own wasi_file_handle_t* wasi_output_capturing_file_new();
|
||||||
|
|
||||||
|
// Delete an owned `wasi_file_handle_t`
|
||||||
|
void wasi_output_capturing_file_delete(own wasi_file_handle_t*);
|
||||||
|
|
||||||
|
// Read from a capturing file (created by `wasi_output_capturing_file_new`.
|
||||||
|
size_t wasi_output_capturing_file_read(wasi_file_handle_t* file,
|
||||||
|
char* buffer,
|
||||||
|
size_t buffer_len,
|
||||||
|
size_t start_offset);
|
||||||
|
|
||||||
|
// Get temporary access to the `stdout` WASI file.
|
||||||
|
wasi_file_handle_t* wasi_state_get_stdout(wasi_state_t*);
|
||||||
|
|
||||||
|
// TODO: figure out if we can do less duplication.
|
||||||
|
/**
|
||||||
|
* Gets the length in bytes of the last error if any.
|
||||||
|
*
|
||||||
|
* This can be used to dynamically allocate a buffer with the correct number of
|
||||||
|
* bytes needed to store a message.
|
||||||
|
*
|
||||||
|
* See `wasmer_last_error_message()` to get a full example.
|
||||||
|
*/
|
||||||
|
int wasmer_last_error_length();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the last error message if any into the provided buffer
|
||||||
|
* `buffer` up to the given `length`.
|
||||||
|
*
|
||||||
|
* The `length` parameter must be large enough to store the last
|
||||||
|
* error message. Ideally, the value should come from
|
||||||
|
* `wasmer_last_error_length()`.
|
||||||
|
*
|
||||||
|
* The function returns the length of the string in bytes, `-1` if an
|
||||||
|
* error occurs. Potential errors are:
|
||||||
|
*
|
||||||
|
* * The buffer is a null pointer,
|
||||||
|
* * The buffer is too smal to hold the error message.
|
||||||
|
*
|
||||||
|
* Note: The error message always has a trailing null character.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* ```c
|
||||||
|
* int error_length = wasmer_last_error_length();
|
||||||
|
*
|
||||||
|
* if (error_length > 0) {
|
||||||
|
* char *error_message = malloc(error_length);
|
||||||
|
* wasmer_last_error_message(error_message, error_length);
|
||||||
|
* printf("Error message: `%s`\n", error_message);
|
||||||
|
* } else {
|
||||||
|
* printf("No error message\n");
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
int wasmer_last_error_message(char* buffer, int length);
|
||||||
|
|
||||||
|
#endif /* WASMER_WASM_H */
|
||||||
Reference in New Issue
Block a user