WebAssembly calling JavaScript methods from wasm i.e. within C++ code - javascript

I was playing with WebAssembly and so far and I was able to manage emscripten compile my test C++ project to wasm file
em++ provides me 2 files i.e.
mainTest.js mainTest.wasm
When I load mainTest.js in my html page then I get a JavaScript object called
"Module".
I did found how to call C++/wasm methods from javascript i.e. something like:
var myTestInteger = Module._callMyTestMethod();
and read strings from the
Module.wasmMemory.buffer
, but I do NOT understand how to call JavaScript from C++ code.
i.e. I would like to be able to do something like that:
#ifdef __cplusplus
extern "C" {
#endif
extern void testExternJSMethod();
int main()
{
cout << " Hello From my Test1 !" << endl;
testExternJSMethod();
return 0;
}
int EMSCRIPTEN_KEEPALIVE callMyTestMethod(){
return 26;
}
#ifdef __cplusplus
}
#endif
and the my js method testExternMethod that I am loading in another js file called utils.js
function testExternMethod() {
console.log("Hello from testExternMethod!" + )
}
Here I would like to call the JavaScript testExternJSMethod from C++.
When I run the page in Firefox get "-1" in the debugger console.
So what I am missing in this case? Unfortunately The Mozilla documentation is giving only examples in those S-expressions instead of C++.
What am I missing in example? In C++ I have defined the method with the extern keyword i.e.
extern void testExternJSMethod();
but I get the feeling that that is not all I have to do.
I believe that I should somehow link that JavaScript method to the Module somehow but I do not know how.
Module.asm gives me the exports. Which method call should give me the imports? since I believe that this _testExternJSMethod() should be in some imports method I can not figure out how to get to it.

I'm not exactly sure of your use case, but you are missing important steps to be able to use your function testExternalJSMethod. You have two options here:
Option 1 - Library
1 - Define your function in c/c++ .
extern void testExternalJSMethod();
2 - Create a file called myLibrary.js
3 - The JS function needs to be added to the LibraryManager in your library file with the following code:
function makeAlert(text) {
alert(text);
}
if (typeof mergeInto !== 'undefined') mergeInto(LibraryManager.library, {
testExternalJSMethod: function() {
makeAlert("Hello world");
}
});
4 - If testExternalJSMethod depends on anything outside of its own scope (for example, makeAlert above), make sure to include the script in your html page
<script async type="text/javascript" src="myLibrary.js"></script>
5 - Add option --js-library to your emcc command, and immediately after the relative path to myLibrary.js
emcc ... --js-library myLibrary.js
Option 2 - Passing Pointers
1 - Define your javascript function type in c/c++
typedef void testExternalJSMethod()
2 - Wherever you want this function to be used, accept an int param which will be the function pointer, and cast the pointer to your function
void passFnPointer(int ptr) {
((testExternalJSMethod*)ptr)();
}
3 - Use emscripten's addFunction() and store its returned value (the pointer in c/c++)
var fnPtr = Module.addFunction(function () {
alert("You called testExternalJSMethod");
});
4 - Use the stored pointer value from step 3 to pass to our function passFnPointer
var passFnPointer = Module.cwrap('passFnPointer', 'undefined', ['number']);
passFnPointer(fnPtr);
5 - Add option -s RESERVED_FUNCTION_POINTERS to your emcc command
emcc ... -s RESERVED_FUNCTION_POINTERS=10

Have you tried looking at the Emscripten documentation? It has a whole section on interacting with code that details how to expose C / C++ functions to JavaScript and call JavaScript functions from C / C++.
Emscripten provides two main approaches for calling JavaScript from C/C++: running the script using emscripten_run_script() or writing “inline JavaScript”.
It is worth noting that the Mozilla documentation details plain WebAssembly, whereas Emscripten adds a lot more framework and tooling around WebAssembly in order to make it easier to port large C / C++ codebases.

Related

How to call from Javascript a method define in Kotlin in a Kvision project

I have a Kotlin/JS project, using Kvision (I think started from a Kvision template)
My build.gradle.kts looks like this
https://ideone.com/yOEcMF
Questions:
How do I call from JS code a function defined by me in Kotlin?
Say I have this in .kt file :
package com.zzz
class KotlinHelper {
fun doXXX(str: String): Int = str.length
fun doYYY(bytes: ByteArray): String = bytes.decodeToString()
}
val kotlinHelper = KotlinHelper()
I want to call it from Javascript +/- like this
kotlinHelper.doXXX("something");
(the other way around I managed, meaning call from Kotlin code defined in JS - by use of "external" modifier on Kotlin class & actual implementation in JS)
Found this https://kotlinlang.org/docs/js-to-kotlin-interop.html .. but still didn't managed.
Can you explain how this works ?
My Kotlin code + whatever Kotlin + Kvision brings seem to get bundled & transpiled to Javascript in "main.bundle.js"
Code also seems obfuscated & minified.. I obviously want to call method with the name that I defined it have - is this possible?
Is the Kotlin code, that is not used - removed?
worked like this
Call from JS
new KTJS_Kvision.com.xxx.yyy.KotlinHelper().doXXX("Ana")
KTJS_Kvision - is my project name
com.xxx.yyy.KotlinHelper is the full name of class (incl packageName)
I also have these annotations:
#JsExport
class KotlinHelper {
#JsName("doXXX")
fun doXXX(str: String): Int = str.length
...

Is it possible to only convert a dart function to javascript

I am currently using the following package where the readme illustrates the following
final bool loaded = await JsIsolatedWorker().importScripts(['test.js']);
I am using the isolate worker package so my code can work on web and outside of web. I would like to generate javascript code from my dart code. I have one file with a top level function and I use
dart compile js -O0 -o test.js test.dart
which I found here
https://dart.dev/tools/dart2js
this is my dart file
void main(List<String> args) {
doComputation('');
}
String doComputation(String input) {
return 'output';
}
I can generate javascript only if I have a main function but this generates a javascript file where the doComutation is not a top level function, so I am not sure if the package can call the function. It looks like it generates an entire program instead of just generating one function.
The generated file is too long to post
So what my question comes down to is this. Is there a way to generate javascript from dart for 1 function with its dependencies included instead of having to generate the entire program? So that I can call this function from dart.
I am not an expert but I also had this problem. Here's what worked for me:
In web_worker.dart:
import 'package:js/js.dart';
main(){
allowInterop(doComputation);
}
#JS('doComputation')
String doComputation(String computationInput) {
// Replace this with your actual computation.
final String computationOutput = "output";
return computationOutput;
}
Compile it using:
$ dart compile js web_worker.dart -o webWorker.js
Manually edit webWorker.js, the JS file generated by the compiler:
Delete this line at the top of the file:
(function dartProgram() {
and the line at the bottom of the file with the corresponding closing brace:
})();
I don't understand what's going on here but I found the Javascript version of the function doComputation() defined in webWorker.js as a property of the object "A".
I defined a wrapper at the top of the file like this:
function doComputation(computationInput) {
return A.doComputation(computationInput)
}
and then I was able to use the file with JsIsolatedWorker like this:
final bool loaded =
await JsIsolatedWorker().importScripts(['../web/webWorker.js']);
if (loaded) {
final String computationResult = await JsIsolatedWorker()
.run(functionName: 'doComputation', arguments: computationInput);
} else {
debugPrint('Web worker is not available');
}
If someone who understands this better can elaborate or improve on this solution, that would be great. I don't really have any idea what I'm doing. I'm just posting this to hopefully help other people save time in the future by sharing what worked for me.
You can use js package to call JavaScript APIs from Dart code, or vice versa. To make a Dart function callable from JavaScript by name, use a setter annotated with #JS().
#JS()
library callable_function;
import 'package:js/js.dart';
/// Allows assigning a function to be callable from `window.functionName()`
#JS('functionName')
external set _functionName(void Function() f);
/// Allows calling the assigned function from Dart as well.
#JS()
external void functionName();
void _someDartFunction() {
print('Hello from Dart!');
}
void main() {
_functionName = allowInterop(_someDartFunction);
}
JavaScript code may now call functionName() or window.functionName().
Check google chartjs https://github.com/google/chartjs.dart/tree/master/example for a complete example.
From dart2js "Helping dart2js generate better code" there is a tip :
Don’t worry about the size of your app’s included libraries. The dart2js compiler performs tree shaking to omit unused classes, functions, methods, and so on. Just import the libraries you need, and let dart2js get rid of what you don’t need.
Related to this post https://stackoverflow.com/a/21124252/3733730.
You can do this instead by using the package dart2js
one make a program for your main.dart dart2js where the function or command you wanted to make is there, then create a separate dart file what function you wanted to call, and it will fix your problem, it is a separate file but will only execute one function that you needed to do so and that is the separate file

How to call pre-existing WebAssembly code from Emscripten

I've made a compiler from my programming language (called AEC) targeting WebAssembly. However, that compiler only produces WebAssembly Binary Toolkit (WABT) compatible code, as I couldn't find sufficient documentation for the assembly compatible with Emscripten. So, how can I call the functions in that language from Emscripten-compatible C or C++?
Previously, I made a compiler for that language targeting x86, and it was rather easy to use C++ to interact with code written in that language. The same, however, doesn't seem to be true when using WebAssembly.
Let's say I have this code in AEC:
Function plusOne(Integer32 integer) Which Returns Integer32 Does
Return integer + 1;
EndFunction
I compile it as follows:
[teo.samarzija#teos-acer-laptop debug]$ ../AECforWebAssembly/aec *.aec
Running the tests...
All the tests passed.
Reading the file...
All characters read!
Tokenizing the program...
Finished tokenizing the program!
Parsing the program...
Finished parsing the program!
Compiling the program...
Compilation finished!
Saving the assembly in file "plusOne.wat"...
Assembly successfully saved, quitting now.
[teo.samarzija#teos-acer-laptop debug]$ wat2wasm plusOne.wat
Now, let's say I want to call it from a C code such as this one:
#include <stdio.h>
extern int plusOne(int);
int main() {
printf("plusOne(4)=%d\n",plusOne(4));
return 0;
}
How do I compile the C program to be able to do that? If I try it in an analogous way I could do it with my AEC compiler targeting x86 assembly, I get this error:
[teo.samarzija#teos-acer-laptop debug]$ emcc -o test.html test.c plusOne.wasm
emcc: error: plusOne.wasm: Input file has an unknown suffix, don't know what to do with it!
So, what should I do?
The following NodeJS code accomplishes what I want:
const FileSystem = require('fs');
const wasmFileContent = FileSystem.readFileSync("plusOne.wasm");
let stack_pointer = new WebAssembly.Global({value : 'i32', mutable : true}, 0);
let memory = new WebAssembly.Memory({initial : 1, maximum : 1});
let importObject = {
JavaScript : {stack_pointer : stack_pointer, memory : memory}
};
WebAssembly.instantiate(wasmFileContent, importObject).then((results) => {
const exports = results.instance.exports;
let plusOne = exports.plusOne;
console.log("plusOne(4)=", plusOne(4));
});
So, how can I do that in C or C++ using Emscripten?

Where should I defined emscripten extern functions in JS?

Suppose I have function x in C++ defined as:
extern "C" void x();
and I implement it in JS in global context
function _x() { console.log('x called'); }
_x is defined in asm compiled js file, which is getting invoked and not my implementation. What am I doing wrong?
I'm getting this warning while linking:
warning: unresolved symbol: x
Here is the stacktrace:
Uncaught abort() at Error
at jsStackTrace (http://localhost/module.js:978:13)
at stackTrace (http://localhost/module.js:995:22)
at abort (http://localhost/module.js:71106:25)
at _x (http://localhost/module.js:5829:46)
at Array._x__wrapper (http://localhost/module.js:68595:41)
at Object.dynCall_vi (http://localhost/module.js:68442:36)
at invoke_vi (http://localhost/module.js:7017:25)
at _LoadFile (http://localhost/module.js:7573:6)
at asm._LoadFile (http://localhost/module.js:69219:25)
at eval (eval at cwrap (http://localhost/module.js:554:17), <anonymous>:6:26)
As described at Implement a C API in Javascript, you define a library by having a Javascript file that calls mergeInto to merge an object with your Javascript functions into LibraryManager.library. You then compile with the --js-library option to pass in the location of your library.
For example, you can have Javascript file with your library of a single function
// In library.js
mergeInto(LibraryManager.library, {
x: function() {
alert('hi');
},
});
a main C++ file that calls this function
// in librarytest.cc
extern "C" void x();
int main()
{
x();
return 0;
}
and compile to HTML and Javascript with
em++ librarytest.cc --js-library library.js -o librarytest.html
then you load librarytest.html in a browser, it will show an alert box with hi.
If you want to pass a string from C++ to Javascript, you don't have to go the route of Implement a C API in Javascript. Instead, you can use the EM_ASM* macros to directly inline the Javascript. This can then call out to Javascript functions you have defined, and pass their values.
Also, to pass a string, you need to make sure you pass a C-style string, and then use the Emscripten-supplied Pointer_stringify Javascript function on the result:
#include <string>
#include <emscripten.h>
int main()
{
std::string myString("This is a string in C++");
EM_ASM_ARGS({
console.log(Pointer_stringify($0));
}, myString.c_str());
return 0;
}

Javascript extension to use C based APIs(clutter) in a webapp

My goal is to use the C libraries to form web apps.
I have chosen the way to do that via using "SWIG" tool.
The Swig tool requires three things:
.c file which defines all the functions.
.i file also called interface file which is creating the
interface to load the APIs wherein I used the extern keyword.
APP written in Javascript extension (.js file).
I used SWIG tool to compile and run this app to verify the .js file has made correctly.
The application is running fine on XMING X11 window.
On compilation it creates _wrap.o, .o file and libFILENAME.so.
Now I want to run this app on browser page.
For this I have used the webkit clutter port which gives us the MxLauncher code.
I'm using webkit_iweb_view_load_uri(WEBKIT_IWEB_VIEW(view), "filename.html"); API to load my html file to run that Javascript on my webpage view.
I'm linking the .so created at the compilation time.
Error Message: JS CONSOLE: file:///filename.js:
ReferenceError: Can't find variable: example
filename.c
int gcd(int x, int y) `enter code here`{
int g;
g = y;
while (x > 0) {
g = x;
x = y % x;
y = g;
}
return g;
}
filename.i
%module example
extern int gcd(int x, int y);
filename.js
x = 42;
y = 105;
g = example.gcd(x,y);
How to get my goal to be achieved?
You also need to tell WebKit/JavaScriptCore at runtime about your bindings (this is in addition to linking with filename_wrap.o).
Specifically you need to bind them to the global JavaScript object (in order to invoke per your .js examples). A callback on the WebKit window can be used to get a timely reference to the global JavaScript context, and then you can register your functions onto it.
Adapting this example of hooking into the window-object-cleared signal the code could look similar to this:
/* the window callback -
fired when the JavaScript window object has been cleared */
static void window_object_cleared_cb(WebKitWebView *web_view,
WebKitWebFrame *frame,
gpointer context,
gpointer window_object,
gpointer user_data)
{
/* Add your classes to JavaScriptCore */
example_init(context); // example_init generated by SWIG
}
/* ... and in your main application set up */
void yourmainfunc()
{
....
g_signal_connect (G_OBJECT (web_view), "window-object-cleared",
G_CALLBACK(window_object_cleared_cb), web_view);
webkit_web_view_load_uri (WEBKIT_WEB_VIEW (web_view), "file://filename.html");
...
}
Depending on which branch of SWIG you are using you may need to generate the example_init function yourself (check filename.cxx); for reference here is what an initializer function to register wrapped C functions would look like in SWIG:
int example_init(JSContextRef context) {
JSObjectRef global = JSContextGetGlobalObject(context);
...
jsc_registerFunction(context, global, "gcd", _wrap_gcd);
...
}
NOTE -- SWIG does not yet officially support JavaScript; the above refers to using the work-in-progress (non-production) SWIG branches.
References:
SWIG-V8 source and its Javascript documentation
swig-jsc source and its example of registering bindings
SWIG JavaScriptCore GSoC project source (Google Summer of Code 2012)
Webkit: Extending JavaScript article-- tutorial / example code

Categories

Resources