36a79b6f34223267de4ee4d42b76715659a4968b
[WebKit-https.git] / JSTests / wasm / references / func_ref.js
1 import * as assert from '../assert.js';
2 import Builder from '../Builder.js';
3
4 fullGC()
5 gc()
6
7 function makeExportedFunction(i) {
8     const builder = (new Builder())
9           .Type().End()
10           .Function().End()
11           .Export()
12               .Function("h")
13           .End()
14           .Code()
15             .Function("h", { params: [], ret: "i32" }, [])
16               .I32Const(i)
17             .End()
18           .End();
19
20     const bin = builder.WebAssembly().get();
21     const module = new WebAssembly.Module(bin);
22     const instance = new WebAssembly.Instance(module);
23
24     return instance.exports.h
25 }
26
27 function makeExportedIdent() {
28     const builder = (new Builder())
29           .Type().End()
30           .Function().End()
31           .Export()
32               .Function("h")
33           .End()
34           .Code()
35             .Function("h", { params: ["i32"], ret: "i32" }, [])
36               .GetLocal(0)
37             .End()
38           .End();
39
40     const bin = builder.WebAssembly().get();
41     const module = new WebAssembly.Module(bin);
42     const instance = new WebAssembly.Instance(module);
43
44     return instance.exports.h
45 }
46
47 function makeAnyfuncIdent() {
48     const builder = (new Builder())
49           .Type().End()
50           .Function().End()
51           .Export()
52               .Function("h")
53           .End()
54           .Code()
55             .Function("h", { params: ["anyfunc"], ret: "anyfunc" }, [])
56               .GetLocal(0)
57             .End()
58           .End();
59
60     const bin = builder.WebAssembly().get();
61     const module = new WebAssembly.Module(bin);
62     const instance = new WebAssembly.Instance(module);
63
64     return instance.exports.h
65 }
66
67 {
68     const myfun = makeExportedFunction(1337);
69     function fun() {
70         return 41;
71     }
72
73     const builder = (new Builder())
74           .Type().End()
75           .Function().End()
76           .Export()
77               .Function("h")
78               .Function("i")
79               .Function("get_h")
80               .Function("fix")
81               .Function("get_not_exported")
82               .Function("local_read")
83           .End()
84           .Code()
85             .Function("h", { params: ["anyfunc"], ret: "anyref" }, ["anyref"])
86               .GetLocal(0)
87               .SetLocal(1)
88               .GetLocal(1)
89             .End()
90
91             .Function("i", { params: ["anyfunc"], ret: "anyfunc" }, ["anyfunc"])
92               .GetLocal(0)
93               .SetLocal(1)
94               .GetLocal(1)
95             .End()
96
97             .Function("get_h", { params: [], ret: "anyfunc" }, ["anyfunc"])
98               .I32Const(0)
99               .RefFunc(0)
100               .SetLocal(0)
101               .If("anyfunc")
102               .Block("anyfunc", (b) =>
103                 b.GetLocal(0)
104               )
105               .Else()
106               .Block("anyfunc", (b) =>
107                 b.GetLocal(0)
108               )
109               .End()
110             .End()
111
112             .Function("fix", { params: [], ret: "anyfunc" }, [])
113               .RefFunc(3)
114             .End()
115
116             .Function("get_not_exported", { params: [], ret: "anyfunc" }, [])
117               .RefFunc(5)
118             .End()
119
120             .Function("ret_42", { params: [], ret: "i32" }, [])
121               .I32Const(42)
122             .End()
123
124             .Function("local_read", { params: [], ret: "i32" }, ["anyfunc"])
125               .GetLocal(0)
126               .RefIsNull()
127             .End()
128           .End();
129
130     const bin = builder.WebAssembly().get();
131     const module = new WebAssembly.Module(bin);
132     const instance = new WebAssembly.Instance(module);
133     fullGC();
134
135     assert.eq(instance.exports.local_read(), 1)
136     assert.eq(instance.exports.h(null), null)
137
138     assert.throws(() => instance.exports.h(fun), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')")
139     assert.eq(instance.exports.h(myfun), myfun)
140     assert.throws(() => instance.exports.h(5), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')")
141     assert.throws(() => instance.exports.h(undefined), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')")
142
143     assert.eq(instance.exports.i(null), null)
144     assert.eq(instance.exports.i(myfun), myfun)
145     assert.throws(() => instance.exports.i(fun), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')")
146     assert.throws(() => instance.exports.i(5), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')")
147
148     assert.throws(() => instance.exports.get_h()(fun), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')")
149     assert.eq(instance.exports.get_h()(null), null)
150     assert.eq(instance.exports.get_h()(myfun), myfun)
151     assert.throws(() => instance.exports.get_h()(5), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')")
152
153     assert.eq(instance.exports.get_not_exported()(), 42)
154
155     assert.eq(instance.exports.fix()(), instance.exports.fix());
156     assert.eq(instance.exports.fix(), instance.exports.fix);
157 }
158
159 // Globals
160
161 {
162     const myfun = makeExportedFunction(42);
163     function fun() {
164         return 41;
165     }
166
167     const $1 = (() => new WebAssembly.Instance(new WebAssembly.Module((new Builder())
168       .Type().End()
169       .Import()
170            .Global().Anyfunc("imp", "ref", "immutable").End()
171       .End()
172       .Function().End()
173       .Global()
174           .RefNull("anyfunc", "mutable")
175           .RefNull("anyfunc", "immutable")
176           .GetGlobal("anyfunc", 0, "mutable")
177           .RefFunc("anyfunc", 2, "immutable")
178       .End()
179       .Export()
180           .Function("set_glob")
181           .Function("get_glob")
182           .Function("glob_is_null")
183           .Function("set_glob_null")
184           .Function("get_import")
185           .Global("expglob", 2)
186           .Global("expglob2", 0)
187           .Global("exp_glob_is_null", 4)
188       .End()
189       .Code()
190         .Function("set_glob", { params: ["anyfunc"], ret: "void" })
191           .GetLocal(0)
192           .SetGlobal(1)
193         .End()
194
195         .Function("get_glob", { params: [], ret: "anyfunc" })
196             .GetGlobal(1)
197         .End()
198
199         .Function("glob_is_null", { params: [], ret: "i32" })
200             .Call(1)
201             .RefIsNull()
202         .End()
203
204         .Function("set_glob_null", { params: [], ret: "void" })
205             .RefNull()
206             .Call(0)
207         .End()
208
209         .Function("get_import", { params: [], ret: "anyfunc" })
210             .GetGlobal(0)
211         .End()
212       .End().WebAssembly().get()), { imp: { ref: makeExportedFunction(1337) } }))();
213
214     fullGC();
215
216     assert.eq($1.exports.get_import()(), 1337)
217     assert.eq($1.exports.expglob, null)
218     assert.eq($1.exports.expglob2(), 1337)
219     assert.eq($1.exports.exp_glob_is_null, $1.exports.glob_is_null);
220     assert.eq($1.exports.get_glob(), null)
221
222     $1.exports.set_glob(myfun); assert.eq($1.exports.get_glob(), myfun); assert.eq($1.exports.get_glob()(), 42); assert.eq($1.exports.expglob2(), 1337)
223     $1.exports.set_glob(null); assert.eq($1.exports.get_glob(), null)
224     $1.exports.set_glob(myfun); assert.eq($1.exports.get_glob()(), 42);
225
226     assert.throws(() => $1.exports.set_glob(fun), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')")
227
228     assert.eq($1.exports.glob_is_null(), 0)
229     $1.exports.set_glob_null(); assert.eq($1.exports.get_glob(), null)
230     assert.eq($1.exports.glob_is_null(), 1)
231 }
232
233 assert.throws(() => new WebAssembly.Instance(new WebAssembly.Module((new Builder())
234   .Type().End()
235   .Import()
236        .Global().Anyfunc("imp", "ref", "immutable").End()
237   .End()
238   .Function().End()
239   .Code().End().WebAssembly().get()), { imp: { ref: function() { return "hi" } } }), Error, "imported global imp:ref must be a wasm exported function or null (evaluating 'new WebAssembly.Instance')");
240
241 assert.throws(() => new WebAssembly.Module((new Builder())
242   .Type().End()
243   .Function().End()
244   .Code()
245     .Function("h", { params: ["anyfunc"], ret: "anyref" })
246       .GetLocal(0)
247     .End()
248   .End().WebAssembly().get()), Error, "WebAssembly.Module doesn't validate: control flow returns with unexpected type, in function at index 0 (evaluating 'new WebAssembly.Module')");
249
250 assert.throws(() => new WebAssembly.Module((new Builder())
251   .Type().End()
252   .Function().End()
253   .Table()
254     .Table({initial: 1, element: "anyfunc"})
255   .End()
256   .Code()
257     .Function("h", { params: ["i32"], ret: "void" })
258       .GetLocal(0)
259       .I32Const(0)
260       .TableSet(0)
261     .End()
262   .End().WebAssembly().get()), Error, "WebAssembly.Module doesn't validate: table.set value to type I32 expected Anyfunc, in function at index 0 (evaluating 'new WebAssembly.Module')");
263
264 // Tables
265 {
266     const $1 = new WebAssembly.Instance(new WebAssembly.Module((new Builder())
267       .Type().End()
268       .Function().End()
269       .Table()
270             .Table({initial: 0, element: "anyref"})
271             .Table({initial: 1, element: "anyfunc"})
272       .End()
273       .Global()
274           .RefNull("anyfunc", "mutable")
275       .End()
276       .Export()
277           .Function("set_glob")
278           .Function("get_glob")
279           .Function("call_glob")
280           .Function("ret_20")
281       .End()
282       .Code()
283         .Function("set_glob", { params: ["anyfunc"], ret: "void" })
284           .GetLocal(0)
285           .SetGlobal(0)
286         .End()
287
288         .Function("get_glob", { params: [], ret: "anyfunc" })
289             .GetGlobal(0)
290         .End()
291
292         .Function("call_glob", { params: ["i32"], ret: "i32" })
293             .I32Const(0)
294             .GetGlobal(0)
295             .TableSet(1)
296
297             .GetLocal(0)
298             .I32Const(0)
299             .CallIndirect(2,1)
300         .End()
301
302         .Function("ret_20", { params: ["i32"], ret: "i32" })
303             .I32Const(20)
304         .End()
305       .End().WebAssembly().get()));
306
307     const myfun = makeExportedFunction(1337);
308     function fun(i) {
309         return 41;
310     }
311     const ident = makeExportedIdent();
312
313     $1.exports.set_glob($1.exports.ret_20); assert.eq($1.exports.get_glob(), $1.exports.ret_20); assert.eq($1.exports.call_glob(42), 20)
314     $1.exports.set_glob(null); assert.eq($1.exports.get_glob(), null); assert.throws(() => $1.exports.call_glob(42), Error, "call_indirect to a null table entry (evaluating 'func(...args)')")
315     $1.exports.set_glob(ident); assert.eq($1.exports.get_glob(), ident); assert.eq($1.exports.call_glob(42), 42)
316
317     assert.throws(() => $1.exports.set_glob(fun), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')")
318     $1.exports.set_glob(myfun); assert.eq($1.exports.get_glob(), myfun); assert.throws(() => $1.exports.call_glob(42), Error, "call_indirect to a signature that does not match (evaluating 'func(...args)')")
319
320     for (let i=0; i<1000; ++i) {
321         assert.throws(() => $1.exports.set_glob(function() {}), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')");
322     }
323 }
324
325 // Table set/get
326
327 {
328     const $1 = new WebAssembly.Instance(new WebAssembly.Module((new Builder())
329       .Type().End()
330       .Function().End()
331       .Table()
332             .Table({initial: 0, element: "anyref"})
333             .Table({initial: 1, element: "anyfunc"})
334       .End()
335       .Export()
336           .Function("set")
337           .Function("get")
338       .End()
339       .Code()
340         .Function("set", { params: ["anyfunc"], ret: "void" })
341           .I32Const(0)
342           .GetLocal(0)
343           .TableSet(1)
344         .End()
345
346         .Function("get", { params: [], ret: "anyfunc" })
347             .I32Const(0)
348             .TableGet(1)
349         .End()
350       .End().WebAssembly().get()));
351
352     function doSet() {
353         const myfun = makeExportedFunction(444);
354         for (let i=0; i<1000; ++i) {
355             $1.exports.set(myfun);
356         }
357         $1.exports.set(myfun); assert.eq($1.exports.get(), myfun); assert.eq($1.exports.get()(), 444);
358     }
359
360     function doTest(j,k, l) {
361         fullGC();
362         let garbage = { val: "hi", val2: 5, arr: [] }
363         for (let i=0; i<100; ++i) garbage.arr += ({ field: i + j + k + l })
364         fullGC();
365
366         for (let i=0; i<100; ++i) {
367             assert.eq($1.exports.get()(), 444);
368             fullGC();
369         }
370     }
371
372
373     doSet()
374     doTest(0,0,0)
375 }
376
377 // Wasm->JS Calls
378
379 for (let importedFun of [function(i) { return i; }, makeAnyfuncIdent()]) {
380     const $1 = new WebAssembly.Instance(new WebAssembly.Module((new Builder())
381       .Type().End()
382       .Import()
383            .Function("imp", "h", { params: ["anyfunc"], ret: "anyfunc" })
384       .End()
385       .Function().End()
386       .Table()
387             .Table({initial: 1, element: "anyfunc"})
388       .End()
389       .Export()
390           .Function("test1")
391           .Function("test2")
392           .Function("test3")
393           .Function("test4")
394       .End()
395       .Code()
396         .Function("test1", { params: ["anyfunc"], ret: "anyfunc" })
397           .GetLocal(0)
398           .Call(0)
399         .End()
400
401         .Function("test2", { params: [], ret: "anyfunc" })
402           .RefFunc(1)
403           .Call(0)
404         .End()
405
406         .Function("test3", { params: ["anyfunc"], ret: "anyfunc" })
407           .GetLocal(0)
408           .I32Const(0)
409           .RefFunc(0)
410           .TableSet(0)
411           .I32Const(0)
412           .CallIndirect(0, 0)
413         .End()
414
415         .Function("test4", { params: [], ret: "anyfunc" })
416           .RefFunc(1)
417           .I32Const(0)
418           .RefFunc(0)
419           .TableSet(0)
420           .I32Const(0)
421           .CallIndirect(0, 0)
422         .End()
423       .End().WebAssembly().get()), { imp: { h: importedFun } });
424
425     const myfun = makeExportedFunction(1337);
426     function fun(i) {
427         return 41;
428     }
429
430     for (let test of [$1.exports.test1, $1.exports.test3]) {
431         assert.eq(test(myfun), myfun)
432         assert.eq(test(myfun)(), 1337)
433         assert.throws(() => test(fun), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')")
434
435         for (let i=0; i<1000; ++i) {
436             assert.throws(() => test(fun), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')")
437         }
438     }
439
440     for (let test of [$1.exports.test2, $1.exports.test4]) {
441         assert.eq(test(), $1.exports.test1)
442         assert.eq(test()(myfun), myfun)
443         assert.throws(() => test()(fun), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')")
444
445         for (let i=0; i<1000; ++i) {
446             assert.throws(() => test()(fun), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')")
447         }
448     }
449 }