github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/sys/error.go (about)

     1  // Package sys includes constants and types used by both public and internal APIs.
     2  package sys
     3  
     4  import (
     5  	"context"
     6  	"fmt"
     7  )
     8  
     9  // These two special exit codes are reserved by wazero for context Cancel and Timeout integrations.
    10  // The assumption here is that well-behaving Wasm programs won't use these two exit codes.
    11  const (
    12  	// ExitCodeContextCanceled corresponds to context.Canceled and returned by ExitError.ExitCode in that case.
    13  	ExitCodeContextCanceled uint32 = 0xffffffff
    14  	// ExitCodeDeadlineExceeded corresponds to context.DeadlineExceeded and returned by ExitError.ExitCode in that case.
    15  	ExitCodeDeadlineExceeded uint32 = 0xefffffff
    16  )
    17  
    18  // ExitError is returned to a caller of api.Function when api.Module CloseWithExitCode was invoked,
    19  // or context.Context passed to api.Function Call was canceled or reached the Timeout.
    20  //
    21  // ExitCode zero value means success while any other value is an error.
    22  //
    23  // Here's an example of how to get the exit code:
    24  //
    25  //	main := module.ExportedFunction("main")
    26  //	if err := main(ctx); err != nil {
    27  //		if exitErr, ok := err.(*sys.ExitError); ok {
    28  //			// This means your module exited with non-zero code!
    29  //		}
    30  //	--snip--
    31  //
    32  // Note: While possible the reason of this was "proc_exit" from "wasi_snapshot_preview1", it could be from other host
    33  // functions, for example an AssemblyScript's abort handler, or any arbitrary caller of CloseWithExitCode.
    34  //
    35  // See https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#proc_exit and
    36  // https://www.assemblyscript.org/concepts.html#special-imports
    37  //
    38  // Note: In the case of context cancellation or timeout, the api.Module from which the api.Function created is closed.
    39  type ExitError struct {
    40  	// Note: this is a struct not a uint32 type as it was originally one and
    41  	// we don't want to break call-sites that cast into it.
    42  	exitCode uint32
    43  }
    44  
    45  var exitZero = &ExitError{}
    46  
    47  func NewExitError(exitCode uint32) *ExitError {
    48  	if exitCode == 0 {
    49  		return exitZero
    50  	}
    51  	return &ExitError{exitCode: exitCode}
    52  }
    53  
    54  // ExitCode returns zero on success, and an arbitrary value otherwise.
    55  func (e *ExitError) ExitCode() uint32 {
    56  	return e.exitCode
    57  }
    58  
    59  // Error implements the error interface.
    60  func (e *ExitError) Error() string {
    61  	switch e.exitCode {
    62  	case ExitCodeContextCanceled:
    63  		return fmt.Sprintf("module closed with %s", context.Canceled)
    64  	case ExitCodeDeadlineExceeded:
    65  		return fmt.Sprintf("module closed with %s", context.DeadlineExceeded)
    66  	default:
    67  		return fmt.Sprintf("module closed with exit_code(%d)", e.exitCode)
    68  	}
    69  }
    70  
    71  // Is allows use via errors.Is
    72  func (e *ExitError) Is(err error) bool {
    73  	if target, ok := err.(*ExitError); ok {
    74  		return e.exitCode == target.exitCode
    75  	}
    76  	if e.exitCode == ExitCodeContextCanceled && err == context.Canceled {
    77  		return true
    78  	}
    79  	if e.exitCode == ExitCodeDeadlineExceeded && err == context.DeadlineExceeded {
    80  		return true
    81  	}
    82  	return false
    83  }