summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCasper Beyer <caspervonb@pm.me>2020-12-04 02:57:35 +0800
committerGitHub <noreply@github.com>2020-12-03 19:57:35 +0100
commit0ea1c6f5b07e18dad60bca5f1c7631be5b5005b3 (patch)
treeafee2a181dc7d2e78cb84c53764d7f9544d1ee43
parentde036e1f08dad4c5d0c6641f1942750558e3e787 (diff)
feat(std/wasi): add return on exit option (#8605)
This adds an exitOnReturn option to context making it possible to unwind the stack on the exit(2) syscall instead of delegating to it directly. Use case is being able to treat WASI execution contexts as children that don't kill the parent on exit.
-rw-r--r--std/wasi/snapshot_preview1.ts21
-rw-r--r--std/wasi/snapshot_preview1_test.ts22
2 files changed, 40 insertions, 3 deletions
diff --git a/std/wasi/snapshot_preview1.ts b/std/wasi/snapshot_preview1.ts
index 4c974fe1d..30756fe82 100644
--- a/std/wasi/snapshot_preview1.ts
+++ b/std/wasi/snapshot_preview1.ts
@@ -207,6 +207,10 @@ function syscall<T extends CallableFunction>(target: T) {
try {
return target(...args);
} catch (err) {
+ if (err instanceof ExitStatus) {
+ throw err;
+ }
+
switch (err.name) {
case "NotFound":
return ERRNO_NOENT;
@@ -266,15 +270,25 @@ interface FileDescriptor {
entries?: Deno.DirEntry[];
}
+export class ExitStatus {
+ code: number;
+
+ constructor(code: number) {
+ this.code = code;
+ }
+}
+
export interface ContextOptions {
args?: string[];
env?: { [key: string]: string | undefined };
preopens?: { [key: string]: string };
+ exitOnReturn?: boolean;
}
export default class Context {
args: string[];
env: { [key: string]: string | undefined };
+ exitOnReturn: boolean;
memory: WebAssembly.Memory;
fds: FileDescriptor[];
@@ -284,6 +298,7 @@ export default class Context {
constructor(options: ContextOptions) {
this.args = options.args ? options.args : [];
this.env = options.env ? options.env : {};
+ this.exitOnReturn = options.exitOnReturn ?? true;
this.memory = null!;
this.fds = [
@@ -1497,7 +1512,11 @@ export default class Context {
"proc_exit": syscall((
rval: number,
): never => {
- Deno.exit(rval);
+ if (this.exitOnReturn) {
+ Deno.exit(rval);
+ }
+
+ throw new ExitStatus(rval);
}),
"proc_raise": syscall((
diff --git a/std/wasi/snapshot_preview1_test.ts b/std/wasi/snapshot_preview1_test.ts
index d7e29e195..01b91bc3e 100644
--- a/std/wasi/snapshot_preview1_test.ts
+++ b/std/wasi/snapshot_preview1_test.ts
@@ -1,6 +1,6 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
-import Context from "./snapshot_preview1.ts";
-import { assertEquals, assertThrows } from "../testing/asserts.ts";
+import Context, { ExitStatus } from "./snapshot_preview1.ts";
+import { assert, assertEquals, assertThrows } from "../testing/asserts.ts";
import { copy } from "../fs/mod.ts";
import * as path from "../path/mod.ts";
@@ -179,6 +179,24 @@ Deno.test("context_start", function () {
TypeError,
"export _start must be a function",
);
+
+ try {
+ const context = new Context({
+ exitOnReturn: false,
+ });
+ context.start({
+ exports: {
+ _start() {
+ const exit = context.exports["proc_exit"] as CallableFunction;
+ exit(0);
+ },
+ memory: new WebAssembly.Memory({ initial: 1 }),
+ },
+ });
+ } catch (err) {
+ assert(err instanceof ExitStatus);
+ assertEquals(err.code, 0);
+ }
});
Deno.test("context_initialize", function () {