www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Web Assembly, struct to JavaScript Object and back

reply tirithen <tirithen gmail.com> writes:
I'm experimenting with generating wasm files with ldc2 but I'm 
having problems when trying to pass JavaScript Objects and 
receive them as structs and the other way around.

I found this super nice getting started guide that works fine for 
basic types like double and so on 
https://wiki.dlang.org/Generating_WebAssembly_with_LDC , but I'm 
puzzled on how to pass more advanced data structures.

I found this C example where it's mentioned that structs in that 
case will always be passed by pointer 
https://stackoverflow.com/questions/50615377/how-do-you-call-a-c-function-that-takes-or-returns-a-struct-by-value-from-
s-v#answer-56405899 . Is something similar possible with D?

My setup so far:

test.d:

extern (C): // disable D mangling

void callback(double a, double b, double c);

double add(double a, double b) {
   const c = a + b;
   callback(a, b, c);
   return c;
}

struct Vector2 {
   double x;
   double y;
}

Vector2 addVectors(Vector2 a, Vector2 b) {
   return Vector2(a.x + b.x, a.y + b.y);
}

void _start() {
}

index.html:

<html>
   <head>
     <script>
       const callback = (a, b, c) => {
         console.log(`callback from D: ${a} + ${b} = ${c}`);
       };

       fetch('test.wasm').then((response) => 
response.arrayBuffer()).then((code) => {
         const importObject = {env: {callback}};
         WebAssembly.instantiate(code, importObject).then(result 
=> {
           const {exports} = result.instance;

           const r = exports.add(42, -2.5);
           console.log('r = ' + r);

           const r2 = exports.addVectors({x: 2, y: 4}, {x: 1, y: 
-1.5});
           console.log('r2 =', r2);
         });
       });
     </script>
   </head>
   <body>
     Test page, open console to see the WASM results
   </body>
</html>

Then I build test.d with:

$ ldc2 -mtriple=wasm32-unknown-unknown-wasm -L-allow-undefined 
-betterC test.d

When I serve and open the address in a browser and open the 
console I get:

r = 39.5
r2 = undefined

Anyone that has something similar working with struct objects?
Jun 23 2020
parent reply Sebastiaan Koppe <mail skoppe.eu> writes:
On Tuesday, 23 June 2020 at 18:15:25 UTC, tirithen wrote:
 Anyone that has something similar working with struct objects?
Passing anything besides int/double/bool between JS and wasm is hard work. Currently the ABI generated by LDC is so that arguments besides int/double/bool (and array/delegates are special too) are passed by pointer. So if you want to call a wasm function that expects a struct, you instead pass a pointer to wasm memory where you have encoded that struct. Similarly if you want to call a js function with rich objects you have to do some handling on the JS side. Return values other than int/double/bool from wasm functions are special and it is expected that the function is called with a pointer as first argument. There are a few gotcha but this basically describes it. In https://github.com/skoppe/spasm I am using this approach as well. Except I don't actually move D struct to and from JS, instead only references to JS objects, strings, delegates, unions and arrays. Same principle applies though.
Jun 24 2020
parent tirithen <tirithen gmail.com> writes:
On Wednesday, 24 June 2020 at 10:53:19 UTC, Sebastiaan Koppe 
wrote:
 On Tuesday, 23 June 2020 at 18:15:25 UTC, tirithen wrote:
 [...]
Passing anything besides int/double/bool between JS and wasm is hard work. [...]
Thanks for a really good explanation, passing pointers over the structs matches well with the C examples I found. Your repo has a lot of really good examples that I'll use as a guide.
Jun 24 2020