Sharing memory between WebAssembly and JavaScript

So far, we have been working with one type of variable in WebAssembly. These are known as local variables, or stack variables. There is another type that will allow us to not only utilize them across our WebAssembly context but also share them between our JavaScript and our WebAssembly. But first, we will need to discuss the differences between the stack and the heap.

There is a difference between global/local variables and the stack/heap. For now, we are going to keep things simple and treat global variables as if they are on the heap and local variables as if they are on the stack. Later, our application will have a global state that is not on the heap, but it is best to try to keep the idea of the local equivalent to the stack and the global equivalent to the heap.

We talked about the stack when we talked about how programs run on a typical computer. The best way to think of this is a stack of wood. We will always pull from the top and always add to the top. This is the same way in programming. We add to the top of the stack and then we pull from those top items. As an example, let's take the add function that we created.

We grabbed the two parameters and added them to the stack. First, parameter one and then parameter two. When we called $externalAdd or even if we called the add function built into WebAssembly, it takes those two items off the stack and replaces them with one item, the result. When we return from a function, we take that item off the local function stack and pop it on the top of the stack of the context for whoever called us.

A heap is just like what its name implies. We have the blob of things that can be grabbed, changed, and replaced from everywhere. Anyone can get to the heap, put items into it, and read from it. It's like a heap of clothes  we can search through it and find the item we need, or we can just add to it at the end of the day.

The main difference between the two is that the stack will get cleaned up. Any variables that we created inside it, once the function returns, are cleaned up. On the other hand, the heap stays there. Since anyone has access to it, we have to explicitly get rid of it; otherwise, it will be there permanently. In garbage-collected environments, the best way to think of this is that our environment doesn't know who else has items on it, so it doesn't know what needs to be cleaned up and what doesn't.

In WebAssembly, we don't have a garbage-collected environment, so we have to recollect the heap once we are done with it in our JavaScript or WebAssembly context. In our examples, we won't be doing this, so note that this is something we would want to do in a production-style environment. To do this, we could just set the memory object in JavaScript to null. This will let the garbage collector know that no one is utilizing it anymore.

Let's learn how to share memory between JavaScript and WebAssembly and how that is equivalent to the heap. Follow these steps:

  1. Create a file named sharing_resources.wat.
  2. Put the following code inside the file:
(module
(import "js" "mem" (memory 1))
(func $storeNumber
(i32.store (i32.const 0) (i32.const 100))
)
(func $readNumber (result i32)
(i32.load (i32.const 0))
)
(export "readNumber" (func $readNumber))
(export "storeNumber" (func $storeNumber))
)

Our first function stores the number 100 at memory location 0. If we were storing an arbitrary amount of data, we would have to let whoever called us know how much we stored. However, in this case, we always know that it is just one number.

Our read function just reads that value from memory and returns it as a value.

  1. Our script section inside the index.html file should look something like the following:
const memory = new WebAssembly.Memory({initial : 1});
const importObject = { js: {mem: memory}};
WebAssembly.instantiateStreaming(fetch('sharing_resources.wasm'), importObject).then(obj => {
obj.instance.exports.storeNumber();
console.log(obj.instance.exports.readNumber());
});

The top section should look different. First, we are creating a piece of memory that both JavaScript and WebAssembly can share. We are going to create and load only one section of memory. In the context of WebAssembly, this is 64 KB worth of data.

Once our WebAssembly has loaded, we store the number and then read it out. Now, we can see that we have a global state in WebAssembly, but how would we share this with our JavaScript? Well, that starting section of the code tells us how. We have access to the memory object, so we should be able to get at it. Let's go ahead and change our script a bit so that we can read the memory directly inside JavaScript instead of calling a function that does this for us.

The following code should do this:

function readNumber() {
const bytes = new Uint32Array(memory.buffer, 0, 1);
console.log('The number that was put here is:', bytes[0]);
}

Now, we can add this to the body after our WebAssembly has loaded:

obj.instance.exports.storeNumber();
readNumber();

If we look inside our console, we should see the exact same output! The final test is to store something from JavaScript and grab it inside WebAssembly. We can achieve this by changing the script to the following:

const memory = new WebAssembly.Memory({initial : 1});
const storeByte = new Int32Array(memory.buffer, 0, 1);
storeByte[0] = 200;
const importObject = {js: {mem: memory}};
WebAssembly.instantiateStreaming(fetch('sharing_resources.wasm'), importObject).then(obj => {
console.log(obj.instance.exports.readNumber());
});

If we save this and go back to our console, we should see that the number 200 is printed out!

Now, we know how to share memory between two instances and how we can utilize this to do some cool stuff. Let's go ahead and put all of our skills to the test and create every programmer's favorite program: FizzBuzz.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.37.154