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