| // Copyright 2016 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // Flags: --expose-wasm |
| |
| load("test/mjsunit/wasm/wasm-module-builder.js"); |
| |
| // The stack trace contains file path, only keep "stack.js". |
| function stripPath(s) { |
| return s.replace(/[^ (]*stack\.js/g, "stack.js"); |
| } |
| |
| function verifyStack(frames, expected) { |
| assertEquals(expected.length, frames.length, "number of frames mismatch"); |
| expected.forEach(function(exp, i) { |
| assertEquals(exp[1], frames[i].getFunctionName(), |
| "["+i+"].getFunctionName()"); |
| assertEquals(exp[2], frames[i].getLineNumber(), "["+i+"].getLineNumber()"); |
| if (exp[0]) |
| assertEquals(exp[3], frames[i].getPosition(), |
| "["+i+"].getPosition()"); |
| assertContains(exp[4], frames[i].getFileName(), "["+i+"].getFileName()"); |
| var toString; |
| if (exp[0]) { |
| toString = "wasm-function[" + exp[2] + "]:" + exp[5]; |
| if (exp[1] !== null) toString = exp[1] + " (" + toString + ")"; |
| } else { |
| toString = exp[4] + ":" + exp[2] + ":"; |
| } |
| assertContains(toString, frames[i].toString(), "["+i+"].toString()"); |
| }); |
| } |
| |
| |
| var stack; |
| function STACK() { |
| var e = new Error(); |
| stack = e.stack; |
| } |
| |
| var builder = new WasmModuleBuilder(); |
| |
| builder.addMemory(0, 1, false); |
| |
| builder.addImport("mod", "func", kSig_v_v); |
| |
| builder.addFunction("main", kSig_v_v) |
| .addBody([kExprCallFunction, 0]) |
| .exportAs("main"); |
| |
| builder.addFunction("exec_unreachable", kSig_v_v) |
| .addBody([kExprUnreachable]) |
| .exportAs("exec_unreachable"); |
| |
| // Make this function unnamed, just to test also this case. |
| var mem_oob_func = builder.addFunction(undefined, kSig_i_v) |
| // Access the memory at offset -1, to provoke a trap. |
| .addBody([kExprI32Const, 0x7f, kExprI32LoadMem8S, 0, 0]) |
| .exportAs("mem_out_of_bounds"); |
| |
| // Call the mem_out_of_bounds function, in order to have two wasm stack frames. |
| builder.addFunction("call_mem_out_of_bounds", kSig_i_v) |
| .addBody([kExprCallFunction, mem_oob_func.index]) |
| .exportAs("call_mem_out_of_bounds"); |
| |
| var module = builder.instantiate({mod: {func: STACK}}); |
| |
| (function testSimpleStack() { |
| var expected_string = 'Error\n' + |
| // The line numbers below will change as this test gains / loses lines.. |
| ' at STACK (stack.js:38:11)\n' + // -- |
| ' at main (wasm-function[1]:0x86)\n' + // -- |
| ' at testSimpleStack (stack.js:77:18)\n' + // -- |
| ' at stack.js:79:3'; // -- |
| |
| module.exports.main(); |
| assertEquals(expected_string, stripPath(stack)); |
| })(); |
| |
| // For the remaining tests, collect the Callsite objects instead of just a |
| // string: |
| Error.prepareStackTrace = function(error, frames) { |
| return frames; |
| }; |
| |
| (function testStackFrames() { |
| module.exports.main(); |
| |
| verifyStack(stack, [ |
| // isWasm function line pos file offset |
| [ false, "STACK", 38, 0, "stack.js"], |
| [ true, "main", 1, 1, null, '0x86'], |
| [ false, "testStackFrames", 88, 0, "stack.js"], |
| [ false, null, 97, 0, "stack.js"] |
| ]); |
| })(); |
| |
| (function testWasmUnreachable() { |
| try { |
| module.exports.exec_unreachable(); |
| fail("expected wasm exception"); |
| } catch (e) { |
| assertContains("unreachable", e.message); |
| verifyStack(e.stack, [ |
| // isWasm function line pos file offset |
| [ true, "exec_unreachable", 2, 1, null, '0x8b'], |
| [ false, "testWasmUnreachable", 101, 0, "stack.js"], |
| [ false, null, 112, 0, "stack.js"] |
| ]); |
| } |
| })(); |
| |
| (function testWasmMemOutOfBounds() { |
| try { |
| module.exports.call_mem_out_of_bounds(); |
| fail("expected wasm exception"); |
| } catch (e) { |
| assertContains("out of bounds", e.message); |
| verifyStack(e.stack, [ |
| // isWasm function line pos file offset |
| [ true, null, 3, 3, null, '0x91'], |
| [ true, "call_mem_out_of_bounds", 4, 1, null, '0x97'], |
| [ false, "testWasmMemOutOfBounds", 116, 0, "stack.js"], |
| [ false, null, 128, 0, "stack.js"] |
| ]); |
| } |
| })(); |
| |
| (function testStackOverflow() { |
| print("testStackOverflow"); |
| var builder = new WasmModuleBuilder(); |
| |
| var sig_index = builder.addType(kSig_v_v); |
| builder.addFunction("recursion", sig_index) |
| .addBody([ |
| kExprI32Const, 0, |
| kExprCallIndirect, sig_index, kTableZero |
| ]) |
| .exportFunc(); |
| builder.appendToTable([0]); |
| |
| try { |
| builder.instantiate().exports.recursion(); |
| fail("expected wasm exception"); |
| } catch (e) { |
| assertEquals("Maximum call stack size exceeded", e.message, "trap reason"); |
| assertTrue(e.stack.length >= 4, "expected at least 4 stack entries"); |
| verifyStack(e.stack.splice(0, 4), [ |
| // isWasm function line pos file offset |
| [ true, "recursion", 0, 0, null, '0x34'], |
| [ true, "recursion", 0, 3, null, '0x37'], |
| [ true, "recursion", 0, 3, null, '0x37'], |
| [ true, "recursion", 0, 3, null, '0x37'] |
| ]); |
| } |
| })(); |
| |
| (function testBigOffset() { |
| print('testBigOffset'); |
| var builder = new WasmModuleBuilder(); |
| |
| let body = [kExprI32Const, 0, kExprI32Add]; |
| while (body.length <= 65536) body = body.concat(body); |
| body.unshift(kExprI32Const, 0); |
| body.push(kExprUnreachable); |
| let unreachable_pos = body.length - 1; |
| |
| builder.addFunction('main', kSig_v_v).addBody(body).exportFunc(); |
| |
| try { |
| builder.instantiate().exports.main(); |
| fail('expected wasm exception'); |
| } catch (e) { |
| assertEquals('unreachable', e.message, 'trap reason'); |
| let hexOffset = '0x' + (unreachable_pos + 0x25).toString(16); |
| verifyStack(e.stack, [ |
| // isWasm, function, line, pos, file, offset |
| [true, 'main', 0, unreachable_pos + 1, null, hexOffset], // - |
| [false, 'testBigOffset', 172, 0, 'stack.js'], //- |
| [false, null, 184, 0, 'stack.js'] |
| ]); |
| } |
| })(); |