const RuntimeError = WebAssembly.RuntimeError;
function loadModuleSrc(type, ext, offset, align, drop = false ) {
let maybeResult = drop ? '' : `(result ${type})`;
let maybeDrop = drop ? 'drop' : '' ;
return `(module
(memory 1 )
(data (i32.const 0 ) "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f" )
(data (i32.const 16 ) "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff" )
(func $load (param i32) ${maybeResult}
(${type}.load${ext}
offset=${offset}
${align != 0 ? 'align=' + align : '' }
(local.get 0 )
)
${maybeDrop}
) (export "" (func 0 )))`;
}
function loadModule(type, ext, offset, align, drop = false ) {
return wasmEvalText(loadModuleSrc(type, ext, offset, align, drop)).exports["" ];
}
function storeModuleSrc(type, ext, offset, align) {
var load_ext = ext === '' ? '' : ext + '_s' ;
return `(module
(memory 1 )
(data (i32.const 0 ) "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f" )
(data (i32.const 16 ) "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff" )
(func $store (param i32) (param ${type})
(${type}.store${ext}
offset=${offset}
${align != 0 ? 'align=' + align : '' }
(local.get 0 )
(local.get 1 )
)
) (export "store" (func 0 ))
(func $load (param i32) (result ${type})
(${type}.load${load_ext}
offset=${offset}
${align != 0 ? 'align=' + align : '' }
(local.get 0 )
)
) (export "load" (func 1 )))`;
}
function storeModule(type, ext, offset, align) {
return wasmEvalText(storeModuleSrc(type, ext, offset, align)).exports;
}
function storeModuleCstSrc(type, ext, offset, align, value) {
var load_ext = ext === '' ? '' : ext + '_s' ;
return `(module
(memory 1 )
(data (i32.const 0 ) "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f" )
(data (i32.const 16 ) "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff" )
(func $store (param i32)
(${type}.store${ext}
offset=${offset}
${align != 0 ? 'align=' + align : '' }
(local.get 0 )
(${type}.const ${value})
)
) (export "store" (func 0 ))
(func $load (param i32) (result ${type})
(${type}.load${load_ext}
offset=${offset}
${align != 0 ? 'align=' + align : '' }
(local.get 0 )
)
) (export "load" (func 1 )))`;
}
function storeModuleCst(type, ext, offset, align, value) {
return wasmEvalText(storeModuleCstSrc(type, ext, offset, align, value)).exports;
}
function testLoad(type, ext, base, offset, align, expect) {
if (type === 'i64' ) {
wasmAssert(loadModuleSrc(type, ext, offset, align), [{
type,
func: '$load' ,
expected: expect,
args: [`i32.const ${base}`]
}]);
}
else {
assertEq(loadModule(type, ext, offset, align)(base), expect);
}
}
function testLoadOOB(type, ext, base, offset, align) {
assertErrorMessage(() => loadModule(type, ext, offset, align, /*drop*/ true)(base), RuntimeError, /index out of bounds/);
}
function testStore(type, ext, base, offset, align, value) {
if (type === 'i64' ) {
wasmAssert(storeModuleSrc(type, ext, offset, align), [
{type, func: '$store' , args: [`i32.const ${base}`, `i64.const ${value}`]},
{type, func: '$load' , args: [`i32.const ${base}`], expected: value},
]);
wasmAssert(storeModuleCstSrc(type, ext, offset, align, value), [
{type, func: '$store' , args: [`i32.const ${base}`]},
{type, func: '$load' , args: [`i32.const ${base}`], expected: value},
]);
} else {
let module = storeModule(type, ext, offset, align);
let moduleCst = storeModuleCst(type, ext, offset, align, value);
module.store(base, value);
assertEq(module.load(base), value);
moduleCst.store(base);
assertEq(moduleCst.load(base), value);
}
}
function testStoreOOB(type, ext, base, offset, align, value) {
if (type === 'i64' ) {
assertErrorMessage(() => wasmAssert(
storeModuleSrc(type, ext, offset, align),
[{type, func: '$store' , args: [`i32.const ${base}`, `i64.const ${value}`]}]
), RuntimeError, /index out of bounds/);
} else {
assertErrorMessage(() => storeModule(type, ext, offset, align).store(base, value), RuntimeError, /index out of bounds/);
}
}
function badLoadModule(type, ext) {
wasmFailValidateText( `(module (func (param i32) (${type}.load${ext} (local.get 0 ))) (export "" (func 0 )))`, /memory index/);
}
function badStoreModule(type, ext) {
wasmFailValidateText(`(module (func (param i32) (${type}.store${ext} (local.get 0 ) (${type}.const 0 ))) (export "" (func 0 )))`, /memory index/);
}
// Can't touch memory.
for (let [type, ext] of [
['i32' , '' ],
['i32' , '8_s' ],
['i32' , '8_u' ],
['i32' , '16_s' ],
['i32' , '16_u' ],
['i64' , '' ],
['i64' , '8_s' ],
['i64' , '8_u' ],
['i64' , '16_s' ],
['i64' , '16_u' ],
['i64' , '32_s' ],
['i64' , '32_u' ],
['f32' , '' ],
['f64' , '' ],
])
{
badLoadModule(type, ext);
}
for (let [type, ext] of [
['i32' , '' ],
['i32' , '8' ],
['i32' , '16' ],
['i64' , '' ],
['i64' , '8' ],
['i64' , '16' ],
['i64' , '32' ],
['f32' , '' ],
['f64' , '' ],
])
{
badStoreModule(type, ext);
}
assertEq(getJitCompilerOptions()['wasm.fold-offsets' ], 1 );
for (var foldOffsets = 0 ; foldOffsets <= 1 ; foldOffsets++) {
setJitCompilerOption('wasm.fold-offsets' , foldOffsets | 0 );
testLoad('i32' , '' , 0 , 0 , 0 , 0 x03020100);
testLoad('i32' , '' , 1 , 0 , 1 , 0 x04030201);
testLoad('i32' , '' , 0 , 4 , 0 , 0 x07060504);
testLoad('i32' , '' , 1 , 3 , 4 , 0 x07060504);
testLoad('f32' , '' , 0 , 0 , 0 , 3 .820471434542632 e-37 );
testLoad('f32' , '' , 1 , 0 , 1 , 1 .539989614439558 e-36 );
testLoad('f32' , '' , 0 , 4 , 0 , 1 .0082513512365273 e-34 );
testLoad('f32' , '' , 1 , 3 , 4 , 1 .0082513512365273 e-34 );
testLoad('f64' , '' , 0 , 0 , 0 , 7 .949928895127363 e-275 );
testLoad('f64' , '' , 1 , 0 , 1 , 5 .447603722011605 e-270 );
testLoad('f64' , '' , 0 , 8 , 0 , 3 .6919162048650923 e-236 );
testLoad('f64' , '' , 1 , 7 , 8 , 3 .6919162048650923 e-236 );
testLoad('i32' , '8_s' , 16 , 0 , 0 , -0 x10);
testLoad('i32' , '8_u' , 16 , 0 , 0 , 0 xf0);
testLoad('i32' , '16_s' , 16 , 0 , 0 , -0 xe10);
testLoad('i32' , '16_u' , 16 , 0 , 0 , 0 xf1f0);
testStore('i32' , '' , 0 , 0 , 0 , -0 x3f3e2c2c);
testStore('i32' , '' , 1 , 0 , 1 , -0 x3f3e2c2c);
testStore('i32' , '' , 0 , 1 , 1 , -0 x3f3e2c2c);
testStore('i32' , '' , 1 , 1 , 4 , -0 x3f3e2c2c);
testStore('f32' , '' , 0 , 0 , 0 , 0 .01234566979110241 );
testStore('f32' , '' , 1 , 0 , 1 , 0 .01234566979110241 );
testStore('f32' , '' , 0 , 4 , 0 , 0 .01234566979110241 );
testStore('f32' , '' , 1 , 3 , 4 , 0 .01234566979110241 );
testStore('f64' , '' , 0 , 0 , 0 , 0 .89012345 );
testStore('f64' , '' , 1 , 0 , 1 , 0 .89012345 );
testStore('f64' , '' , 0 , 8 , 0 , 0 .89012345 );
testStore('f64' , '' , 1 , 7 , 8 , 0 .89012345 );
testStore('i32' , '8' , 0 , 0 , 0 , 0 x23);
testStore('i32' , '16' , 0 , 0 , 0 , 0 x2345);
wasmFailValidateText('(module (memory 2 1))' , /maximum length 1 is less than initial length 2 /);
// Test bounds checks and edge cases.
for (let align of [0 ,1 ,2 ,4 ]) {
for (let offset of [0 , 1 , 2 , 3 , 4 , 8 , 16 , 41 , 0 xfff8]) {
// Accesses of 1 byte.
let lastValidIndex = 0 x10000 - 1 - offset;
if (align < 2 ) {
testLoad('i32' , '8_s' , lastValidIndex, offset, align, 0 );
testLoadOOB('i32' , '8_s' , lastValidIndex + 1 , offset, align);
testLoad('i32' , '8_u' , lastValidIndex, offset, align, 0 );
testLoadOOB('i32' , '8_u' , lastValidIndex + 1 , offset, align);
testStore('i32' , '8' , lastValidIndex, offset, align, -42 );
testStoreOOB('i32' , '8' , lastValidIndex + 1 , offset, align, -42 );
}
// Accesses of 2 bytes.
lastValidIndex = 0 x10000 - 2 - offset;
if (align < 4 ) {
testLoad('i32' , '16_s' , lastValidIndex, offset, align, 0 );
testLoadOOB('i32' , '16_s' , lastValidIndex + 1 , offset, align);
testLoad('i32' , '16_u' , lastValidIndex, offset, align, 0 );
testLoadOOB('i32' , '16_u' , lastValidIndex + 1 , offset, align);
testStore('i32' , '16' , lastValidIndex, offset, align, -32768 );
testStoreOOB('i32' , '16' , lastValidIndex + 1 , offset, align, -32768 );
}
// Accesses of 4 bytes.
lastValidIndex = 0 x10000 - 4 - offset;
testLoad('i32' , '' , lastValidIndex, offset, align, 0 );
testLoadOOB('i32' , '' , lastValidIndex + 1 , offset, align);
testLoad('f32' , '' , lastValidIndex, offset, align, 0 );
testLoadOOB('f32' , '' , lastValidIndex + 1 , offset, align);
testStore('i32' , '' , lastValidIndex, offset, align, 1337 );
testStoreOOB('i32' , '' , lastValidIndex + 1 , offset, align, 1337 );
testStore('f32' , '' , lastValidIndex, offset, align, Math.fround(13 .37 ));
testStoreOOB('f32' , '' , lastValidIndex + 1 , offset, align, Math.fround(13 .37 ));
// Accesses of 8 bytes.
lastValidIndex = 0 x10000 - 8 - offset;
testLoad('f64' , '' , lastValidIndex, offset, align, 0 );
testLoadOOB('f64' , '' , lastValidIndex + 1 , offset, align);
testStore('f64' , '' , lastValidIndex, offset, align, 1 .23456789 );
testStoreOOB('f64' , '' , lastValidIndex + 1 , offset, align, 1 .23456789 );
}
// Ensure wrapping doesn't apply.
offset = 0 x7fffffff;
for (let index of [0 , 1 , 2 , 3 , 0 x7fffffff, 0 x80000000, 0 x80000001]) {
if (align < 2 ) {
testLoadOOB('i32' , '8_s' , index, offset, align);
}
if (align < 4 ) {
testLoadOOB('i32' , '16_s' , index, offset, align);
}
testLoadOOB('i32' , '' , index, offset, align);
testLoadOOB('f32' , '' , index, offset, align);
testLoadOOB('f64' , '' , index, offset, align);
}
// Ensure out of bounds when the offset is greater than the immediate range.
index = 0 ;
for (let offset of [0 x80000000, 0 xfffffffe, 0 xffffffff]) {
testLoadOOB('i32' , '8_s' , index, offset, 1 );
testLoadOOB('i32' , '16_s' , index, offset, 1 );
testLoadOOB('i32' , '16_s' , index, offset, 2 );
testLoadOOB('i32' , '' , index, offset, 1 );
testLoadOOB('i32' , '' , index, offset, 4 );
testLoadOOB('f32' , '' , index, offset, 1 );
testLoadOOB('f32' , '' , index, offset, 4 );
testLoadOOB('f64' , '' , index, offset, 1 );
testLoadOOB('f64' , '' , index, offset, 8 );
}
wasmFailValidateText('(module (memory 1) (func (f64.store offset=0 (i32.const 0) (i32.const 0))))' , mismatchError("i32" , "f64" ));
wasmFailValidateText('(module (memory 1) (func (f64.store offset=0 (i32.const 0) (f32.const 0))))' , mismatchError("f32" , "f64" ));
wasmFailValidateText('(module (memory 1) (func (f32.store offset=0 (i32.const 0) (i32.const 0))))' , mismatchError("i32" , "f32" ));
wasmFailValidateText('(module (memory 1) (func (f32.store offset=0 (i32.const 0) (f64.const 0))))' , mismatchError("f64" , "f32" ));
wasmFailValidateText('(module (memory 1) (func (i32.store offset=0 (i32.const 0) (f32.const 0))))' , mismatchError("f32" , "i32" ));
wasmFailValidateText('(module (memory 1) (func (i32.store offset=0 (i32.const 0) (f64.const 0))))' , mismatchError("f64" , "i32" ));
// Test high number of registers.
function testRegisters() {
assertEq(wasmEvalText(
`(module
(memory 1 )
(data (i32.const 0 ) "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f" )
(data (i32.const 16 ) "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff" )
(func (param i32) (result i32) (local i32 i32 i32 i32 f32 f64)
(local.set 1 (i32.load8_s offset=4 (local.get 0 )))
(local.set 2 (i32.load16_s (local.get 1 )))
(i32.store8 offset=4 (local.get 0 ) (local.get 1 ))
(local.set 3 (i32.load16_u (local.get 2 )))
(i32.store16 (local.get 1 ) (local.get 2 ))
(local.set 4 (i32.load (local.get 2 )))
(i32.store (local.get 1 ) (local.get 2 ))
(local.set 5 (f32.load (local.get 4 )))
(f32.store (local.get 4 ) (local.get 5 ))
(local.set 6 (f64.load (local.get 4 )))
(f64.store (local.get 4 ) (local.get 6 ))
(i32.add
(i32.add
(local.get 0 )
(local.get 1 )
)
(i32.add
(i32.add
(local.get 2 )
(local.get 3 )
)
(i32.add
(local.get 4 )
(i32.reinterpret_f32 (local.get 5 ))
)
)
)
) (export "" (func 0 )))`
).exports["" ](1 ), 50464523 );
}
testRegisters();
testLoad('i64' , '' , 0 , 0 , 0 , '0x0706050403020100' );
testLoad('i64' , '' , 1 , 0 , 0 , '0x0807060504030201' );
testLoad('i64' , '' , 0 , 1 , 0 , '0x0807060504030201' );
testLoad('i64' , '' , 1 , 1 , 4 , '0x0908070605040302' );
testLoad('i64' , '8_s' , 16 , 0 , 0 , -0 x10);
testLoad('i64' , '8_u' , 16 , 0 , 0 , 0 xf0);
testLoad('i64' , '16_s' , 16 , 0 , 0 , -0 xe10);
testLoad('i64' , '16_u' , 16 , 0 , 0 , 0 xf1f0);
testLoad('i64' , '32_s' , 16 , 0 , 0 , 0 xf3f2f1f0 | 0 );
testLoad('i64' , '32_u' , 16 , 0 , 0 , '0xf3f2f1f0' );
testStore('i64' , '' , 0 , 0 , 0 , '0xc0c1d3d4e6e7090a' );
testStore('i64' , '' , 1 , 0 , 0 , '0xc0c1d3d4e6e7090a' );
testStore('i64' , '' , 0 , 1 , 0 , '0xc0c1d3d4e6e7090a' );
testStore('i64' , '' , 1 , 1 , 4 , '0xc0c1d3d4e6e7090a' );
testStore('i64' , '8' , 0 , 0 , 0 , 0 x23);
testStore('i64' , '16' , 0 , 0 , 0 , 0 x23);
testStore('i64' , '32' , 0 , 0 , 0 , 0 x23);
for (let offset of [0 , 1 , 2 , 3 , 4 , 8 , 16 , 41 , 0 xfff8]) {
// Accesses of 1 byte.
let lastValidIndex = 0 x10000 - 1 - offset;
if (align < 2 ) {
testLoad('i64' , '8_s' , lastValidIndex, offset, align, 0 );
testLoadOOB('i64' , '8_s' , lastValidIndex + 1 , offset, align);
testLoad('i64' , '8_u' , lastValidIndex, offset, align, 0 );
testLoadOOB('i64' , '8_u' , lastValidIndex + 1 , offset, align);
testStore('i64' , '8' , lastValidIndex, offset, align, -42 );
testStoreOOB('i64' , '8' , lastValidIndex + 1 , offset, align, -42 );
}
// Accesses of 2 bytes.
lastValidIndex = 0 x10000 - 2 - offset;
if (align < 4 ) {
testLoad('i64' , '16_s' , lastValidIndex, offset, align, 0 );
testLoadOOB('i64' , '16_s' , lastValidIndex + 1 , offset, align);
testLoad('i64' , '16_u' , lastValidIndex, offset, align, 0 );
testLoadOOB('i64' , '16_u' , lastValidIndex + 1 , offset, align);
testStore('i64' , '16' , lastValidIndex, offset, align, -32768 );
testStoreOOB('i64' , '16' , lastValidIndex + 1 , offset, align, -32768 );
}
// Accesses of 4 bytes.
lastValidIndex = 0 x10000 - 4 - offset;
testLoad('i64' , '32_s' , lastValidIndex, offset, align, 0 );
testLoadOOB('i64' , '32_s' , lastValidIndex + 1 , offset, align);
testLoad('i64' , '32_u' , lastValidIndex, offset, align, 0 );
testLoadOOB('i64' , '32_u' , lastValidIndex + 1 , offset, align);
testStore('i64' , '32' , lastValidIndex, offset, align, 0 xf1231337 | 0 );
testStoreOOB('i64' , '32' , lastValidIndex + 1 , offset, align, 0 xf1231337 | 0 );
// Accesses of 8 bytes.
lastValidIndex = 0 x10000 - 8 - offset;
testLoad('i64' , '' , lastValidIndex, offset, align, 0 );
testLoadOOB('i64' , '' , lastValidIndex + 1 , offset, align);
testStore('i64' , '' , lastValidIndex, offset, align, '0x1234567887654321' );
testStoreOOB('i64' , '' , lastValidIndex + 1 , offset, align, '0x1234567887654321' );
}
}
}
setJitCompilerOption('wasm.fold-offsets' , 1 );
// Test active segments with a memory index.
{
function makeIt(flag, memindex) {
return new Uint8Array([0 x00, 0 x61, 0 x73, 0 x6d,
0 x01, 0 x00, 0 x00, 0 x00,
0 x05, // Memory section
0 x03, // Section size
0 x01, // One memory
0 x00, // Unshared, min only
0 x01, // Min
0 x0b, // Data section
0 x0a, // Section size
0 x01, // One data segment
flag, // Flag should be 2, or > 2 if invalid
memindex, // Memory index should be 0, or > 0 if invalid
0 x41, // Init expr: i32.const
0 x00, // Init expr: zero (payload)
0 x0b, // Init expr: end
0 x03, // Three bytes, because why not?
0 x01,
0 x02,
0 x03]);
}
// Should succeed because this is what an active segment with index looks like
new WebAssembly.Module(makeIt(0 x02, 0 x00));
// Should fail because the kind is unknown
assertErrorMessage(() => new WebAssembly.Module(makeIt(0 x03, 0 x00)),
WebAssembly.CompileError,
/invalid data initializer-kind/);
// Should fail because the memory index is bad
assertErrorMessage(() => new WebAssembly.Module(makeIt(0 x02, 0 x01)),
WebAssembly.CompileError,
/invalid memory index/);
}
// Misc syntax for data.
// When memory index is present it must be zero, and the offset must be present too;
// but it's OK for there to be neither
new WebAssembly.Module(wasmTextToBinary(`(module (memory 1 ) (data 0 (i32.const 0 ) "" ))`));
new WebAssembly.Module(wasmTextToBinary(`(module (memory 1 ) (data 0 (offset (i32.const 0 )) "" ))`));
new WebAssembly.Module(wasmTextToBinary(`(module (memory 1 ) (data "" ))`));
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(`(module (memory 1 ) (data 0 "" ))`)),
SyntaxError,
/wasm text error/);
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(`(module (memory 1 ) (data 1 (i32.const 0 ) "" ))`)),
WebAssembly.CompileError,
/invalid memory index/);
// Make sure we handle memory instructions without memory
var nomem = /memory index out of range/;
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(`(module (func (result i32) memory.size))`)),
WebAssembly.CompileError,
nomem);
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(`(module (func (result i32) (memory.grow (i32.const 1 ))))`)),
WebAssembly.CompileError,
nomem);
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(`
(module (func (param i32 i32 i32)
(memory.copy (local.get 0 ) (local.get 1 ) (local.get 2 ))))`)),
WebAssembly.CompileError,
nomem);
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(`
(module (func (param i32 i32 i32)
(memory.fill (local.get 0 ) (local.get 1 ) (local.get 2 ))))`)),
WebAssembly.CompileError,
nomem);
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(`
(module
(data $d "01234" )
(func (param i32 i32)
(memory.init $d (local.get 0 ) (local.get 1 ))))`)),
WebAssembly.CompileError,
nomem);
Messung V0.5 in Prozent C=94 H=90 G=91
¤ Dauer der Verarbeitung: 0.25 Sekunden
(vorverarbeitet am 2026-06-10)
¤
*© Formatika GbR, Deutschland