// valid to define a memory with i64
validAndInstantiableMemoryType(false, 0n); // valid to define max with i64
validAndInstantiableMemoryType(false, 0n, 1n); // invalid for min to be greater than max with i64
invalidMemoryType(false, 2n, 1n, /minimum must not be greater than maximum/, /initial Memory size cannot be greater than maximum/); // valid to define shared memory with max with i64
validAndInstantiableMemoryType(true, 1n, 2n); // invalid to define shared memory without max with i64
invalidMemoryType(true, 1n, undefined, /maximum length required for shared memory/, /maximum is not specified/);
// valid to define a table with i64
validAndInstantiableTableType('funcref', 0n); // valid to define table max with i64
validAndInstantiableTableType('funcref', 0n, 1n); // invalid for table min to be greater than max with i64
invalidTableType('funcref', 2n, 1n, /minimum must not be greater than maximum/, /initial Table size cannot be greater than maximum/);
// test the validation limits of memory64 memories
validButNotInstantiableMemoryType(false, MaxMemory64PagesValidation, undefined, /too many memory pages/);
validButNotInstantiableMemoryType(false, MaxMemory64PagesValidation, MaxMemory64PagesValidation, /too many memory pages/);
validAndInstantiableMemoryType(false, 0n, MaxMemory64PagesValidation);
invalidMemoryType(false, 0n, MaxMemory64PagesValidation + 1n, /maximum memory size too big/, /bad Memory maximum/);
validAndInstantiableMemoryType(true, 0n, MaxMemory64PagesValidation);
invalidMemoryType(true, 0n, MaxMemory64PagesValidation + 1n, /maximum memory size too big/, /bad Memory maximum/);
// test the validation limits of memory64 tables
validButNotInstantiableTableType('funcref', MaxTable64ElemsValidation, undefined, /too many table elements/);
validButNotInstantiableTableType('funcref', MaxTable64ElemsValidation, MaxTable64ElemsValidation, /too many table elements/);
validAndInstantiableTableType('funcref', 0n, MaxTable64ElemsValidation); // cannot create oversize table via either text or binary format since the full u64 range is valid
assertErrorMessage(() => new WebAssembly.Table(tableTypeDescriptor('funcref', 0n, MaxTable64ElemsValidation + 1n)), TypeError, /bad Table maximum/);
// further validation of memory64 descriptor params
assertErrorMessage(() => new WebAssembly.Memory(memoryTypeDescriptor(false, 0)), TypeError, /can't convert 0 to BigInt/);
assertErrorMessage(() => new WebAssembly.Memory(memoryTypeDescriptor(false, -1n)), TypeError, /bad Memory initial size/);
assertErrorMessage(() => new WebAssembly.Memory(memoryTypeDescriptor(false, 2n**64n)), TypeError, /bad Memory initial size/);
assertErrorMessage(() => new WebAssembly.Memory(memoryTypeDescriptor(false, 0n, 1)), TypeError, /can't convert 1 to BigInt/);
assertErrorMessage(() => new WebAssembly.Memory(memoryTypeDescriptor(false, 0n, -1n)), TypeError, /bad Memory maximum size/);
assertErrorMessage(() => new WebAssembly.Memory(memoryTypeDescriptor(false, 0n, 2n**64n)), TypeError, /bad Memory maximum size/);
assertErrorMessage(() => new WebAssembly.Table(tableTypeDescriptor('funcref', 0)), TypeError, /can't convert 0 to BigInt/);
assertErrorMessage(() => new WebAssembly.Table(tableTypeDescriptor('funcref', -1n)), TypeError, /bad Table initial size/);
assertErrorMessage(() => new WebAssembly.Table(tableTypeDescriptor('funcref', 2n**64n)), TypeError, /bad Table initial size/);
assertErrorMessage(() => new WebAssembly.Table(tableTypeDescriptor('funcref', 0n, 1)), TypeError, /can't convert 1 to BigInt/);
assertErrorMessage(() => new WebAssembly.Table(tableTypeDescriptor('funcref', 0n, -1n)), TypeError, /bad Table maximum size/);
assertErrorMessage(() => new WebAssembly.Table(tableTypeDescriptor('funcref', 0n, 2n**64n)), TypeError, /bad Table maximum size/);
// test that linking requires address types to be equal function testLinkMemory(importedAddressType, importAddressType) {
let imported = new WebAssembly.Memory({
address: importedAddressType,
initial: importedAddressType === 'i64' ? 0n : 0,
});
let testModule =
`(module
(memory (import"""imported") ${importAddressType} 0))`; if (importedAddressType === importAddressType) {
wasmEvalText(testModule, {"": {imported}});
} else {
assertErrorMessage(() => wasmEvalText(testModule, {"": {imported}}), WebAssembly.LinkError, /address type/);
}
} function testLinkTable(importedAddressType, importAddressType) { const imported = new WebAssembly.Table({
element: 'funcref',
address: importedAddressType,
initial: importedAddressType === 'i64' ? 0n : 0,
}); const testModule =
`(module
(table (import"""imported") ${importAddressType} 0 funcref))`; if (importedAddressType === importAddressType) {
wasmEvalText(testModule, {"": {imported}});
} else {
assertErrorMessage(() => wasmEvalText(testModule, {"": {imported}}), WebAssembly.LinkError, /address type/);
}
}
;; About the test cases: there are various optimizations in the engine
;; for different shapes of a pointer+offset. Constant pointers are
;; resolved early; large offsets are folded early using explicit code
;; with an overflow check (but "large" depends on 32-bit vs 64-bit);
;; wait/notify fold offsets early regardless; zero offsets lead to
;; tighter code with variable pointers; and don't get me started on
;; alignment checks. These test cases are not exhaustive but aim
;; to test at least some things.
;; TODO: more sizes for all operations, though this is not critical
;; TODO: sign extending loads, again not critical
function i32Random() { // Limit this to small positive numbers to keep life simple. for (;;) {
let r = (Math.random() * 0x3FFF_FFFF) | 0; if (r != 0) return r;
}
}
function i64Random() { return (BigInt(i32Random()) << 32n) | BigInt(i32Random());
}
function Random(sz) { if (sz == 4) return i32Random(); return i64Random();
}
function Random2(sz) { return [Random(sz), Random(sz)];
}
function Random4(sz) { return [Random(sz), Random(sz), Random(sz), Random(sz)];
}
function Zero(sz) { if (sz == 4) return 0; return 0n;
}
function testRead(ins, mem, LOC, prefix) {
let r = 0;
let SZ = mem.BYTES_PER_ELEMENT;
let len = mem.length * SZ;
let NM = prefix + "readi" + (SZ * 8);
// This is OOB if we consider the whole pointer as we must, but if we // mistakenly only look at the low bits then it's in-bounds. if (len < 0x1_0000_0000) {
assertErrorMessage(() => ins.exports[NM + "@0"](0x1_0000_0000n),
WebAssembly.RuntimeError,
/out of bounds/);
}
if (len < HUGE) {
assertErrorMessage(() => ins.exports[NM + "@huge"](0n),
WebAssembly.RuntimeError,
/out of bounds/);
}
if (len < VAST) {
assertErrorMessage(() => ins.exports[NM + "@vast"](0n),
WebAssembly.RuntimeError,
/out of bounds/);
}
}
function testReadV128(ins, mem, LOC) {
let r = 0;
let SZ = mem.BYTES_PER_ELEMENT;
let len = mem.length * SZ;
let NM = "readv128";
assertEq(SZ, 4);
// Read in-bounds
r = Random4(4);
mem.set(r, LOC / SZ);
ins.exports[NM + "@0"](BigInt(LOC))
assertSame(mem.slice(0, 4), r);
ins.exports[NM + "/const@0"]();
assertSame(mem.slice(0, 4), r);
r = new Int32Array([0,0,0,0]);
mem.set(r, (len / SZ) - 4);
ins.exports[NM + "@0"](BigInt(len - (SZ * 4))); // Just barely in-bounds
assertSame(mem.slice(0, 4), r);
if (len >= LOC + BIG + SZ * 4) {
r = Random4(SZ);
mem.set(r, (LOC + BIG) / SZ);
ins.exports[NM + "@big"](BigInt(LOC));
assertSame(mem.slice(0, 4), r);
ins.exports[NM + "/const@big"]();
assertSame(mem.slice(0, 4), r);
}
// Read out-of-bounds
assertErrorMessage(() => ins.exports[NM + "@0"](BigInt(len)),
WebAssembly.RuntimeError,
/out of bounds/);
assertErrorMessage(() => ins.exports[NM + "@0"](BigInt(len-((SZ*4)-1))),
WebAssembly.RuntimeError,
/out of bounds/);
// This is OOB if we consider the whole pointer as we must, but if we // mistakenly only look at the low bits then it's in-bounds. if (len < 0x1_0000_0000) {
assertErrorMessage(() => ins.exports[NM + "@0"](0x1_0000_0000n),
WebAssembly.RuntimeError,
/out of bounds/);
}
if (len < HUGE) {
assertErrorMessage(() => ins.exports[NM + "@huge"](0n),
WebAssembly.RuntimeError,
/out of bounds/);
}
r = Random4(SZ)
mem.set(r, 0);
let s = i32Random()
mem[(LOC + SMALL) / SZ] = s;
ins.exports["v128.load_lane@small"](BigInt(LOC));
assertSame(mem.slice(0, 4), [r[0], r[1], s, r[3]]);
}
function testWrite(ins, mem, LOC, prefix) {
let r = 0;
let SZ = mem.BYTES_PER_ELEMENT;
let len = mem.length * SZ;
let WNM = prefix + "writei" + (SZ * 8);
let RNM = prefix + "readi" + (SZ * 8);
if (len < 0x1_0000_0000) {
let xs = ins.exports[RNM + "@0"](0n);
assertErrorMessage(() => ins.exports[WNM + "@0"](0x1_0000_0000n, Random(SZ)),
WebAssembly.RuntimeError,
/out of bounds/); // Check for scribbling
assertEq(ins.exports[RNM + "@0"](0n), xs);
}
if (len < HUGE) {
assertErrorMessage(() => ins.exports[WNM + "@huge"](0n, Random(SZ)),
WebAssembly.RuntimeError,
/out of bounds/);
}
if (len < VAST) {
assertErrorMessage(() => ins.exports[WNM + "@vast"](0n, Random(SZ)),
WebAssembly.RuntimeError,
/out of bounds/);
}
}
function testWriteV128(ins, mem, LOC) {
let r = 0;
let p = 0;
let SZ = mem.BYTES_PER_ELEMENT;
let len = mem.length * SZ;
let WNM = "writev128";
let RNM = "readv128";
assertEq(SZ, 4);
// Write in-bounds
r = Random4(SZ);
mem.set(r, 0);
p = LOC / SZ;
ins.exports[WNM + "@0"](BigInt(LOC));
assertSame(mem.slice(p, p + 4), r);
r = Random4(SZ);
mem.set(r, 0);
p = (LOC + SMALL) / SZ;
ins.exports[WNM + "@small"](BigInt(LOC));
assertSame(mem.slice(p, p + 4), r);
if (len >= LOC + BIG + SZ) {
r = Random4(SZ);
mem.set(r, 0);
p = (LOC + BIG) / SZ;
ins.exports[WNM + "@big"](BigInt(LOC));
assertSame(mem.slice(p, p + 4), r);
}
r = Random4(SZ);
mem.set(r, 0);
p = (len - (SZ * 4)) / SZ;
ins.exports[WNM + "@0"](BigInt(len - (SZ * 4))); // Just barely in-bounds
assertSame(mem.slice(p, p + 4), r);
// Write out-of-bounds
assertErrorMessage(() => ins.exports[WNM + "@0"](BigInt(len)),
WebAssembly.RuntimeError,
/out of bounds/);
function testAtomicRMW(ins, mem, LOC, op, fn) {
let r = 0, s = 0;
let SZ = mem.BYTES_PER_ELEMENT;
let len = mem.length * SZ;
let NM = op + "i" + (SZ * 8);
if (len < HUGE) {
assertErrorMessage(() => ins.exports[NM + "@huge"](0n, Zero(SZ)),
WebAssembly.RuntimeError,
/out of bounds/);
}
}
function testAtomicCmpxchg(ins, mem, LOC) {
let r = 0, s = 0;
let SZ = mem.BYTES_PER_ELEMENT;
let len = mem.length * SZ;
let NM = "cmpxchgi" + (SZ * 8);
if (len < HUGE) {
assertErrorMessage(() => ins.exports["wake@huge"](BigInt(LOC)),
WebAssembly.RuntimeError,
/out of bounds/);
}
}
// Sometimes we start memory at zero to disable certain bounds checking // optimizations. Other times we start memory at something beyond most of // our references to enable those optimizations.
let configs = [[40, 0, 3], [40, 3, '']];
// On 64-bit systems, test beyond 2GB and also beyond 4GB if (getBuildConfiguration("pointer-byte-size") == 8) {
configs.push([Math.pow(2, 31) + 40, 32771, '']);
configs.push([Math.pow(2, 32) + 40, 65539, '']);
configs.push([Math.pow(2, 31) + 40, 32771, 32773]);
configs.push([Math.pow(2, 32) + 40, 65539, 65541]);
}
for ( let shared of ['','shared'] ) { for (let [LOC, start, max] of configs) { if (shared != '' && max == '') { continue;
} const ins = makeTest(LOC, start, max, shared); if (max != '') { // This can OOM legitimately; let it.
let res = Number(ins.exports.mem.grow(BigInt(max - start))); if (res == -1) {
print("SPURIOUS OOM"); continue;
}
assertEq(res, start);
}
const mem32 = new Int32Array(ins.exports.mem.buffer); const mem64 = new BigInt64Array(ins.exports.mem.buffer);
for ( let m of [mem32, mem64] ) {
testRead(ins, m, LOC, "");
testWrite(ins, m, LOC, "");
testRead(ins, m, LOC, "a");
testWrite(ins, m, LOC, "a");
testAtomicRMW(ins, m, LOC, "add", (r,s) => r+s);
testAtomicRMW(ins, m, LOC, "sub", (r,s) => r-s);
testAtomicRMW(ins, m, LOC, "and", (r,s) => r&s);
testAtomicRMW(ins, m, LOC, "or", (r,s) => r|s);
testAtomicRMW(ins, m, LOC, "xor", (r,s) => r^s);
testAtomicRMW(ins, m, LOC, "xchg", (r,s) => s);
testAtomicCmpxchg(ins, m, LOC);
testAtomicWake(ins, m, LOC);
}
for ( let shared of ['','shared'] ) {
let ins = wasmEvalText(makeModule(1, 3, shared));
assertEq(ins.exports.size(), 1n);
// OOM with very low probability will result in test failure
assertEq(ins.exports.grow(2n), 1n);
assertEq(ins.exports.size(), 3n);
// OOM with very low probability will result in test failure
assertEq(ins.exports.grow(1n), -1n);
assertEq(ins.exports.size(), 3n);
// More than max pages
assertEq(ins.exports.grow(100000n), -1n);
assertEq(ins.exports.size(), 3n);
// More than 2^48 pages
assertEq(ins.exports.grow(0x1_0000_0000_0000n), -1n);
assertEq(ins.exports.size(), 3n);
// More than 2^48 pages - interpreted as unsigned
assertEq(ins.exports.grow(-1n), -1n);
assertEq(ins.exports.size(), 3n);
var mem = new Uint8Array(ins.exports.mem.buffer); var val = [1,2,3,4,5];
mem.set(val, 20);
ins.exports.copy(40n, 20n, 5n);
assertSame(val, mem.slice(40, 45));
if (getBuildConfiguration("pointer-byte-size") == 8) { for ( let shared of ['','shared'] ) {
let limit = wasmMaxMemoryPages('i64');
let initial = 65537;
let maximum = limit + 1;
let pagesize = 65536n;
let ins = wasmEvalText(makeModule(initial, maximum, shared));
assertEq(ins.exports.size(), BigInt(initial));
// This can OOM legitimately; let it.
{
let res = ins.exports.grow(2n); if (res == -1) {
print("SPURIOUS OOM"); continue;
}
assertEq(res, BigInt(initial));
}
assertEq(ins.exports.size(), BigInt(initial + 2));
// This must fail
assertEq(ins.exports.grow(BigInt(limit - (initial + 2) + 1)), -1n);
assertEq(ins.exports.size(), BigInt(initial + 2));
// This can OOM legitimately; let it.
{
let res = ins.exports.grow(BigInt(limit - (initial + 2))); if (res == -1) {
print("SPURIOUS OOM"); continue;
}
assertEq(res, BigInt(initial + 2));
}
assertEq(ins.exports.size(), BigInt(limit));
let mem = new Uint8Array(ins.exports.mem.buffer);
let copyval = [1,2,3,4,5];
let source = 20n;
let target = BigInt(initial) * pagesize + 40n;
let oobTarget = BigInt(limit) * pagesize - 1n;
// Copy from memory below 4GB to memory beyond 4GB
mem.set(copyval, Number(source));
ins.exports.copy(target, source, BigInt(copyval.length));
assertSame(copyval, mem.slice(Number(target), Number(target) + copyval.length))
// Try to copy out of bounds // src and target are both in bounds but len brings it oob
assertErrorMessage(() => ins.exports.copy(oobTarget, source, BigInt(copyval.length)),
WebAssembly.RuntimeError,
/out of bounds/);
assertEq(mem[Number(oobTarget-1n)], 0);
assertErrorMessage(() => ins.exports.copy(-1n, source, BigInt(copyval.length)),
WebAssembly.RuntimeError,
/out of bounds/);
assertEq(mem[Number(oobTarget-1n)], 0);
// Try to fill out of bounds
assertErrorMessage(() => ins.exports.fill(oobTarget, 37, 2n),
WebAssembly.RuntimeError,
/out of bounds/);
assertEq(mem[Number(oobTarget-1n)], 0);
// Try to init out of bounds
assertErrorMessage(() => ins.exports.init(oobTarget, 1, 5),
WebAssembly.RuntimeError,
/out of bounds/);
assertEq(mem[Number(oobTarget-1n)], 0);
}
}
for (let i = 0; i < vals.length; i++) { const addr = table === 1 ? addr1(i) : addr2(i); const expected = typeof vals[i] === "string" ? vals[i].charCodeAt(0) : vals[i]; const actual = (table === 1 ? callF1 : callF2)(addr);
assertEq(actual, expected, `table $e${table} index ${i}`);
}
}
// "extern" is "extn" here to line up with "func" for aesthetic reasons. // I will not apologize for this. function testExtnTable(table, vals) { const actual = (table === 1 ? sizeE1 : sizeE2)(); const expected = table === 1 ? addr1(vals.length) : addr2(vals.length);
assertEq(actual, expected, `table $e${table} had wrong size`);
for (let i = 0; i < vals.length; i++) { const addr = table === 1 ? addr1(i) : addr2(i); const expected = vals[i]; const actual = (table === 1 ? getE1 : getE2)(addr);
assertEq(actual, expected, `table $e${table} index ${i}`);
}
}
if (addrType1 === "i64") { // JS API must use BigInt for i64
assertErrorMessage(() => f1.grow(1), TypeError, /can't convert 1 to BigInt/);
assertErrorMessage(() => f1.get(0), TypeError, /can't convert 0 to BigInt/);
assertErrorMessage(() => f1.set(0, null), TypeError, /can't convert 0 to BigInt/);
assertEq(typeof f1.length, "bigint");
}
const indexes = [
-1,
11, // length is 12, so test around the boundary
12,
13,
0 + (MaxUint32 + 1),
1 + (MaxUint32 + 1),
2 + (MaxUint32 + 1),
Number.MAX_SAFE_INTEGER,
Number.MAX_SAFE_INTEGER + 1,
]; if (addrType1 === "i64") { for (const i of indexes) {
assertErrorMessage(
() => fillF1WithA(addr1(i), addr1(2)),
WebAssembly.RuntimeError, /index out of bounds/,
`start index ${i}`,
);
assertErrorMessage(
() => fillE1WithA(addr1(i), addr1(2)),
WebAssembly.RuntimeError, /index out of bounds/,
`start index ${i}`,
);
assertErrorMessage(
() => copyF(addr2(0), addr1(i), len(2)),
WebAssembly.RuntimeError, /index out of bounds/,
`src index ${i}`,
);
assertErrorMessage(
() => copyE(addr2(0), addr1(i), len(2)),
WebAssembly.RuntimeError, /index out of bounds/,
`src index ${i}`,
);
assertErrorMessage(
() => initF1(addr1(i), 0, 2),
WebAssembly.RuntimeError, /index out of bounds/,
`dst index ${i}`,
);
assertErrorMessage(
() => initE1(addr1(i), 0, 2),
WebAssembly.RuntimeError, /index out of bounds/,
`dst index ${i}`,
);
}
} if (addrType2 === "i64") { for (const i of indexes) {
assertErrorMessage(
() => copyF(addr2(i), addr1(0), len(2)),
WebAssembly.RuntimeError, /index out of bounds/,
`dst index ${i}`,
);
assertErrorMessage(
() => copyE(addr2(i), addr1(0), len(2)),
WebAssembly.RuntimeError, /index out of bounds/,
`dst index ${i}`,
);
}
}
const maxDelta = MaxTableElemsRuntime - 12;
assertEq(maxDelta % 2, 0, "maxDelta needs to be even for this test to work");
try { // Grow the tables up to max size using both the instruction and the JS API
tryGrow(() => growF1(addr1(maxDelta / 2)));
tryGrow(() => growF2(addr2(maxDelta / 2)));
tryGrow(() => growE1(addr1(maxDelta / 2)));
tryGrow(() => growE2(addr2(maxDelta / 2)));
f1.grow(addr1(maxDelta / 2), null);
f2.grow(addr2(maxDelta / 2), null);
e1.grow(addr1(maxDelta / 2), null);
e2.grow(addr2(maxDelta / 2), null);
for (const delta of indexes) { if (addrType1 === "i64") {
console.log(delta);
assertEq(growF1(addr1(delta)), addr1(-1), `growing by ${delta}`);
assertEq(growE1(addr1(delta)), addr1(-1), `growing by ${delta}`);
assertErrorMessage(() => f1.grow(addr1(delta), null), Error, /grow/); // Loose to accept either TypeError or RangeError
assertErrorMessage(() => e1.grow(addr1(delta), null), Error, /grow/);
} if (addrType2 === "i64") {
assertEq(growF2(addr2(delta)), addr2(-1), `growing by ${delta}`);
assertEq(growE2(addr2(delta)), addr2(-1), `growing by ${delta}`);
assertErrorMessage(() => f2.grow(addr2(delta), null), Error, /grow/);
assertErrorMessage(() => e2.grow(addr2(delta), null), Error, /grow/);
}
}
assertEq(sizeF1(), addr1(MaxTableElemsRuntime));
assertEq(sizeF2(), addr2(MaxTableElemsRuntime));
assertEq(sizeE1(), addr1(MaxTableElemsRuntime));
assertEq(sizeE2(), addr2(MaxTableElemsRuntime));
} catch (e) { if (e instanceof RangeError) { // This can happen due to resource exhaustion on some platforms and is not worth // failing the whole test over.
print("WARNING: Failed to test all cases of table grow.", e);
} else { throw e;
}
}
}
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.59Bemerkung:
(vorverarbeitet am 2026-04-25)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.