Add support for the Wasm multi-value proposal
[WebKit-https.git] / JSTests / wasm / spec-harness / index.js
1 /*
2  * Copyright 2017 WebAssembly Community Group participants
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15 */
16
17 'use strict';
18
19 let testNum = (function() {
20     let count = 1;
21     return function() {
22         return `#${count++} `;
23     }
24 })();
25
26 // WPT's assert_throw uses a list of predefined, hardcoded known errors. Since
27 // it is not aware of the WebAssembly error types (yet), implement our own
28 // version.
29 function assertThrows(func, err) {
30     let caught = false;
31     try {
32         func();
33     } catch(e) {
34         assert_true(e instanceof err, `expected ${err.name}, observed ${e.constructor.name}`);
35         caught = true;
36     }
37     assert_true(caught, testNum() + "assertThrows must catch any error.")
38 }
39
40 /******************************************************************************
41 ***************************** WAST HARNESS ************************************
42 ******************************************************************************/
43
44 // For assertions internal to our test harness.
45 function _assert(x) {
46     if (!x) {
47         throw new Error(`Assertion failure: ${x}`);
48     }
49 }
50
51 // A simple sum type that can either be a valid Value or an Error.
52 function Result(type, maybeValue) {
53     this.value = maybeValue;
54     this.type = type;
55 };
56
57 Result.VALUE = 'VALUE';
58 Result.ERROR = 'ERROR';
59
60 function ValueResult(val) { return new Result(Result.VALUE, val); }
61 function ErrorResult(err) { return new Result(Result.ERROR, err); }
62
63 Result.prototype.isError = function() { return this.type === Result.ERROR; }
64
65 const EXPECT_INVALID = false;
66
67 /* DATA **********************************************************************/
68
69 let soft_validate = true;
70
71 let $$;
72
73 // Default imports.
74 var registry = {};
75
76 // Resets the registry between two different WPT tests.
77 function reinitializeRegistry() {
78     if (typeof WebAssembly === 'undefined')
79         return;
80
81     registry = {
82         spectest: {
83             print: print,
84             global: 666,
85             table: new WebAssembly.Table({initial: 10, maximum: 20, element: 'funcref'}),
86             memory: new WebAssembly.Memory({initial: 1, maximum: 2})
87         }
88     };
89 }
90
91 reinitializeRegistry();
92
93 /* WAST POLYFILL *************************************************************/
94
95 function binary(bytes) {
96     let buffer = new ArrayBuffer(bytes.length);
97     let view = new Uint8Array(buffer);
98     for (let i = 0; i < bytes.length; ++i) {
99         view[i] = bytes.charCodeAt(i);
100     }
101     return buffer;
102 }
103
104 /**
105  * Returns a compiled module, or throws if there was an error at compilation.
106  */
107 function module(bytes, valid = true) {
108     let buffer = binary(bytes);
109     let validated;
110
111     try {
112         validated = WebAssembly.validate(buffer);
113     } catch (e) {
114         throw new Error(`WebAssembly.validate throws ${typeof e}: ${e}${e.stack}`);
115     }
116
117     if (validated !== valid) {
118         // Try to get a more precise error message from the WebAssembly.CompileError.
119         let err = '';
120         try {
121             new WebAssembly.Module(buffer);
122         } catch (e) {
123             if (e instanceof WebAssembly.CompileError)
124                 throw new WebAssembly.CompileError(`WebAssembly.validate error: ${e.toString()}${e.stack}\n`);
125             else
126                 throw new Error(`WebAssembly.validate throws ${typeof e}: ${e}${e.stack}`);
127         }
128         throw new Error(`WebAssembly.validate was expected to fail, but didn't`);
129     }
130
131     let module;
132     try {
133         module = new WebAssembly.Module(buffer);
134     } catch(e) {
135         if (valid)
136             throw new Error('WebAssembly.Module ctor unexpectedly throws ${typeof e}: ${e}${e.stack}');
137         throw e;
138     }
139
140     return module;
141 }
142
143 function uniqueTest(func, desc) {
144     test(func, testNum() + desc);
145 }
146
147 function assert_invalid(bytes) {
148     uniqueTest(() => {
149         try {
150             module(bytes, /* valid */ false);
151             throw new Error('did not fail');
152         } catch(e) {
153             assert_true(e instanceof WebAssembly.CompileError, "expected invalid failure:");
154         }
155     }, "A wast module that should be invalid or malformed.");
156 }
157
158 const assert_malformed = assert_invalid;
159
160 function assert_soft_invalid(bytes) {
161     uniqueTest(() => {
162         try {
163             module(bytes, /* valid */ soft_validate);
164             if (soft_validate)
165                 throw new Error('did not fail');
166         } catch(e) {
167             if (soft_validate)
168                 assert_true(e instanceof WebAssembly.CompileError, "expected soft invalid failure:");
169         }
170     }, "A wast module that *could* be invalid under certain engines.");
171 }
172
173 function instance(bytes, imports = registry, valid = true) {
174     if (imports instanceof Result) {
175         if (imports.isError())
176             return imports;
177         imports = imports.value;
178     }
179
180     let err = null;
181
182     let m, i;
183     try {
184         let m = module(bytes);
185         i = new WebAssembly.Instance(m, imports);
186     } catch(e) {
187         err = e;
188     }
189
190     if (valid) {
191         uniqueTest(() => {
192             let instantiated = err === null;
193             if (!instantiated) print(err);
194             assert_true(instantiated, err);
195         }, "module successfully instantiated");
196     }
197
198     return err !== null ? ErrorResult(err) : ValueResult(i);
199 }
200
201 function register(name, instance) {
202     _assert(instance instanceof Result);
203
204     if (instance.isError())
205         return;
206
207     registry[name] = instance.value.exports;
208 }
209
210 function call(instance, name, args) {
211     _assert(instance instanceof Result);
212
213     if (instance.isError())
214         return instance;
215
216     let err = null;
217     let result;
218     try {
219         result = instance.value.exports[name](...args);
220     } catch(e) {
221         err = e;
222     }
223
224     return err !== null ? ErrorResult(err) : ValueResult(result);
225 };
226
227 function get(instance, name) {
228     _assert(instance instanceof Result);
229
230     if (instance.isError())
231         return instance;
232
233     return ValueResult(instance.value.exports[name]);
234 }
235
236 function exports(name, instance) {
237     _assert(instance instanceof Result);
238
239     if (instance.isError())
240         return instance;
241
242     return ValueResult({ [name]: instance.value.exports });
243 }
244
245 function run(action) {
246     print("running new test: " + (new Error()).stack);
247     let result = action();
248
249     _assert(result instanceof Result);
250
251     uniqueTest(() => {
252         if (result.isError())
253             throw result.value;
254     }, "A wast test that runs without any special assertion.");
255 }
256
257 function assert_unlinkable(bytes) {
258     let result = instance(bytes, registry, EXPECT_INVALID);
259
260     _assert(result instanceof Result);
261
262     uniqueTest(() => {
263         assert_true(result.isError(), 'expected error result');
264         if (result.isError()) {
265             let e = result.value;
266             assert_true(e instanceof WebAssembly.LinkError, `expected link error, observed ${e}:`);
267         }
268     }, "A wast module that is unlinkable.");
269 }
270
271 function assert_uninstantiable(bytes) {
272     let result = instance(bytes, registry, EXPECT_INVALID);
273
274     _assert(result instanceof Result);
275
276     uniqueTest(() => {
277         assert_true(result.isError(), 'expected error result');
278         if (result.isError()) {
279             let e = result.value;
280             assert_true(e instanceof WebAssembly.RuntimeError, `expected runtime error, observed ${e}:`);
281         }
282     }, "A wast module that is uninstantiable.");
283 }
284
285 function assert_trap(action) {
286     let result = action();
287
288     _assert(result instanceof Result);
289
290     uniqueTest(() => {
291         assert_true(result.isError(), 'expected error result');
292         if (result.isError()) {
293             let e = result.value;
294             assert_true(e instanceof WebAssembly.RuntimeError, `expected runtime error, observed ${e}:`);
295         }
296     }, "A wast module that must trap at runtime.");
297 }
298
299 let StackOverflow;
300 try { (function f() { 1 + f() })() } catch (e) { StackOverflow = e.constructor }
301
302 function assert_exhaustion(action) {
303     let result = action();
304
305     _assert(result instanceof Result);
306
307     uniqueTest(() => {
308         assert_true(result.isError(), 'expected error result');
309         if (result.isError()) {
310             let e = result.value;
311             assert_true(e instanceof StackOverflow, `expected stack overflow error, observed ${e}:`);
312         }
313     }, "A wast module that must exhaust the stack space.");
314 }
315
316 function assert_return(action, ...expectedValues) {
317     if (expectedValues[0] instanceof Result) {
318         if (expectedValues[0].isError())
319             return;
320         expected = expected.value;
321     }
322
323     let result = action();
324
325     _assert(result instanceof Result);
326
327     uniqueTest(() => {
328         assert_true(!result.isError(), `expected success result, got: ${result.value}.`);
329         if (!result.isError()) {
330             assert_equals(result.value, expectedValues.length < 2 ? expectedValues[0] : expectedValues);
331         };
332     }, "A wast module that must return a particular value.");
333 };
334
335 function assert_return_nan(action) {
336     let result = action();
337
338     _assert(result instanceof Result);
339
340     uniqueTest(() => {
341         assert_true(!result.isError(), 'expected success result');
342         if (!result.isError()) {
343             assert_true(Number.isNaN(result.value), `expected NaN, observed ${result.value}.`);
344         };
345     }, "A wast module that must return NaN.");
346 }