second-state / wasmedge-quickjs

A high-performance, secure, extensible, and OCI-complaint JavaScript runtime for WasmEdge.
Apache License 2.0
477 stars 59 forks source link

Can I use this lib to embed JS code into C++ app? #106

Open IKhomZera opened 1 year ago

IKhomZera commented 1 year ago

Similar to https://github.com/second-state/wasmedge-quickjs/blob/main/examples/embed_js/src/main.rs

Can I use this lib to embed JS code into C++ app?

L-jasmine commented 1 year ago

If you don't want to use WebAssembly and prefer to directly run JavaScript with C++, then this library is not suitable. But if you want to run WebAssembly through C++, and execute some JavaScript inside WebAssembly, then this library is somewhat suitable.

IKhomZera commented 1 year ago

@L-jasmine I want to run WebAssembly through C++, and execute some JavaScript inside WebAssembly.

I see here https://github.com/second-state/wasmedge-quickjs/blob/main/examples/embed_js/src/main.rs how to use it through Rust app.

What I want to understand is re how to use this lib through C++

L-jasmine commented 1 year ago

You can use the Wasmedge CPP API to run Wasmedge inside your C++ program, and then use Wasmedge to load the WebAssembly from the 'release' page. Your C++ program will not directly use this library.

IKhomZera commented 1 year ago

@L-jasmine Here is what I'm trying currently to do:

#include <iostream>
#include <wasmedge/wasmedge.h>
#include <stdio.h>
#include <string>
#include <sstream>

using namespace std;

int runJS() {
  /* Create the configure context and add the WASI support. */
  /* This step is not necessary unless you need WASI support. */
  WasmEdge_ConfigureContext *ConfCxt = WasmEdge_ConfigureCreate();
  WasmEdge_ConfigureAddHostRegistration(ConfCxt,
                                        WasmEdge_HostRegistration_Wasi);
  /* The configure and store context to the VM creation can be NULL. */
  WasmEdge_VMContext *VMCxt = WasmEdge_VMCreate(ConfCxt, NULL);

  // /* The parameters and returns arrays. */
  WasmEdge_Value Params[2] = {WasmEdge_ValueGenI32(5), WasmEdge_ValueGenI32(5)};
  WasmEdge_Value Returns[1];
  // /* Function name. */
  WasmEdge_String FuncName = WasmEdge_StringCreateByCString("main");

  /* Result. */
  WasmEdge_Result Res;

 /* Step 1: Load WASM file. */
  Res = WasmEdge_VMLoadWasmFromFile(VMCxt, "wasmedge_quickjs.wasm");
  /*
   * Developers can load the WASM binary from buffer with the
   * `WasmEdge_VMLoadWasmFromBuffer()` API, or from
   * `WasmEdge_ASTModuleContext` object with the
   * `WasmEdge_VMLoadWasmFromASTModule()` API.
   */
  if (!WasmEdge_ResultOK(Res)) {
    printf("Loading phase failed: %s\n", WasmEdge_ResultGetMessage(Res));
    return 1;
  }
  /* Step 2: Validate the WASM module. */
  Res = WasmEdge_VMValidate(VMCxt);
  if (!WasmEdge_ResultOK(Res)) {
    printf("Validation phase failed: %s\n", WasmEdge_ResultGetMessage(Res));
    return 1;
  }
  /* Step 3: Instantiate the WASM module. */
  Res = WasmEdge_VMInstantiate(VMCxt);
  /*
   * Developers can load, validate, and instantiate another WASM module to
   * replace the instantiated one. In this case, the old module will be
   * cleared, but the registered modules are still kept.
   */
  if (!WasmEdge_ResultOK(Res)) {
    printf("Instantiation phase failed: %s\n",
           WasmEdge_ResultGetMessage(Res));
    return 1;
  }

   /*
   * Step 4: Execute WASM functions. You can execute functions repeatedly
   * after instantiation.
   */
  Res = WasmEdge_VMExecute(VMCxt, FuncName, Params, 2, Returns, 1);
  if (WasmEdge_ResultOK(Res)) {
    printf("Get the result: %d\n", WasmEdge_ValueGetI32(Returns[0]));
  } else {
    printf("Execution phase failed: %s\n", WasmEdge_ResultGetMessage(Res));
  }

  /* Resources deallocations. */
  WasmEdge_VMDelete(VMCxt);
  WasmEdge_ConfigureDelete(ConfCxt);
  WasmEdge_StringDelete(FuncName);

  return 0;
}

int main(int argc, char **argv) {
  runJS();

  return 0;
}

I load wasmedge-quickjs wasm file wasmedge_quickjs.wasm and then trying to run there a main function.

Then run it this way:

gcc main.cpp -lwasmedge && ./a.out

and it prints some result Get the result: 39894674

My current issue is the following:

Could you help me with this?

L-jasmine commented 1 year ago

https://github.com/WasmEdge/WasmEdge/blob/53a01b1b5f7aabc598be4298445074afcd6c8aff/examples/capi/wasi-env/set_wasi_env.c#L28C28-L28C28

The arguments for the main function in WebAssembly are obtained from WASI, so you need to set the arguments you want to pass to the main function in the WASI module, just like what was done in the link provided above.

Another point is that, for WebAssembly supporting WASI, their startup function is often _start instead of main. This is a function with no parameters and no return. So your RunJs() should be modified to

int runJS() {
  /* Create the configure context and add the WASI support. */
  /* This step is not necessary unless you need WASI support. */
  WasmEdge_ConfigureContext *ConfCxt = WasmEdge_ConfigureCreate();
  WasmEdge_ConfigureAddHostRegistration(ConfCxt,
                                        WasmEdge_HostRegistration_Wasi);
  /* The configure and store context to the VM creation can be NULL. */
  WasmEdge_VMContext *VMCxt = WasmEdge_VMCreate(ConfCxt, NULL);

  // /* The parameters and returns arrays. */
  WasmEdge_Value Params[0] = {};           //maybe,  I am not very familiar with C++ syntax.
  WasmEdge_Value Returns[0] = {};           //maybe,  I am not very familiar with C++ syntax.
  // /* Function name. */
  WasmEdge_String FuncName = WasmEdge_StringCreateByCString("_start");

  /* Result. */
  WasmEdge_Result Res;
.....
IKhomZera commented 1 year ago

@L-jasmine that does work quite well, thanks a lot for your support 🥇

Just one final thing

My js file looks like the following:

import * as os from 'os';
import * as std from 'std';
import * as process from 'process'

args = args.slice(1);
print('echo Hello: ', ...args);

Is there a way to get this output after calling WasmEdge_VMExecute and store into C++ variable?

L-jasmine commented 1 year ago

Sorry for the delayed response. To solve this issue, you need to make changes in the Rust part. You can either fork this repository or create a new one that references this Rust library. In this way, Rust (Wasm) can retain the execution result from JavaScript, and then you can retrieve the saved result from Rust (Wasm) in C++ using Wasmedge.