Giter VIP home page Giter VIP logo

wasmexec's Introduction

wasmexec Go Report Card GoDoc

wasmexec is runtime-agnostic implementation of Go's wasm_exec.js in Go. It currently has import hooks for wasmer, wasmtime and wazero. Each runtime-dedicated package has its own example of an implementation that can run any of the examples.

1. Minimum implementation

When a Go Wasm module is instantiated, it needs to be wrapped up in a structure that implements the minimum set of methods in order to run. wasmexec.New() requires at least this Instance interface.

type Instance interface {
    Memory

    GetSP() (uint32, error)
    Resume() error
}

The GetSP() and Resume() methods are calls directly to the Go Wasm exports. The Memory interface wraps the Go Wasm module's memory.

type Memory interface {
    Range(offset, length uint32) ([]byte, error)
		
    GetUInt32(offset uint32) (uint32, error)
    GetInt64(offset uint32) (int64, error)
    GetFloat64(offset uint32) (float64, error)
    SetUInt8(offset uint32, val uint8) error
    SetUInt32(offset, val uint32) error
    SetInt64(offset uint32, val int64) error
    SetFloat64(offset uint32, val float64) error
}

If your runtime exposes the memory as a []byte (as wasmer and wasmtime do) then you can easily use the NewMemory() function to satisfy this interface. If not, a custom implementation needs to be written (like wazero).

2. Optional implementation

The above-mentioned instance wrapper may also implement additional methods for extra functionality.

2.1. Debug logging

If the debugLogger interface is implemented, Debug() is called with debug messages. This is only useful when you're debugging issues with this package.

type debugLogger interface {
    Debug(format string, params ...any)
}

2.2. Error logging

If the errorLogger interface is implemented, Error() is called for any error that might pop up during execution. At this stage, it is probably useful to implement this as this package isn't battle tested yet.

type errorLogger interface {
    Error(format string, params ...any)
}

2.3. Writer

If the fdWriter interface is implemented, Write() is called for any data being sent to stdout or stderr. It is highly recommended that this is implemented.

type fdWriter interface {
    Write(fd int, data []byte) (n int, err error)
}

2.4. Exiting

If the exiter interface is implemented, Exit() is called whenever the call to the run() Wasm function is done.

type exiter interface {
    Exit(code int)
}

2.5. waPC

If the hostCaller interface is implemented, HostCall() is called whenever the waPC guest sends information to the host. More information on this can be found in the wapc package.

type hostCaller interface {
    HostCall(string, string, string, []byte) ([]byte, error)
}

3. js.FuncOf()

The guest can use js.FuncOf() to create functions that can be called from the host.

var uint8Array = js.Global().Get("Uint8Array")

func main() {
    myEvent := js.FuncOf(func(this js.Value, args []js.Value) any {
        arg := args[0]

        if arg.InstanceOf(uint8Array) {
            dst := make([]byte, arg.Length())
            js.CopyBytesToGo(dst, arg)
						
            fmt.Printf("Received: %v\n", string(dst))
        }
				
        return nil
    }
		
    js.Global().Set("myEvent", myEvent)
}

On the host these functions can be called using Call() on *wasmexec.Module:

mod.Call("myEvent", []byte("Hello World!"))

4. Acknowledgements

This implementation was made possible by allowing me to peek at mattn's implementation as well as Vedhavyas Singareddi's go-wasm-adapter.

wasmexec's People

Contributors

prep avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

wasmexec's Issues

External FS implementation

I'm interested in implementing an external FS:

All the lines which are defined with errorCallback(ENOSYS) would instead call into a FS interface (or if the interface is nil, return ENosys)

Panic when flag package is used in wasm

No matter if the flag package is a direct dependency or transitive dependency, it will panic.

Minimum steps to reproduce it:

  1. Change main.go in https://github.com/prep/wasmexec/blob/main/examples/hello/main.go to:
package main

import (
	"os"
	_ "flag"
)

func main() {
	_, _ = os.Stdout.Write([]byte("Hello from Go!\n"))
}
  1. Compile it to wasm:
GOOS=js GOARCH=wasm go build -o hello.wasm ./main.go
  1. Build wasmtime exec runtime
$ cd wasmtimexec/example
$ go build -o wasmtimeexec ./main.go
  1. Run it with hello.wasm:
./wasmtimeexec ../../examples/hello/hello.wasm

It will produce the following error:

panic: runtime error: index out of range [0] with length 0

goroutine 1 [running]:
flag.init()
        /usr/local/go/src/flag/flag.go:1047 +0x2f

It seems the problem is that: flag pacakge try to run var CommandLine = NewFlagSet(os.Args[0], ExitOnError) and os.Args is expected to hold the command-line arguments, starting with the program name.
It seems os.Args is not correctly populated somehow.

example with custom import

Users can define their own "go" module function imports by defining a func
without a body in their source and a%_wasm.s or %_js.s file that uses the
CallImport instruction.

The compiler chooses a naming prefix of "main.$funcName", if defined in the
main package, or a fully-qualified based on the module, if elsewhere.

For example, given func logString(msg string) and the below assembly:

#include "textflag.h"
TEXT ·logString(SB), NOSPLIT, $0
CallImport
RET

If the package was main, the WebAssembly function name would be
"main.logString". If it was util and your go.mod module was
"github.com/user/me", the WebAssembly function name would be
"github.com/prep/user/me/util.logString"

You may want to add an example of this, so that you can see the impact
to the various runtimes. It is the same as the other "go".

Note that you will need to put this in a separate directory so you can
compile . or ./... and get the .s file. Also, the func has to be called, or
the compiler will skip writing it!

Here's an example which would work the same way in user-code as go's
source tree

https://github.com/golang/go/blob/master/src/syscall/js/js.go#L569
https://github.com/golang/go/blob/master/src/syscall/js/js_js.s#L63-L65

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.