nnl-test.js (4508B)
1 // Generates combinations of different block types and operations for 2 // non-defaultable locals (local.set / .tee / .get). 3 // See the function references specification on the updated algorithm 4 // for validating non-null references in locals. 5 6 const KINDS = [ 7 "block", 8 "loop", 9 "try", 10 "catch", 11 "delegate", 12 "if", 13 "else", 14 ] 15 const INITIALIZED = [ 16 "nowhere", 17 "outer", 18 "inner", 19 "outer-tee", 20 "inner-tee", 21 ]; 22 const USED = [ 23 "outer", 24 "inner", 25 "after-inner", 26 "after-outer", 27 ]; 28 29 function generateBlock(kind, contents) { 30 switch (kind) { 31 case "block": { 32 return `block\n${contents}end\n` 33 } 34 case "loop": { 35 return `loop\n${contents}end\n` 36 } 37 case "try": { 38 return `try\n${contents}end\n` 39 } 40 case "catch": { 41 return `try\ncatch_all\n${contents}end\n` 42 } 43 case "delegate": { 44 return `try\n${contents}\ndelegate 0\n` 45 } 46 case "if": { 47 return `i32.const 0\nif\n${contents}end\n` 48 } 49 case "else": { 50 return `i32.const 0\nif\nelse\n${contents}end\n` 51 } 52 } 53 } 54 55 // Generate a variation of the module below: 56 // 57 // (func 58 // (block 59 // $outer 60 // (block 61 // $inner 62 // ) 63 // $after-inner 64 // ) 65 // $after-outer 66 // ) 67 // 68 // Where a local is used and initialized at different points depending on the 69 // parameters. The block kinds of the inner and outer block may also be 70 // customized. 71 function generateModule(outerBlockKind, innerBlockKind, initializedWhere, usedWhere) { 72 const INITIALIZE_STMT = '(local.set 0 ref.func 0)\n'; 73 const INITIALIZE_STMT2 = '(drop (local.tee 0 ref.func 0))\n'; 74 const USE_STMT = '(drop local.get 0)\n'; 75 76 // inner block 77 let innerBlockContents = ''; 78 if (initializedWhere === 'inner') { 79 innerBlockContents += INITIALIZE_STMT; 80 } else if (initializedWhere === 'inner-tee') { 81 innerBlockContents += INITIALIZE_STMT2; 82 } 83 if (usedWhere === 'inner') { 84 innerBlockContents += USE_STMT; 85 } 86 let innerBlock = generateBlock(innerBlockKind, innerBlockContents); 87 88 // outer block 89 let outerBlockContents = ''; 90 if (initializedWhere === 'outer') { 91 outerBlockContents += INITIALIZE_STMT; 92 } else if (initializedWhere === 'outer-tee') { 93 outerBlockContents += INITIALIZE_STMT2; 94 } 95 if (usedWhere === 'outer') { 96 outerBlockContents += USE_STMT; 97 } 98 outerBlockContents += innerBlock; 99 if (usedWhere === 'after-inner') { 100 outerBlockContents += USE_STMT; 101 } 102 let outerBlock = generateBlock(outerBlockKind, outerBlockContents); 103 104 // after outer block 105 let afterOuterBlock = ''; 106 if (usedWhere === 'after-outer') { 107 afterOuterBlock += USE_STMT; 108 } 109 110 return `(module 111 (type $t (func)) 112 (func (export "test") 113 (local (ref $t)) 114 ${outerBlock}${afterOuterBlock} ) 115 )`; 116 } 117 118 const LOGGING = false; 119 120 for (let outer of KINDS) { 121 for (let inner of KINDS) { 122 for (let initialized of INITIALIZED) { 123 for (let used of USED) { 124 let text = generateModule(outer, inner, initialized, used); 125 126 let expectPass; 127 switch (initialized) { 128 case "outer": 129 case "outer-tee": { 130 // Defining the local in the outer block makes it valid 131 // in the outer block, the inner block, and after the 132 // inner block 133 expectPass = used !== "after-outer"; 134 break; 135 } 136 case "inner": 137 case "inner-tee": { 138 // Defining the local in the inner block makes it valid 139 // in the inner block 140 // 141 // NOTE: an extension to typing could make this valid 142 // after the inner block in some cases 143 expectPass = used === "inner"; 144 break; 145 } 146 case "nowhere": { 147 // Not defining the local makes it always invalid to 148 // use 149 expectPass = false; 150 break; 151 } 152 } 153 154 if (LOGGING) { 155 console.log(); 156 console.log(`TEST: outer=${outer}, inner=${inner}, initialized=${initialized}, used=${used}`); 157 console.log(expectPass ? "EXPECT PASS" : "EXPECT FAIL"); 158 console.log(text); 159 } 160 161 let binary = wasmTextToBinary(text); 162 assertEq(WebAssembly.validate(binary), expectPass); 163 if (!expectPass) { 164 // Check if the error message is right. 165 try { 166 new WebAssembly.Module(binary); 167 } catch (ex) { 168 assertEq(true, /local\.get read from unset local/.test(ex.message)); 169 } 170 } 171 } 172 } 173 } 174 }