plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
0f2502c
— Peter John
2026-10-25T16:08:43+05:30
feat(hosts): link plum::crash host import (prints to stderr, then traps)
- plum-cli/src/main.rs +20 -0
- plum-cli/tests/crash_test.rs +29 -0
- plum-runtime/src/main.rs +14 -0
plum-cli/src/main.rs
CHANGED
|
@@ -233,6 +233,26 @@ fn hostImports(
|
|
|
233
233
|
},
|
|
234
234
|
)))
|
|
235
235
|
}
|
|
236
|
+
("plum", "crash") => {
|
|
237
|
+
let func_ty = imp.ty().func().cloned().ok_or_else(|| {
|
|
238
|
+
anyhow::anyhow!("plum::crash import must be a function")
|
|
239
|
+
})?;
|
|
240
|
+
Ok(wasmtime::Extern::Func(wasmtime::Func::new(
|
|
241
|
+
&mut *store,
|
|
242
|
+
func_ty,
|
|
243
|
+
|mut caller, params, _results| {
|
|
244
|
+
let wasmtime::Val::AnyRef(Some(s)) = ¶ms[0] else {
|
|
245
|
+
return Err(wasmtime::Error::msg("crash expects a Str argument"));
|
|
246
|
+
};
|
|
247
|
+
let arr = unwrapStrToArray(&mut caller, s)?;
|
|
248
|
+
let len = arr.len(&caller)?;
|
|
249
|
+
let mut buf = vec![0u8; len as usize];
|
|
250
|
+
arr.copy_to_i8_slice(&mut caller, &mut buf)?;
|
|
251
|
+
eprintln!("crash: {}", String::from_utf8_lossy(&buf));
|
|
252
|
+
Ok(())
|
|
253
|
+
},
|
|
254
|
+
)))
|
|
255
|
+
}
|
|
236
256
|
("plum", "rawRandomInt") => {
|
|
237
257
|
let func_ty = imp.ty().func().cloned().ok_or_else(|| {
|
|
238
258
|
anyhow::anyhow!("plum::rawRandomInt import must be a function")
|
plum-cli/tests/crash_test.rs
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#![allow(non_snake_case)]
|
|
2
|
+
use std::process::Command;
|
|
3
|
+
|
|
4
|
+
fn plumBin() -> std::path::PathBuf {
|
|
5
|
+
let mut path = std::env::current_exe().unwrap();
|
|
6
|
+
path.pop();
|
|
7
|
+
if path.ends_with("deps") {
|
|
8
|
+
path.pop();
|
|
9
|
+
}
|
|
10
|
+
path.push("plum");
|
|
11
|
+
path
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
#[test]
|
|
15
|
+
fn crash_prints_message() {
|
|
16
|
+
let dir = std::env::temp_dir().join(format!("plum_crash_{}", std::process::id()));
|
|
17
|
+
std::fs::create_dir_all(&dir).unwrap();
|
|
18
|
+
let src = dir.join("crash.plum");
|
|
19
|
+
std::fs::write(&src, "import std/Str\n\nfun main() =\n crash \"boom\"\n").unwrap();
|
|
20
|
+
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap();
|
|
21
|
+
let out = Command::new(plumBin())
|
|
22
|
+
.current_dir(root)
|
|
23
|
+
.args(["run", src.to_str().unwrap()])
|
|
24
|
+
.output()
|
|
25
|
+
.expect("failed to run plum");
|
|
26
|
+
let stderr = String::from_utf8_lossy(&out.stderr);
|
|
27
|
+
assert!(!out.status.success(), "expected non-zero exit; stderr: {}", stderr);
|
|
28
|
+
assert!(stderr.contains("crash: boom"), "stderr was: {}", stderr);
|
|
29
|
+
}
|
plum-runtime/src/main.rs
CHANGED
|
@@ -31,6 +31,20 @@ fn hostImports(store: &mut wasmtime::Store<()>, module: &wasmtime::Module) -> Ve
|
|
|
31
31
|
Ok(())
|
|
32
32
|
}))
|
|
33
33
|
}
|
|
34
|
+
("plum", "crash") => {
|
|
35
|
+
let func_ty = imp.ty().func().cloned().expect("plum::crash import should be a function");
|
|
36
|
+
wasmtime::Extern::Func(wasmtime::Func::new(&mut *store, func_ty, |mut caller, params, _results| {
|
|
37
|
+
let wasmtime::Val::AnyRef(Some(s)) = ¶ms[0] else {
|
|
38
|
+
return Err(wasmtime::Error::msg("crash expects a Str argument"));
|
|
39
|
+
};
|
|
40
|
+
let arr = unwrapStrToArray(&mut caller, s)?;
|
|
41
|
+
let len = arr.len(&caller)?;
|
|
42
|
+
let mut buf = vec![0u8; len as usize];
|
|
43
|
+
arr.copy_to_i8_slice(&mut caller, &mut buf)?;
|
|
44
|
+
eprintln!("crash: {}", String::from_utf8_lossy(&buf));
|
|
45
|
+
Ok(())
|
|
46
|
+
}))
|
|
47
|
+
}
|
|
34
48
|
("plum", "rawRandomInt") => {
|
|
35
49
|
let func_ty = imp.ty().func().cloned().expect("plum::rawRandomInt import should be a function");
|
|
36
50
|
wasmtime::Extern::Func(wasmtime::Func::new(&mut *store, func_ty, |_caller, _params, results| {
|