[wasm] Add normal GC support. (#15577)
[mono-project.git] / sdks / wasm / runtime-tests.js
blobcd308a8298c29595efeff00b06c117ba669d0166
1 // -*- mode: js; js-indent-level: 4; -*-
2 //
3 // Run runtime tests under a JS shell or a browser
4 //
6 //glue code to deal with the differences between chrome, ch, d8, jsc and sm.
7 var is_browser = typeof window != "undefined";
9 if (is_browser) {
10         // We expect to be run by tests/runtime/run.js which passes in the arguments using http parameters
11         var url = new URL (decodeURI (window.location));
12         arguments = [];
13         for (var v of url.searchParams) {
14                 if (v [0] == "arg") {
15                         console.log ("URL ARG: " + v [0] + "=" + v [1]);
16                         arguments.push (v [1]);
17                 }
18         }
21 if (is_browser || typeof print === "undefined")
22         print = console.log;
24 // JavaScript core does not have a console defined
25 if (typeof console === "undefined") {
26         var Console = function () {
27                 this.log = function(msg){ print(msg) };
28         };
29         console = new Console();
32 if (typeof console !== "undefined") {
33         var has_console_warn = false;
34         try {
35                 if (typeof console.warn !== "undefined")
36                         has_console_warn = true;
37         } catch(e) {}
39         if (!has_console_warn)
40                 console.warn = console.log;
43 if (typeof crypto == 'undefined') {
44         // /dev/random doesn't work on js shells, so define our own
45         // See library_fs.js:createDefaultDevices ()
46         var crypto = {
47                 getRandomValues: function (buffer) {
48                         buffer[0] = (Math.random()*256)|0;
49                 }
50         }
53 try {
54         if (typeof arguments == "undefined")
55                 arguments = WScript.Arguments;
56         load = WScript.LoadScriptFile;
57         read = WScript.LoadBinaryFile;
58 } catch (e) {
61 try {
62         if (typeof arguments == "undefined") {
63                 if (typeof scriptArgs !== "undefined")
64                         arguments = scriptArgs;
65         }
66 } catch (e) {
68 //end of all the nice shell glue code.
70 // set up a global variable to be accessed in App.init
71 var testArguments = arguments;
73 function test_exit (exit_code) {
74         if (is_browser) {
75                 // Notify the puppeteer script
76                 Module.exit_code = exit_code;
77                 print ("WASM EXIT " + exit_code);
78         } else {
79                 Module.wasm_exit (exit_code);
80         }
83 function fail_exec (reason) {
84         print (reason);
85         test_exit (1);
88 function inspect_object (o) {
89     var r = "";
90     for(var p in o) {
91         var t = typeof o[p];
92         r += "'" + p + "' => '" + t + "', ";
93     }
94     return r;
97 // Preprocess arguments
98 var args = testArguments;
99 print("Arguments: " + testArguments);
100 profilers = [];
101 setenv = {};
102 runtime_args = [];
103 enable_gc = false;
104 while (true) {
105         if (args [0].startsWith ("--profile=")) {
106                 var arg = args [0].substring ("--profile=".length);
108                 profilers.push (arg);
110                 args = args.slice (1);
111         } else if (args [0].startsWith ("--setenv=")) {
112                 var arg = args [0].substring ("--setenv=".length);
113                 var parts = arg.split ('=');
114                 if (parts.length != 2)
115                         fail_exec ("Error: malformed argument: '" + args [0]);
116                 setenv [parts [0]] = parts [1];
117                 args = args.slice (1);
118         } else if (args [0].startsWith ("--runtime-arg=")) {
119                 var arg = args [0].substring ("--runtime-arg=".length);
120                 runtime_args.push (arg);
121                 args = args.slice (1);
122         } else if (args [0] == "--enable-gc") {
123                 enable_gc = true;
124                 args = args.slice (1);
125         } else {
126                 break;
127         }
129 testArguments = args;
131 if (typeof window == "undefined")
132   load ("mono-config.js");
134 var Module = { 
135         mainScriptUrlOrBlob: "mono.js",
137         print: function(x) { print ("WASM: " + x) },
138         printErr: function(x) { print ("WASM-ERR: " + x) },
140         onAbort: function(x) {
141                 print ("ABORT: " + x);
142                 var err = new Error();
143                 print ("Stacktrace: \n");
144                 print (err.stack);
145                 test_exit (1);
146         },
148         onRuntimeInitialized: function () {
149                 // Have to set env vars here to enable setting MONO_LOG_LEVEL etc.
150                 var wasm_setenv = Module.cwrap ('mono_wasm_setenv', 'void', ['string', 'string']);
151                 for (var variable in setenv) {
152                         MONO.mono_wasm_setenv (variable, setenv [variable]);
153                 }
155                 if (enable_gc) {
156                         var f = Module.cwrap ('mono_wasm_enable_on_demand_gc', 'void', []);
157                         f ();
158                 }
160                 MONO.mono_load_runtime_and_bcl (
161                         config.vfs_prefix,
162                         config.deploy_prefix,
163                         config.enable_debugging,
164                         config.file_list,
165                         function () {
166                                 App.init ();
167                         },
168                         function (asset ) 
169                         {
170                           if (typeof window != 'undefined') {
171                                 return fetch (asset, { credentials: 'same-origin' });
172                           } else {
173                                 // The default mono_load_runtime_and_bcl defaults to using
174                                 // fetch to load the assets.  It also provides a way to set a 
175                                 // fetch promise callback.
176                                 // Here we wrap the file read in a promise and fake a fetch response
177                                 // structure.
178                                 return new Promise((resolve, reject) => {
179                                          var response = { ok: true, url: asset, 
180                                                         arrayBuffer: function() {
181                                                                 return new Promise((resolve2, reject2) => {
182                                                                         resolve2(new Uint8Array (read (asset, 'binary')));
183                                                         }
184                                                 )}
185                                         }
186                                    resolve(response)
187                                  })
188                           }
189                         }
190                 );
191         },
194 if (typeof window == "undefined")
195   load ("mono.js");
197 const IGNORE_PARAM_COUNT = -1;
199 var App = {
200     init: function () {
202           var assembly_load = Module.cwrap ('mono_wasm_assembly_load', 'number', ['string'])
203           var find_class = Module.cwrap ('mono_wasm_assembly_find_class', 'number', ['number', 'string', 'string'])
204           var find_method = Module.cwrap ('mono_wasm_assembly_find_method', 'number', ['number', 'string', 'number'])
205           var runtime_invoke = Module.cwrap ('mono_wasm_invoke_method', 'number', ['number', 'number', 'number', 'number']);
206           var string_from_js = Module.cwrap ('mono_wasm_string_from_js', 'number', ['string']);
207           var assembly_get_entry_point = Module.cwrap ('mono_wasm_assembly_get_entry_point', 'number', ['number']);
208           var string_get_utf8 = Module.cwrap ('mono_wasm_string_get_utf8', 'string', ['number']);
209           var string_array_new = Module.cwrap ('mono_wasm_string_array_new', 'number', ['number']);
210           var obj_array_set = Module.cwrap ('mono_wasm_obj_array_set', 'void', ['number', 'number', 'number']);
211           var exit = Module.cwrap ('mono_wasm_exit', 'void', ['number']);
212           var wasm_setenv = Module.cwrap ('mono_wasm_setenv', 'void', ['string', 'string']);
213           var wasm_set_main_args = Module.cwrap ('mono_wasm_set_main_args', 'void', ['number', 'number']);
214           var wasm_strdup = Module.cwrap ('mono_wasm_strdup', 'number', ['string'])
216                 Module.wasm_exit = Module.cwrap ('mono_wasm_exit', 'void', ['number']);
218                 Module.print("Initializing.....");
220                 for (var i = 0; i < profilers.length; ++i) {
221                         var init = Module.cwrap ('mono_wasm_load_profiler_' + profilers [i], 'void', ['string'])
223                         init ("");
224                 }
226                 if (args[0] == "--regression") {
227                         var exec_regresion = Module.cwrap ('mono_wasm_exec_regression', 'number', ['number', 'string'])
229                         var res = 0;
230                                 try {
231                                         res = exec_regresion (10, args[1]);
232                                         Module.print ("REGRESSION RESULT: " + res);
233                                 } catch (e) {
234                                         Module.print ("ABORT: " + e);
235                                         print (e.stack);
236                                         res = 1;
237                                 }
239                         if (res)
240                                 fail_exec ("REGRESSION TEST FAILED");
242                         return;
243                 }
245                 if (runtime_args.length > 0)
246                         MONO.mono_wasm_set_runtime_options (runtime_args);
248                 if (args[0] == "--run") {
249                         // Run an exe
250                         if (args.length == 1)
251                                 fail_exec ("Error: Missing main executable argument.");
252                         main_assembly = assembly_load (args[1]);
253                         if (main_assembly == 0)
254                                 fail_exec ("Error: Unable to load main executable '" + args[1] + "'");
255                         main_method = assembly_get_entry_point (main_assembly);
256                         if (main_method == 0)
257                                 fail_exec ("Error: Main (string[]) method not found.");
259                         var app_args = string_array_new (args.length - 2);
260                         for (var i = 2; i < args.length; ++i) {
261                                 obj_array_set (app_args, i - 2, string_from_js (args [i]));
262                         }
264                         var main_argc = args.length - 2 + 1;
265                         var main_argv = Module._malloc (main_argc * 4);
266                         aindex = 0;
267                         Module.setValue (main_argv + (aindex * 4), wasm_strdup (args [1]), "i32")
268                         aindex += 1;
269                         for (var i = 2; i < args.length; ++i) {
270                                 Module.setValue (main_argv + (aindex * 4), wasm_strdup (args [i]), "i32");
271                                 aindex += 1;
272                         }
273                         wasm_set_main_args (main_argc, main_argv);
275                         try {
276                                 var invoke_args = Module._malloc (4);
277                                 Module.setValue (invoke_args, app_args, "i32");
278                                 var eh_throw = Module._malloc (4);
279                                 Module.setValue (eh_throw, 0, "i32");
280                                 var res = runtime_invoke (main_method, 0, invoke_args, eh_throw);
281                                 var eh_res = Module.getValue (eh_throw, "i32");
282                                 if (eh_res == 1) {
283                                         print ("Exception:" + string_get_utf8 (res));
284                                         test_exit (1);
285                                 }
286                         } catch (ex) {
287                                 print ("JS exception: " + ex);
288                                 print (ex.stack);
289                                 test_exit (1);
290                         }
292                         if (is_browser)
293                                 test_exit (0);
294                         return;
295                 }
297                 Module.print("Initializing Binding Test Suite support.....");
299                 //binding test suite support code
300                 binding_test_module = assembly_load ("binding_tests");
301                 if (!binding_test_module)
302                 {
303                         Module.printErr("Binding tests module 'binding_tests' not found.  Exiting Tests.")
304                         throw new Error("Binding tests module 'binding_tests' not found.  Exiting Tests.");
305                 }
306                 
307                 binding_test_class = find_class (binding_test_module, "", "TestClass");
308                 if (!binding_test_class)
309                 {
310                         Module.printErr("Binding tests class 'TestClass' not found.  Exiting Tests.")
311                         throw new Error("Binding tests class 'TestClass' not found.  Exiting Tests.");
312                 }               
314                 Module.print("Binding support complete.");
316                 
317                 Module.print("Checking for [main]Driver:Send ....");
318                 
319                 var send_message = undefined;
320                 
321                 try
322                 {
323                         send_message = BINDING.bind_static_method("[main]Driver:Send");
324                 }
325                 catch (e)
326                 {
327                         Module.printErr("[main]Driver:Send not found: " + e);
328                         throw e;
329                 
330                 }
332                 Module.print("Driver binding complete.");
334                 var main_argc = 1
335                 var main_argv = Module._malloc (main_argc * 4);
336                 Module.setValue (main_argv, wasm_strdup ("mono-wasm"), "i32")
337                 wasm_set_main_args (main_argc, main_argv);
339                 var bad_send_msg_detected = false;
340                 for (var i = 0; i < testArguments.length; ++i) {
341                         if (testArguments [i] == "--exclude") {
342                                 send_message ("--exclude", testArguments [i + 1]);
343                                 i ++;
344                                 continue;
345                         }
346                         var res = "";
347                         try
348                         {
349                                 res = send_message("start-test", testArguments [i])
350                         } catch (e) {
351                                 Module.printErr ("BAD SEND MSG: " + e);
352                                 bad_send_msg_detected = true;
353                         }
354                         print ("-----STARTED " + testArguments [i] + "---- " + res);
356                         if (res == "SUCCESS") {
357                                 while (send_message ("pump-test", testArguments [i]) != "DONE") 
358                                 {
359                                         Module.pump_message ();
360                                         print ("|");
361                                 }
362                                 print ("\nDONE")
363                         }
364                 }
366                 var status = send_message ("test-result", "");
367                 print ("Test status " + status)
368                 if (status != "PASS")
369                         fail_exec ("BAD TEST STATUS");
371                 if (bad_send_msg_detected)
372                         fail_exec ("BAD MSG SEND DETECTED");
373                 test_exit (0);
374     },
377 //binding test suite support code
378 var binding_test_module = undefined;
379 var binding_test_class = undefined;
381 // This function is called from the binding test suite
382 function call_test_method(method_name, signature, args)
384         var target_method = find_method (binding_test_class, method_name, IGNORE_PARAM_COUNT)
385         if (!target_method)
386                 throw "Could not find " + method_name;
388         return Module.mono_method_invoke (target_method, null, signature, args);