Skip to content

Commit 0c7970f

Browse files
zrr1999Copilot
andcommitted
Align process exit APIs with host runtime
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent f457d08 commit 0c7970f

3 files changed

Lines changed: 29 additions & 20 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ The current Platform contract MVP is intentionally split across two artifacts:
102102
- a hole-backed `main` function carries the authoritative startup signature
103103
- `main_for_host` is the Platform-owned adapter that receives the application startup function
104104

105-
Applications targeting `basic-cli` must implement the same startup function name/signature in their entry module. The current compiler already resolves the package's `[platform]` metadata and `src/platform_contract.sp` to validate that startup shape. Runtime support is currently specialized to package-backed `basic-cli`: imported `basic_cli.*` foreign functions route through the built-in basic-cli host profile, while generic handled-effects enforcement and startup `spec` stacking are still follow-up work.
105+
Applications targeting `basic-cli` must implement the same startup function name/signature in their entry module. The current compiler already resolves the package's `[platform]` metadata and `src/platform_contract.sp` to validate that startup shape. Project-mode interpreter runtime support now goes through a generic package-backed host path: imported public `foreign fn` declarations bind by canonical operation name (`print`, `file_exists`, `exit`, etc.) instead of hard-coding the `basic-cli` package name. Startup `spec` stacking and native lowering for `foreign fn` platform projects are still follow-up work.
106106

107107
`src/host.sp` remains as a compatibility copy of the adapter for older references; current manifest-backed projects use `src/platform_contract.sp`.
108108

@@ -152,7 +152,7 @@ Following Spore's [SEP-0003 (Effect System)](https://github.com/spore-lang/spore
152152

153153
The canonical example is the **package-backed project-mode** `examples/hello-app/` application. It already validates and runs with `[project].platform = "basic-cli"`, an in-repo path dependency, and `import basic_cli.stdout`.
154154

155-
The pure standalone file example (`examples/hello.sp`) stays around for quick experiments. The main remaining platform gaps are generic handled-effects enforcement, startup `spec` stacking, native lowering for `foreign fn` platform projects, and lifting the runtime from its current explicit `basic-cli` host profile to a more general package-backed mechanism.
155+
The pure standalone file example (`examples/hello.sp`) stays around for quick experiments. The main remaining platform gaps are startup `spec` stacking and native lowering for `foreign fn` platform projects.
156156

157157
## License
158158

host/src/lib.rs

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -192,15 +192,20 @@ pub fn host_process_run_status(cmd: &str, args: &[String]) -> HostResult {
192192
kind: "ExecError".into(),
193193
message: format!("{cmd}: {e}"),
194194
})?;
195-
Ok(HostValue::Int(status.code().unwrap_or(-1) as i64))
195+
let code = match status.code() {
196+
Some(code) => {
197+
let code = u8::try_from(code).map_err(|_| HostError {
198+
kind: "ExecError".into(),
199+
message: format!("{cmd}: exit code {code} is out of range for U8"),
200+
})?;
201+
Some(Box::new(HostValue::Int(i64::from(code))))
202+
}
203+
None => None,
204+
};
205+
Ok(HostValue::Option(code))
196206
}
197207

198-
pub fn host_exit(code: i64) -> HostResult {
199-
let code = u8::try_from(code).map_err(|_| HostError {
200-
kind: "ExitError".into(),
201-
message: format!("exit code {code} is out of range for 0..=255"),
202-
})?;
203-
208+
pub fn host_exit(code: u8) -> HostResult {
204209
#[cfg(test)]
205210
{
206211
Err(HostError {
@@ -355,11 +360,22 @@ mod tests {
355360
fn process_run_status_echo() {
356361
let result = host_process_run_status("echo", &["hello".into()]).unwrap();
357362
match result {
358-
HostValue::Int(code) => assert_eq!(code, 0),
359-
_ => panic!("expected Int"),
363+
HostValue::Option(Some(code)) => match *code {
364+
HostValue::Int(code) => assert_eq!(code, 0),
365+
other => panic!("expected U8 code payload, got {other:?}"),
366+
},
367+
other => panic!("expected Some(U8), got {other:?}"),
360368
}
361369
}
362370

371+
#[test]
372+
#[cfg(unix)]
373+
fn process_run_status_signal_returns_none() {
374+
let result = host_process_run_status("sh", &["-c".into(), "kill -TERM $$".into()])
375+
.expect("signal termination should still report status");
376+
assert!(matches!(result, HostValue::Option(None)));
377+
}
378+
363379
#[test]
364380
fn process_run_captures_stdout() {
365381
let result =
@@ -385,13 +401,6 @@ mod tests {
385401
assert!(err.message.contains("17"));
386402
}
387403

388-
#[test]
389-
fn exit_rejects_out_of_range_codes() {
390-
let err = host_exit(256).unwrap_err();
391-
assert_eq!(err.kind, "ExitError");
392-
assert!(err.message.contains("0..=255"));
393-
}
394-
395404
#[test]
396405
fn dispatch_table_has_all_functions() {
397406
let table = dispatch_table();

src/basic_cli/cmd.sp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
pub foreign fn process_run(cmd: Str, args: List[Str]) -> Str ! ExecError uses [Spawn]
88

99
/// Run a command and return its exit code.
10-
pub foreign fn process_run_status(cmd: Str, args: List[Str]) -> I32 ! ExecError uses [Spawn]
10+
pub foreign fn process_run_status(cmd: Str, args: List[Str]) -> Option[U8] ! ExecError uses [Spawn]
1111

1212
/// Exit the current process with the provided status code.
13-
pub foreign fn exit(code: I32) -> Never uses [Exit]
13+
pub foreign fn exit(code: U8) -> Never uses [Exit]

0 commit comments

Comments
 (0)