tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

call-indirect-subtyping.js (3847B)


      1 // Test that call_indirect will respect subtyping by defining a bunch of types
      2 // and checking every combination of (expected, actual) type.
      3 //
      4 // NOTE: Several of these types are identical to each other to test
      5 // canonicalization as well, and this causes some bloat in the 'subtypeOf'
      6 // lists.
      7 const TESTS = [
      8 {
      9 	// (type 0) (equivalent to 1)
     10 	type: `(type (sub (func)))`,
     11 	subtypeOf: [0, 1],
     12 },
     13 {
     14 	// (type 1) (equivalent to 0)
     15 	type: `(rec (type (sub (func))))`,
     16 	subtypeOf: [0, 1],
     17 },
     18 {
     19 	// (type 2)
     20 	type: `(rec (type (sub (func))) (type (sub (func))))`,
     21 	subtypeOf: [2],
     22 },
     23 {
     24 	// (type 3)
     25 	// Hack entry of previous to capture that it actually defines
     26 	// two types in the recursion group.
     27 	type: undefined,
     28 	subtypeOf: [3],
     29 },
     30 {
     31 	// (type 4) (equivalent to 7)
     32 	type: `(type (sub 0 (func)))`,
     33 	subtypeOf: [0, 1, 4, 7],
     34 },
     35 {
     36 	// (type 5) (equivalent to 8)
     37 	type: `(type (sub 4 (func)))`,
     38 	subtypeOf: [0, 1, 4, 5, 7, 8],
     39 },
     40 {
     41 	// (type 6)
     42 	type: `(type (sub 5 (func)))`,
     43 	subtypeOf: [0, 1, 4, 5, 6, 7, 8],
     44 },
     45 {
     46 	// (type 7) (equivalent to 4)
     47 	type: `(type (sub 0 (func)))`,
     48 	subtypeOf: [0, 1, 4, 7],
     49 },
     50 {
     51 	// (type 8) (equivalent to 5)
     52 	type: `(type (sub 7 (func)))`,
     53 	subtypeOf: [0, 1, 4, 5, 7, 8],
     54 },
     55 {
     56 	// (type 9) - a final type that has an immediate form
     57 	type: `(type (func))`,
     58 	subtypeOf: [9],
     59 }
     60 ];
     61 
     62 // Build a module with all the types, functions with those types, and functions
     63 // that call_indirect with those types, and a table with all the functions in
     64 // it.
     65 let typeSection = '';
     66 let importedFuncs = '';
     67 let definedFuncs = '';
     68 let callIndirectFuncs = '';
     69 let returnCallIndirectFuncs = '';
     70 let i = 0;
     71 for (let {type} of TESTS) {
     72 if (type) {
     73 	typeSection += type + '\n';
     74 }
     75 importedFuncs += `(func \$import${i} (import "" "import${i}") (type ${i}))\n`;
     76 definedFuncs += `(func \$define${i} (export "define${i}") (type ${i}))\n`;
     77 callIndirectFuncs += `(func (export "call_indirect ${i}") (param i32)
     78 	(drop (ref.cast (ref ${i}) (table.get local.get 0)))
     79 	(call_indirect (type ${i}) local.get 0)
     80 )\n`;
     81 returnCallIndirectFuncs += `(func (export "return_call_indirect ${i}") (param i32)
     82 	(drop (ref.cast (ref ${i}) (table.get local.get 0)))
     83 	(return_call_indirect (type ${i}) local.get 0)
     84 )\n`;
     85 i++;
     86 }
     87 let moduleText = `(module
     88 ${typeSection}
     89 ${importedFuncs}
     90 ${definedFuncs}
     91 ${callIndirectFuncs}
     92 ${returnCallIndirectFuncs}
     93 (table
     94 	(export "table")
     95 	funcref
     96 	(elem ${TESTS.map((x, i) => `\$import${i} \$define${i}`).join(" ")})
     97 )
     98 )`;
     99 
    100 // Now go over every combination of (actual, expected). In this case the caller
    101 // (which does the call_indirect) specifies expected and the callee will be the
    102 // actual.
    103 let imports = {
    104 "": Object.fromEntries(TESTS.map((x, i) => [`import${i}`, () => {}])),
    105 };
    106 let exports = wasmEvalText(moduleText, imports).exports;
    107 for (let callerTypeIndex = 0; callerTypeIndex < TESTS.length; callerTypeIndex++) {
    108 for (let calleeTypeIndex = 0; calleeTypeIndex < TESTS.length; calleeTypeIndex++) {
    109 	let calleeType = TESTS[calleeTypeIndex];
    110 
    111 	// If the callee (actual) is a subtype of caller (expected), then this
    112 	// should succeed.
    113 	let shouldPass = calleeType.subtypeOf.includes(callerTypeIndex);
    114 
    115 	let calleeImportFuncIndex = calleeTypeIndex * 2;
    116 	let calleeDefinedFuncIndex = calleeTypeIndex * 2 + 1;
    117 
    118 	// print(`expected (type ${callerTypeIndex}) (actual ${calleeTypeIndex})`);
    119 	let test = () => {
    120 		exports[`call_indirect ${callerTypeIndex}`](calleeImportFuncIndex)
    121 		exports[`call_indirect ${callerTypeIndex}`](calleeDefinedFuncIndex)
    122 		exports[`return_call_indirect ${callerTypeIndex}`](calleeImportFuncIndex)
    123 		exports[`return_call_indirect ${callerTypeIndex}`](calleeDefinedFuncIndex)	
    124 	};
    125 	if (shouldPass) {
    126 		test();
    127 	} else {
    128 		assertErrorMessage(test, WebAssembly.RuntimeError, /mismatch|cast/);
    129 	}
    130 }
    131 }