blob: b6c23872ce2ae299705a8a43b3484259069d0a94 [file] [edit]
//@ requireOptions("--useWasmJSTypes=1")
//@ memoryHog!
//@ skip if $addressBits <= 32
import { instantiate } from "../wabt-wrapper.js";
import * as assert from "../assert.js";
// A memory64 may declare a maximum anywhere in its 2**48-page address space, well past what any port
// could map. What its buffer reports is bounded twice over: by the largest representable ArrayBuffer
// byte length, and by the address space one reservation may claim on this platform, so that a resize
// within maxByteLength never fails deterministically.
const options = { memory64: true, threads: true };
const pageSize = 65536;
const maxMemory64Pages = 2 ** 48;
const maxRepresentablePages = 262144;
const maxMemory32Pages = 65536;
// A memory with no declared maximum reports its own address type's ceiling, so it discovers what this
// platform will reserve. A memory32's ceiling is reservable in full everywhere.
const ceilingBytes = new WebAssembly.Memory({ initial: 1n, address: "i64" }).toResizableBuffer().maxByteLength;
assert.truthy(ceilingBytes <= maxRepresentablePages * pageSize, `memory64 ceiling ${ceilingBytes} is not a representable byte length`);
assert.truthy(ceilingBytes >= maxMemory32Pages * pageSize, `memory64 ceiling ${ceilingBytes} is below the memory32 ceiling`);
assert.eq(new WebAssembly.Memory({ initial: 1 }).toResizableBuffer().maxByteLength, maxMemory32Pages * pageSize);
// A declared maximum at the largest representable byte length is accepted, and the buffer reports it
// clamped to that ceiling. The shared memory reserves the whole thing up front, so this arm is why the
// file is a memory hog, and a port that cannot spare the address space must refuse cleanly rather than
// fail the test.
for (const shared of [false, true]) {
let mem;
try {
mem = (await instantiate(`
(module
(memory (export "mem") i64 1 ${maxRepresentablePages} ${shared ? "shared" : ""}))
`, {}, options)).exports.mem;
} catch (e) {
assert.truthy(e instanceof RangeError && e.message === "Out of memory", `expected an out of memory RangeError, got ${e}`);
continue;
}
assert.eq(mem.type().maximum, BigInt(maxRepresentablePages));
assert.eq(mem.type().address, "i64");
assert.eq(mem.toResizableBuffer().maxByteLength, Math.min(maxRepresentablePages * pageSize, ceilingBytes));
}
// A maximum past that is a declaration and nothing more: it is reported back verbatim, and the buffer
// still only advertises what could be reached. A non-shared memory reserves nothing up front, so this
// costs no address space.
{
const mem = new WebAssembly.Memory({ initial: 1n, maximum: BigInt(maxMemory64Pages), address: "i64" });
assert.eq(mem.type().maximum, BigInt(maxMemory64Pages));
assert.eq(mem.toResizableBuffer().maxByteLength, ceilingBytes);
}
// The JS constructor applies the same bound. Module decoding is covered by
// memory64-oversized-limits.js.
assert.throws(() => new WebAssembly.Memory({ initial: 1n, maximum: BigInt(maxMemory64Pages) + 1n, address: "i64" }),
RangeError, "WebAssembly.Memory 'maximum' page count is too large");
// A memory32 is bounded by the memory32 page limit rather than the memory64 one.
assert.throws(() => new WebAssembly.Memory({ initial: 1, maximum: maxMemory32Pages + 1 }),
RangeError, "WebAssembly.Memory 'maximum' page count is too large");