// |jit-test| skip-if: wasmCompilersPresent().match('cranelift') && !getBuildConfiguration()['arm64'] // // (Reason: the Cranelift backend does not support shared memory on non-AArch64 // hosts yet.) // // Tests of limits of memory and table types const PageSize = 65536; const MemoryMaxValid = 65536; const MemoryMaxRuntime = Math.floor(0x7fff_ffff / PageSize); const TableMaxValid = 0xffff_ffff; const TableMaxRuntime = 10_000_000; // Test that a memory type is valid within a module function testMemoryValidate(initial, maximum, shared) { wasmValidateText(`(module (memory ${initial} ${maximum || ''} ${shared ? 'shared' : ''}) )`); } testMemoryValidate(0, undefined, false); testMemoryValidate(1, undefined, false); testMemoryValidate(0, 1, false); testMemoryValidate(0, 1, true); testMemoryValidate(1, 1, false); testMemoryValidate(1, 1, true); testMemoryValidate(MemoryMaxValid, undefined, false); testMemoryValidate(MemoryMaxValid, MemoryMaxValid, false); testMemoryValidate(MemoryMaxValid, MemoryMaxValid, true); // Test that a memory type is not valid within a module function testMemoryFailValidate(initial, maximum, shared, pattern) { wasmFailValidateText(`(module (memory ${initial} ${maximum || ''} ${shared ? 'shared' : ''}) )`, pattern); } testMemoryFailValidate(2, 1, false, /size minimum must not be greater than maximum/); testMemoryFailValidate(1, undefined, true, /maximum length required for shared memory/); testMemoryFailValidate(MemoryMaxValid + 1, undefined, false, /initial memory size too big/); testMemoryFailValidate(MemoryMaxValid, MemoryMaxValid + 1, false, /maximum memory size too big/); testMemoryFailValidate(MemoryMaxValid, MemoryMaxValid + 1, true, /maximum memory size too big/); // Test that a memory type is invalid for constructing a WebAssembly.Memory function testMemoryFailConstruct(initial, maximum, shared, pattern) { assertErrorMessage(() => new WebAssembly.Memory({ initial, maximum, shared }), RangeError, pattern); } testMemoryFailConstruct(MemoryMaxValid + 1, undefined, false, /bad Memory initial size/); testMemoryFailConstruct(0, MemoryMaxValid + 1, false, /bad Memory maximum size/); testMemoryFailConstruct(MemoryMaxValid + 1, undefined, true, /bad Memory initial size/); testMemoryFailConstruct(0, MemoryMaxValid + 1, true, /bad Memory maximum size/); // Test that a memory type can be instantiated within a module or constructed // with a WebAssembly.Memory function testMemoryCreate(initial, maximum, shared) { // May OOM, but must not fail to validate try { wasmEvalText(`(module (memory ${initial} ${maximum || ''} ${shared ? 'shared' : ''}) )`); } catch (e) { assertEq(String(e).indexOf("out of memory") !== -1, true, `${e}`); } try { new WebAssembly.Memory({initial, maximum, shared}); } catch (e) { assertEq(String(e).indexOf("out of memory") !== -1, true, `${e}`); } } testMemoryCreate(0, undefined, false); testMemoryCreate(1, undefined, false); testMemoryCreate(0, 1, false); testMemoryCreate(0, 1, true); testMemoryCreate(1, 1, false); testMemoryCreate(1, 1, true); testMemoryCreate(MemoryMaxRuntime, undefined, false); testMemoryCreate(MemoryMaxRuntime, MemoryMaxValid, false); testMemoryCreate(MemoryMaxRuntime, MemoryMaxValid, true); // Test that a memory type cannot be instantiated within a module or constructed // with a WebAssembly.Memory function testMemoryFailCreate(initial, maximum, shared, pattern) { assertErrorMessage(() => wasmEvalText(`(module (memory ${initial} ${maximum || ''} ${shared ? 'shared' : ''}) )`), WebAssembly.RuntimeError, pattern); assertErrorMessage(() => new WebAssembly.Memory({ initial, maximum, shared }), WebAssembly.RuntimeError, pattern); } testMemoryFailCreate(MemoryMaxRuntime + 1, undefined, false, /too many memory pages/); testMemoryFailCreate(MemoryMaxRuntime + 1, MemoryMaxValid, false, /too many memory pages/); testMemoryFailCreate(MemoryMaxRuntime + 1, MemoryMaxValid, true, /too many memory pages/); // Test that a memory type cannot be grown from initial to a target due to an // implementation limit function testMemoryFailGrow(initial, maximum, target, shared) { let {run} = wasmEvalText(`(module (memory ${initial} ${maximum || ''} ${shared ? 'shared' : ''}) (func (export "run") (result i32) i32.const ${target - initial} memory.grow ) )`).exports; assertEq(run(), -1, 'failed to grow'); let mem = new WebAssembly.Memory({ initial, maximum, shared }); assertErrorMessage(() => mem.grow(target - initial), RangeError, /failed to grow memory/); } testMemoryFailGrow(1, undefined, MemoryMaxRuntime + 1, false); testMemoryFailGrow(1, MemoryMaxValid, MemoryMaxRuntime + 1, false); testMemoryFailGrow(1, MemoryMaxValid, MemoryMaxRuntime + 1, true); // Test that a table type is valid within a module function testTableValidate(initial, maximum) { wasmValidateText(`(module (table ${initial} ${maximum || ''} anyfunc) )`); } testTableValidate(0, undefined); testTableValidate(1, undefined); testTableValidate(0, 1); testTableValidate(1, 1); testTableValidate(TableMaxValid, undefined); testTableValidate(TableMaxValid, TableMaxValid); // Test that a table type is not valid within a module function testTableFailValidate(initial, maximum, pattern) { wasmFailValidateText(`(module (table ${initial} ${maximum || ''} anyfunc) )`, pattern); } testTableFailValidate(2, 1, /size minimum must not be greater than maximum/); // The maximum valid table value is equivalent to the maximum encodable limit // value, so we cannot test too large of a table limit in a module. assertEq(TableMaxValid + 1 > 0xffffffff, true); // Test that a table type is invalid for constructing a WebAssembly.Table function testTableFailConstruct(initial, maximum, pattern) { assertErrorMessage(() => new WebAssembly.Table({ initial, maximum, element: 'anyfunc', }), TypeError, pattern); } testTableFailConstruct(TableMaxValid + 1, undefined, /bad Table initial size/); testTableFailConstruct(0, TableMaxValid + 1, /bad Table maximum size/); // Test that a table type can be instantiated within a module or constructed // with a WebAssembly.Table function testTableCreate(initial, maximum) { // May OOM, but must not fail to validate try { wasmEvalText(`(module (table ${initial} ${maximum || ''} anyfunc) )`); } catch (e) { assertEq(String(e).indexOf("out of memory") !== -1, true, `${e}`); } try { new WebAssembly.Table({ initial, maximum, element: 'anyfunc', }); } catch (e) { assertEq(String(e).indexOf("out of memory") !== -1, true, `${e}`); } } testTableCreate(0, undefined); testTableCreate(1, undefined); testTableCreate(0, 1); testTableCreate(1, 1); testTableCreate(TableMaxRuntime, undefined); testTableCreate(TableMaxRuntime, TableMaxValid); // Test that a table type cannot be instantiated within a module or constructed // with a WebAssembly.Table function testTableFailCreate(initial, maximum, pattern) { assertErrorMessage(() => wasmEvalText(`(module (table ${initial} ${maximum || ''} anyfunc) )`), WebAssembly.RuntimeError, pattern); assertErrorMessage(() => new WebAssembly.Table({ initial, maximum, element: 'anyfunc', }), WebAssembly.RuntimeError, pattern); } testTableFailCreate(TableMaxRuntime + 1, undefined, /too many table elements/); testTableFailCreate(TableMaxRuntime + 1, TableMaxValid, /too many table elements/); if (wasmReftypesEnabled()) { // Test that a table type cannot be grown from initial to a target due to an // implementation limit function testTableFailGrow(initial, maximum, target) { let {run} = wasmEvalText(`(module (table ${initial} ${maximum || ''} externref) (func (export "run") (result i32) ref.null extern i32.const ${target - initial} table.grow ) )`).exports; assertEq(run(), -1, 'failed to grow'); let tab = new WebAssembly.Table({ initial, maximum, element: 'externref', }); assertErrorMessage(() => tab.grow(target - initial), RangeError, /failed to grow table/); } testTableFailGrow(1, undefined, TableMaxRuntime + 1); testTableFailGrow(1, TableMaxValid, TableMaxRuntime + 1); }