Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Webassembly: possible to have shared objects?

I am wondering if, using C (or C++ or Rust) and javascript, I am able to do CRUD operations to a shared data object. Using the most basic example, here would be an example or each of the operations:

#include <stdio.h>
typedef struct Person {
    int age;
    char* name;
} Person;

int main(void) {

    // init
    Person* sharedPersons[100];
    int idx=0;

    // create
    sharedPersons[idx] = (Person*) {12, "Tom"};

    // read
    printf("{name: %s, age: %d}", sharedPersons[idx]->name, sharedPersons[idx]->age);

    // update
    sharedPersons[idx]->age = 11;

    // delete
    sharedPersons[idx] = NULL;

}

Then, I would like to be able to do the exact same thing in Javascript, and both be able to write to the same shared sharedPersons object. How could this be done? Or does the setup need to be something like a 'master-slave' where one just needs to pass back information to the other and the master does all the relevant actions? I'm hoping that there's a way do CRUD on a shared data object in webassembly, and any help would be greatly appreciated.

As a reference: https://rustwasm.github.io/wasm-bindgen/contributing/design/js-objects-in-rust.html

like image 233
David542 Avatar asked May 23 '21 01:05

David542


People also ask

What is a WebAssembly AnyRef?

That proposal adds a new basic WebAssembly type called anyref. With an anyref, JavaScript just gives WebAssembly a reference object (basically a pointer that doesn’t disclose the memory address). This reference points to the object on the JS heap. Then WebAssembly can pass it to other JS functions, which know exactly how to use it.

Will WebAssembly solve the interoperability problem with JavaScript?

So that solves one of the most annoying interoperability problems with JavaScript. But that’s not the only interoperability problem to solve in the browser. There’s another, much larger, set of types in the browser. WebAssembly needs to be able to interoperate with these types if we’re going to have good performance.

Can JavaScript and WebAssembly talk to each other?

In the browser, WebAssembly and JS both run in the same engine, so that engine can help them efficiently talk to each other. But there is one problem when JS and WebAssembly try to talk to each other… they use different types. Currently, WebAssembly can only talk in numbers. JavaScript has numbers, but also quite a few more types.

What are the benefits of WebAssembly over JavaScript?

There are two main benefits WebAssembly provides: The kind of binary format being considered for WebAssembly can be natively decoded much faster than JavaScript can be parsed (experiments show more than 20× faster).


1 Answers

Creating the object

Let's create the object in C and return it:

typedef struct Person {
    int age;
    char* name;
} Person;

Person *get_persons(void) {
    Person* sharedPersons[100];
    return sharedPersons;
}

You could also create the object in JS, but it's harder. I'll come back to this later.

In order for JS to get the object, we've defined a function (get_persons) that returns (a pointer to) it. In this case it's an array, but of course it could have been a single object. The thing is, there must be a function that will be called from JS and that provides the object.

Compiling the program

emcc \
    -s "SINGLE_FILE=1" \
    -s "MODULARIZE=1" \
    -s "ALLOW_MEMORY_GROWTH=1" \
    -s "EXPORT_NAME=createModule" \
    -s "EXPORTED_FUNCTIONS=['_get_persons', '_malloc', '_free']" \
    -s "EXPORTED_RUNTIME_METHODS=['cwrap', 'setValue', 'getValue', 'AsciiToString', 'writeStringToMemory']" \
    -o myclib.js
    person.c

I don't remember why we have a leading underscore in _get_persons, but that's how Emscripten works.

Getting the object in JS

const createModule = require('./myclib');

let myclib;
let Module;

export const myclibRuntime = createModule().then((module) => {
  get_persons: Module.cwrap('get_persons', 'number', []),
});

What this does is create a get_persons() JS function that is a wrapper of the C get_persons() function. The return value of the JS function is "number". Emscripten knows that the C get_persons() function returns a pointer, and the wrapper will convert that pointer to a JS number. (Pointers in WASM are 32-bit.)

Manipulating the object in JS

const persons = get_persons();
Module.getValue(persons, 'i32');  // Returns the age of the first person
Module.AsciiToString(Module.getValue(persons + 4, 'i32'));  // Name of first person

// Set the second person to be "Alice", age 18
const second_person = persons + 8;
Module.setValue(second_person, 18, 'i32');
const buffer = Module._malloc(6);  // Length of "Alice" plus the null terminator
Module.writeStringToMemory("Alice", buffer);
Module.setValue(second_person + 4, buffer, 'i32');

This is a fairly low level way of doing it, although there seems to be an even lower level way. As other people have suggested, there may be higher level tools to help in C++ and Rust.

Creating the object in JS

You can create objects in JS by using _malloc() (and free them with _free()) as we did with the string above, and then pass their pointers to C functions. But, as I said, creating them in C is probably easier. In any case, anything _malloc()ed must eventually be freed (so the string creation above is incomplete). The FinalizationRegistry can help with this.

like image 175
Antonis Christofides Avatar answered Sep 23 '22 15:09

Antonis Christofides