Skip to content

Commit

Permalink
Use DataView to access WebAssembly memory (#33960)
Browse files Browse the repository at this point in the history
* Use DataView to access WebAssembly memory

* Apply suggestions from code review

Co-authored-by: Hamish Willee <[email protected]>

* Update files/en-us/webassembly/javascript_interface/memory/memory/index.md

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

---------

Co-authored-by: Hamish Willee <[email protected]>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Jun 7, 2024
1 parent 4ebcf6b commit 58d3a9a
Show file tree
Hide file tree
Showing 6 changed files with 22 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ The **`DataView`** view provides a low-level interface for reading and writing m

### Endianness

Multi-byte number formats are represented in memory differently depending on machine architecture — see [Endianness](/en-US/docs/Glossary/Endianness) for an explanation. `DataView` accessors provide explicit control of how data is accessed, regardless of the executing computer's endianness.
Multi-byte number formats are represented in memory differently depending on machine architecture — see [Endianness](/en-US/docs/Glossary/Endianness) for an explanation. `DataView` accessors provide explicit control of how data is accessed, regardless of the executing computer's endianness. For example, [WebAssembly](/en-US/docs/WebAssembly) memory is always little-endian, so you should use `DataView` instead of typed arrays to read and write multi-byte values. See [`WebAssembly.Memory`](/en-US/docs/WebAssembly/JavaScript_interface/Memory) for an example.

```js
const littleEndian = (() => {
Expand All @@ -25,6 +25,8 @@ const littleEndian = (() => {
console.log(littleEndian); // true or false
```

> **Note:** `DataView` defaults to big-endian read and write, but most platforms use little-endian.
### 64-bit Integer Values

Some browsers don't have support for {{jsxref("DataView.prototype.setBigInt64()")}} and {{jsxref("DataView.prototype.setBigUint64()")}}. So to enable 64-bit operations in your code that will work across browsers, you could implement your own `getUint64()` function, to obtain values with precision up to {{jsxref("Number.MAX_SAFE_INTEGER")}} — which could suffice for certain cases.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ const memory = new WebAssembly.Memory({
WebAssembly.instantiateStreaming(fetch("memory.wasm"), {
js: { mem: memory },
}).then((obj) => {
const summands = new Uint32Array(memory.buffer);
const summands = new DataView(memory.buffer);
for (let i = 0; i < 10; i++) {
summands[i] = i;
summands.setUint32(i * 4, i, true); // WebAssembly is little endian
}
const sum = obj.instance.exports.accumulate(0, 10);
console.log(sum);
Expand Down
12 changes: 7 additions & 5 deletions files/en-us/webassembly/javascript_interface/memory/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Originally you could only perform memory operations on a single memory in the Wa
More recent implementations allow WebAssembly [memory instructions](/en-US/docs/WebAssembly/Reference/Memory) to operate on a specified memory.
For more information see [Multiple memories](/en-US/docs/WebAssembly/Understanding_the_text_format#multiple_memories) in _Understanding WebAssembly text format_.

> **Note:** WebAssembly memory is always in little-endian format, regardless of the platform it's run on. Therefore, for portability, you should read and write multi-byte values in JavaScript using {{jsxref("DataView")}}.
## Constructor

- [`WebAssembly.Memory()`](/en-US/docs/WebAssembly/JavaScript_interface/Memory/Memory)
Expand Down Expand Up @@ -46,7 +48,7 @@ const memory = new WebAssembly.Memory({
});
```

The following example (see [memory.html](https://github.com/mdn/webassembly-examples/blob/main/js-api-examples/memory.html) on GitHub, and [view it live also](https://mdn.github.io/webassembly-examples/js-api-examples/memory.html)) fetches and instantiates the loaded "memory.wasm" bytecode using the [`WebAssembly.instantiateStreaming()`](/en-US/docs/WebAssembly/JavaScript_interface/instantiateStreaming_static) function, while importing the memory created in the line above. It then stores some values in that memory, exports a function, and uses the exported function to sum those values.
The following example (see [memory.html](https://github.com/mdn/webassembly-examples/blob/main/js-api-examples/memory.html) on GitHub, and [view it live also](https://mdn.github.io/webassembly-examples/js-api-examples/memory.html)) fetches and instantiates the loaded "memory.wasm" bytecode using the [`WebAssembly.instantiateStreaming()`](/en-US/docs/WebAssembly/JavaScript_interface/instantiateStreaming_static) function, while importing the memory created in the line above. It then stores some values in that memory, exports a function, and uses the exported function to sum those values. Note the use of {{jsxref("DataView")}} to access the memory so we always use little-endian format.

```js
const memory = new WebAssembly.Memory({
Expand All @@ -57,9 +59,9 @@ const memory = new WebAssembly.Memory({
WebAssembly.instantiateStreaming(fetch("memory.wasm"), {
js: { mem: memory },
}).then((obj) => {
const summands = new Uint32Array(memory.buffer);
const summands = new DataView(memory.buffer);
for (let i = 0; i < 10; i++) {
summands[i] = i;
summands.setUint32(i * 4, i, true); // WebAssembly is little endian
}
const sum = obj.instance.exports.accumulate(0, 10);
console.log(sum);
Expand All @@ -70,8 +72,8 @@ Another way to get a `WebAssembly.Memory` object is to have it exported by a Web

```js
WebAssembly.instantiateStreaming(fetch("memory.wasm")).then((obj) => {
const values = new Uint32Array(obj.instance.exports.memory.buffer);
console.log(values[0]);
const values = new DataView(obj.instance.exports.memory.buffer);
console.log(values.getUint32(0, true));
});
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ const memory = new WebAssembly.Memory({
WebAssembly.instantiateStreaming(fetch("memory.wasm"), {
js: { mem: memory },
}).then((obj) => {
const summands = new Uint32Array(memory.buffer);
const summands = new DataView(memory.buffer);
for (let i = 0; i < 10; i++) {
summands[i] = i;
summands.setUint32(i * 4, i, true); // WebAssembly is little endian
}
const sum = obj.instance.exports.accumulate(0, 10);
console.log(sum);
Expand Down
2 changes: 1 addition & 1 deletion files/en-us/webassembly/loading_and_running/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ WebAssembly.instantiateStreaming(fetch("myModule.wasm"), importObject).then(
obj.instance.exports.exported_func();

// or access the buffer contents of an exported memory:
const i32 = new Uint32Array(obj.instance.exports.memory.buffer);
const dv = new DataView(obj.instance.exports.memory.buffer);

// or access the elements of an exported table:
const table = obj.instance.exports.table;
Expand Down
15 changes: 7 additions & 8 deletions files/en-us/webassembly/using_the_javascript_api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,14 @@ Let's start exploring this by looking at a quick example.
WebAssembly memory exposes its bytes by providing a buffer getter/setter that returns an ArrayBuffer. For example, to write 42 directly into the first word of linear memory, you can do this:

```js
new Uint32Array(memory.buffer)[0] = 42;
const data = new DataView(memory.buffer);
data.setUint32(0, 42, true);
```

You can then return the same value using:
Note the use of `true`, which enforces little-endian read and write, since WebAssembly memory is always little-endian. You can then return the same value using:

```js
new Uint32Array(memory.buffer)[0];
data.getUint32(0, true);
```

3. Try this now in your demo — save what you've added so far, load it in your browser, then try entering the above two lines in your JavaScript console.
Expand Down Expand Up @@ -149,17 +150,15 @@ Let's make the above assertions clearer by looking at a more involved memory exa
3. Since this module exports its memory, given an Instance of this module called instance we can use an exported function `accumulate()` to create and populate an input array directly in the module instance's linear memory (`mem`). Add the following into your code, where indicated:

```js
const i32 = new Uint32Array(memory.buffer);

const summands = new DataView(memory.buffer);
for (let i = 0; i < 10; i++) {
i32[i] = i;
summands.setUint32(i * 4, i, true);
}

const sum = results.instance.exports.accumulate(0, 10);
console.log(sum);
```

Note how we create the {{jsxref("Uint32Array")}} view on the Memory object's buffer ([`Memory.prototype.buffer`](/en-US/docs/WebAssembly/JavaScript_interface/Memory/buffer)), not on the Memory itself.
Note how we create the {{jsxref("DataView")}} view on the Memory object's buffer ([`Memory.prototype.buffer`](/en-US/docs/WebAssembly/JavaScript_interface/Memory/buffer)), not on the Memory itself.

Memory imports work just like function imports, only Memory objects are passed as values instead of JavaScript functions. Memory imports are useful for two reasons:

Expand Down

0 comments on commit 58d3a9a

Please sign in to comment.