[WASM-References] Rename anyfunc to funcref
[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 makeFuncrefIdent() {
48     const builder = (new Builder())
49           .Type().End()
50           .Function().End()
51           .Export()
52               .Function("h")
53           .End()
54           .Code()
55             .Function("h", { params: ["funcref"], ret: "funcref" }, [])
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: ["funcref"], ret: "anyref" }, ["anyref"])
86               .GetLocal(0)
87               .SetLocal(1)
88               .GetLocal(1)
89             .End()
90
91             .Function("i", { params: ["funcref"], ret: "funcref" }, ["funcref"])
92               .GetLocal(0)
93               .SetLocal(1)
94               .GetLocal(1)
95             .End()
96
97             .Function("get_h", { params: [], ret: "funcref" }, ["funcref"])
98               .I32Const(0)
99               .RefFunc(0)
100               .SetLocal(0)
101               .If("funcref")
102               .Block("funcref", (b) =>
103                 b.GetLocal(0)
104               )
105               .Else()
106               .Block("funcref", (b) =>
107                 b.GetLocal(0)
108               )
109               .End()
110             .End()
111
112             .Function("fix", { params: [], ret: "funcref" }, [])
113               .RefFunc(3)
114             .End()
115
116             .Function("get_not_exported", { params: [], ret: "funcref" }, [])
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" }, ["funcref"])
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, "Funcref 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, "Funcref must be an exported wasm function (evaluating 'func(...args)')")
141     assert.throws(() => instance.exports.h(undefined), Error, "Funcref 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, "Funcref must be an exported wasm function (evaluating 'func(...args)')")
146     assert.throws(() => instance.exports.i(5), Error, "Funcref must be an exported wasm function (evaluating 'func(...args)')")
147
148     assert.throws(() => instance.exports.get_h()(fun), Error, "Funcref 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, "Funcref 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().Funcref("imp", "ref", "immutable").End()
171       .End()
172       .Function().End()
173       .Global()
174           .RefNull("funcref", "mutable")
175           .RefNull("funcref", "immutable")
176           .GetGlobal("funcref", 0, "mutable")
177           .RefFunc("funcref", 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: ["funcref"], ret: "void" })
191           .GetLocal(0)
192           .SetGlobal(1)
193         .End()
194
195         .Function("get_glob", { params: [], ret: "funcref" })
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: "funcref" })
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, "Funcref 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().Funcref("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: ["funcref"], 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: "funcref"})
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 Funcref, 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: "funcref"})
272       .End()
273       .Global()
274           .RefNull("funcref", "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: ["funcref"], ret: "void" })
284           .GetLocal(0)
285           .SetGlobal(0)
286         .End()
287
288         .Function("get_glob", { params: [], ret: "funcref" })
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, "Funcref 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, "Funcref 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: "funcref"})
334       .End()
335       .Export()
336           .Function("set")
337           .Function("get")
338       .End()
339       .Code()
340         .Function("set", { params: ["funcref"], ret: "void" })
341           .I32Const(0)
342           .GetLocal(0)
343           .TableSet(1)
344         .End()
345
346         .Function("get", { params: [], ret: "funcref" })
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; }, makeFuncrefIdent()]) {
380     const $1 = new WebAssembly.Instance(new WebAssembly.Module((new Builder())
381       .Type().End()
382       .Import()
383            .Function("imp", "h", { params: ["funcref"], ret: "funcref" })
384       .End()
385       .Function().End()
386       .Table()
387             .Table({initial: 1, element: "funcref"})
388       .End()
389       .Export()
390           .Function("test1")
391           .Function("test2")
392           .Function("test3")
393           .Function("test4")
394       .End()
395       .Code()
396         .Function("test1", { params: ["funcref"], ret: "funcref" })
397           .GetLocal(0)
398           .Call(0)
399         .End()
400
401         .Function("test2", { params: [], ret: "funcref" })
402           .RefFunc(1)
403           .Call(0)
404         .End()
405
406         .Function("test3", { params: ["funcref"], ret: "funcref" })
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: "funcref" })
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, "Funcref must be an exported wasm function (evaluating 'func(...args)')")
434
435         for (let i=0; i<1000; ++i) {
436             assert.throws(() => test(fun), Error, "Funcref 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, "Funcref must be an exported wasm function (evaluating 'func(...args)')")
444
445         for (let i=0; i<1000; ++i) {
446             assert.throws(() => test()(fun), Error, "Funcref must be an exported wasm function (evaluating 'func(...args)')")
447         }
448     }
449 }