Repository: tetratelabs/wazero
Pull request #256 (2022-02-17 11:08:53)

doc: fix drifts
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>


Patch:
diff --git a/RATIONALE.md b/RATIONALE.md
index 2c06e373..9f4aee2a 100644
--- a/RATIONALE.md
+++ b/RATIONALE.md
@@ -111,4 +111,4 @@ means that we have 1 GiB size of slice which seems large enough for most applica
 
 ## JIT engine implementation
 
-See [wasm/jit/RATIONALE.md](wasm/jit/RATIONALE.md).
+See [wasm/jit/RATIONALE.md](internal/wasm/jit/RATIONALE.md).
diff --git a/README.md b/README.md
index 62f45fbd..17ca34a2 100644
--- a/README.md
+++ b/README.md
@@ -41,7 +41,7 @@ There's the concept called "engine" in wazero (which is a word commonly used in
 There are two types of engines are available for wazero:
 
 1. _Interpreter_: a naive interpreter-based implementation of Wasm virtual machine. Its implementation doesn't have any platform (GOARCH, GOOS) specific code, therefore _interpreter_ engine can be used for any compilation target available for Go (such as `arm64`).
-2. _JIT engine_: compiles WebAssembly modules, generates the machine code, and executing it all at runtime. Currently wazero only implements the JIT compiler for `amd64` target. Generally speaking, _JIT engine_ is faster than _Interpreter_ by order of magnitude. However, the implementation is immature and has bunch of aspects that could be improved (for example, it just does a singlepass compilation and doesn't do any optimizations, etc.). Please refer to [wasm/jit/RATIONALE.md](wasm/jit/RATIONALE.md) for the design choices and considerations in our JIT engine.
+2. _JIT engine_: compiles WebAssembly modules, generates the machine code, and executing it all at runtime. Currently wazero only implements the JIT compiler for `amd64` target. Generally speaking, _JIT engine_ is faster than _Interpreter_ by order of magnitude. However, the implementation is immature and has bunch of aspects that could be improved (for example, it just does a singlepass compilation and doesn't do any optimizations, etc.). Please refer to [internal/wasm/jit/RATIONALE.md](internal/wasm/jit/RATIONALE.md) for the design choices and considerations in our JIT engine.
 
 Both of engines passes 100% of [WebAssembly spec test suites]((https://github.com/WebAssembly/spec/tree/wg-1.0/test/core)) (on supported platforms).
 
diff --git a/internal/moremath/moremath.go b/internal/moremath/moremath.go
index 027b9090..f7ba1365 100644
--- a/internal/moremath/moremath.go
+++ b/internal/moremath/moremath.go
@@ -5,8 +5,6 @@ import "math"
 // math.Min doen't comply with the Wasm spec, so we borrow from the original
 // with a change that either one of NaN results in NaN even if another is -Inf.
 // https://github.com/golang/go/blob/1d20a362d0ca4898d77865e314ef6f73582daef0/src/math/dim.go#L74-L91
-//
-// Exported to be used in JIT compiler test.
 func WasmCompatMin(x, y float64) float64 {
 	switch {
 	case math.IsNaN(x) || math.IsNaN(y):
@@ -28,8 +26,6 @@ func WasmCompatMin(x, y float64) float64 {
 // math.Max doen't comply with the Wasm spec, so we borrow from the original
 // with a change that either one of NaN results in NaN even if another is Inf.
 // https://github.com/golang/go/blob/1d20a362d0ca4898d77865e314ef6f73582daef0/src/math/dim.go#L42-L59
-//
-// Exported to be used in JIT compiler test.
 func WasmCompatMax(x, y float64) float64 {
 	switch {
 	case math.IsNaN(x) || math.IsNaN(y):
diff --git a/internal/wasi/wasi.go b/internal/wasi/wasi.go
index 82ffc886..453b2e27 100644
--- a/internal/wasi/wasi.go
+++ b/internal/wasi/wasi.go
@@ -112,9 +112,22 @@ const (
 	FunctionPathSymlink          = "path_symlink"
 	FunctionPathUnlinkFile       = "path_unlink_file"
 	FunctionPollOneoff           = "poll_oneoff"
-	FunctionProcExit             = "proc_exit"
-	FunctionProcRaise            = "proc_raise"
-	FunctionSchedYield           = "sched_yield"
+
+	// ProcExit terminates the execution of the module with an exit code.
+	// See https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#proc_exit
+	FunctionProcExit = "proc_exit"
+
+	// ImportProcExit is the WebAssembly 1.0 (MVP) Text format import of ProcExit
+	//
+	// See ImportProcExit
+	// See API.ProcExit
+	// See FunctionProcExit
+	// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#proc_exit
+	ImportProcExit = `(import "wasi_snapshot_preview1" "proc_exit"
+    (func $wasi.proc_exit (param $rval i32)))`
+
+	FunctionProcRaise  = "proc_raise"
+	FunctionSchedYield = "sched_yield"
 
 	// FunctionRandomGet write random data in buffer
 	// See: https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-random_getbuf-pointeru8-buf_len-size---errno
@@ -314,7 +327,20 @@ type SnapshotPreview1 interface {
 	// TODO: PathSymlink
 	// TODO: PathUnlinkFile
 	// TODO: PollOneoff
-	// TODO: ProcExit
+
+	// ProcExit is the WASI function that terminates the execution of the module with an exit code.
+	// An exit code of 0 indicates successful termination. The meanings of other values are not defined by WASI.
+	//
+	// * rval - The exit code.
+	//
+	// In wazero, if ProcExit is called, the calling function returns immediately, returning the given exit code as the error.
+	// You can get the exit code by casting the error to wasi.ExitCode.
+	// See wasi.ExitCode
+	//
+	// Note: ImportProcExit shows this signature in the WebAssembly 1.0 (MVP) Text Format.
+	// See https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#proc_exit
+	ProcExit(ctx wasm.HostFunctionCallContext, rval uint32)
+
 	// TODO: ProcRaise
 	// TODO: SchedYield
 
@@ -401,7 +427,7 @@ func SnapshotPreview1Functions(opts ...Option) (a *wasiAPI, nameToGoFunc map[str
 		// TODO: FunctionPathSymlink
 		// TODO: FunctionPathUnlinkFile
 		// TODO: FunctionPollOneoff
-		FunctionProcExit: proc_exit,
+		FunctionProcExit: a.ProcExit,
 		// TODO: FunctionProcRaise
 		// TODO: FunctionSchedYield
 		FunctionRandomGet: a.RandomGet,
@@ -479,6 +505,13 @@ func (a *wasiAPI) ClockTimeGet(ctx wasm.HostFunctionCallContext, id uint32, prec
 	return wasi.ErrnoSuccess
 }
 
+// ProcExit implements API.ProcExit
+func (a *wasiAPI) ProcExit(ctx wasm.HostFunctionCallContext, exitCode uint32) {
+	// Panic in a host function is caught by the engines, and the value of the panic is returned as the error of the CallFunction.
+	// See the document of API.ProcExit.
+	panic(wasi.ExitCode(exitCode))
+}
+
 type fileEntry struct {
 	path    string
 	fileSys wasi.FS
@@ -774,10 +807,6 @@ func (a *wasiAPI) RandomGet(ctx wasm.HostFunctionCallContext, buf uint32, bufLen
 	return wasi.ErrnoSuccess
 }
 
-func proc_exit(wasm.HostFunctionCallContext, uint32) {
-	// TODO: implement
-}
-
 func ValidateWASICommand(module *internalwasm.Module, moduleName string) error {
 	if start, err := requireExport(module, moduleName, FunctionStart, internalwasm.ExportKindFunc); err != nil {
 		return err
diff --git a/wasi/wasi.go b/wasi/wasi.go
index 9fd5f9d6..54c2508b 100644
--- a/wasi/wasi.go
+++ b/wasi/wasi.go
@@ -293,3 +293,24 @@ var errnoToString = [...]string{
 	ErrnoXdev:           "EXDEV",
 	ErrnoNotcapable:     "ENOTCAPABLE",
 }
+
+// ExitCode is an arbitrary uint32 number returned by proc_exit.
+// An exit code of 0 indicates successful termination. The meanings of other values are not defined by WASI.
+//
+// In wazero, if ProcExit is called, the calling function returns immediately, returning the given exit code as the error.
+// You can get the exit code by casting the error as follows.
+//
+//   wasmFunction := m.GetFunctionVoidReturn(/* omitted */)  // Some function which may call proc_exit
+//   err := wasmFunction()
+//   var exitCode wasi.ExitCode
+//   if errors.As(err, &exitCode) {
+//   	// The function is terminated by proc_exit.
+//   }
+//
+// See https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#exitcode
+// See https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#proc_exit
+type ExitCode uint32
+
+func (err ExitCode) Error() string {
+	return fmt.Sprintf("terminated by proc_exit(%d)", uint32(err))
+}


Test patch:
diff --git a/internal/wasi/wasi_test.go b/internal/wasi/wasi_test.go
index 7366fd37..0b9d4d4a 100644
--- a/internal/wasi/wasi_test.go
+++ b/internal/wasi/wasi_test.go
@@ -491,7 +491,38 @@ func TestAPI_ClockTimeGet_Errors(t *testing.T) {
 // TODO: TestAPI_PathSymlink TestAPI_PathSymlink_Errors
 // TODO: TestAPI_PathUnlinkFile TestAPI_PathUnlinkFile_Errors
 // TODO: TestAPI_PollOneoff TestAPI_PollOneoff_Errors
-// TODO: TestAPI_ProcExit TestAPI_ProcExit_Errors
+
+func TestAPI_ProcExit(t *testing.T) {
+	store, _ := instantiateWasmStore(t, FunctionProcExit, ImportProcExit, "test")
+
+	tests := []struct {
+		name     string
+		exitCode uint32
+	}{
+		{
+			name:     "success (exitcode 0)",
+			exitCode: 0,
+		},
+
+		{
+			name:     "arbitrary non-zero exitcode",
+			exitCode: 42,
+		},
+	}
+
+	for _, tt := range tests {
+		tc := tt
+
+		t.Run(tc.name, func(t *testing.T) {
+			// When ProcExit is called, store.CallFunction returns immediately, returning the exit code as the error.
+			_, _, err := store.CallFunction(context.Background(), "test", FunctionProcExit, uint64(tc.exitCode))
+			var code wasi.ExitCode
+			require.ErrorAs(t, err, &code)
+			require.Equal(t, code, wasi.ExitCode(tc.exitCode))
+		})
+	}
+}
+
 // TODO: TestAPI_ProcRaise TestAPI_ProcRaise_Errors
 // TODO: TestAPI_SchedYield TestAPI_SchedYield_Errors