Skip to content

Commit

Permalink
Implement WebAssembly.Memory#grow()
Browse files Browse the repository at this point in the history
  • Loading branch information
TooTallNate committed Oct 10, 2023
1 parent 9ef02b1 commit ec79b19
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/gentle-garlics-fry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'nxjs-runtime': patch
---

Implement `WebAssembly.Memory#grow()`
Binary file modified apps/tests/romfs/grow.wasm
Binary file not shown.
11 changes: 10 additions & 1 deletion apps/tests/src/wasm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,33 +230,42 @@ test('grow.wasm', async () => {

assert.equal(WebAssembly.Module.exports(module), [
{ name: 'grow', kind: 'function' },
{ name: 'getPageCount', kind: 'function' },
{ name: 'mem', kind: 'memory' },
]);

const grow = instance.exports.grow as Function;
const getPageCount = instance.exports.getPageCount as Function;
const mem = instance.exports.mem as WebAssembly.Memory;
assert.instance(mem, WebAssembly.Memory);

const buf = mem.buffer;
assert.equal(buf.byteLength, 65536, 'Initially, page size = 1');
assert.equal(getPageCount(), 1);
// TODO: make work
//assert.ok(buf === mem.buffer, 'Same size, same instance (size = 1)');

grow();
const buf2 = mem.buffer;
assert.equal(buf2.byteLength, 65536 * 2, 'Page size = 2');
assert.equal(getPageCount(), 2);
assert.ok(buf !== buf2, 'Different size, different instance (size = 2)');
// TODO: make work
//assert.ok(buf2 === mem.buffer, 'Same size, same instance (size = 2)');

grow();
const buf3 = mem.buffer;
assert.equal(buf3.byteLength, 65536 * 3, 'Page size = 3');
assert.equal(getPageCount(), 3);
assert.ok(buf2 !== buf3, 'Different size, different instance (size = 3)');
// TODO: make work
//assert.ok(buf3 === mem.buffer, 'Same size, same instance (size = 3)');

// TODO: `mem.grow(1)` test, once that function is implemented
// Now using `mem.grow()` from the JavaScript side
mem.grow(2);
const buf4 = mem.buffer;
assert.equal(buf4.byteLength, 65536 * 5, 'Page size = 5');
assert.equal(getPageCount(), 5);
});

test('compute.wasm', async () => {
Expand Down
5 changes: 2 additions & 3 deletions packages/runtime/src/wasm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,9 +237,8 @@ export class Memory implements WebAssembly.Memory {
declare readonly buffer: ArrayBuffer;

/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Memory/grow) */
grow(delta: number): number {
throw new Error('Method not implemented.');
}
// @ts-expect-error This is a native function
grow(delta: number): number {}
}
$.wasmInitMemory(Memory);

Expand Down
51 changes: 51 additions & 0 deletions source/wasm.c
Original file line number Diff line number Diff line change
Expand Up @@ -982,6 +982,56 @@ static JSValue nx_wasm_memory_buffer_get(JSContext *ctx, JSValueConst this_val,
return buf;
}

// `Memory#grow()` function
static JSValue nx_wasm_memory_grow(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
nx_wasm_memory_t *data = nx_wasm_memory_get(ctx, this_val);
if (!data)
return JS_EXCEPTION;

IM3Memory memory = data->mem;
if (!memory)
{
JS_ThrowTypeError(ctx, "Memory not set");
return JS_EXCEPTION;
}

M3MemoryHeader *mallocated = memory->mallocated;
if (!mallocated)
{
JS_ThrowTypeError(ctx, "Memory not allocated");
return JS_EXCEPTION;
}

i32 numPagesToGrow;
if (JS_ToInt32(ctx, &numPagesToGrow, argv[0]))
return JS_EXCEPTION;

if (numPagesToGrow < 0)
{
JS_ThrowTypeError(ctx, "WebAssembly.Memory.grow(): Argument 0 must be non-negative");
return JS_EXCEPTION;
}

JSValue prevSize = JS_NewUint32(ctx, memory->numPages);

if (numPagesToGrow > 0)
{
IM3Runtime runtime = m3MemRuntime(mallocated);
if (!runtime)
{
JS_ThrowTypeError(ctx, "WebAssembly.Memory.grow(): Memory not bound to an instance");
return JS_EXCEPTION;
}
u32 requiredPages = memory->numPages + numPagesToGrow;
M3Result r = ResizeMemory(runtime, requiredPages);
if (r)
return nx_throw_wasm_error(ctx, "RuntimeError", r);
}

return prevSize;
}

// `Table#get()` function
static JSValue nx_wasm_table_get_fn(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
Expand Down Expand Up @@ -1034,6 +1084,7 @@ static JSValue nx_wasm_init_memory_class(JSContext *ctx, JSValueConst this_val,
JSAtom atom;
JSValue proto = JS_GetPropertyStr(ctx, argv[0], "prototype");
NX_DEF_GETTER(proto, "buffer", nx_wasm_memory_buffer_get);
NX_DEF_FUNC(proto, "grow", nx_wasm_memory_grow, 1);
JS_FreeValue(ctx, proto);
return JS_UNDEFINED;
}
Expand Down

0 comments on commit ec79b19

Please sign in to comment.