bytecodealliance / wasm-micro-runtime

WebAssembly Micro Runtime (WAMR)
Apache License 2.0
4.84k stars 618 forks source link

Support customize wasm_runtime_invoke_method with cookie #220

Closed Hanks10100 closed 4 years ago

Hanks10100 commented 4 years ago

Background

I am trying to embed a standalone wasm runtime on the mobile. Both wasm3 and wasm-micro-runtime are under my consideration, so I provide a high-level API for developers to extending native APIs without specifying underlying wasm runtime (wasm3 or wamr or v8).

For now, I am using wasm3, I wish to switch to wamr without changing existing native API implementations.

Common Native API

If the original c APIs are as following:

int  foo(int a, int b);
void bar(const char* msg, const char* buffer, int len);

The common native api wrappers for wasm looks like:

void wasm_foo(const WasmFunctionContext& info) {
  int32_t a = info.GetI32(0);
  int32_t b = info.GetI32(1);

  info.ReturnI32(foo(a, b));
}

void wasm_bar(const WasmFunctionContext& info) {
  const char* msg = info.GetRef<char>(0);
  const char* buffer = info.GetRef<char>(1);
  int32_t len = info.GetI32(2);

  bar(msg, buffer, len);
}

Reading arguments and set return values can all be done by the WasmFunctionContext object (it stores the api signature), which may have different implementations for wasm3 and wamr.

Feature Request

What I want is to register a common_native_api with a cookie to wamr, process args and dispatch to the actual api function then set returns.

typedef void (* common_fn_t) (const WasmFunctionContext& info);

void common_native_api (wasm_exec_env_t* env, void* cookie) {
  WasmFunction* fn = (WasmFunction*) cookie;

  WasmFunctionContext info;
  for (const auto& sign : fn->argument_types()) {
    switch (sign) {
      // Read arguments from env and set to info
    }
  }

  // Equivalent to ((common_fn_t) fn->get())(info);
  fn->Invoke(info);

  for (const auto& sign : fn->return_types()) {
    switch (sign) {
      // Set the return values from info to env
    }
  }
}

The registration code looks like:

WasmFunction* cookie_foo = new WasmFunction("(ii)i", &wasm_foo);
WasmFunction* cookie_bar = new WasmFunction("($*~)", &wasm_bar);

NativeSymbolWithCookie native_symbols[2] = {
  { "foo", common_native_api, cookie_foo, cookie_foo->signature() },
  { "bar", common_native_api, cookie_bar, cookie_bar->signature() },
};

wasm_runtime_register_natives_with_cookie("env", native_symbols, 2);

The NativeSymbolWithCookie extends NativeSymbol with an extra void * ptr for cookie, which is the actual native api.

From my point of view, there are two things for wamr to do to achieve it:

  1. Extends NativeSymbol with a void* pointer as a cookie.
  2. Enable developers to customize their own wasm_runtime_invoke_method.

This feature request sounds tricky, but it can bring engineering advantages. Looking forward to your opinions. Thanks.

xwang98 commented 4 years ago

@Hanks10100 Fortuntely we are working on adding support for native APIs manually fetching the parameters passed from WASM app. It should be ready soon.

xwang98 commented 4 years ago

@Hanks10100 please refer to the pull #222 https://github.com/bytecodealliance/wasm-micro-runtime/pull/222/files

Please call the new introduced API for registering the native API that will fetch the arguments manually.

And in your native function, please call following function and marco for getting the "cookie" and fetching the arguments:

wenyongh commented 4 years ago

@Hanks10100 here are the detailed steps:

(1) Define the native symbol array with macro EXPORT_WASM_API_WITH_ATT/ATT2 (or fill in item fields one by one):

static NativeSymbol native_symbols[] = {
    EXPORT_WASM_API_WITH_ATT(foo, “(ii)i",  cookie),
    ...
};

Note that here '*', '~' and '$' are also supported for native pointer parameters.

(2) Register the native symbols after calling wasm_runtime_init() or wasm_runtime_full(), and before calling wasm_runtime_load(), call:

wasm_runtime_register_natives_raw() to register the symbols

(3) Define native API and use macro get the arguments, get attachment and set return value for example:

void foo(wasm_exec_env_t exec_env, uint64 *args)
{
    native_raw_return_type(int, args);
    native_raw_get_arg(int, x, args);
    native_raw_get_arg(int, y, args);
    void *attachment= wasm_runtime_get_function_attachment(exec_env)
    int res = x + y;
    native_raw_set_return(res);
}