Thread overview
Web Assembly, struct to JavaScript Object and back
Jun 23, 2020
tirithen
Jun 24, 2020
Sebastiaan Koppe
Jun 24, 2020
tirithen
June 23, 2020
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-js-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?
June 24, 2020
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.
June 24, 2020
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.