errors.js (10141B)
1 load(libdir + "wasm-binary.js"); 2 3 const Module = WebAssembly.Module; 4 const Instance = WebAssembly.Instance; 5 const CompileError = WebAssembly.CompileError; 6 const RuntimeError = WebAssembly.RuntimeError; 7 8 function getWasmFunctionIndex(line) { 9 return Number(line.match(/^wasm-function\[(\d*)\]$/)[1]); 10 } 11 12 function getWasmBytecode(column) { 13 return parseInt(column.match(/^0x([0-9a-f]*)$/)[1], 16); 14 } 15 16 function parseStack(stack) { 17 var frames = stack.split('\n'); 18 assertEq(frames[frames.length-1], ""); 19 frames.length--; 20 return frames.map(frame => { 21 var res = frame.match(/^(.*)@(.*):(.*):(.*)$/); 22 assertEq(res !== null, true); 23 return {name: res[1], url: res[2], line: res[3], column: res[4]}; 24 }); 25 } 26 27 function testExn(opcode, binary, type, msg, exn) { 28 assertEq(exn instanceof type, true); 29 assertEq(msg.test(exn.message), true); 30 31 var stack = parseStack(exn.stack); 32 assertEq(stack.length > 1, true); 33 var innermost = stack[0]; 34 var funcIndex = getWasmFunctionIndex(innermost.line); 35 var bytecode = getWasmBytecode(innermost.column); 36 assertEq(exn.lineNumber, bytecode); 37 assertEq(exn.columnNumber, 1); 38 assertEq(binary[bytecode], opcode); 39 40 return {stack, binary}; 41 } 42 43 function test(opcode, text, type, msg) { 44 var binary = new Uint8Array(wasmTextToBinary(text)); 45 var exn; 46 try { 47 new Instance(new Module(binary)); 48 } catch (e) { 49 exn = e; 50 } 51 52 return testExn(opcode, binary, type, msg, exn); 53 } 54 55 function testAccess(opcode, text, width, type, msg) { 56 var binary = new Uint8Array(wasmTextToBinary(text)); 57 var instance = new Instance(new Module(binary)); 58 for (var base of [64 * 1024, 2 * 64 * 1024, Math.pow(2, 30), Math.pow(2, 31), Math.pow(2, 32) - 1]) { 59 for (var sub = 0; sub < width; sub++) { 60 var ptr = base - sub; 61 let exn = null; 62 try { 63 instance.exports[''](ptr); 64 } catch (e) { 65 exn = e; 66 } 67 testExn(opcode, binary, type, msg, exn); 68 } 69 } 70 } 71 72 function testLoad(opcode, optext, width, type, msg) { 73 var text = `(module (memory 1) (func (export "") (param i32) (drop (${optext} (local.get 0)))))`; 74 testAccess(opcode, text, width, type, msg); 75 } 76 77 function testStore(opcode, optext, consttext, width, type, msg) { 78 var text = `(module (memory 1) (func (export "") (param i32) (${optext} (local.get 0) (${consttext}.const 0))))`; 79 testAccess(opcode, text, width, type, msg); 80 } 81 82 test(UnreachableCode, '(module (func unreachable) (start 0))', RuntimeError, /unreachable executed/); 83 test(I32DivSCode, '(module (func (drop (i32.div_s (i32.const 1) (i32.const 0)))) (start 0))', RuntimeError, /integer divide by zero/); 84 test(I32DivSCode, '(module (func (drop (i32.div_s (i32.const -2147483648) (i32.const -1)))) (start 0))', RuntimeError, /integer overflow/); 85 test(I32DivUCode, '(module (func (drop (i32.div_u (i32.const 1) (i32.const 0)))) (start 0))', RuntimeError, /integer divide by zero/); 86 test(I32RemSCode, '(module (func (drop (i32.rem_s (i32.const 1) (i32.const 0)))) (start 0))', RuntimeError, /integer divide by zero/); 87 test(I32RemUCode, '(module (func (drop (i32.rem_u (i32.const 1) (i32.const 0)))) (start 0))', RuntimeError, /integer divide by zero/); 88 test(I64DivSCode, '(module (func (drop (i64.div_s (i64.const 1) (i64.const 0)))) (start 0))', RuntimeError, /integer divide by zero/); 89 test(I64DivSCode, '(module (func (drop (i64.div_s (i64.const -9223372036854775808) (i64.const -1)))) (start 0))', RuntimeError, /integer overflow/); 90 test(I64DivUCode, '(module (func (drop (i64.div_u (i64.const 1) (i64.const 0)))) (start 0))', RuntimeError, /integer divide by zero/); 91 test(I64RemSCode, '(module (func (drop (i64.rem_s (i64.const 1) (i64.const 0)))) (start 0))', RuntimeError, /integer divide by zero/); 92 test(I64RemUCode, '(module (func (drop (i64.rem_u (i64.const 1) (i64.const 0)))) (start 0))', RuntimeError, /integer divide by zero/); 93 test(I32TruncSF32Code, '(module (func (drop (i32.trunc_f32_s (f32.const 1e30)))) (start 0))', RuntimeError, /integer overflow/); 94 test(I32TruncSF64Code, '(module (func (drop (i32.trunc_f64_s (f64.const 1e30)))) (start 0))', RuntimeError, /integer overflow/); 95 test(I32TruncUF32Code, '(module (func (drop (i32.trunc_f32_u (f32.const 1e30)))) (start 0))', RuntimeError, /integer overflow/); 96 test(I32TruncUF64Code, '(module (func (drop (i32.trunc_f64_u (f64.const 1e30)))) (start 0))', RuntimeError, /integer overflow/); 97 test(I64TruncSF32Code, '(module (func (drop (i64.trunc_f32_s (f32.const 1e30)))) (start 0))', RuntimeError, /integer overflow/); 98 test(I64TruncSF64Code, '(module (func (drop (i64.trunc_f64_s (f64.const 1e30)))) (start 0))', RuntimeError, /integer overflow/); 99 test(I64TruncUF32Code, '(module (func (drop (i64.trunc_f32_u (f32.const 1e30)))) (start 0))', RuntimeError, /integer overflow/); 100 test(I64TruncUF64Code, '(module (func (drop (i64.trunc_f64_u (f64.const 1e30)))) (start 0))', RuntimeError, /integer overflow/); 101 test(I32TruncSF32Code, '(module (func (drop (i32.trunc_f32_s (f32.const nan)))) (start 0))', RuntimeError, /invalid conversion to integer/); 102 test(I32TruncSF64Code, '(module (func (drop (i32.trunc_f64_s (f64.const nan)))) (start 0))', RuntimeError, /invalid conversion to integer/); 103 test(I32TruncUF32Code, '(module (func (drop (i32.trunc_f32_u (f32.const nan)))) (start 0))', RuntimeError, /invalid conversion to integer/); 104 test(I32TruncUF64Code, '(module (func (drop (i32.trunc_f64_u (f64.const nan)))) (start 0))', RuntimeError, /invalid conversion to integer/); 105 test(I64TruncSF32Code, '(module (func (drop (i64.trunc_f32_s (f32.const nan)))) (start 0))', RuntimeError, /invalid conversion to integer/); 106 test(I64TruncSF64Code, '(module (func (drop (i64.trunc_f64_s (f64.const nan)))) (start 0))', RuntimeError, /invalid conversion to integer/); 107 test(I64TruncUF32Code, '(module (func (drop (i64.trunc_f32_u (f32.const nan)))) (start 0))', RuntimeError, /invalid conversion to integer/); 108 test(I64TruncUF64Code, '(module (func (drop (i64.trunc_f64_u (f64.const nan)))) (start 0))', RuntimeError, /invalid conversion to integer/); 109 test(CallIndirectCode, '(module (table 1 funcref) (func (call_indirect (type 0) (i32.const 0))) (start 0))', RuntimeError, /indirect call to null/); 110 test(CallIndirectCode, '(module (table 1 funcref) (func (call_indirect (type 0) (i32.const 1))) (start 0))', RuntimeError, /index out of bounds/); 111 test(CallIndirectCode, '(module (table funcref (elem $blah)) (func (call_indirect (type 0) (i32.const 0))) (func $blah (param i32)) (start 0))', RuntimeError, /indirect call signature mismatch/); 112 testLoad(I32Load8S, 'i32.load8_s', 1, RuntimeError, /index out of bounds/); 113 testLoad(I32Load8U, 'i32.load8_u', 1, RuntimeError, /index out of bounds/); 114 testLoad(I32Load16S, 'i32.load16_s', 2, RuntimeError, /index out of bounds/); 115 testLoad(I32Load16U, 'i32.load16_u', 2, RuntimeError, /index out of bounds/); 116 testLoad(I64Load8S, 'i64.load8_s', 1, RuntimeError, /index out of bounds/); 117 testLoad(I64Load8U, 'i64.load8_u', 1, RuntimeError, /index out of bounds/); 118 testLoad(I64Load16S, 'i64.load16_s', 2, RuntimeError, /index out of bounds/); 119 testLoad(I64Load16U, 'i64.load16_u', 2, RuntimeError, /index out of bounds/); 120 testLoad(I64Load32S, 'i64.load32_s', 4, RuntimeError, /index out of bounds/); 121 testLoad(I64Load32U, 'i64.load32_u', 4, RuntimeError, /index out of bounds/); 122 testLoad(I32Load, 'i32.load', 4, RuntimeError, /index out of bounds/); 123 testLoad(I64Load, 'i64.load', 8, RuntimeError, /index out of bounds/); 124 testLoad(F32Load, 'f32.load', 4, RuntimeError, /index out of bounds/); 125 testLoad(F64Load, 'f64.load', 8, RuntimeError, /index out of bounds/); 126 testStore(I32Store8, 'i32.store8', 'i32', 1, RuntimeError, /index out of bounds/); 127 testStore(I32Store16, 'i32.store16', 'i32', 2, RuntimeError, /index out of bounds/); 128 testStore(I64Store8, 'i64.store8', 'i64', 1, RuntimeError, /index out of bounds/); 129 testStore(I64Store16, 'i64.store16', 'i64', 2, RuntimeError, /index out of bounds/); 130 testStore(I64Store32, 'i64.store32', 'i64', 4, RuntimeError, /index out of bounds/); 131 testStore(I32Store, 'i32.store', 'i32', 4, RuntimeError, /index out of bounds/); 132 testStore(I64Store, 'i64.store', 'i64', 8, RuntimeError, /index out of bounds/); 133 testStore(F32Store, 'f32.store', 'f32', 4, RuntimeError, /index out of bounds/); 134 testStore(F64Store, 'f64.store', 'f64', 8, RuntimeError, /index out of bounds/); 135 136 // Stack overflow isn't really a trap or part of the formally-specified 137 // semantics of call so use the same InternalError as JS and use the bytecode 138 // offset of the function body (which happens to start with the number of 139 // local entries). 140 test(4 /* = num locals */, '(module (func (local i32 i64 f32 f64) (call 0)) (start 0))', InternalError, /too much recursion/); 141 142 // Test whole callstack. 143 var {stack, binary} = test(UnreachableCode, `(module 144 (type $v2v (func)) 145 (func $a unreachable) 146 (func $b call $a) 147 (func $c call $b) 148 (table funcref (elem $c)) 149 (func $d (call_indirect (type $v2v) (i32.const 0))) 150 (func $e call $d) 151 (start $e) 152 )`, RuntimeError, /unreachable executed/); 153 const N = 5; 154 assertEq(stack.length > N, true); 155 assertEq(getWasmFunctionIndex(stack[0].line), 0); 156 var lastLine = stack[0].line; 157 for (var i = 1; i < N; i++) { 158 assertEq(getWasmFunctionIndex(stack[i].line), i); 159 assertEq(stack[i].line > lastLine, true); 160 lastLine = stack[i].line; 161 assertEq(binary[getWasmBytecode(stack[i].column)], i == 3 ? CallIndirectCode : CallCode); 162 } 163 164 function testCompileError(opcode, text) { 165 var binary = new Uint8Array(wasmTextToBinary(text)); 166 var exn; 167 try { 168 new Instance(new Module(binary)); 169 } catch (e) { 170 exn = e; 171 } 172 173 assertEq(exn instanceof CompileError, true); 174 var offset = Number(exn.message.match(/at offset (\d*)/)[1]); 175 assertEq(binary[offset], opcode); 176 } 177 178 testCompileError(CallCode, '(module (func $f (param i32)) (func $g call $f))'); 179 testCompileError(I32AddCode, '(module (func (i32.add (i32.const 1) (f32.const 1))))'); 180 testCompileError(EndCode, '(module (func (block (result i32))))');