memory-sharing.js (8168B)
1 // |jit-test| skip-if: !wasmThreadsEnabled() 2 3 const WASMPAGE = 65536; 4 5 // A shared memory should yield a SharedArrayBuffer of appropriate length 6 7 { 8 let mem = new WebAssembly.Memory({initial: 2, maximum: 4, shared: true}); 9 assertEq(mem.buffer instanceof SharedArrayBuffer, true); 10 assertEq(mem.buffer.byteLength, WASMPAGE*2); 11 } 12 13 // Ditto, when the memory was created by instantiation and exported 14 15 { 16 let text = `(module 17 (memory (export "memory") 1 2 shared) 18 (func (export "l0") (result i32) (i32.load (i32.const 0))))`; 19 let mod = new WebAssembly.Module(wasmTextToBinary(text)); 20 let ins = new WebAssembly.Instance(mod); 21 let mem = ins.exports.memory; 22 let buf = mem.buffer; 23 assertEq(buf instanceof SharedArrayBuffer, true); 24 assertEq(buf.byteLength, WASMPAGE); 25 } 26 27 // Shared memory requires a maximum size 28 29 { 30 assertErrorMessage(() => new WebAssembly.Memory({initial: 2, shared: true}), 31 TypeError, 32 /'shared' is true but maximum is not specified/); 33 } 34 35 // Ditto, syntactically 36 37 { 38 let text = `(module 39 (memory 1 shared) 40 (func (export "l0") (result i32) (i32.load (i32.const 0))))`; 41 assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(text)), 42 WebAssembly.CompileError, 43 /maximum length required for shared memory/); 44 } 45 46 // Ditto, in the binary. The flags field can be 0 (unshared, min only), 1 47 // (unshared, min and max), or 3 (shared, min and max), but not 2 (shared, min 48 // only). So construct a module that has that, and make sure it's rejected. 49 50 { 51 let bin = new Uint8Array([0x00, 0x61, 0x73, 0x6d, 52 0x01, 0x00, 0x00, 0x00, 53 0x05, // Memory 54 0x03, // Section size 55 0x01, // One memory 56 0x02, // Shared, min only (illegal) 57 0x01]); // Min 58 assertErrorMessage(() => new WebAssembly.Module(bin), 59 WebAssembly.CompileError, 60 /maximum length required for shared memory/); 61 } 62 63 // Importing shared memory and being provided with shared should work 64 65 { 66 let text = `(module 67 (memory (import "" "memory") 1 1 shared) 68 (func (export "id") (param i32) (result i32) (local.get 0)))`; 69 let mod = new WebAssembly.Module(wasmTextToBinary(text)); 70 let mem = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true}); 71 let ins = new WebAssembly.Instance(mod, {"": {memory: mem}}); 72 assertEq(ins.exports.id(0x12345678), 0x12345678); 73 } 74 75 // Importing shared memory but being provided with unshared should fail 76 77 { 78 let text = `(module 79 (memory (import "" "memory") 1 1 shared) 80 (func (export "id") (param i32) (result i32) (local.get 0)))`; 81 let mod = new WebAssembly.Module(wasmTextToBinary(text)); 82 let mem = new WebAssembly.Memory({initial: 1, maximum: 1}); 83 assertErrorMessage(() => new WebAssembly.Instance(mod, {"": {memory: mem}}), 84 WebAssembly.LinkError, 85 /unshared memory but shared required/); 86 } 87 88 // Importing unshared memory but being provided with shared should fail 89 90 { 91 let text = `(module 92 (memory (import "" "memory") 1 1) 93 (func (export "id") (param i32) (result i32) (local.get 0)))`; 94 let mod = new WebAssembly.Module(wasmTextToBinary(text)); 95 let mem = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true}); 96 assertErrorMessage(() => new WebAssembly.Instance(mod, {"": {memory: mem}}), 97 WebAssembly.LinkError, 98 /shared memory but unshared required/); 99 } 100 101 // Importing shared memory and being provided with shared memory with 102 // incompatible parameters should fail 103 104 { 105 let text = `(module 106 (memory (import "" "memory") 2 4 shared) 107 (func (export "id") (param i32) (result i32) (local.get 0)))`; 108 let mod = new WebAssembly.Module(wasmTextToBinary(text)); 109 110 // some cases that are non-matching are allowed, eg, initial > declared min 111 112 // initial < declared min 113 let mem3 = new WebAssembly.Memory({initial: 1, maximum: 4, shared: true}); 114 assertErrorMessage(() => new WebAssembly.Instance(mod, {"": {memory: mem3}}), 115 WebAssembly.LinkError, 116 /imported Memory with incompatible size/); 117 118 // initial > declared max 119 let mem4 = new WebAssembly.Memory({initial: 5, maximum: 8, shared: true}); 120 assertErrorMessage(() => new WebAssembly.Instance(mod, {"": {memory: mem4}}), 121 WebAssembly.LinkError, 122 /imported Memory with incompatible size/); 123 124 // max > declared max 125 let mem5 = new WebAssembly.Memory({initial: 2, maximum: 8, shared: true}); 126 assertErrorMessage(() => new WebAssembly.Instance(mod, {"": {memory: mem5}}), 127 WebAssembly.LinkError, 128 /imported Memory with incompatible maximum size/); 129 } 130 131 132 // basic memory.size and memory.grow operation, with bounds checking near the 133 // valid/invalid boundary 134 135 { 136 let text = `(module 137 (memory (export "memory") 2 4 shared) 138 (func (export "c") (result i32) memory.size) 139 (func (export "g") (result i32) (memory.grow (i32.const 1))) 140 (func (export "l") (param i32) (result i32) (i32.load (local.get 0))) 141 (func (export "s") (param i32) (param i32) (i32.store (local.get 0) (local.get 1))))`; 142 let mod = new WebAssembly.Module(wasmTextToBinary(text)); 143 let ins = new WebAssembly.Instance(mod); 144 let exp = ins.exports; 145 let mem = exp.memory; 146 147 let b1 = mem.buffer; 148 assertEq(exp.c(), 2); 149 assertEq(b1.byteLength, WASMPAGE*2); 150 assertEq(mem.buffer === b1, true); // memory.size does not affect buffer 151 exp.s(WASMPAGE*2-4, 0x12345678) // access near end 152 assertEq(exp.l(WASMPAGE*2-4), 0x12345678); 153 assertErrorMessage(() => exp.l(WASMPAGE*2), // beyond current end (but below max) 154 WebAssembly.RuntimeError, 155 /index out of bounds/); 156 assertEq(exp.g(), 2); 157 assertEq(b1.byteLength, WASMPAGE*2); // growing does not affect existing buffer length 158 let b2 = mem.buffer; 159 assertEq(b1 !== b2, true); // growing produces a new buffer 160 assertEq(b2.byteLength, WASMPAGE*3); // new buffer has appropriate length 161 assertEq(exp.c(), 3); 162 exp.s(WASMPAGE*3-4, 0x12344321); // access near new end 163 assertEq(exp.l(WASMPAGE*3-4), 0x12344321); 164 assertErrorMessage(() => exp.l(WASMPAGE*3), // beyond current end (but below max) 165 WebAssembly.RuntimeError, 166 /index out of bounds/); 167 assertEq(exp.g(), 3); 168 assertEq(b2.byteLength, WASMPAGE*3); // growing does not affect existing buffer length 169 let b3 = mem.buffer; 170 assertEq(b2 !== b3, true); // growing produces a new buffer 171 assertEq(b3.byteLength, WASMPAGE*4); // new buffer has appropriate length 172 assertEq(exp.c(), 4); 173 exp.s(WASMPAGE*4-4, 0x12121212); // access near new end 174 assertEq(exp.l(WASMPAGE*4-4), 0x12121212); 175 assertErrorMessage(() => exp.l(WASMPAGE*4), // beyond current end (and beyond max) 176 WebAssembly.RuntimeError, 177 /index out of bounds/); 178 assertEq(exp.g(), -1); 179 assertEq(exp.c(), 4); // failure to grow -> no change 180 let b4 = mem.buffer; 181 assertEq(b3 === b4, true); // failure to grow -> same ol' buffer 182 assertEq(exp.g(), -1); // we can fail repeatedly 183 } 184 185 // Test the grow() API with shared memory. In the implementation this API 186 // shares almost all code with the wasm instruction, so don't bother going deep. 187 188 { 189 let mem = new WebAssembly.Memory({initial: 2, maximum: 4, shared: true}); 190 let buf = mem.buffer; 191 assertEq(mem.grow(1), 2); 192 assertEq(buf.byteLength, WASMPAGE*2); 193 assertEq(mem.grow(1), 3); 194 assertErrorMessage(() => mem.grow(1), RangeError, /failed to grow memory/); 195 } 196 197 // Initializing shared memory with data 198 199 { 200 let text = `(module 201 (memory (import "" "memory") 2 4 shared) 202 (data (i32.const 0) "abcdefghijklmnopqrstuvwxyz") 203 (func (export "l") (param i32) (result i32) (i32.load8_u (local.get 0))))`; 204 let mod = new WebAssembly.Module(wasmTextToBinary(text)); 205 let mem = new WebAssembly.Memory({initial: 2, maximum: 4, shared: true}); 206 let ins = new WebAssembly.Instance(mod, {"": {memory: mem}}); 207 let exp = ins.exports; 208 assertEq(exp.l(12), "a".charCodeAt(0) + 12); 209 }