WASM 调用 js 代码

Emscripten 提供了两种方式,用于从 C/C++ 调用 JavaScript 的方法:

  • 使用 emscripten_run_script() 运行脚本
  • 编写 inline JavaScript
  1. 最直接但稍微慢的方式是使用 emscripten_run_script()。这有效地使用 eval() 在 C/C++ 中运行指定的 JavaScript 代码。例如,调用浏览器的 alert() 函数,例如下面的代码:

    int EMSCRIPTEN_KEEPALIVE runScript(){
        emscripten_run_script("alert('hi')");
        emscripten_run_script("console.log('hello world!')");
        return 0;
    }
    
  2. 从 C 中调用 JavaScript 接口的一种更快的方法是编写 inline JavaScript,使用 EM_JS()EM_ASM() (以及其它相关的宏)。

    • EM_JS 是在 C 文件中声明一个 JavaScript 函数,使用方法参考这里

      #include <emscripten.h>
      
      EM_JS(void, myAlert, (), {
          alert('hello world!');
          throw 'all done';       // exception
      });
      
      EM_JS(void, take_args, (int x, float y), {
          console.log('I received: ' + [x, y]);
      });
      
      EM_JS(void, say_hello, (const char* cstr), {
          console.log('Hello, ', UTF8ToString(cstr));
      });
      
      EM_JS(int, add_forty_two, (int n), {
          return n + 42;
      });
      
      EM_JS(int, get_memory_size, (), {
          return HEAP8.length;
      });
      
      int main() {
          myAlert();
          take_args(10, 20.4f);
          say_hello("C++");
          int x = add_forty_two(100);
          int y = get_memory_size();
          cout << "return value of add_forty_two is " << x << endl;
          cout << "HEAP8 memory size is " << y << endl;
          return 0;
      }
      
    • EM_ASM 的使用方式与 inline assembly 代码类似

      #include <emscripten.h>
      
      int main() {
          EM_ASM(
              alert('hello world!');
              throw 'all done';
          );
      
          // pass params from C to javascript
          EM_ASM({
              console.log('I received: ' + $0);
          }, 100);
      
          // return value back
          int x = EM_ASM_INT({
              console.log('I received: ' + $0);
              return $0 + 1;
          }, 100);
      
          printf("%d\n", x);
      
          return 0;
      }
      

    注意:

    • 您需要使用适当的宏 EM_ASM_INTEM_ASM_DOUBLE 来指定返回值是 int 还是 double;
    • 输入参数使用 $0,$1 等;
    • return 被用来提供从 JavaScript 发送回 c 的值;
    • 使用 {} 来封装代码,以便将代码与稍后传递的参数区分开来,后面是输入参数;
    • 当使用 EM_ASM 宏时,请确保只使用单引号,双引号将导致编译器无法检测到的语法错误

js 回调

目标: 在 WebAssembly 端接收并解析 JSON 字符串后,做一些逻辑处理,然后返回修改后的 JSON 字符串

C++ 代码:

#include <emscripten.h>
int jsonParse(const char *jsonStr)
{
    auto j = nlohmann::json::parse(jsonStr);
    if (j.find("data") != j.end())
    {
        j["data"] = "Hi there!";
        string res = j.dump();

        EM_ASM({
            if (typeof window.onRspHandler == 'function')
            {
                window.onRspHandler(UTF8ToString($0))
            }
        }, res.c_str());
    }

    return 0;
}

使用 EM_ASM 调用外部 js 的 window.onRspHandler 回调方法。EM_ASM 大括号内可以书写任意的 JavaScript 代码,并且可以对其进行传参操作。在本例中,我们将 result 传递给 EM_ASM 方法,其 $0 为传参的等价替换,若还有更多参数则可以写为 $1、$2 等。

js 代码:

// js 回调函数
window.onRspHandler = (result) => {
    console.log(result); // 在控制台输出: {"data":"Hi there!"}
};

const jsonstr = JSON.stringify({data:"Hello Json!"});
const ptr = allocateUTF8(jsonstr);
Module._jsonParse(ptr);
_free(ptr);

参考:

  1. Interacting with code