Created
November 4, 2018 15:47
-
-
Save icexin/3123b51d2ae97c21aade3ea2975ecea2 to your computer and use it in GitHub Desktop.
A simple go wasm interpreter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bytes" | |
"encoding/binary" | |
"flag" | |
"fmt" | |
"io/ioutil" | |
"syscall" | |
"time" | |
"github.com/perlin-network/life/exec" | |
) | |
var timeorigin = time.Now() | |
func dummyMethod(vm *exec.VirtualMachine) int64 { | |
return 0 | |
} | |
// Resolver defines imports for WebAssembly modules ran in Life. | |
type Resolver struct { | |
tempRet0 int64 | |
} | |
// ResolveFunc defines a set of import functions that may be called within a WebAssembly module. | |
func (r *Resolver) ResolveFunc(module, field string) exec.FunctionImport { | |
fmt.Printf("Resolve func: %s %s\n", module, field) | |
switch module { | |
case "env": | |
switch field { | |
case "__life_ping": | |
return func(vm *exec.VirtualMachine) int64 { | |
return vm.GetCurrentFrame().Locals[0] + 1 | |
} | |
case "__life_log": | |
return func(vm *exec.VirtualMachine) int64 { | |
ptr := int(uint32(vm.GetCurrentFrame().Locals[0])) | |
msgLen := int(uint32(vm.GetCurrentFrame().Locals[1])) | |
msg := vm.Memory[ptr : ptr+msgLen] | |
fmt.Printf("[app] %s\n", string(msg)) | |
return 0 | |
} | |
default: | |
panic(fmt.Errorf("unknown field: %s", field)) | |
} | |
case "go": | |
switch field { | |
case "runtime.nanotime": | |
return func(vm *exec.VirtualMachine) int64 { | |
sp := vm.GetCurrentFrame().Locals[0] | |
mem := vm.Memory | |
binary.LittleEndian.PutUint64(mem[sp+8:], uint64(time.Since(timeorigin).Nanoseconds())) | |
return 0 | |
} | |
case "runtime.wasmWrite": | |
return func(vm *exec.VirtualMachine) int64 { | |
sp := vm.GetCurrentFrame().Locals[0] | |
mem := vm.Memory | |
buf := bytes.NewBuffer(mem[sp+8:]) | |
var fd, p int64 | |
var n int32 | |
binary.Read(buf, binary.LittleEndian, &fd) | |
binary.Read(buf, binary.LittleEndian, &p) | |
binary.Read(buf, binary.LittleEndian, &n) | |
syscall.Write(int(fd), mem[p:p+int64(n)]) | |
return 0 | |
} | |
default: | |
return dummyMethod | |
} | |
default: | |
panic(fmt.Errorf("unknown module: %s", module)) | |
} | |
} | |
// ResolveGlobal defines a set of global variables for use within a WebAssembly module. | |
func (r *Resolver) ResolveGlobal(module, field string) int64 { | |
fmt.Printf("Resolve global: %s %s\n", module, field) | |
switch module { | |
case "env": | |
switch field { | |
case "__life_magic": | |
return 424 | |
default: | |
panic(fmt.Errorf("unknown field: %s", field)) | |
} | |
default: | |
panic(fmt.Errorf("unknown module: %s", module)) | |
} | |
} | |
func main() { | |
entryFunctionFlag := flag.String("entry", "run", "entry function id") | |
jitFlag := flag.Bool("jit", false, "enable jit") | |
flag.Parse() | |
// Read WebAssembly *.wasm file. | |
input, err := ioutil.ReadFile(flag.Arg(0)) | |
if err != nil { | |
panic(err) | |
} | |
// Instantiate a new WebAssembly VM with a few resolved imports. | |
vm, err := exec.NewVirtualMachine(input, exec.VMConfig{ | |
EnableJIT: *jitFlag, | |
DefaultMemoryPages: 128, | |
DefaultTableSize: 65536, | |
}, new(Resolver), nil) | |
if err != nil { | |
panic(err) | |
} | |
// Get the function ID of the entry function to be executed. | |
entryID, ok := vm.GetFunctionExport(*entryFunctionFlag) | |
if !ok { | |
fmt.Printf("Entry function %s not found; starting from 0.\n", *entryFunctionFlag) | |
entryID = 0 | |
} | |
start := time.Now() | |
// If any function prior to the entry function was declared to be | |
// called by the module, run it first. | |
if vm.Module.Base.Start != nil { | |
startID := int(vm.Module.Base.Start.Index) | |
_, err := vm.Run(startID) | |
if err != nil { | |
vm.PrintStackTrace() | |
panic(err) | |
} | |
} | |
// Run the WebAssembly module's entry function. | |
ret, err := vm.Run(entryID, 0, 0) | |
if err != nil { | |
vm.PrintStackTrace() | |
panic(err) | |
} | |
end := time.Now() | |
fmt.Printf("return value = %d, duration = %v\n", ret, end.Sub(start)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment