Skip to content

Commit 8a923ab

Browse files
committed
Display panic payloads and locations in wasm demo
1 parent 01bd3b6 commit 8a923ab

File tree

3 files changed

+102
-12
lines changed

3 files changed

+102
-12
lines changed

crates/aoc_wasm/src/lib.rs

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use aoc::all_puzzles;
77
use aoc::utils::input::InputType;
88
use std::error::Error;
99
use std::ffi::CStr;
10+
use std::sync::Once;
1011

1112
const BUFFER_LENGTH: usize = 1024 * 1024;
1213

@@ -17,6 +18,8 @@ static mut PART1: [u8; BUFFER_LENGTH] = [0u8; BUFFER_LENGTH];
1718
#[unsafe(no_mangle)]
1819
static mut PART2: [u8; BUFFER_LENGTH] = [0u8; BUFFER_LENGTH];
1920

21+
static ONCE_PANIC_HANDLER: Once = Once::new();
22+
2023
#[unsafe(no_mangle)]
2124
extern "C" fn run_puzzle(
2225
year: u16,
@@ -25,17 +28,44 @@ extern "C" fn run_puzzle(
2528
run_part1: bool,
2629
run_part2: bool,
2730
) -> bool {
31+
ONCE_PANIC_HANDLER.call_once(|| {
32+
std::panic::set_hook(Box::new(|info| {
33+
let payload = info.payload();
34+
let error: &str = if let Some(s) = payload.downcast_ref::<&str>() {
35+
s
36+
} else if let Some(s) = payload.downcast_ref::<String>() {
37+
s
38+
} else {
39+
return;
40+
};
41+
42+
let location = if let Some(location) = info.location() {
43+
// Trying to format the location may cause another panic, particularly on allocation
44+
// failure, but it should work on most explicit panics (e.g. "no solution found")
45+
&format!(
46+
"{}:{}:{}",
47+
location.file(),
48+
location.line(),
49+
location.column()
50+
)
51+
} else {
52+
""
53+
};
54+
55+
write_results(error, location);
56+
}));
57+
});
58+
59+
// Clear result buffers before running the solution, so if it panics, the buffers either contain
60+
// the panic payload and location or are empty.
61+
write_results("", "");
62+
2863
let (success, part1, part2) = match run(year, day, is_example, run_part1, run_part2) {
2964
Ok((part1, part2)) => (true, part1, part2),
3065
Err(err) => (false, err.to_string(), String::new()),
3166
};
3267

33-
// SAFETY: No other Rust code accesses these variables or creates references - they're only read
34-
// from JS.
35-
unsafe {
36-
write_string((&raw mut PART1).cast(), &part1);
37-
write_string((&raw mut PART2).cast(), &part2);
38-
}
68+
write_results(&part1, &part2);
3969

4070
success
4171
}
@@ -75,6 +105,15 @@ fn run(
75105
all_puzzles! {matcher}
76106
}
77107

108+
fn write_results(part1: &str, part2: &str) {
109+
// SAFETY: No other Rust code accesses these variables or creates references - they're only read
110+
// from JS.
111+
unsafe {
112+
write_string((&raw mut PART1).cast(), part1);
113+
write_string((&raw mut PART2).cast(), part2);
114+
}
115+
}
116+
78117
unsafe fn write_string(buf: *mut u8, str: &str) {
79118
let len = str.len().min(BUFFER_LENGTH - 1);
80119
unsafe {

crates/aoc_wasm/web/aoc.mjs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -173,18 +173,39 @@ export class Aoc {
173173
* @param {boolean} [isExample]
174174
* @param {boolean} [part1]
175175
* @param {boolean} [part2]
176-
* @return {{success: true, part1: string, part2: string} | {success: false, error: string}}
176+
* @return {{success: true, part1: string, part2: string} | {success: false, error: string, stack?: string, panic_location?: string}}
177177
*/
178178
run(year, day, input, isExample = false, part1 = true, part2 = true) {
179179
let success;
180180
try {
181181
this.#write(input);
182182
success = this.#exports.run_puzzle(year, day, isExample, part1, part2);
183183
} catch (e) {
184-
this.newInstance();
185-
return {
186-
success: false,
187-
error: "Unexpected error: " + e.toString() + (e.stack ? "\n\n" + e.stack : ""),
184+
console.error(e);
185+
let panic_payload = "";
186+
let panic_location = "";
187+
try {
188+
panic_payload = this.#read("PART1");
189+
panic_location = this.#read("PART2");
190+
} catch (e2) {
191+
console.warn(e2);
192+
}
193+
194+
this.newInstance()
195+
196+
if (panic_payload.length > 0) {
197+
return {
198+
success: false,
199+
error: "panic: " + panic_payload,
200+
panic_location,
201+
stack: e.stack,
202+
}
203+
} else {
204+
return {
205+
success: false,
206+
error: e.toString(),
207+
stack: e.stack,
208+
}
188209
}
189210
}
190211

crates/aoc_wasm/web/web.mjs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {Aoc} from "./aoc.mjs";
22

3-
console.log("Commit", "${GIT_COMMIT}");
3+
const COMMIT = "${GIT_COMMIT}";
4+
console.log("Commit", COMMIT);
45

56
const MODULE_PATHS = [
67
"./aoc-simd128.wasm",
@@ -99,6 +100,35 @@ async function run(input, isExample, part) {
99100
const body = document.createElement("pre");
100101
body.classList.add("message-body", "is-family-monospace", "has-text-warning");
101102
body.innerText = result.error;
103+
if (result.panic_location !== undefined && result.panic_location.length > 0) {
104+
body.appendChild(document.createTextNode("\nat "));
105+
106+
const match = result.panic_location.match(/^crates\/([^\/]+)\/src\/(.+)[.]rs:(\d+):(\d+)$/);
107+
if (match !== null) {
108+
const [_, crate, path, line, _column] = match;
109+
110+
const rustdoc_link = document.createElement("a");
111+
rustdoc_link.href = `doc/src/${crate}/${path}.rs.html#${line}`;
112+
rustdoc_link.innerText = result.panic_location;
113+
body.appendChild(rustdoc_link);
114+
115+
if (COMMIT.match(/^[0-9a-f]{40}$/)) {
116+
const github_link = document.createElement("a");
117+
github_link.href = `https://github.com/ictrobot/aoc-rs/blob/${COMMIT}/crates/${crate}/src/${path}.rs#L${line}`;
118+
github_link.innerText = "GitHub";
119+
120+
body.appendChild(document.createTextNode(" ("));
121+
body.appendChild(github_link)
122+
body.appendChild(document.createTextNode(")"));
123+
}
124+
} else {
125+
body.appendChild(document.createTextNode(result.panic_location));
126+
}
127+
}
128+
if (result.stack !== undefined && result.stack.length > 0) {
129+
body.appendChild(document.createTextNode("\n\n" + result.stack));
130+
}
131+
102132
article.appendChild(body);
103133
}
104134

0 commit comments

Comments
 (0)