mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 10:22:23 +00:00
core/vm: Add wagon/wasm as an interpreter
* Implement the ewasm module and the EEI. * Add an ewasm standalone binary to test standalone files * use reflection to declare EEI module exports * check the module name when importing EEI functions * make eeiFuncs a function to avoid an init loop issue in call * Add a reference to the ewasm tests * wagon implementation of the Interpreter interface * update to wagon's PR #59 and uses the Process structure * Adapt to endianness change * All tests but 1 passing * CanRun only checks for the header * Add a "debug" module to support "printHex" in contracts Notes: * There are very little checks, e.g. of the call depths and so on.
This commit is contained in:
parent
5d3b7bb023
commit
9927596822
57 changed files with 7176 additions and 63 deletions
2
.gitmodules
vendored
2
.gitmodules
vendored
|
|
@ -1,3 +1,3 @@
|
|||
[submodule "tests"]
|
||||
path = tests/testdata
|
||||
url = https://github.com/ethereum/tests
|
||||
url = https://github.com/ewasm/tests
|
||||
|
|
|
|||
125
cmd/ewasm/main.go
Normal file
125
cmd/ewasm/main.go
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// ewasm executes eWASM modules.
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
coreVM "github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
var (
|
||||
gitCommit = "" // Git SHA1 commit hash of the release (set via linker flags)
|
||||
|
||||
app = utils.NewApp(gitCommit, "the ewasm command line interface")
|
||||
)
|
||||
|
||||
var runCommand = cli.Command{
|
||||
Action: runCmd,
|
||||
Name: "run",
|
||||
Usage: "run and arbitrary eWASM module",
|
||||
ArgsUsage: "<module name> <input> <gas>",
|
||||
Description: `The run command runs an arbitrary eWASM module.`,
|
||||
}
|
||||
|
||||
func runCmd(ctx *cli.Context) error {
|
||||
args := ctx.Args()
|
||||
|
||||
statedb, err := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
|
||||
if err != nil {
|
||||
utils.Fatalf("Could not create the state database: %v", err)
|
||||
}
|
||||
|
||||
if args.Present() {
|
||||
filename := args.First()
|
||||
input := args.Get(1)
|
||||
gas, err := strconv.ParseUint(args.Get(2), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error parsing gas number: %v", err)
|
||||
}
|
||||
|
||||
if fd, err := os.Open(filename); err == nil {
|
||||
|
||||
fi, _ := fd.Stat()
|
||||
code := make([]byte, fi.Size())
|
||||
n, err := fd.Read(code)
|
||||
if n != len(code) || err != nil {
|
||||
return fmt.Errorf("Read %d bytes out of %d, err: %v", n, len(code), err)
|
||||
}
|
||||
|
||||
randomContractAddress := make([]byte, common.HashLength)
|
||||
rand.Read(randomContractAddress)
|
||||
contractAddr := common.BytesToAddress(randomContractAddress)
|
||||
|
||||
randomCallerAddress := make([]byte, common.HashLength)
|
||||
rand.Read(randomContractAddress)
|
||||
callerAddr := common.BytesToAddress(randomCallerAddress)
|
||||
|
||||
contract := coreVM.NewContract(coreVM.AccountRef(callerAddr), coreVM.AccountRef(contractAddr), big.NewInt(100), gas)
|
||||
contract.Code = code
|
||||
|
||||
evm := coreVM.NewEVM(coreVM.Context{}, statedb, ¶ms.ChainConfig{}, coreVM.Config{EnableWagon: true})
|
||||
// &ewasm.Contract{
|
||||
// StateDB: statedb,
|
||||
// Gas: 100,
|
||||
// Address: &contractAddr,
|
||||
// CodeAddr: &codeAddr,
|
||||
// Module: m,
|
||||
// VM: vm,
|
||||
// }
|
||||
|
||||
output, err := evm.Interpreter().Run(contract, []byte(input))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Output: %v\n", output)
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("Error opening module file: %v", err)
|
||||
}
|
||||
|
||||
return errors.New("You need to specify a module name")
|
||||
}
|
||||
|
||||
func init() {
|
||||
app.Commands = []cli.Command{
|
||||
runCommand,
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
|
@ -157,12 +157,12 @@ func (c *Contract) UseGas(gas uint64) (ok bool) {
|
|||
return true
|
||||
}
|
||||
|
||||
// Address returns the contracts address
|
||||
// Address returns the contract's address
|
||||
func (c *Contract) Address() common.Address {
|
||||
return c.self.Address()
|
||||
}
|
||||
|
||||
// Value returns the contracts value (sent to it from it's caller)
|
||||
// Value returns the contracts value (sent to it from its caller)
|
||||
func (c *Contract) Value() *big.Int {
|
||||
return c.value
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,3 +22,12 @@ over a set of bytes and executes them according to the set of rules defined
|
|||
in the Ethereum yellow paper.
|
||||
*/
|
||||
package vm
|
||||
|
||||
// Size (in bytes) of a u256
|
||||
const u256Len = 32
|
||||
|
||||
// Size (in bytes) of a u128
|
||||
const u128Len = 16
|
||||
|
||||
// Max recursion depth for contracts
|
||||
const maxCallDepth = 4
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmCon
|
|||
// }
|
||||
// evm.interpreters = append(evm.interpreters, NewEVMVCInterpreter(evm, vmConfig, options))
|
||||
// } else {
|
||||
// evm.interpreters = append(evm.interpreters, NewEWASMInterpreter(evm, vmConfig))
|
||||
evm.interpreters = append(evm.interpreters, NewEWASMInterpreter(evm, vmConfig))
|
||||
// }
|
||||
panic("No supported ewasm interpreter yet.")
|
||||
}
|
||||
|
|
@ -158,7 +158,7 @@ func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmCon
|
|||
// vmConfig.EVMInterpreter will be used by EVM-C, it won't be checked here
|
||||
// as we always want to have the built-in EVM as the failover option.
|
||||
evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, vmConfig))
|
||||
evm.interpreter = evm.interpreters[0]
|
||||
evm.interpreter = evm.interpreters[len(evm.interpreters)-1]
|
||||
|
||||
return evm
|
||||
}
|
||||
|
|
|
|||
141
core/vm/ewasm.go
Normal file
141
core/vm/ewasm.go
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// This file lists the EEI functions, so that they can be bound to any
|
||||
// eWASM-compatible module, as well as the types of these functions
|
||||
|
||||
package vm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/go-interpreter/wagon/wasm"
|
||||
|
||||
"github.com/go-interpreter/wagon/exec"
|
||||
)
|
||||
|
||||
type terminationType int
|
||||
|
||||
// List of termination reasons
|
||||
const (
|
||||
TerminateFinish = iota
|
||||
TerminateRevert
|
||||
TerminateSuicide
|
||||
TerminateInvalid
|
||||
)
|
||||
|
||||
// InterpreterEWASM implements the Interpreter interface for eWASM.
|
||||
type InterpreterEWASM struct {
|
||||
vm *exec.VM
|
||||
|
||||
evm *EVM
|
||||
|
||||
StateDB StateDB
|
||||
|
||||
gasTable params.GasTable
|
||||
|
||||
contract *Contract
|
||||
|
||||
returnData []byte
|
||||
|
||||
terminationType terminationType
|
||||
}
|
||||
|
||||
// NewEWASMInterpreter creates a new wagon-based eWASM interpreter. It
|
||||
// currently takes a *vm.EVM pointer as a proxy to the client's internal
|
||||
// state; this will be fixed in subsequent updates.
|
||||
func NewEWASMInterpreter(evm *EVM, cfg Config) Interpreter {
|
||||
return &InterpreterEWASM{StateDB: evm.StateDB, evm: evm, gasTable: evm.chainConfig.GasTable(evm.BlockNumber)}
|
||||
}
|
||||
|
||||
// Run loops and evaluates the contract's code with the given input data and returns
|
||||
// the return byte-slice and an error if one occurred.
|
||||
func (in *InterpreterEWASM) Run(contract *Contract, input []byte, ro bool) ([]byte, error) {
|
||||
in.contract = contract
|
||||
in.contract.Input = input
|
||||
initialGas := contract.Gas
|
||||
|
||||
module, err := wasm.ReadModule(bytes.NewReader(contract.Code), WrappedModuleResolver(in))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error decoding module at address %s: %v", contract.Address(), err)
|
||||
}
|
||||
|
||||
// The module should not have any start function
|
||||
if module.Start != nil {
|
||||
return nil, fmt.Errorf("A contract should not have a start function: found #%d", module.Start.Index)
|
||||
}
|
||||
|
||||
vm, err := exec.NewVM(module)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create the vm: %v", err)
|
||||
}
|
||||
vm.RecoverPanic = true
|
||||
in.vm = vm
|
||||
|
||||
// Look for the "main" function and execute it after checking it
|
||||
// has the right kind of signature.
|
||||
for name, entry := range module.Export.Entries {
|
||||
if name == "main" && entry.Kind == wasm.ExternalFunction {
|
||||
|
||||
// Check input and output types
|
||||
sig := module.FunctionIndexSpace[entry.Index].Sig
|
||||
if len(sig.ParamTypes) == 0 && len(sig.ReturnTypes) == 0 {
|
||||
_, err = vm.ExecCode(int64(entry.Index))
|
||||
|
||||
if in.StateDB.HasSuicided(contract.Address()) {
|
||||
if initialGas-contract.Gas-params.TxGas < 2*params.SuicideRefundGas {
|
||||
in.StateDB.AddRefund((initialGas - contract.Gas - params.TxGas) / 2)
|
||||
} else {
|
||||
in.StateDB.AddRefund(params.SuicideRefundGas)
|
||||
}
|
||||
err = nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
in.terminationType = TerminateInvalid
|
||||
}
|
||||
|
||||
return in.returnData, err
|
||||
}
|
||||
|
||||
// Found a main but it doesn't have the right signature - fail
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("Could not find a suitable 'main' function in that contract")
|
||||
}
|
||||
|
||||
// CanRun checks the binary for a WASM header and accepts the binary blob
|
||||
// if it matches.
|
||||
func (in *InterpreterEWASM) CanRun(file []byte) bool {
|
||||
// Check the header
|
||||
if len(file) <= 8 || string(file[:4]) != "\000asm" {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check the version
|
||||
ver := binary.LittleEndian.Uint32(file[4:])
|
||||
if ver != 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
874
core/vm/ewasm_eei.go
Normal file
874
core/vm/ewasm_eei.go
Normal file
|
|
@ -0,0 +1,874 @@
|
|||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// This file lists the EEI functions, so that they can be bound to any
|
||||
// eWASM-compatible module, as well as the types of these functions
|
||||
|
||||
package vm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/go-interpreter/wagon/exec"
|
||||
"github.com/go-interpreter/wagon/wasm"
|
||||
)
|
||||
|
||||
const (
|
||||
// EEICallSuccess is the return value in case of a successful contract execution
|
||||
EEICallSuccess = 0
|
||||
// ErrEEICallFailure is the return value in case of a contract execution failture
|
||||
ErrEEICallFailure = 1
|
||||
// ErrEEICallRevert is the return value in case a contract calls `revert`
|
||||
ErrEEICallRevert = 2
|
||||
)
|
||||
|
||||
// List of gas costs
|
||||
const (
|
||||
GasCostZero = 0
|
||||
GasCostBase = 2
|
||||
GasCostVeryLow = 3
|
||||
GasCostLow = 5
|
||||
GasCostMid = 8
|
||||
GasCostHigh = 10
|
||||
GasCostExtCode = 700
|
||||
GasCostBalance = 400
|
||||
GasCostSLoad = 200
|
||||
GasCostJumpDest = 1
|
||||
GasCostSSet = 20000
|
||||
GasCostSReset = 5000
|
||||
GasRefundSClear = 15000
|
||||
GasRefundSelfDestruct = 24000
|
||||
GasCostCreate = 32000
|
||||
GasCostCall = 700
|
||||
GasCostCallValue = 9000
|
||||
GasCostCallStipend = 2300
|
||||
GasCostLog = 375
|
||||
GasCostLogData = 8
|
||||
GasCostLogTopic = 375
|
||||
GasCostCopy = 3
|
||||
GasCostBlockHash = 800
|
||||
)
|
||||
|
||||
var eeiTypes = &wasm.SectionTypes{
|
||||
Entries: []wasm.FunctionSig{
|
||||
{
|
||||
ParamTypes: []wasm.ValueType{wasm.ValueTypeI64},
|
||||
ReturnTypes: []wasm.ValueType{},
|
||||
},
|
||||
{
|
||||
ParamTypes: []wasm.ValueType{wasm.ValueTypeI32},
|
||||
ReturnTypes: []wasm.ValueType{},
|
||||
},
|
||||
{
|
||||
ParamTypes: []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32},
|
||||
ReturnTypes: []wasm.ValueType{},
|
||||
},
|
||||
{
|
||||
ParamTypes: []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI32},
|
||||
ReturnTypes: []wasm.ValueType{wasm.ValueTypeI32},
|
||||
},
|
||||
{
|
||||
ParamTypes: []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI32, wasm.ValueTypeI32, wasm.ValueTypeI32, wasm.ValueTypeI32},
|
||||
ReturnTypes: []wasm.ValueType{wasm.ValueTypeI32},
|
||||
},
|
||||
{
|
||||
ParamTypes: []wasm.ValueType{wasm.ValueTypeI32},
|
||||
ReturnTypes: []wasm.ValueType{wasm.ValueTypeI32},
|
||||
},
|
||||
{
|
||||
ParamTypes: []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32, wasm.ValueTypeI32},
|
||||
ReturnTypes: []wasm.ValueType{},
|
||||
},
|
||||
{
|
||||
ParamTypes: []wasm.ValueType{},
|
||||
ReturnTypes: []wasm.ValueType{wasm.ValueTypeI32},
|
||||
},
|
||||
{
|
||||
ParamTypes: []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI32, wasm.ValueTypeI32, wasm.ValueTypeI32},
|
||||
ReturnTypes: []wasm.ValueType{wasm.ValueTypeI32},
|
||||
},
|
||||
{
|
||||
ParamTypes: []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI32, wasm.ValueTypeI32, wasm.ValueTypeI32, wasm.ValueTypeI32},
|
||||
ReturnTypes: []wasm.ValueType{wasm.ValueTypeI32},
|
||||
},
|
||||
{
|
||||
ParamTypes: []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32, wasm.ValueTypeI32, wasm.ValueTypeI32},
|
||||
ReturnTypes: []wasm.ValueType{wasm.ValueTypeI32},
|
||||
},
|
||||
{
|
||||
ParamTypes: []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32, wasm.ValueTypeI32, wasm.ValueTypeI32},
|
||||
ReturnTypes: []wasm.ValueType{},
|
||||
},
|
||||
{
|
||||
ParamTypes: []wasm.ValueType{},
|
||||
ReturnTypes: []wasm.ValueType{wasm.ValueTypeI64},
|
||||
},
|
||||
{
|
||||
ParamTypes: []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32, wasm.ValueTypeI32, wasm.ValueTypeI32, wasm.ValueTypeI32, wasm.ValueTypeI32, wasm.ValueTypeI32},
|
||||
ReturnTypes: []wasm.ValueType{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func swapEndian(src []byte) []byte {
|
||||
ret := make([]byte, len(src))
|
||||
for i, v := range src {
|
||||
ret[len(src)-i-1] = v
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (in *InterpreterEWASM) gasAccounting(cost uint64) {
|
||||
if in.contract == nil {
|
||||
panic("nil contract")
|
||||
}
|
||||
if cost > in.contract.Gas {
|
||||
panic("out of gas")
|
||||
}
|
||||
in.contract.Gas -= cost
|
||||
}
|
||||
|
||||
func getDebugFuncs(in *InterpreterEWASM) []wasm.Function {
|
||||
return []wasm.Function{
|
||||
{
|
||||
Sig: &eeiTypes.Entries[2],
|
||||
Host: reflect.ValueOf(func(p *exec.Process, o, l int32) { printMemHex(p, in, o, l) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[1],
|
||||
Host: reflect.ValueOf(func(p *exec.Process, o int32) { printStorageHex(p, in, o) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func printMemHex(p *exec.Process, in *InterpreterEWASM, offset, length int32) {
|
||||
data := readSize(p, offset, int(length))
|
||||
for _, v := range data {
|
||||
fmt.Printf("%02x", v)
|
||||
}
|
||||
fmt.Println("")
|
||||
}
|
||||
|
||||
func printStorageHex(p *exec.Process, in *InterpreterEWASM, pathOffset int32) {
|
||||
|
||||
path := common.BytesToHash(readSize(p, pathOffset, common.HashLength))
|
||||
val := in.StateDB.GetState(in.contract.Address(), path)
|
||||
for v := range val {
|
||||
fmt.Printf("%02x", v)
|
||||
}
|
||||
fmt.Println("")
|
||||
}
|
||||
|
||||
// Return the list of function descriptors. This is a function instead of
|
||||
// a variable in order to avoid an initialization loop.
|
||||
func eeiFuncs(in *InterpreterEWASM) []wasm.Function {
|
||||
return []wasm.Function{
|
||||
{
|
||||
Sig: &eeiTypes.Entries[0], // TODO use constants or find the right entry in the list
|
||||
Host: reflect.ValueOf(func(p *exec.Process, a int64) { useGas(p, in, a) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[1],
|
||||
Host: reflect.ValueOf(func(p *exec.Process, r int32) { getAddress(p, in, r) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[2],
|
||||
Host: reflect.ValueOf(func(p *exec.Process, a, r int32) { getExternalBalance(p, in, a, r) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[3],
|
||||
Host: reflect.ValueOf(func(p *exec.Process, n int64, r int32) int32 { return getBlockHash(p, in, n, r) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[4],
|
||||
Host: reflect.ValueOf(func(p *exec.Process, g int64, a, v, d, l int32) int32 { return call(p, in, g, a, v, d, l) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[6],
|
||||
Host: reflect.ValueOf(func(p *exec.Process, r, d, l int32) { callDataCopy(p, in, r, d, l) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[7],
|
||||
Host: reflect.ValueOf(func(p *exec.Process) int32 { return getCallDataSize(p, in) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[9],
|
||||
Host: reflect.ValueOf(func(p *exec.Process, g int64, a, v, d, l int32) int32 { return callCode(p, in, g, a, v, d, l) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[8],
|
||||
Host: reflect.ValueOf(func(p *exec.Process, g int64, a, d, l int32) int32 { return callDelegate(p, in, g, a, d, l) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[8],
|
||||
Host: reflect.ValueOf(func(p *exec.Process, g int64, a, d, l int32) int32 { return callStatic(p, in, g, a, d, l) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[2],
|
||||
Host: reflect.ValueOf(func(pr *exec.Process, p, v int32) { storageStore(pr, in, p, v) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[2],
|
||||
Host: reflect.ValueOf(func(pr *exec.Process, p, r int32) { storageLoad(pr, in, p, r) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[1],
|
||||
Host: reflect.ValueOf(func(p *exec.Process, r int32) { getCaller(p, in, r) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[2],
|
||||
Host: reflect.ValueOf(func(p *exec.Process, r int32) { getCallValue(p, in, r) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[6],
|
||||
Host: reflect.ValueOf(func(p *exec.Process, r, c, l int32) { codeCopy(p, in, r, c, l) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[7],
|
||||
Host: reflect.ValueOf(func(p *exec.Process) int32 { return getCodeSize(p, in) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[1],
|
||||
Host: reflect.ValueOf(func(p *exec.Process, r int32) { getBlockCoinbase(p, in, r) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[10],
|
||||
Host: reflect.ValueOf(func(p *exec.Process, v, d, l, r uint32) int32 { return create(p, in, v, d, l, r) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[1],
|
||||
Host: reflect.ValueOf(func(p *exec.Process, r int32) { getBlockDifficulty(p, in, r) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[11],
|
||||
Host: reflect.ValueOf(func(p *exec.Process, a, r, c, l int32) { externalCodeCopy(p, in, a, r, c, l) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[5],
|
||||
Host: reflect.ValueOf(func(p *exec.Process, a int32) int32 { return getExternalCodeSize(p, in, a) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[12],
|
||||
Host: reflect.ValueOf(func(p *exec.Process) int64 { return getGasLeft(p, in) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[12],
|
||||
Host: reflect.ValueOf(func(p *exec.Process) int64 { return getBlockGasLimit(p, in) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[5],
|
||||
Host: reflect.ValueOf(func(p *exec.Process, v int32) { getTxGasPrice(p, in, v) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[13],
|
||||
Host: reflect.ValueOf(func(p *exec.Process, d, l, n, t1, t2, t3, t4 int32) { log(p, in, d, l, n, t1, t2, t3, t4) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[12],
|
||||
Host: reflect.ValueOf(func(p *exec.Process) int64 { return getBlockNumber(p, in) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[1],
|
||||
Host: reflect.ValueOf(func(p *exec.Process, r int32) { getTxOrigin(p, in, r) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[2],
|
||||
Host: reflect.ValueOf(func(p *exec.Process, d, l int32) { finish(p, in, d, l) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[2],
|
||||
Host: reflect.ValueOf(func(p *exec.Process, d, l int32) { revert(p, in, d, l) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[5],
|
||||
Host: reflect.ValueOf(func(p *exec.Process) int32 { return getReturnDataSize(p, in) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[6],
|
||||
Host: reflect.ValueOf(func(p *exec.Process, r, d, l int32) { returnDataCopy(p, in, r, d, l) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[1],
|
||||
Host: reflect.ValueOf(func(p *exec.Process, a int32) { selfDestruct(p, in, a) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
{
|
||||
Sig: &eeiTypes.Entries[12],
|
||||
Host: reflect.ValueOf(func(p *exec.Process) int64 { return getBlockTimestamp(p, in) }),
|
||||
Body: &wasm.FunctionBody{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func readSize(p *exec.Process, offset int32, size int) []byte {
|
||||
// TODO modify the process interface to find out how much memory is
|
||||
// available on the system.
|
||||
val := make([]byte, size)
|
||||
p.ReadAt(val, int64(offset))
|
||||
return val
|
||||
}
|
||||
|
||||
func readU256(p *exec.Process, offset int32) []byte {
|
||||
return readSize(p, offset, u256Len)
|
||||
}
|
||||
|
||||
func useGas(p *exec.Process, in *InterpreterEWASM, amount int64) {
|
||||
in.gasAccounting(uint64(amount))
|
||||
}
|
||||
|
||||
func getAddress(p *exec.Process, in *InterpreterEWASM, resultOffset int32) {
|
||||
in.gasAccounting(GasCostBase)
|
||||
contractBytes := in.contract.CodeAddr.Bytes()
|
||||
p.WriteAt(contractBytes, int64(resultOffset))
|
||||
}
|
||||
|
||||
func getExternalBalance(p *exec.Process, in *InterpreterEWASM, addressOffset int32, resultOffset int32) {
|
||||
in.gasAccounting(in.gasTable.Balance)
|
||||
addr := common.BytesToAddress(readSize(p, addressOffset, common.AddressLength))
|
||||
balance := swapEndian(in.StateDB.GetBalance(addr).Bytes())
|
||||
p.WriteAt(balance, int64(resultOffset))
|
||||
}
|
||||
|
||||
func getBlockHash(p *exec.Process, in *InterpreterEWASM, number int64, resultOffset int32) int32 {
|
||||
in.gasAccounting(GasCostBlockHash)
|
||||
n := big.NewInt(number)
|
||||
fmt.Println(n)
|
||||
n.Sub(in.evm.Context.BlockNumber, n)
|
||||
fmt.Println(n, n.Cmp(big.NewInt(256)), n.Cmp(big.NewInt(0)))
|
||||
if n.Cmp(big.NewInt(256)) > 0 || n.Cmp(big.NewInt(0)) <= 0 {
|
||||
return 1
|
||||
}
|
||||
h := in.evm.GetHash(uint64(number))
|
||||
p.WriteAt(h.Bytes(), int64(resultOffset))
|
||||
return 0
|
||||
}
|
||||
|
||||
func callCommon(in *InterpreterEWASM, contract, targetContract *Contract, input []byte, value *big.Int, snapshot int, gas int64, ro bool) int32 {
|
||||
savedVM := in.vm
|
||||
|
||||
in.Run(targetContract, input, ro)
|
||||
|
||||
in.vm = savedVM
|
||||
in.contract = contract
|
||||
|
||||
if value.Cmp(big.NewInt(0)) != 0 {
|
||||
in.gasAccounting(uint64(gas) - targetContract.Gas - GasCostCallStipend)
|
||||
} else {
|
||||
in.gasAccounting(uint64(gas) - targetContract.Gas)
|
||||
}
|
||||
|
||||
switch in.terminationType {
|
||||
case TerminateFinish:
|
||||
return EEICallSuccess
|
||||
case TerminateRevert:
|
||||
in.StateDB.RevertToSnapshot(snapshot)
|
||||
return ErrEEICallRevert
|
||||
default:
|
||||
in.StateDB.RevertToSnapshot(snapshot)
|
||||
contract.UseGas(targetContract.Gas)
|
||||
return ErrEEICallFailure
|
||||
}
|
||||
}
|
||||
|
||||
func call(p *exec.Process, in *InterpreterEWASM, gas int64, addressOffset int32, valueOffset int32, dataOffset int32, dataLength int32) int32 {
|
||||
in.gasAccounting(GasCostCall)
|
||||
|
||||
contract := in.contract
|
||||
|
||||
// Get the address of the contract to call
|
||||
addr := common.BytesToAddress(readSize(p, addressOffset, common.AddressLength))
|
||||
|
||||
// Get the value. The [spec](https://github.com/ewasm/design/blob/master/eth_interface.md#call)
|
||||
// requires this operation to be U128, which is incompatible with the EVM version that expects
|
||||
// a u256.
|
||||
value := big.NewInt(0).SetBytes(swapEndian(readSize(p, valueOffset, u128Len)))
|
||||
|
||||
if value.Cmp(big.NewInt(0)) != 0 {
|
||||
in.gasAccounting(GasCostCallValue)
|
||||
gas += GasCostCallStipend
|
||||
}
|
||||
|
||||
// Get the arguments.
|
||||
// TODO check the need for callvalue (seems not, a lot of that stuff is
|
||||
// already accounted for in the functions that I already called - need to
|
||||
// refactor all that)
|
||||
input := readSize(p, dataOffset, int(dataLength))
|
||||
|
||||
snapshot := in.StateDB.Snapshot()
|
||||
|
||||
// Check that the contract exists
|
||||
if !in.StateDB.Exist(addr) {
|
||||
// TODO check that no new account creation stuff is required
|
||||
in.StateDB.CreateAccount(addr)
|
||||
}
|
||||
|
||||
// Check that there is enough balance to transfer the value
|
||||
if in.StateDB.GetBalance(contract.Address()).Cmp(value) < 0 {
|
||||
fmt.Printf("Not enough balance: wanted to use %v, got %v\n", value, in.StateDB.GetBalance(addr))
|
||||
in.contract.Gas += GasCostCallStipend
|
||||
return ErrEEICallFailure
|
||||
}
|
||||
|
||||
// TODO tracing
|
||||
// TODO check that EIP-150 is respected
|
||||
|
||||
// Add amount to recipient
|
||||
in.evm.Transfer(in.StateDB, contract.Address(), addr, value)
|
||||
|
||||
// Load the contract code in a new VM structure
|
||||
targetContract := NewContract(contract, AccountRef(addr), value, uint64(gas))
|
||||
code := in.StateDB.GetCode(addr)
|
||||
targetContract.SetCallCode(&addr, in.StateDB.GetCodeHash(addr), code)
|
||||
|
||||
return callCommon(in, contract, targetContract, input, value, snapshot, gas, false)
|
||||
}
|
||||
|
||||
func callDataCopy(p *exec.Process, in *InterpreterEWASM, resultOffset int32, dataOffset int32, length int32) {
|
||||
in.gasAccounting(GasCostVeryLow + GasCostCopy*(uint64(length+31)>>5))
|
||||
p.WriteAt(in.contract.Input[dataOffset:dataOffset+length], int64(resultOffset))
|
||||
}
|
||||
|
||||
func getCallDataSize(p *exec.Process, in *InterpreterEWASM) int32 {
|
||||
in.gasAccounting(GasCostBase)
|
||||
return int32(len(in.contract.Input))
|
||||
}
|
||||
|
||||
func callCode(p *exec.Process, in *InterpreterEWASM, gas int64, addressOffset int32, valueOffset int32, dataOffset int32, dataLength int32) int32 {
|
||||
in.gasAccounting(GasCostCall)
|
||||
|
||||
contract := in.contract
|
||||
|
||||
// Get the address of the contract to call
|
||||
addr := common.BytesToAddress(readSize(p, addressOffset, common.AddressLength))
|
||||
|
||||
// Get the value. The [spec](https://github.com/ewasm/design/blob/master/eth_interface.md#call)
|
||||
// requires this operation to be U128, which is incompatible with the EVM version that expects
|
||||
// a u256.
|
||||
value := big.NewInt(0).SetBytes(readSize(p, valueOffset, u128Len))
|
||||
|
||||
if value.Cmp(big.NewInt(0)) != 0 {
|
||||
in.gasAccounting(GasCostCallValue)
|
||||
gas += GasCostCallStipend
|
||||
}
|
||||
|
||||
// Get the arguments.
|
||||
// TODO check the need for callvalue (seems not, a lot of that stuff is
|
||||
// already accounted for in the functions that I already called - need to
|
||||
// refactor all that)
|
||||
input := readSize(p, dataOffset, int(dataLength))
|
||||
|
||||
snapshot := in.StateDB.Snapshot()
|
||||
|
||||
// Check that there is enough balance to transfer the value
|
||||
if in.StateDB.GetBalance(addr).Cmp(value) < 0 {
|
||||
fmt.Printf("Not enough balance: wanted to use %v, got %v\n", value, in.StateDB.GetBalance(addr))
|
||||
return ErrEEICallFailure
|
||||
}
|
||||
|
||||
// TODO tracing
|
||||
// TODO check that EIP-150 is respected
|
||||
|
||||
// Load the contract code in a new VM structure
|
||||
targetContract := NewContract(contract.caller, AccountRef(contract.Address()), value, uint64(gas))
|
||||
code := in.StateDB.GetCode(addr)
|
||||
targetContract.SetCallCode(&addr, in.StateDB.GetCodeHash(addr), code)
|
||||
|
||||
return callCommon(in, contract, targetContract, input, value, snapshot, gas, false)
|
||||
}
|
||||
|
||||
func callDelegate(p *exec.Process, in *InterpreterEWASM, gas int64, addressOffset int32, dataOffset int32, dataLength int32) int32 {
|
||||
in.gasAccounting(GasCostCall)
|
||||
|
||||
contract := in.contract
|
||||
|
||||
// Get the address of the contract to call
|
||||
addr := common.BytesToAddress(readSize(p, addressOffset, common.AddressLength))
|
||||
|
||||
// Get the value. The [spec](https://github.com/ewasm/design/blob/master/eth_interface.md#call)
|
||||
// requires this operation to be U128, which is incompatible with the EVM version that expects
|
||||
// a u256.
|
||||
value := contract.value
|
||||
|
||||
if value.Cmp(big.NewInt(0)) != 0 {
|
||||
in.gasAccounting(GasCostCallValue)
|
||||
gas += GasCostCallStipend
|
||||
}
|
||||
|
||||
// Get the arguments.
|
||||
// TODO check the need for callvalue (seems not, a lot of that stuff is
|
||||
// already accounted for in the functions that I already called - need to
|
||||
// refactor all that)
|
||||
input := readSize(p, dataOffset, int(dataLength))
|
||||
|
||||
snapshot := in.StateDB.Snapshot()
|
||||
|
||||
// Check that there is enough balance to transfer the value
|
||||
if in.StateDB.GetBalance(addr).Cmp(value) < 0 {
|
||||
fmt.Printf("Not enough balance: wanted to use %v, got %v\n", value, in.StateDB.GetBalance(addr))
|
||||
return ErrEEICallFailure
|
||||
}
|
||||
|
||||
// TODO tracing
|
||||
// TODO check that EIP-150 is respected
|
||||
|
||||
// Load the contract code in a new VM structure
|
||||
targetContract := NewContract(AccountRef(contract.Address()), AccountRef(contract.Address()), value, uint64(gas))
|
||||
code := in.StateDB.GetCode(addr)
|
||||
caddr := contract.Address()
|
||||
targetContract.SetCallCode(&caddr, in.StateDB.GetCodeHash(addr), code)
|
||||
|
||||
return callCommon(in, contract, targetContract, input, value, snapshot, gas, false)
|
||||
}
|
||||
|
||||
func callStatic(p *exec.Process, in *InterpreterEWASM, gas int64, addressOffset int32, dataOffset int32, dataLength int32) int32 {
|
||||
in.gasAccounting(GasCostCall)
|
||||
|
||||
contract := in.contract
|
||||
|
||||
// Get the address of the contract to call
|
||||
addr := common.BytesToAddress(readSize(p, addressOffset, common.AddressLength))
|
||||
|
||||
value := big.NewInt(0)
|
||||
|
||||
// Get the arguments.
|
||||
// TODO check the need for callvalue (seems not, a lot of that stuff is
|
||||
// already accounted for in the functions that I already called - need to
|
||||
// refactor all that)
|
||||
input := readSize(p, dataOffset, int(dataLength))
|
||||
|
||||
snapshot := in.StateDB.Snapshot()
|
||||
|
||||
// Check that the contract exists
|
||||
if !in.StateDB.Exist(addr) {
|
||||
// TODO check that no new account creation stuff is required
|
||||
in.StateDB.CreateAccount(addr)
|
||||
}
|
||||
|
||||
// Check that there is enough balance to transfer the value
|
||||
if in.StateDB.GetBalance(addr).Cmp(value) < 0 {
|
||||
fmt.Printf("Not enough balance: wanted to use %v, got %v\n", value, in.StateDB.GetBalance(addr))
|
||||
in.contract.Gas += GasCostCallStipend
|
||||
return ErrEEICallFailure
|
||||
}
|
||||
|
||||
// TODO tracing
|
||||
// TODO check that EIP-150 is respected
|
||||
|
||||
// Add amount to recipient
|
||||
in.evm.Transfer(in.StateDB, contract.Address(), addr, value)
|
||||
|
||||
// Load the contract code in a new VM structure
|
||||
targetContract := NewContract(contract, AccountRef(addr), value, uint64(gas))
|
||||
code := in.StateDB.GetCode(addr)
|
||||
targetContract.SetCallCode(&addr, in.StateDB.GetCodeHash(addr), code)
|
||||
|
||||
return callCommon(in, contract, targetContract, input, value, snapshot, gas, true)
|
||||
}
|
||||
|
||||
func storageStore(p *exec.Process, interpreter *InterpreterEWASM, pathOffset int32, valueOffset int32) {
|
||||
loc := common.BytesToHash(readU256(p, pathOffset))
|
||||
val := common.BytesToHash(readU256(p, valueOffset))
|
||||
|
||||
fmt.Println(val, loc)
|
||||
nonZeroBytes := 0
|
||||
for _, b := range val.Bytes() {
|
||||
if b != 0 {
|
||||
nonZeroBytes++
|
||||
}
|
||||
}
|
||||
|
||||
oldValue := interpreter.StateDB.GetState(interpreter.contract.Address(), loc)
|
||||
oldNonZeroBytes := 0
|
||||
for _, b := range oldValue.Bytes() {
|
||||
if b != 0 {
|
||||
oldNonZeroBytes++
|
||||
}
|
||||
}
|
||||
|
||||
if (nonZeroBytes > 0 && oldNonZeroBytes != nonZeroBytes) || (oldNonZeroBytes != 0 && nonZeroBytes == 0) {
|
||||
interpreter.gasAccounting(GasCostSSet)
|
||||
} else {
|
||||
// Refund for setting one value to 0 or if the "zeroness" remains
|
||||
// unchanged.
|
||||
interpreter.gasAccounting(GasCostSReset)
|
||||
}
|
||||
|
||||
interpreter.StateDB.SetState(interpreter.contract.Address(), loc, val)
|
||||
}
|
||||
|
||||
func storageLoad(p *exec.Process, interpreter *InterpreterEWASM, pathOffset int32, resultOffset int32) {
|
||||
interpreter.gasAccounting(interpreter.gasTable.SLoad)
|
||||
loc := common.BytesToHash(readU256(p, pathOffset))
|
||||
valBytes := interpreter.StateDB.GetState(interpreter.contract.Address(), loc).Bytes()
|
||||
p.WriteAt(valBytes, int64(resultOffset))
|
||||
}
|
||||
|
||||
func getCaller(p *exec.Process, in *InterpreterEWASM, resultOffset int32) {
|
||||
callerAddress := in.contract.CallerAddress
|
||||
in.gasAccounting(GasCostBase)
|
||||
p.WriteAt(callerAddress.Bytes(), int64(resultOffset))
|
||||
}
|
||||
|
||||
func getCallValue(p *exec.Process, in *InterpreterEWASM, resultOffset int32) {
|
||||
in.gasAccounting(GasCostBase)
|
||||
p.WriteAt(swapEndian(in.contract.Value().Bytes()), int64(resultOffset))
|
||||
}
|
||||
|
||||
func codeCopy(p *exec.Process, in *InterpreterEWASM, resultOffset int32, codeOffset int32, length int32) {
|
||||
in.gasAccounting(GasCostVeryLow + GasCostCopy*(uint64(length+31)>>5))
|
||||
code := in.contract.Code
|
||||
p.WriteAt(code[codeOffset:codeOffset+length], int64(resultOffset))
|
||||
}
|
||||
|
||||
func getCodeSize(p *exec.Process, in *InterpreterEWASM) int32 {
|
||||
in.gasAccounting(GasCostBase)
|
||||
code := in.StateDB.GetCode(*in.contract.CodeAddr)
|
||||
return int32(len(code))
|
||||
}
|
||||
|
||||
func getBlockCoinbase(p *exec.Process, in *InterpreterEWASM, resultOffset int32) {
|
||||
in.gasAccounting(GasCostBase)
|
||||
p.WriteAt(in.evm.Coinbase.Bytes(), int64(resultOffset))
|
||||
}
|
||||
|
||||
func create(p *exec.Process, in *InterpreterEWASM, valueOffset uint32, codeOffset uint32, length uint32, resultOffset uint32) int32 {
|
||||
in.gasAccounting(GasCostCreate)
|
||||
savedVM := in.vm
|
||||
savedContract := in.contract
|
||||
in.terminationType = TerminateInvalid
|
||||
|
||||
if int(codeOffset)+int(length) > len(in.vm.Memory()) {
|
||||
return ErrEEICallFailure
|
||||
}
|
||||
input := readSize(p, int32(codeOffset), int(length))
|
||||
|
||||
if (int(valueOffset) + u128Len) > len(in.vm.Memory()) {
|
||||
return ErrEEICallFailure
|
||||
}
|
||||
value := swapEndian(readSize(p, int32(valueOffset), u128Len))
|
||||
|
||||
// EIP150 says that the calling contract should keep 1/64th of the
|
||||
// leftover gas.
|
||||
gas := in.contract.Gas - in.contract.Gas/64
|
||||
in.gasAccounting(gas)
|
||||
|
||||
_, addr, gasLeft, _ := in.evm.Create(in.contract, input, gas, big.NewInt(0).SetBytes(value))
|
||||
|
||||
in.vm = savedVM
|
||||
in.contract = savedContract
|
||||
in.contract.Gas += gasLeft
|
||||
p.WriteAt(addr.Bytes(), int64(resultOffset))
|
||||
|
||||
switch in.terminationType {
|
||||
case TerminateFinish:
|
||||
return EEICallSuccess
|
||||
case TerminateRevert:
|
||||
return ErrEEICallRevert
|
||||
default:
|
||||
return ErrEEICallFailure
|
||||
}
|
||||
}
|
||||
|
||||
func getBlockDifficulty(p *exec.Process, in *InterpreterEWASM, resultOffset int32) {
|
||||
in.gasAccounting(GasCostBase)
|
||||
p.WriteAt(swapEndian(in.evm.Difficulty.Bytes()), int64(resultOffset))
|
||||
}
|
||||
|
||||
func externalCodeCopy(p *exec.Process, in *InterpreterEWASM, addressOffset int32, resultOffset int32, codeOffset int32, length int32) {
|
||||
in.gasAccounting(in.gasTable.ExtcodeCopy + GasCostCopy*(uint64(length+31)>>5))
|
||||
addr := common.BytesToAddress(readSize(p, addressOffset, common.AddressLength))
|
||||
code := in.StateDB.GetCode(addr)
|
||||
p.WriteAt(code[codeOffset:codeOffset+length], int64(resultOffset))
|
||||
}
|
||||
|
||||
func getExternalCodeSize(p *exec.Process, in *InterpreterEWASM, addressOffset int32) int32 {
|
||||
in.gasAccounting(in.gasTable.ExtcodeSize)
|
||||
addr := common.BytesToAddress(readSize(p, addressOffset, common.AddressLength))
|
||||
code := in.StateDB.GetCode(addr)
|
||||
return int32(len(code))
|
||||
}
|
||||
|
||||
func getGasLeft(p *exec.Process, in *InterpreterEWASM) int64 {
|
||||
in.gasAccounting(GasCostBase)
|
||||
return int64(in.contract.Gas)
|
||||
}
|
||||
|
||||
func getBlockGasLimit(p *exec.Process, in *InterpreterEWASM) int64 {
|
||||
in.gasAccounting(GasCostBase)
|
||||
return int64(in.evm.GasLimit)
|
||||
}
|
||||
|
||||
func getTxGasPrice(p *exec.Process, in *InterpreterEWASM, valueOffset int32) {
|
||||
in.gasAccounting(GasCostBase)
|
||||
p.WriteAt(in.evm.GasPrice.Bytes(), int64(valueOffset))
|
||||
}
|
||||
|
||||
// It would be nice to be able to use variadic functions to pass the number of topics,
|
||||
// however this imposes a change in wagon because the number of arguments is being
|
||||
// checked when calling a function.
|
||||
func log(p *exec.Process, in *InterpreterEWASM, dataOffset int32, length int32, numberOfTopics int32, topic1 int32, topic2 int32, topic3 int32, topic4 int32) {
|
||||
in.gasAccounting(GasCostLog + GasCostLogData*uint64(length) + uint64(numberOfTopics)*GasCostLogTopic)
|
||||
|
||||
// TODO need to add some info about the memory boundary on wagon
|
||||
if uint64(len(in.vm.Memory())) <= uint64(length)+uint64(dataOffset) {
|
||||
panic("out of memory")
|
||||
}
|
||||
data := readSize(p, dataOffset, int(uint32(length)))
|
||||
topics := make([]common.Hash, numberOfTopics)
|
||||
|
||||
if numberOfTopics > 4 || numberOfTopics < 0 {
|
||||
in.terminationType = TerminateInvalid
|
||||
p.Terminate()
|
||||
}
|
||||
|
||||
// Variadic functions FTW
|
||||
if numberOfTopics > 0 {
|
||||
if uint64(len(in.vm.Memory())) <= uint64(topic1) {
|
||||
panic("out of memory")
|
||||
}
|
||||
topics[0] = common.BigToHash(big.NewInt(0).SetBytes(readU256(p, topic1)))
|
||||
}
|
||||
if numberOfTopics > 1 {
|
||||
if uint64(len(in.vm.Memory())) <= uint64(topic2) {
|
||||
panic("out of memory")
|
||||
}
|
||||
topics[1] = common.BigToHash(big.NewInt(0).SetBytes(readU256(p, topic2)))
|
||||
}
|
||||
if numberOfTopics > 2 {
|
||||
if uint64(len(in.vm.Memory())) <= uint64(topic3) {
|
||||
panic("out of memory")
|
||||
}
|
||||
topics[2] = common.BigToHash(big.NewInt(0).SetBytes(readU256(p, topic3)))
|
||||
}
|
||||
if numberOfTopics > 3 {
|
||||
if uint64(len(in.vm.Memory())) <= uint64(topic3) {
|
||||
panic("out of memory")
|
||||
}
|
||||
topics[3] = common.BigToHash(big.NewInt(0).SetBytes(readU256(p, topic4)))
|
||||
}
|
||||
|
||||
in.StateDB.AddLog(&types.Log{
|
||||
Address: in.contract.Address(),
|
||||
Topics: topics,
|
||||
Data: data,
|
||||
BlockNumber: in.evm.BlockNumber.Uint64(),
|
||||
})
|
||||
}
|
||||
|
||||
func getBlockNumber(p *exec.Process, in *InterpreterEWASM) int64 {
|
||||
in.gasAccounting(GasCostBase)
|
||||
return in.evm.BlockNumber.Int64()
|
||||
}
|
||||
|
||||
func getTxOrigin(p *exec.Process, in *InterpreterEWASM, resultOffset int32) {
|
||||
in.gasAccounting(GasCostBase)
|
||||
p.WriteAt(in.evm.Origin.Big().Bytes(), int64(resultOffset))
|
||||
}
|
||||
|
||||
func unWindContract(p *exec.Process, in *InterpreterEWASM, dataOffset int32, length int32) {
|
||||
in.returnData = make([]byte, length)
|
||||
p.ReadAt(in.returnData, int64(dataOffset))
|
||||
}
|
||||
|
||||
func finish(p *exec.Process, in *InterpreterEWASM, dataOffset int32, length int32) {
|
||||
unWindContract(p, in, dataOffset, length)
|
||||
|
||||
in.terminationType = TerminateFinish
|
||||
p.Terminate()
|
||||
}
|
||||
|
||||
func revert(p *exec.Process, in *InterpreterEWASM, dataOffset int32, length int32) {
|
||||
unWindContract(p, in, dataOffset, length)
|
||||
|
||||
in.terminationType = TerminateRevert
|
||||
p.Terminate()
|
||||
}
|
||||
|
||||
func getReturnDataSize(p *exec.Process, in *InterpreterEWASM) int32 {
|
||||
in.gasAccounting(GasCostBase)
|
||||
return int32(len(in.returnData))
|
||||
}
|
||||
|
||||
func returnDataCopy(p *exec.Process, in *InterpreterEWASM, resultOffset int32, dataOffset int32, length int32) {
|
||||
in.gasAccounting(GasCostVeryLow + GasCostCopy*(uint64(length+31)>>5))
|
||||
p.WriteAt(in.returnData[dataOffset:dataOffset+length], int64(resultOffset))
|
||||
}
|
||||
|
||||
func selfDestruct(p *exec.Process, in *InterpreterEWASM, addressOffset int32) {
|
||||
contract := in.contract
|
||||
mem := in.vm.Memory()
|
||||
|
||||
balance := in.StateDB.GetBalance(contract.Address())
|
||||
|
||||
addr := common.BytesToAddress(mem[addressOffset : addressOffset+common.AddressLength])
|
||||
in.StateDB.AddBalance(addr, balance)
|
||||
|
||||
totalGas := in.gasTable.Suicide
|
||||
// If the destination address doesn't exist, add the account creation costs
|
||||
if in.StateDB.Empty(addr) && balance.Sign() != 0 {
|
||||
totalGas += in.gasTable.CreateBySuicide
|
||||
}
|
||||
in.gasAccounting(totalGas)
|
||||
|
||||
in.StateDB.Suicide(contract.Address())
|
||||
|
||||
// Same as for `revert` and `return`, I need to forcefuly terminate
|
||||
// the execution of the contract.
|
||||
in.terminationType = TerminateSuicide
|
||||
p.Terminate()
|
||||
}
|
||||
|
||||
func getBlockTimestamp(p *exec.Process, in *InterpreterEWASM) int64 {
|
||||
in.gasAccounting(GasCostBase)
|
||||
return in.evm.Time.Int64()
|
||||
}
|
||||
119
core/vm/ewasm_linker.go
Normal file
119
core/vm/ewasm_linker.go
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package vm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-interpreter/wagon/wasm"
|
||||
)
|
||||
|
||||
var eeiFunctionList = []string{
|
||||
"useGas",
|
||||
"getAddress",
|
||||
"getExternalBalance",
|
||||
"getBlockHash",
|
||||
"call",
|
||||
"callDataCopy",
|
||||
"getCallDataSize",
|
||||
"callCode",
|
||||
"callDelegate",
|
||||
"callStatic",
|
||||
"storageStore",
|
||||
"storageLoad",
|
||||
"getCaller",
|
||||
"getCallValue",
|
||||
"codeCopy",
|
||||
"getCodeSize",
|
||||
"getBlockCoinbase",
|
||||
"create",
|
||||
"getBlockDifficulty",
|
||||
"externalCodeCopy",
|
||||
"getExternalCodeSize",
|
||||
"getGasLeft",
|
||||
"getBlockGasLimit",
|
||||
"getTxGasPrice",
|
||||
"log",
|
||||
"getBlockNumber",
|
||||
"getTxOrigin",
|
||||
"finish",
|
||||
"revert",
|
||||
"getReturnDataSize",
|
||||
"returnDataCopy",
|
||||
"selfDestruct",
|
||||
"getBlockTimestamp",
|
||||
}
|
||||
|
||||
var debugFunctionList = []string{
|
||||
"printMemHex",
|
||||
"printStorageHex",
|
||||
}
|
||||
|
||||
// ModuleResolver matches all EEI functions to native go functions
|
||||
func ModuleResolver(interpreter *InterpreterEWASM, name string) (*wasm.Module, error) {
|
||||
if name == "debug" {
|
||||
debugModule := wasm.NewModule()
|
||||
debugModule.Types = eeiTypes
|
||||
debugModule.FunctionIndexSpace = getDebugFuncs(interpreter)
|
||||
entries := make(map[string]wasm.ExportEntry)
|
||||
for idx, name := range debugFunctionList {
|
||||
entries[name] = wasm.ExportEntry{
|
||||
FieldStr: name,
|
||||
Kind: wasm.ExternalFunction,
|
||||
Index: uint32(idx),
|
||||
}
|
||||
}
|
||||
debugModule.Export = &wasm.SectionExports{
|
||||
Entries: entries,
|
||||
}
|
||||
return debugModule, nil
|
||||
}
|
||||
|
||||
if name != "env" && name != "ethereum" {
|
||||
return nil, fmt.Errorf("Unknown module name: %s", name)
|
||||
}
|
||||
|
||||
m := wasm.NewModule()
|
||||
m.Types = eeiTypes
|
||||
m.FunctionIndexSpace = eeiFuncs(interpreter)
|
||||
|
||||
entries := make(map[string]wasm.ExportEntry)
|
||||
|
||||
for idx, name := range eeiFunctionList {
|
||||
entries[name] = wasm.ExportEntry{
|
||||
FieldStr: name,
|
||||
Kind: wasm.ExternalFunction,
|
||||
Index: uint32(idx),
|
||||
}
|
||||
}
|
||||
|
||||
m.Export = &wasm.SectionExports{
|
||||
Entries: entries,
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// WrappedModuleResolver returns a module resolver function that whose
|
||||
// EEI functions are closure-bound to a given interpreter.
|
||||
// This is the first step to closure hell, the plan is to improve PR #59
|
||||
// in wagon to be able to pass some context.
|
||||
func WrappedModuleResolver(in *InterpreterEWASM) wasm.ResolveFunc {
|
||||
return func(name string) (*wasm.Module, error) {
|
||||
return ModuleResolver(in, name)
|
||||
}
|
||||
}
|
||||
|
|
@ -177,7 +177,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine, config.MinerRecommit, config.MinerGasFloor, config.MinerGasCeil, eth.isLocalBlock)
|
||||
eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine, config.MinerRecommit, config.MinerGasFloor, config.MinerGasCeil)
|
||||
eth.miner.SetExtra(makeExtraData(config.MinerExtraData))
|
||||
|
||||
eth.APIBackend = &EthAPIBackend{eth, nil}
|
||||
|
|
@ -334,60 +334,6 @@ func (s *Ethereum) Etherbase() (eb common.Address, err error) {
|
|||
return common.Address{}, fmt.Errorf("etherbase must be explicitly specified")
|
||||
}
|
||||
|
||||
// isLocalBlock checks whether the specified block is mined
|
||||
// by local miner accounts.
|
||||
//
|
||||
// We regard two types of accounts as local miner account: etherbase
|
||||
// and accounts specified via `txpool.locals` flag.
|
||||
func (s *Ethereum) isLocalBlock(block *types.Block) bool {
|
||||
author, err := s.engine.Author(block.Header())
|
||||
if err != nil {
|
||||
log.Warn("Failed to retrieve block author", "number", block.NumberU64(), "hash", block.Hash(), "err", err)
|
||||
return false
|
||||
}
|
||||
// Check whether the given address is etherbase.
|
||||
s.lock.RLock()
|
||||
etherbase := s.etherbase
|
||||
s.lock.RUnlock()
|
||||
if author == etherbase {
|
||||
return true
|
||||
}
|
||||
// Check whether the given address is specified by `txpool.local`
|
||||
// CLI flag.
|
||||
for _, account := range s.config.TxPool.Locals {
|
||||
if account == author {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// shouldPreserve checks whether we should preserve the given block
|
||||
// during the chain reorg depending on whether the author of block
|
||||
// is a local account.
|
||||
func (s *Ethereum) shouldPreserve(block *types.Block) bool {
|
||||
// The reason we need to disable the self-reorg preserving for clique
|
||||
// is it can be probable to introduce a deadlock.
|
||||
//
|
||||
// e.g. If there are 7 available signers
|
||||
//
|
||||
// r1 A
|
||||
// r2 B
|
||||
// r3 C
|
||||
// r4 D
|
||||
// r5 A [X] F G
|
||||
// r6 [X]
|
||||
//
|
||||
// In the round5, the inturn signer E is offline, so the worst case
|
||||
// is A, F and G sign the block of round5 and reject the block of opponents
|
||||
// and in the round6, the last available signer B is offline, the whole
|
||||
// network is stuck.
|
||||
if _, ok := s.engine.(*clique.Clique); ok {
|
||||
return false
|
||||
}
|
||||
return s.isLocalBlock(block)
|
||||
}
|
||||
|
||||
// SetEtherbase sets the mining reward address.
|
||||
func (s *Ethereum) SetEtherbase(etherbase common.Address) {
|
||||
s.lock.Lock()
|
||||
|
|
@ -420,7 +366,7 @@ func (s *Ethereum) StartMining(threads int) error {
|
|||
s.lock.RUnlock()
|
||||
s.txPool.SetGasPrice(price)
|
||||
|
||||
// Configure the local mining address
|
||||
// Configure the local mining addess
|
||||
eb, err := s.Etherbase()
|
||||
if err != nil {
|
||||
log.Error("Cannot start mining without etherbase", "err", err)
|
||||
|
|
|
|||
46
tests/ewasm_test.go
Normal file
46
tests/ewasm_test.go
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package tests
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
)
|
||||
|
||||
func TestEWASMVM(t *testing.T) {
|
||||
t.Parallel()
|
||||
st := new(testMatcher)
|
||||
st.walk(t, stateTestDir+"/stEWASMTests", func(t *testing.T, name string, test *StateTest) {
|
||||
for _, subtest := range test.Subtests() {
|
||||
subtest := subtest
|
||||
key := fmt.Sprintf("%s/%d", subtest.Fork, subtest.Index)
|
||||
name := name + "/" + key
|
||||
t.Run(key, func(t *testing.T) {
|
||||
if subtest.Fork == "Constantinople" {
|
||||
t.Skip("constantinople not supported yet")
|
||||
}
|
||||
withTrace(t, test.gasLimit(subtest), func(vmconfig vm.Config) error {
|
||||
vmconfig.EnableWagon = true
|
||||
_, err := test.Run(subtest, vmconfig)
|
||||
return st.checkFailure(t, name, err)
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -66,7 +66,7 @@ func TestState(t *testing.T) {
|
|||
}
|
||||
|
||||
// Transactions with gasLimit above this value will not get a VM trace on failure.
|
||||
const traceErrorLimit = 400000
|
||||
const traceErrorLimit = 5242880
|
||||
|
||||
func withTrace(t *testing.T, gasLimit uint64, test func(vm.Config) error) {
|
||||
err := test(vm.Config{})
|
||||
|
|
@ -75,7 +75,7 @@ func withTrace(t *testing.T, gasLimit uint64, test func(vm.Config) error) {
|
|||
}
|
||||
t.Error(err)
|
||||
if gasLimit > traceErrorLimit {
|
||||
t.Log("gas limit too high for EVM trace")
|
||||
t.Log(fmt.Sprintf("gas limit too high for EVM trace %d > %d", gasLimit, traceErrorLimit))
|
||||
return
|
||||
}
|
||||
tracer := vm.NewStructLogger(nil)
|
||||
|
|
|
|||
24
vendor/github.com/go-interpreter/wagon/LICENSE
generated
vendored
Normal file
24
vendor/github.com/go-interpreter/wagon/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
Copyright ©2017 The go-interpreter Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the go-interpreter project nor the names of its authors and
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
28
vendor/github.com/go-interpreter/wagon/README.md
generated
vendored
Normal file
28
vendor/github.com/go-interpreter/wagon/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
wagon
|
||||
=====
|
||||
|
||||
[](https://travis-ci.org/go-interpreter/wagon)
|
||||
[](https://codecov.io/gh/go-interpreter/wagon)
|
||||
[](https://godoc.org/github.com/go-interpreter/wagon)
|
||||
|
||||
`wagon` is a [WebAssembly](http://webassembly.org)-based interpreter in [Go](https://golang.org), for [Go](https://golang.org).
|
||||
|
||||
**NOTE:** `wagon` requires `Go >= 1.9.x`.
|
||||
|
||||
## Purpose
|
||||
|
||||
`wagon` aims to provide tools (executables+libraries) to:
|
||||
|
||||
- decode `wasm` binary files
|
||||
- load and execute `wasm` modules' bytecode.
|
||||
|
||||
`wagon` doesn't concern itself with the production of the `wasm` binary files;
|
||||
these files should be produced with another tool (such as [wabt](https://github.com/WebAssembly/wabt) or [binaryen](https://github.com/WebAssembly/binaryen).)
|
||||
`wagon` *may* provide a utility to produce `wasm` files from `wast` or `wat` files (and vice versa.)
|
||||
|
||||
The primary goal of `wagon` is to provide the building blocks to be able to build an interpreter for Go code, that could be embedded in Jupyter or any Go program.
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
See the [CONTRIBUTING](https://github.com/go-interpreter/license/blob/master/CONTRIBUTE.md) guide for pointers on how to contribute to `go-interpreter` and `wagon`.
|
||||
62
vendor/github.com/go-interpreter/wagon/disasm/asm.go
generated
vendored
Normal file
62
vendor/github.com/go-interpreter/wagon/disasm/asm.go
generated
vendored
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
// Copyright 2018 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package disasm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"math"
|
||||
|
||||
"github.com/go-interpreter/wagon/wasm/leb128"
|
||||
ops "github.com/go-interpreter/wagon/wasm/operators"
|
||||
)
|
||||
|
||||
// Assemble encodes a set of instructions into binary representation.
|
||||
func Assemble(instr []Instr) ([]byte, error) {
|
||||
body := new(bytes.Buffer)
|
||||
for _, ins := range instr {
|
||||
body.WriteByte(ins.Op.Code)
|
||||
switch op := ins.Op.Code; op {
|
||||
case ops.Block, ops.Loop, ops.If:
|
||||
leb128.WriteVarint64(body, int64(ins.Block.Signature))
|
||||
case ops.Br, ops.BrIf:
|
||||
leb128.WriteVarUint32(body, ins.Immediates[0].(uint32))
|
||||
case ops.BrTable:
|
||||
cnt := ins.Immediates[0].(uint32)
|
||||
leb128.WriteVarUint32(body, cnt)
|
||||
for i := uint32(0); i < cnt; i++ {
|
||||
leb128.WriteVarUint32(body, ins.Immediates[i+1].(uint32))
|
||||
}
|
||||
leb128.WriteVarUint32(body, ins.Immediates[1+cnt].(uint32))
|
||||
case ops.Call, ops.CallIndirect:
|
||||
leb128.WriteVarUint32(body, ins.Immediates[0].(uint32))
|
||||
if op == ops.CallIndirect {
|
||||
leb128.WriteVarUint32(body, ins.Immediates[1].(uint32))
|
||||
}
|
||||
case ops.GetLocal, ops.SetLocal, ops.TeeLocal, ops.GetGlobal, ops.SetGlobal:
|
||||
leb128.WriteVarUint32(body, ins.Immediates[0].(uint32))
|
||||
case ops.I32Const:
|
||||
leb128.WriteVarint64(body, int64(ins.Immediates[0].(int32)))
|
||||
case ops.I64Const:
|
||||
leb128.WriteVarint64(body, ins.Immediates[0].(int64))
|
||||
case ops.F32Const:
|
||||
f := ins.Immediates[0].(float32)
|
||||
var b [4]byte
|
||||
binary.LittleEndian.PutUint32(b[:], math.Float32bits(f))
|
||||
body.Write(b[:])
|
||||
case ops.F64Const:
|
||||
f := ins.Immediates[0].(float64)
|
||||
var b [8]byte
|
||||
binary.LittleEndian.PutUint64(b[:], math.Float64bits(f))
|
||||
body.Write(b[:])
|
||||
case ops.I32Load, ops.I64Load, ops.F32Load, ops.F64Load, ops.I32Load8s, ops.I32Load8u, ops.I32Load16s, ops.I32Load16u, ops.I64Load8s, ops.I64Load8u, ops.I64Load16s, ops.I64Load16u, ops.I64Load32s, ops.I64Load32u, ops.I32Store, ops.I64Store, ops.F32Store, ops.F64Store, ops.I32Store8, ops.I32Store16, ops.I64Store8, ops.I64Store16, ops.I64Store32:
|
||||
leb128.WriteVarUint32(body, ins.Immediates[0].(uint32))
|
||||
leb128.WriteVarUint32(body, ins.Immediates[1].(uint32))
|
||||
case ops.CurrentMemory, ops.GrowMemory:
|
||||
leb128.WriteVarUint32(body, uint32(ins.Immediates[0].(uint8)))
|
||||
}
|
||||
}
|
||||
return body.Bytes(), nil
|
||||
}
|
||||
464
vendor/github.com/go-interpreter/wagon/disasm/disasm.go
generated
vendored
Normal file
464
vendor/github.com/go-interpreter/wagon/disasm/disasm.go
generated
vendored
Normal file
|
|
@ -0,0 +1,464 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package disasm provides functions for disassembling WebAssembly bytecode.
|
||||
package disasm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"math"
|
||||
|
||||
"github.com/go-interpreter/wagon/internal/stack"
|
||||
"github.com/go-interpreter/wagon/wasm"
|
||||
"github.com/go-interpreter/wagon/wasm/leb128"
|
||||
ops "github.com/go-interpreter/wagon/wasm/operators"
|
||||
)
|
||||
|
||||
// Instr describes an instruction, consisting of an operator, with its
|
||||
// appropriate immediate value(s).
|
||||
type Instr struct {
|
||||
Op ops.Op
|
||||
|
||||
// Immediates are arguments to an operator in the bytecode stream itself.
|
||||
// Valid value types are:
|
||||
// - (u)(int/float)(32/64)
|
||||
// - wasm.BlockType
|
||||
Immediates []interface{}
|
||||
NewStack *StackInfo // non-nil if the instruction creates or unwinds a stack.
|
||||
Block *BlockInfo // non-nil if the instruction starts or ends a new block.
|
||||
Unreachable bool // whether the operator can be reached during execution
|
||||
// IsReturn is true if executing this instruction will result in the
|
||||
// function returning. This is true for branches (br, br_if) to
|
||||
// the depth <max_relative_depth> + 1, or the return operator itself.
|
||||
// If true, NewStack for this instruction is nil.
|
||||
IsReturn bool
|
||||
// If the operator is br_table (ops.BrTable), this is a list of StackInfo
|
||||
// fields for each of the blocks/branches referenced by the operator.
|
||||
Branches []StackInfo
|
||||
}
|
||||
|
||||
// StackInfo stores details about a new stack created or unwinded by an instruction.
|
||||
type StackInfo struct {
|
||||
StackTopDiff int64 // The difference between the stack depths at the end of the block
|
||||
PreserveTop bool // Whether the value on the top of the stack should be preserved while unwinding
|
||||
IsReturn bool // Whether the unwind is equivalent to a return
|
||||
}
|
||||
|
||||
// BlockInfo stores details about a block created or ended by an instruction.
|
||||
type BlockInfo struct {
|
||||
Start bool // If true, this instruction starts a block. Else this instruction ends it.
|
||||
Signature wasm.BlockType // The block signature
|
||||
|
||||
// Indices to the accompanying control operator.
|
||||
// For 'if', this is the index to the 'else' operator.
|
||||
IfElseIndex int
|
||||
// For 'else', this is the index to the 'if' operator.
|
||||
ElseIfIndex int
|
||||
// The index to the `end' operator for if/else/loop/block.
|
||||
EndIndex int
|
||||
// For end, it is the index to the operator that starts the block.
|
||||
BlockStartIndex int
|
||||
}
|
||||
|
||||
// Disassembly is the result of disassembling a WebAssembly function.
|
||||
type Disassembly struct {
|
||||
Code []Instr
|
||||
MaxDepth int // The maximum stack depth that can be reached while executing this function
|
||||
}
|
||||
|
||||
func (d *Disassembly) checkMaxDepth(depth int) {
|
||||
if depth > d.MaxDepth {
|
||||
d.MaxDepth = depth
|
||||
}
|
||||
}
|
||||
|
||||
func pushPolymorphicOp(indexStack [][]int, index int) {
|
||||
indexStack[len(indexStack)-1] = append(indexStack[len(indexStack)-1], index)
|
||||
}
|
||||
|
||||
func isInstrReachable(indexStack [][]int) bool {
|
||||
return len(indexStack[len(indexStack)-1]) == 0
|
||||
}
|
||||
|
||||
var ErrStackUnderflow = errors.New("disasm: stack underflow")
|
||||
|
||||
// Disassemble disassembles the given function. It also takes the function's
|
||||
// parent module as an argument for locating any other functions referenced by
|
||||
// fn.
|
||||
func Disassemble(fn wasm.Function, module *wasm.Module) (*Disassembly, error) {
|
||||
code := fn.Body.Code
|
||||
reader := bytes.NewReader(code)
|
||||
disas := &Disassembly{}
|
||||
|
||||
// A stack of int arrays holding indices to instructions that make the stack
|
||||
// polymorphic. Each block has its corresponding array. We start with one
|
||||
// array for the root stack
|
||||
blockPolymorphicOps := [][]int{{}}
|
||||
// a stack of current execution stack depth values, so that the depth for each
|
||||
// stack is maintained indepepdently for calculating discard values
|
||||
stackDepths := &stack.Stack{}
|
||||
stackDepths.Push(0)
|
||||
blockIndices := &stack.Stack{} // a stack of indices to operators which start new blocks
|
||||
curIndex := 0
|
||||
var lastOpReturn bool
|
||||
|
||||
for {
|
||||
op, err := reader.ReadByte()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logger.Printf("stack top is %d", stackDepths.Top())
|
||||
|
||||
opStr, err := ops.New(op)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instr := Instr{
|
||||
Op: opStr,
|
||||
}
|
||||
if op == ops.End || op == ops.Else {
|
||||
// There are two possible cases here:
|
||||
// 1. The corresponding block/if/loop instruction
|
||||
// *is* reachable, and an instruction somewhere in this
|
||||
// block (and NOT in a nested block) makes the stack
|
||||
// polymorphic. In this case, this end/else is reachable.
|
||||
//
|
||||
// 2. The corresponding block/if/loop instruction
|
||||
// is *not* reachable, which makes this end/else unreachable
|
||||
// too.
|
||||
isUnreachable := blockIndices.Len() != len(blockPolymorphicOps)-1
|
||||
instr.Unreachable = isUnreachable
|
||||
} else {
|
||||
instr.Unreachable = !isInstrReachable(blockPolymorphicOps)
|
||||
}
|
||||
|
||||
logger.Printf("op: %s, unreachable: %v", opStr.Name, instr.Unreachable)
|
||||
if !opStr.Polymorphic && !instr.Unreachable {
|
||||
top := int(stackDepths.Top())
|
||||
top -= len(opStr.Args)
|
||||
stackDepths.SetTop(uint64(top))
|
||||
if top < -1 {
|
||||
return nil, ErrStackUnderflow
|
||||
}
|
||||
if opStr.Returns != wasm.ValueType(wasm.BlockTypeEmpty) {
|
||||
top++
|
||||
stackDepths.SetTop(uint64(top))
|
||||
}
|
||||
disas.checkMaxDepth(top)
|
||||
}
|
||||
|
||||
switch op {
|
||||
case ops.Unreachable:
|
||||
pushPolymorphicOp(blockPolymorphicOps, curIndex)
|
||||
case ops.Drop:
|
||||
if !instr.Unreachable {
|
||||
stackDepths.SetTop(stackDepths.Top() - 1)
|
||||
}
|
||||
case ops.Select:
|
||||
if !instr.Unreachable {
|
||||
stackDepths.SetTop(stackDepths.Top() - 2)
|
||||
}
|
||||
case ops.Return:
|
||||
if !instr.Unreachable {
|
||||
stackDepths.SetTop(stackDepths.Top() - uint64(len(fn.Sig.ReturnTypes)))
|
||||
}
|
||||
pushPolymorphicOp(blockPolymorphicOps, curIndex)
|
||||
lastOpReturn = true
|
||||
case ops.End, ops.Else:
|
||||
// The max depth reached while execing the current block
|
||||
curDepth := stackDepths.Top()
|
||||
blockStartIndex := blockIndices.Pop()
|
||||
blockSig := disas.Code[blockStartIndex].Block.Signature
|
||||
instr.Block = &BlockInfo{
|
||||
Start: false,
|
||||
Signature: blockSig,
|
||||
}
|
||||
if op == ops.End {
|
||||
instr.Block.BlockStartIndex = int(blockStartIndex)
|
||||
disas.Code[blockStartIndex].Block.EndIndex = curIndex
|
||||
} else { // ops.Else
|
||||
instr.Block.ElseIfIndex = int(blockStartIndex)
|
||||
disas.Code[blockStartIndex].Block.IfElseIndex = int(blockStartIndex)
|
||||
}
|
||||
|
||||
// The max depth reached while execing the last block
|
||||
// If the signature of the current block is not empty,
|
||||
// this will be incremented.
|
||||
// Same with ops.Br/BrIf, we subtract 2 instead of 1
|
||||
// to get the depth of the *parent* block of the branch
|
||||
// we want to take.
|
||||
prevDepthIndex := stackDepths.Len() - 2
|
||||
prevDepth := stackDepths.Get(prevDepthIndex)
|
||||
|
||||
if op != ops.Else && blockSig != wasm.BlockTypeEmpty && !instr.Unreachable {
|
||||
stackDepths.Set(prevDepthIndex, prevDepth+1)
|
||||
disas.checkMaxDepth(int(stackDepths.Get(prevDepthIndex)))
|
||||
}
|
||||
|
||||
if !lastOpReturn {
|
||||
elemsDiscard := int(curDepth) - int(prevDepth)
|
||||
if elemsDiscard < -1 {
|
||||
return nil, ErrStackUnderflow
|
||||
}
|
||||
instr.NewStack = &StackInfo{
|
||||
StackTopDiff: int64(elemsDiscard),
|
||||
PreserveTop: blockSig != wasm.BlockTypeEmpty,
|
||||
}
|
||||
logger.Printf("discard %d elements, preserve top: %v", elemsDiscard, instr.NewStack.PreserveTop)
|
||||
} else {
|
||||
instr.NewStack = &StackInfo{}
|
||||
}
|
||||
|
||||
logger.Printf("setting new stack for %s block (%d)", disas.Code[blockStartIndex].Op.Name, blockStartIndex)
|
||||
disas.Code[blockStartIndex].NewStack = instr.NewStack
|
||||
if !instr.Unreachable {
|
||||
blockPolymorphicOps = blockPolymorphicOps[:len(blockPolymorphicOps)-1]
|
||||
}
|
||||
|
||||
stackDepths.Pop()
|
||||
if op == ops.Else {
|
||||
stackDepths.Push(0)
|
||||
blockIndices.Push(uint64(curIndex))
|
||||
if !instr.Unreachable {
|
||||
blockPolymorphicOps = append(blockPolymorphicOps, []int{})
|
||||
}
|
||||
}
|
||||
|
||||
case ops.Block, ops.Loop, ops.If:
|
||||
sig, err := leb128.ReadVarint32(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logger.Printf("if, depth is %d", stackDepths.Top())
|
||||
stackDepths.Push(stackDepths.Top())
|
||||
// If this new block is unreachable, its
|
||||
// entire instruction sequence is unreachable
|
||||
// as well. To make sure that isInstrReachable
|
||||
// returns the correct value, we don't push a new
|
||||
// array to blockPolymorphicOps.
|
||||
if !instr.Unreachable {
|
||||
// Therefore, only push a new array if this instruction
|
||||
// is reachable.
|
||||
blockPolymorphicOps = append(blockPolymorphicOps, []int{})
|
||||
}
|
||||
instr.Block = &BlockInfo{
|
||||
Start: true,
|
||||
Signature: wasm.BlockType(sig),
|
||||
}
|
||||
|
||||
blockIndices.Push(uint64(curIndex))
|
||||
instr.Immediates = append(instr.Immediates, wasm.BlockType(sig))
|
||||
case ops.Br, ops.BrIf:
|
||||
depth, err := leb128.ReadVarUint32(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instr.Immediates = append(instr.Immediates, depth)
|
||||
|
||||
if int(depth) == blockIndices.Len() {
|
||||
instr.IsReturn = true
|
||||
} else {
|
||||
curDepth := stackDepths.Top()
|
||||
// whenever we take a branch, the stack is unwound
|
||||
// to the height of stack of its *parent* block, which
|
||||
// is why we subtract 2 instead of 1.
|
||||
// prevDepth holds the height of the stack when
|
||||
// the block that we branch to started.
|
||||
prevDepth := stackDepths.Get(stackDepths.Len() - 2 - int(depth))
|
||||
elemsDiscard := int(curDepth) - int(prevDepth)
|
||||
if elemsDiscard < 0 {
|
||||
return nil, ErrStackUnderflow
|
||||
}
|
||||
|
||||
// No need to subtract 2 here, we are getting the block
|
||||
// we need to branch to.
|
||||
index := blockIndices.Get(blockIndices.Len() - 1 - int(depth))
|
||||
instr.NewStack = &StackInfo{
|
||||
StackTopDiff: int64(elemsDiscard),
|
||||
PreserveTop: disas.Code[index].Block.Signature != wasm.BlockTypeEmpty,
|
||||
}
|
||||
}
|
||||
if op == ops.Br {
|
||||
pushPolymorphicOp(blockPolymorphicOps, curIndex)
|
||||
}
|
||||
|
||||
case ops.BrTable:
|
||||
if !instr.Unreachable {
|
||||
stackDepths.SetTop(stackDepths.Top() - 1)
|
||||
}
|
||||
|
||||
targetCount, err := leb128.ReadVarUint32(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instr.Immediates = append(instr.Immediates, targetCount)
|
||||
for i := uint32(0); i < targetCount; i++ {
|
||||
entry, err := leb128.ReadVarUint32(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instr.Immediates = append(instr.Immediates, entry)
|
||||
|
||||
var info StackInfo
|
||||
if int(entry) == blockIndices.Len() {
|
||||
info.IsReturn = true
|
||||
} else {
|
||||
curDepth := stackDepths.Top()
|
||||
branchDepth := stackDepths.Get(stackDepths.Len() - 2 - int(entry))
|
||||
elemsDiscard := int(curDepth) - int(branchDepth)
|
||||
logger.Printf("Curdepth %d branchDepth %d discard %d", curDepth, branchDepth, elemsDiscard)
|
||||
|
||||
if elemsDiscard < 0 {
|
||||
return nil, ErrStackUnderflow
|
||||
}
|
||||
index := blockIndices.Get(blockIndices.Len() - 1 - int(entry))
|
||||
info.StackTopDiff = int64(elemsDiscard)
|
||||
info.PreserveTop = disas.Code[index].Block.Signature != wasm.BlockTypeEmpty
|
||||
}
|
||||
instr.Branches = append(instr.Branches, info)
|
||||
}
|
||||
|
||||
defaultTarget, err := leb128.ReadVarUint32(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instr.Immediates = append(instr.Immediates, defaultTarget)
|
||||
|
||||
var info StackInfo
|
||||
if int(defaultTarget) == blockIndices.Len() {
|
||||
info.IsReturn = true
|
||||
} else {
|
||||
|
||||
curDepth := stackDepths.Top()
|
||||
branchDepth := stackDepths.Get(stackDepths.Len() - 2 - int(defaultTarget))
|
||||
elemsDiscard := int(curDepth) - int(branchDepth)
|
||||
|
||||
if elemsDiscard < 0 {
|
||||
return nil, ErrStackUnderflow
|
||||
}
|
||||
index := blockIndices.Get(blockIndices.Len() - 1 - int(defaultTarget))
|
||||
info.StackTopDiff = int64(elemsDiscard)
|
||||
info.PreserveTop = disas.Code[index].Block.Signature != wasm.BlockTypeEmpty
|
||||
}
|
||||
instr.Branches = append(instr.Branches, info)
|
||||
pushPolymorphicOp(blockPolymorphicOps, curIndex)
|
||||
case ops.Call, ops.CallIndirect:
|
||||
index, err := leb128.ReadVarUint32(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instr.Immediates = append(instr.Immediates, index)
|
||||
if op == ops.CallIndirect {
|
||||
reserved, err := leb128.ReadVarUint32(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instr.Immediates = append(instr.Immediates, reserved)
|
||||
}
|
||||
if !instr.Unreachable {
|
||||
var sig *wasm.FunctionSig
|
||||
top := int(stackDepths.Top())
|
||||
if op == ops.CallIndirect {
|
||||
if module.Types == nil {
|
||||
return nil, errors.New("missing types section")
|
||||
}
|
||||
sig = &module.Types.Entries[index]
|
||||
top--
|
||||
} else {
|
||||
sig = module.GetFunction(int(index)).Sig
|
||||
}
|
||||
top -= len(sig.ParamTypes)
|
||||
top += len(sig.ReturnTypes)
|
||||
stackDepths.SetTop(uint64(top))
|
||||
disas.checkMaxDepth(top)
|
||||
}
|
||||
case ops.GetLocal, ops.SetLocal, ops.TeeLocal, ops.GetGlobal, ops.SetGlobal:
|
||||
index, err := leb128.ReadVarUint32(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instr.Immediates = append(instr.Immediates, index)
|
||||
|
||||
if !instr.Unreachable {
|
||||
top := stackDepths.Top()
|
||||
switch op {
|
||||
case ops.GetLocal, ops.GetGlobal:
|
||||
top++
|
||||
stackDepths.SetTop(top)
|
||||
disas.checkMaxDepth(int(top))
|
||||
case ops.SetLocal, ops.SetGlobal:
|
||||
top--
|
||||
stackDepths.SetTop(top)
|
||||
case ops.TeeLocal:
|
||||
// stack remains unchanged for tee_local
|
||||
}
|
||||
}
|
||||
case ops.I32Const:
|
||||
i, err := leb128.ReadVarint32(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instr.Immediates = append(instr.Immediates, i)
|
||||
case ops.I64Const:
|
||||
i, err := leb128.ReadVarint64(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instr.Immediates = append(instr.Immediates, i)
|
||||
case ops.F32Const:
|
||||
var b [4]byte
|
||||
if _, err := io.ReadFull(reader, b[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
i := binary.LittleEndian.Uint32(b[:])
|
||||
instr.Immediates = append(instr.Immediates, math.Float32frombits(i))
|
||||
case ops.F64Const:
|
||||
var b [8]byte
|
||||
if _, err := io.ReadFull(reader, b[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
i := binary.LittleEndian.Uint64(b[:])
|
||||
instr.Immediates = append(instr.Immediates, math.Float64frombits(i))
|
||||
case ops.I32Load, ops.I64Load, ops.F32Load, ops.F64Load, ops.I32Load8s, ops.I32Load8u, ops.I32Load16s, ops.I32Load16u, ops.I64Load8s, ops.I64Load8u, ops.I64Load16s, ops.I64Load16u, ops.I64Load32s, ops.I64Load32u, ops.I32Store, ops.I64Store, ops.F32Store, ops.F64Store, ops.I32Store8, ops.I32Store16, ops.I64Store8, ops.I64Store16, ops.I64Store32:
|
||||
// read memory_immediate
|
||||
flags, err := leb128.ReadVarUint32(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instr.Immediates = append(instr.Immediates, flags)
|
||||
|
||||
offset, err := leb128.ReadVarUint32(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instr.Immediates = append(instr.Immediates, offset)
|
||||
case ops.CurrentMemory, ops.GrowMemory:
|
||||
res, err := leb128.ReadVarUint32(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instr.Immediates = append(instr.Immediates, uint8(res))
|
||||
}
|
||||
|
||||
if op != ops.Return {
|
||||
lastOpReturn = false
|
||||
}
|
||||
|
||||
disas.Code = append(disas.Code, instr)
|
||||
curIndex++
|
||||
}
|
||||
|
||||
if logging {
|
||||
for _, instr := range disas.Code {
|
||||
logger.Printf("%v %v", instr.Op.Name, instr.NewStack)
|
||||
}
|
||||
}
|
||||
|
||||
return disas, nil
|
||||
}
|
||||
33
vendor/github.com/go-interpreter/wagon/disasm/log.go
generated
vendored
Normal file
33
vendor/github.com/go-interpreter/wagon/disasm/log.go
generated
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package disasm
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
logger *log.Logger
|
||||
logging bool
|
||||
)
|
||||
|
||||
func SetDebugMode(l bool) {
|
||||
w := ioutil.Discard
|
||||
logging = l
|
||||
|
||||
if l {
|
||||
w = os.Stderr
|
||||
}
|
||||
|
||||
logger = log.New(w, "", log.Lshortfile)
|
||||
logger.SetFlags(log.Lshortfile)
|
||||
|
||||
}
|
||||
|
||||
func init() {
|
||||
SetDebugMode(false)
|
||||
}
|
||||
6
vendor/github.com/go-interpreter/wagon/doc.go
generated
vendored
Normal file
6
vendor/github.com/go-interpreter/wagon/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package wagon is a WebAssembly-based interpreter in Go, for Go.
|
||||
package wagon
|
||||
57
vendor/github.com/go-interpreter/wagon/exec/call.go
generated
vendored
Normal file
57
vendor/github.com/go-interpreter/wagon/exec/call.go
generated
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package exec
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
// ErrSignatureMismatch is the error value used while trapping the VM when
|
||||
// a signature mismatch between the table entry and the type entry is found
|
||||
// in a call_indirect operation.
|
||||
ErrSignatureMismatch = errors.New("exec: signature mismatch in call_indirect")
|
||||
// ErrUndefinedElementIndex is the error value used while trapping the VM when
|
||||
// an invalid index to the module's table space is used as an operand to
|
||||
// call_indirect
|
||||
ErrUndefinedElementIndex = errors.New("exec: undefined element index")
|
||||
)
|
||||
|
||||
func (vm *VM) call() {
|
||||
index := vm.fetchUint32()
|
||||
|
||||
vm.funcs[index].call(vm, int64(index))
|
||||
}
|
||||
|
||||
func (vm *VM) callIndirect() {
|
||||
index := vm.fetchUint32()
|
||||
fnExpect := vm.module.Types.Entries[index]
|
||||
_ = vm.fetchUint32() // reserved (https://github.com/WebAssembly/design/blob/27ac254c854994103c24834a994be16f74f54186/BinaryEncoding.md#call-operators-described-here)
|
||||
tableIndex := vm.popUint32()
|
||||
if int(tableIndex) >= len(vm.module.TableIndexSpace[0]) {
|
||||
panic(ErrUndefinedElementIndex)
|
||||
}
|
||||
elemIndex := vm.module.TableIndexSpace[0][tableIndex]
|
||||
fnActual := vm.module.FunctionIndexSpace[elemIndex]
|
||||
|
||||
if len(fnExpect.ParamTypes) != len(fnActual.Sig.ParamTypes) {
|
||||
panic(ErrSignatureMismatch)
|
||||
}
|
||||
if len(fnExpect.ReturnTypes) != len(fnActual.Sig.ReturnTypes) {
|
||||
panic(ErrSignatureMismatch)
|
||||
}
|
||||
|
||||
for i := range fnExpect.ParamTypes {
|
||||
if fnExpect.ParamTypes[i] != fnActual.Sig.ParamTypes[i] {
|
||||
panic(ErrSignatureMismatch)
|
||||
}
|
||||
}
|
||||
|
||||
for i := range fnExpect.ReturnTypes {
|
||||
if fnExpect.ReturnTypes[i] != fnActual.Sig.ReturnTypes[i] {
|
||||
panic(ErrSignatureMismatch)
|
||||
}
|
||||
}
|
||||
|
||||
vm.funcs[elemIndex].call(vm, int64(elemIndex))
|
||||
}
|
||||
21
vendor/github.com/go-interpreter/wagon/exec/const.go
generated
vendored
Normal file
21
vendor/github.com/go-interpreter/wagon/exec/const.go
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package exec
|
||||
|
||||
func (vm *VM) i32Const() {
|
||||
vm.pushUint32(vm.fetchUint32())
|
||||
}
|
||||
|
||||
func (vm *VM) i64Const() {
|
||||
vm.pushUint64(vm.fetchUint64())
|
||||
}
|
||||
|
||||
func (vm *VM) f32Const() {
|
||||
vm.pushFloat32(vm.fetchFloat32())
|
||||
}
|
||||
|
||||
func (vm *VM) f64Const() {
|
||||
vm.pushFloat64(vm.fetchFloat64())
|
||||
}
|
||||
17
vendor/github.com/go-interpreter/wagon/exec/control.go
generated
vendored
Normal file
17
vendor/github.com/go-interpreter/wagon/exec/control.go
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package exec
|
||||
|
||||
import "errors"
|
||||
|
||||
// ErrUnreachable is the error value used while trapping the VM when
|
||||
// an unreachable operator is reached during execution.
|
||||
var ErrUnreachable = errors.New("exec: reached unreachable")
|
||||
|
||||
func (vm *VM) unreachable() {
|
||||
panic(ErrUnreachable)
|
||||
}
|
||||
|
||||
func (vm *VM) nop() {}
|
||||
93
vendor/github.com/go-interpreter/wagon/exec/conv.go
generated
vendored
Normal file
93
vendor/github.com/go-interpreter/wagon/exec/conv.go
generated
vendored
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package exec
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
func (vm *VM) i32Wrapi64() {
|
||||
vm.pushUint32(uint32(vm.popUint64()))
|
||||
}
|
||||
|
||||
func (vm *VM) i32TruncSF32() {
|
||||
vm.pushInt32(int32(math.Trunc(float64(vm.popFloat32()))))
|
||||
}
|
||||
|
||||
func (vm *VM) i32TruncUF32() {
|
||||
vm.pushUint32(uint32(math.Trunc(float64(vm.popFloat32()))))
|
||||
}
|
||||
|
||||
func (vm *VM) i32TruncSF64() {
|
||||
vm.pushInt32(int32(math.Trunc(vm.popFloat64())))
|
||||
}
|
||||
|
||||
func (vm *VM) i32TruncUF64() {
|
||||
vm.pushUint32(uint32(math.Trunc(vm.popFloat64())))
|
||||
}
|
||||
|
||||
func (vm *VM) i64ExtendSI32() {
|
||||
vm.pushInt64(int64(vm.popInt32()))
|
||||
}
|
||||
|
||||
func (vm *VM) i64ExtendUI32() {
|
||||
vm.pushUint64(uint64(vm.popUint32()))
|
||||
}
|
||||
|
||||
func (vm *VM) i64TruncSF32() {
|
||||
vm.pushInt64(int64(math.Trunc(float64(vm.popFloat32()))))
|
||||
}
|
||||
|
||||
func (vm *VM) i64TruncUF32() {
|
||||
vm.pushUint64(uint64(math.Trunc(float64(vm.popFloat32()))))
|
||||
}
|
||||
|
||||
func (vm *VM) i64TruncSF64() {
|
||||
vm.pushInt64(int64(math.Trunc(vm.popFloat64())))
|
||||
}
|
||||
|
||||
func (vm *VM) i64TruncUF64() {
|
||||
vm.pushUint64(uint64(math.Trunc(vm.popFloat64())))
|
||||
}
|
||||
|
||||
func (vm *VM) f32ConvertSI32() {
|
||||
vm.pushFloat32(float32(vm.popInt32()))
|
||||
}
|
||||
|
||||
func (vm *VM) f32ConvertUI32() {
|
||||
vm.pushFloat32(float32(vm.popUint32()))
|
||||
}
|
||||
|
||||
func (vm *VM) f32ConvertSI64() {
|
||||
vm.pushFloat32(float32(vm.popInt64()))
|
||||
}
|
||||
|
||||
func (vm *VM) f32ConvertUI64() {
|
||||
vm.pushFloat32(float32(vm.popUint64()))
|
||||
}
|
||||
|
||||
func (vm *VM) f32DemoteF64() {
|
||||
vm.pushFloat32(float32(vm.popFloat64()))
|
||||
}
|
||||
|
||||
func (vm *VM) f64ConvertSI32() {
|
||||
vm.pushFloat64(float64(vm.popInt32()))
|
||||
}
|
||||
|
||||
func (vm *VM) f64ConvertUI32() {
|
||||
vm.pushFloat64(float64(vm.popUint32()))
|
||||
}
|
||||
|
||||
func (vm *VM) f64ConvertSI64() {
|
||||
vm.pushFloat64(float64(vm.popInt64()))
|
||||
}
|
||||
|
||||
func (vm *VM) f64ConvertUI64() {
|
||||
vm.pushFloat64(float64(vm.popUint64()))
|
||||
}
|
||||
|
||||
func (vm *VM) f64PromoteF32() {
|
||||
vm.pushFloat64(float64(vm.popFloat32()))
|
||||
}
|
||||
109
vendor/github.com/go-interpreter/wagon/exec/func.go
generated
vendored
Normal file
109
vendor/github.com/go-interpreter/wagon/exec/func.go
generated
vendored
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package exec
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
|
||||
"github.com/go-interpreter/wagon/exec/internal/compile"
|
||||
)
|
||||
|
||||
type function interface {
|
||||
call(vm *VM, index int64)
|
||||
}
|
||||
|
||||
type compiledFunction struct {
|
||||
code []byte
|
||||
branchTables []*compile.BranchTable
|
||||
maxDepth int // maximum stack depth reached while executing the function body
|
||||
totalLocalVars int // number of local variables used by the function
|
||||
args int // number of arguments the function accepts
|
||||
returns bool // whether the function returns a value
|
||||
}
|
||||
|
||||
type goFunction struct {
|
||||
val reflect.Value
|
||||
typ reflect.Type
|
||||
}
|
||||
|
||||
func (fn goFunction) call(vm *VM, index int64) {
|
||||
// numIn = # of call inputs + vm, as the function expects
|
||||
// an additional *VM argument
|
||||
numIn := fn.typ.NumIn()
|
||||
args := make([]reflect.Value, numIn)
|
||||
proc := NewProcess(vm)
|
||||
|
||||
// Pass proc as an argument. Check that the function indeed
|
||||
// expects a *Process argument.
|
||||
if reflect.ValueOf(proc).Kind() != fn.typ.In(0).Kind() {
|
||||
panic(fmt.Sprintf("exec: the first argument of a host function was %s, expected %s", fn.typ.In(0).Kind(), reflect.ValueOf(vm).Kind()))
|
||||
}
|
||||
args[0] = reflect.ValueOf(proc)
|
||||
|
||||
for i := numIn - 1; i >= 1; i-- {
|
||||
val := reflect.New(fn.typ.In(i)).Elem()
|
||||
raw := vm.popUint64()
|
||||
kind := fn.typ.In(i).Kind()
|
||||
|
||||
switch kind {
|
||||
case reflect.Float64, reflect.Float32:
|
||||
val.SetFloat(math.Float64frombits(raw))
|
||||
case reflect.Uint32, reflect.Uint64:
|
||||
val.SetUint(raw)
|
||||
case reflect.Int32, reflect.Int64:
|
||||
val.SetInt(int64(raw))
|
||||
default:
|
||||
panic(fmt.Sprintf("exec: args %d invalid kind=%v", i, kind))
|
||||
}
|
||||
|
||||
args[i] = val
|
||||
}
|
||||
|
||||
rtrns := fn.val.Call(args)
|
||||
for i, out := range rtrns {
|
||||
kind := out.Kind()
|
||||
switch kind {
|
||||
case reflect.Float64, reflect.Float32:
|
||||
vm.pushFloat64(out.Float())
|
||||
case reflect.Uint32, reflect.Uint64:
|
||||
vm.pushUint64(out.Uint())
|
||||
case reflect.Int32, reflect.Int64:
|
||||
vm.pushInt64(out.Int())
|
||||
default:
|
||||
panic(fmt.Sprintf("exec: return value %d invalid kind=%v", i, kind))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (compiled compiledFunction) call(vm *VM, index int64) {
|
||||
newStack := make([]uint64, compiled.maxDepth)
|
||||
locals := make([]uint64, compiled.totalLocalVars)
|
||||
|
||||
for i := compiled.args - 1; i >= 0; i-- {
|
||||
locals[i] = vm.popUint64()
|
||||
}
|
||||
|
||||
//save execution context
|
||||
prevCtxt := vm.ctx
|
||||
|
||||
vm.ctx = context{
|
||||
stack: newStack,
|
||||
locals: locals,
|
||||
code: compiled.code,
|
||||
pc: 0,
|
||||
curFunc: index,
|
||||
}
|
||||
|
||||
rtrn := vm.execCode(compiled)
|
||||
|
||||
//restore execution context
|
||||
vm.ctx = prevCtxt
|
||||
|
||||
if compiled.returns {
|
||||
vm.pushUint64(rtrn)
|
||||
}
|
||||
}
|
||||
186
vendor/github.com/go-interpreter/wagon/exec/func_table.go
generated
vendored
Normal file
186
vendor/github.com/go-interpreter/wagon/exec/func_table.go
generated
vendored
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package exec
|
||||
|
||||
import (
|
||||
ops "github.com/go-interpreter/wagon/wasm/operators"
|
||||
)
|
||||
|
||||
func (vm *VM) newFuncTable() {
|
||||
vm.funcTable[ops.I32Clz] = vm.i32Clz
|
||||
vm.funcTable[ops.I32Ctz] = vm.i32Ctz
|
||||
vm.funcTable[ops.I32Popcnt] = vm.i32Popcnt
|
||||
vm.funcTable[ops.I32Add] = vm.i32Add
|
||||
vm.funcTable[ops.I32Sub] = vm.i32Sub
|
||||
vm.funcTable[ops.I32Mul] = vm.i32Mul
|
||||
vm.funcTable[ops.I32DivS] = vm.i32DivS
|
||||
vm.funcTable[ops.I32DivU] = vm.i32DivU
|
||||
vm.funcTable[ops.I32RemS] = vm.i32RemS
|
||||
vm.funcTable[ops.I32RemU] = vm.i32RemU
|
||||
vm.funcTable[ops.I32And] = vm.i32And
|
||||
vm.funcTable[ops.I32Or] = vm.i32Or
|
||||
vm.funcTable[ops.I32Xor] = vm.i32Xor
|
||||
vm.funcTable[ops.I32Shl] = vm.i32Shl
|
||||
vm.funcTable[ops.I32ShrS] = vm.i32ShrS
|
||||
vm.funcTable[ops.I32ShrU] = vm.i32ShrU
|
||||
vm.funcTable[ops.I32Rotl] = vm.i32Rotl
|
||||
vm.funcTable[ops.I32Rotr] = vm.i32Rotr
|
||||
vm.funcTable[ops.I32Eqz] = vm.i32Eqz
|
||||
vm.funcTable[ops.I32Eq] = vm.i32Eq
|
||||
vm.funcTable[ops.I32Ne] = vm.i32Ne
|
||||
vm.funcTable[ops.I32LtS] = vm.i32LtS
|
||||
vm.funcTable[ops.I32LtU] = vm.i32LtU
|
||||
vm.funcTable[ops.I32GtS] = vm.i32GtS
|
||||
vm.funcTable[ops.I32GtU] = vm.i32GtU
|
||||
vm.funcTable[ops.I32LeS] = vm.i32LeS
|
||||
vm.funcTable[ops.I32LeU] = vm.i32LeU
|
||||
vm.funcTable[ops.I32GeS] = vm.i32GeS
|
||||
vm.funcTable[ops.I32GeU] = vm.i32GeU
|
||||
|
||||
vm.funcTable[ops.I64Clz] = vm.i64Clz
|
||||
vm.funcTable[ops.I64Ctz] = vm.i64Ctz
|
||||
vm.funcTable[ops.I64Popcnt] = vm.i64Popcnt
|
||||
vm.funcTable[ops.I64Add] = vm.i64Add
|
||||
vm.funcTable[ops.I64Sub] = vm.i64Sub
|
||||
vm.funcTable[ops.I64Mul] = vm.i64Mul
|
||||
vm.funcTable[ops.I64DivS] = vm.i64DivS
|
||||
vm.funcTable[ops.I64DivU] = vm.i64DivU
|
||||
vm.funcTable[ops.I64RemS] = vm.i64RemS
|
||||
vm.funcTable[ops.I64RemU] = vm.i64RemU
|
||||
vm.funcTable[ops.I64And] = vm.i64And
|
||||
vm.funcTable[ops.I64Or] = vm.i64Or
|
||||
vm.funcTable[ops.I64Xor] = vm.i64Xor
|
||||
vm.funcTable[ops.I64Shl] = vm.i64Shl
|
||||
vm.funcTable[ops.I64ShrS] = vm.i64ShrS
|
||||
vm.funcTable[ops.I64ShrU] = vm.i64ShrU
|
||||
vm.funcTable[ops.I64Rotl] = vm.i64Rotl
|
||||
vm.funcTable[ops.I64Rotr] = vm.i64Rotr
|
||||
vm.funcTable[ops.I64Eqz] = vm.i64Eqz
|
||||
vm.funcTable[ops.I64Eq] = vm.i64Eq
|
||||
vm.funcTable[ops.I64Ne] = vm.i64Ne
|
||||
vm.funcTable[ops.I64LtS] = vm.i64LtS
|
||||
vm.funcTable[ops.I64LtU] = vm.i64LtU
|
||||
vm.funcTable[ops.I64GtS] = vm.i64GtS
|
||||
vm.funcTable[ops.I64GtU] = vm.i64GtU
|
||||
vm.funcTable[ops.I64LeS] = vm.i64LeS
|
||||
vm.funcTable[ops.I64LeU] = vm.i64LeU
|
||||
vm.funcTable[ops.I64GeS] = vm.i64GeS
|
||||
vm.funcTable[ops.I64GeU] = vm.i64GeU
|
||||
|
||||
vm.funcTable[ops.F32Eq] = vm.f32Eq
|
||||
vm.funcTable[ops.F32Ne] = vm.f32Ne
|
||||
vm.funcTable[ops.F32Lt] = vm.f32Lt
|
||||
vm.funcTable[ops.F32Gt] = vm.f32Gt
|
||||
vm.funcTable[ops.F32Le] = vm.f32Le
|
||||
vm.funcTable[ops.F32Ge] = vm.f32Ge
|
||||
vm.funcTable[ops.F32Abs] = vm.f32Abs
|
||||
vm.funcTable[ops.F32Neg] = vm.f32Neg
|
||||
vm.funcTable[ops.F32Ceil] = vm.f32Ceil
|
||||
vm.funcTable[ops.F32Floor] = vm.f32Floor
|
||||
vm.funcTable[ops.F32Trunc] = vm.f32Trunc
|
||||
vm.funcTable[ops.F32Nearest] = vm.f32Nearest
|
||||
vm.funcTable[ops.F32Sqrt] = vm.f32Sqrt
|
||||
vm.funcTable[ops.F32Add] = vm.f32Add
|
||||
vm.funcTable[ops.F32Sub] = vm.f32Sub
|
||||
vm.funcTable[ops.F32Mul] = vm.f32Mul
|
||||
vm.funcTable[ops.F32Div] = vm.f32Div
|
||||
vm.funcTable[ops.F32Min] = vm.f32Min
|
||||
vm.funcTable[ops.F32Max] = vm.f32Max
|
||||
vm.funcTable[ops.F32Copysign] = vm.f32Copysign
|
||||
|
||||
vm.funcTable[ops.F64Eq] = vm.f64Eq
|
||||
vm.funcTable[ops.F64Ne] = vm.f64Ne
|
||||
vm.funcTable[ops.F64Lt] = vm.f64Lt
|
||||
vm.funcTable[ops.F64Gt] = vm.f64Gt
|
||||
vm.funcTable[ops.F64Le] = vm.f64Le
|
||||
vm.funcTable[ops.F64Ge] = vm.f64Ge
|
||||
vm.funcTable[ops.F64Abs] = vm.f64Abs
|
||||
vm.funcTable[ops.F64Neg] = vm.f64Neg
|
||||
vm.funcTable[ops.F64Ceil] = vm.f64Ceil
|
||||
vm.funcTable[ops.F64Floor] = vm.f64Floor
|
||||
vm.funcTable[ops.F64Trunc] = vm.f64Trunc
|
||||
vm.funcTable[ops.F64Nearest] = vm.f64Nearest
|
||||
vm.funcTable[ops.F64Sqrt] = vm.f64Sqrt
|
||||
vm.funcTable[ops.F64Add] = vm.f64Add
|
||||
vm.funcTable[ops.F64Sub] = vm.f64Sub
|
||||
vm.funcTable[ops.F64Mul] = vm.f64Mul
|
||||
vm.funcTable[ops.F64Div] = vm.f64Div
|
||||
vm.funcTable[ops.F64Min] = vm.f64Min
|
||||
vm.funcTable[ops.F64Max] = vm.f64Max
|
||||
vm.funcTable[ops.F64Copysign] = vm.f64Copysign
|
||||
|
||||
vm.funcTable[ops.I32Const] = vm.i32Const
|
||||
vm.funcTable[ops.I64Const] = vm.i64Const
|
||||
vm.funcTable[ops.F32Const] = vm.f32Const
|
||||
vm.funcTable[ops.F64Const] = vm.f64Const
|
||||
|
||||
vm.funcTable[ops.I32ReinterpretF32] = vm.i32ReinterpretF32
|
||||
vm.funcTable[ops.I64ReinterpretF64] = vm.i64ReinterpretF64
|
||||
vm.funcTable[ops.F32ReinterpretI32] = vm.f32ReinterpretI32
|
||||
vm.funcTable[ops.F64ReinterpretI64] = vm.f64ReinterpretI64
|
||||
|
||||
vm.funcTable[ops.I32WrapI64] = vm.i32Wrapi64
|
||||
vm.funcTable[ops.I32TruncSF32] = vm.i32TruncSF32
|
||||
vm.funcTable[ops.I32TruncUF32] = vm.i32TruncUF32
|
||||
vm.funcTable[ops.I32TruncSF64] = vm.i32TruncSF64
|
||||
vm.funcTable[ops.I32TruncUF64] = vm.i32TruncUF64
|
||||
vm.funcTable[ops.I64ExtendSI32] = vm.i64ExtendSI32
|
||||
vm.funcTable[ops.I64ExtendUI32] = vm.i64ExtendUI32
|
||||
vm.funcTable[ops.I64TruncSF32] = vm.i64TruncSF32
|
||||
vm.funcTable[ops.I64TruncUF32] = vm.i64TruncUF32
|
||||
vm.funcTable[ops.I64TruncSF64] = vm.i64TruncSF64
|
||||
vm.funcTable[ops.I64TruncUF64] = vm.i64TruncUF64
|
||||
vm.funcTable[ops.F32ConvertSI32] = vm.f32ConvertSI32
|
||||
vm.funcTable[ops.F32ConvertUI32] = vm.f32ConvertUI32
|
||||
vm.funcTable[ops.F32ConvertSI64] = vm.f32ConvertSI64
|
||||
vm.funcTable[ops.F32ConvertUI64] = vm.f32ConvertUI64
|
||||
vm.funcTable[ops.F32DemoteF64] = vm.f32DemoteF64
|
||||
vm.funcTable[ops.F64ConvertSI32] = vm.f64ConvertSI32
|
||||
vm.funcTable[ops.F64ConvertUI32] = vm.f64ConvertUI32
|
||||
vm.funcTable[ops.F64ConvertSI64] = vm.f64ConvertSI64
|
||||
vm.funcTable[ops.F64ConvertUI64] = vm.f64ConvertUI64
|
||||
vm.funcTable[ops.F64PromoteF32] = vm.f64PromoteF32
|
||||
|
||||
vm.funcTable[ops.I32Load] = vm.i32Load
|
||||
vm.funcTable[ops.I64Load] = vm.i64Load
|
||||
vm.funcTable[ops.F32Load] = vm.f32Load
|
||||
vm.funcTable[ops.F64Load] = vm.f64Load
|
||||
vm.funcTable[ops.I32Load8s] = vm.i32Load8s
|
||||
vm.funcTable[ops.I32Load8u] = vm.i32Load8u
|
||||
vm.funcTable[ops.I32Load16s] = vm.i32Load16s
|
||||
vm.funcTable[ops.I32Load16u] = vm.i32Load16u
|
||||
vm.funcTable[ops.I64Load8s] = vm.i64Load8s
|
||||
vm.funcTable[ops.I64Load8u] = vm.i64Load8u
|
||||
vm.funcTable[ops.I64Load16s] = vm.i64Load16s
|
||||
vm.funcTable[ops.I64Load16u] = vm.i64Load16u
|
||||
vm.funcTable[ops.I64Load32s] = vm.i64Load32s
|
||||
vm.funcTable[ops.I64Load32u] = vm.i64Load32u
|
||||
vm.funcTable[ops.I32Store] = vm.i32Store
|
||||
vm.funcTable[ops.I64Store] = vm.i64Store
|
||||
vm.funcTable[ops.F32Store] = vm.f32Store
|
||||
vm.funcTable[ops.F64Store] = vm.f64Store
|
||||
vm.funcTable[ops.I32Store8] = vm.i32Store8
|
||||
vm.funcTable[ops.I32Store16] = vm.i32Store16
|
||||
vm.funcTable[ops.I64Store8] = vm.i64Store8
|
||||
vm.funcTable[ops.I64Store16] = vm.i64Store16
|
||||
vm.funcTable[ops.I64Store32] = vm.i64Store32
|
||||
vm.funcTable[ops.CurrentMemory] = vm.currentMemory
|
||||
vm.funcTable[ops.GrowMemory] = vm.growMemory
|
||||
|
||||
vm.funcTable[ops.Drop] = vm.drop
|
||||
vm.funcTable[ops.Select] = vm.selectOp
|
||||
|
||||
vm.funcTable[ops.GetLocal] = vm.getLocal
|
||||
vm.funcTable[ops.SetLocal] = vm.setLocal
|
||||
vm.funcTable[ops.TeeLocal] = vm.teeLocal
|
||||
vm.funcTable[ops.GetGlobal] = vm.getGlobal
|
||||
vm.funcTable[ops.SetGlobal] = vm.setGlobal
|
||||
|
||||
vm.funcTable[ops.Unreachable] = vm.unreachable
|
||||
vm.funcTable[ops.Nop] = vm.nop
|
||||
|
||||
vm.funcTable[ops.Call] = vm.call
|
||||
vm.funcTable[ops.CallIndirect] = vm.callIndirect
|
||||
}
|
||||
377
vendor/github.com/go-interpreter/wagon/exec/internal/compile/compile.go
generated
vendored
Normal file
377
vendor/github.com/go-interpreter/wagon/exec/internal/compile/compile.go
generated
vendored
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package compile is used internally by wagon to convert standard structured
|
||||
// WebAssembly bytecode into an unstructured form suitable for execution by
|
||||
// it's VM.
|
||||
// The conversion process consists of translating block instruction sequences
|
||||
// and branch operators (br, br_if, br_table) to absolute jumps to PC values.
|
||||
// For instance, an instruction sequence like:
|
||||
// loop
|
||||
// i32.const 1
|
||||
// get_local 0
|
||||
// i32.add
|
||||
// set_local 0
|
||||
// get_local 1
|
||||
// i32.const 1
|
||||
// i32.add
|
||||
// tee_local 1
|
||||
// get_local 2
|
||||
// i32.eq
|
||||
// br_if 0
|
||||
// end
|
||||
// Is "compiled" to:
|
||||
// i32.const 1
|
||||
// i32.add
|
||||
// set_local 0
|
||||
// get_local 1
|
||||
// i32.const 1
|
||||
// i32.add
|
||||
// tee_local 1
|
||||
// get_local 2
|
||||
// i32.eq
|
||||
// jmpnz <addr> <preserve> <discard>
|
||||
// Where jmpnz is a jump-if-not-zero operator that takes certain arguments
|
||||
// plus the jump address as immediates.
|
||||
// This is in contrast with original WebAssembly bytecode, where the target
|
||||
// of branch operators are relative block depths instead.
|
||||
package compile
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/go-interpreter/wagon/disasm"
|
||||
ops "github.com/go-interpreter/wagon/wasm/operators"
|
||||
)
|
||||
|
||||
// A small note on the usage of discard instructions:
|
||||
// A control operator sequence isn't allowed to access nor modify (pop) operands
|
||||
// that were pushed outside it. Therefore, each sequence has its own stack
|
||||
// that may or may not push a value to the original stack, depending on the
|
||||
// block's signature.
|
||||
// Instead of creating a new stack every time we enter a control structure,
|
||||
// we record the current stack height on encountering a control operator.
|
||||
// After we leave the sequence, the stack height is restored using the discard
|
||||
// operator. A block with a signature will push a value of that type on the parent
|
||||
// stack (that is, the stack of the parent block where this block started). The
|
||||
// OpDiscardPreserveTop operator allows us to preserve this value while
|
||||
// discarding the remaining ones.
|
||||
|
||||
// Branches are rewritten as
|
||||
// <jmp> <addr>
|
||||
// Where the address is an 8 byte address, initially set to zero. It is
|
||||
// later "patched" by patchOffset.
|
||||
|
||||
var (
|
||||
// OpJmp unconditionally jumps to the provided address.
|
||||
OpJmp byte = 0x0c
|
||||
// OpJmpZ jumps to the given address if the value at the top of the stack is zero.
|
||||
OpJmpZ byte = 0x03
|
||||
// OpJmpNz jumps to the given address if the value at the top of the
|
||||
// stack is not zero. It also discards elements and optionally preserves
|
||||
// the topmost value on the stack
|
||||
OpJmpNz byte = 0x0d
|
||||
// OpDiscard discards a given number of elements from the execution stack.
|
||||
OpDiscard byte = 0x0b
|
||||
// OpDiscardPreserveTop discards a given number of elements from the
|
||||
// execution stack, while preserving the value on the top of the stack.
|
||||
OpDiscardPreserveTop byte = 0x05
|
||||
)
|
||||
|
||||
// Target is the "target" of a br_table instruction.
|
||||
// Unlike other control instructions, br_table does jumps and discarding all
|
||||
// by itself.
|
||||
type Target struct {
|
||||
Addr int64 // The absolute address of the target
|
||||
Discard int64 // The number of elements to discard
|
||||
PreserveTop bool // Whether the top of the stack is to be preserved
|
||||
Return bool // Whether to return in order to take this branch/target
|
||||
}
|
||||
|
||||
// BranchTable is the structure pointed to by a rewritten br_table instruction.
|
||||
// A rewritten br_table instruction is of the format:
|
||||
// br_table <table_index>
|
||||
// where <table_index> is the index to an array of
|
||||
// BranchTable objects stored by the VM.
|
||||
type BranchTable struct {
|
||||
Targets []Target // A list of targets, br_table pops an int value, and jumps to Targets[val]
|
||||
DefaultTarget Target // If val > len(Targets), the VM will jump here
|
||||
patchedAddrs []int64 // A list of already patched addresses
|
||||
blocksLen int // The length of the blocks map in Compile when this table was initialized
|
||||
}
|
||||
|
||||
// block stores the information relevant for a block created by a control operator
|
||||
// sequence (if...else...end, loop...end, and block...end)
|
||||
type block struct {
|
||||
// the byte offset to which the continuation of the label
|
||||
// created by the block operator is located
|
||||
// for 'loop', this is the offset of the loop operator itself
|
||||
// for 'if', 'else', 'block', this is the 'end' operator
|
||||
offset int64
|
||||
|
||||
// Whether this block is created by an 'if' operator
|
||||
// in that case, the 'offset' field is set to the byte offset
|
||||
// of the else branch, once the else operator is reached.
|
||||
ifBlock bool
|
||||
// if ... else ... end is compiled to
|
||||
// jmpnz <else-addr> ... jmp <end-addr> ... <discard>
|
||||
// elseAddrOffset is the byte offset of the else-addr address
|
||||
// in the new/compiled byte buffer.
|
||||
elseAddrOffset int64
|
||||
|
||||
// Whether this block is created by a 'loop' operator
|
||||
// in that case, the 'offset' field is set at the end of the block
|
||||
loopBlock bool
|
||||
|
||||
patchOffsets []int64 // A list of offsets in the bytecode stream that need to be patched with the correct jump addresses
|
||||
|
||||
discard disasm.StackInfo // Information about the stack created in this block, used while creating Discard instructions
|
||||
branchTables []*BranchTable // All branch tables that were defined in this block.
|
||||
}
|
||||
|
||||
// Compile rewrites WebAssembly bytecode from its disassembly.
|
||||
// TODO(vibhavp): Add options for optimizing code. Operators like i32.reinterpret/f32
|
||||
// are no-ops, and can be safely removed.
|
||||
func Compile(disassembly []disasm.Instr) ([]byte, []*BranchTable) {
|
||||
buffer := new(bytes.Buffer)
|
||||
branchTables := []*BranchTable{}
|
||||
|
||||
curBlockDepth := -1
|
||||
blocks := make(map[int]*block) // maps nesting depths (labels) to blocks
|
||||
|
||||
blocks[-1] = &block{}
|
||||
for _, instr := range disassembly {
|
||||
if instr.Unreachable {
|
||||
continue
|
||||
}
|
||||
switch instr.Op.Code {
|
||||
case ops.I32Load, ops.I64Load, ops.F32Load, ops.F64Load, ops.I32Load8s, ops.I32Load8u, ops.I32Load16s, ops.I32Load16u, ops.I64Load8s, ops.I64Load8u, ops.I64Load16s, ops.I64Load16u, ops.I64Load32s, ops.I64Load32u, ops.I32Store, ops.I64Store, ops.F32Store, ops.F64Store, ops.I32Store8, ops.I32Store16, ops.I64Store8, ops.I64Store16, ops.I64Store32:
|
||||
// memory_immediate has two fields, the alignment and the offset.
|
||||
// The former is simply an optimization hint and can be safely
|
||||
// discarded.
|
||||
instr.Immediates = []interface{}{instr.Immediates[1].(uint32)}
|
||||
case ops.If:
|
||||
curBlockDepth++
|
||||
buffer.WriteByte(OpJmpZ)
|
||||
blocks[curBlockDepth] = &block{
|
||||
ifBlock: true,
|
||||
elseAddrOffset: int64(buffer.Len()),
|
||||
}
|
||||
// the address to jump to if the condition for `if` is false
|
||||
// (i.e when the value on the top of the stack is 0)
|
||||
binary.Write(buffer, binary.LittleEndian, int64(0))
|
||||
continue
|
||||
case ops.Loop:
|
||||
// there is no condition for entering a loop block
|
||||
curBlockDepth++
|
||||
blocks[curBlockDepth] = &block{
|
||||
offset: int64(buffer.Len()),
|
||||
ifBlock: false,
|
||||
loopBlock: true,
|
||||
discard: *instr.NewStack,
|
||||
}
|
||||
continue
|
||||
case ops.Block:
|
||||
curBlockDepth++
|
||||
blocks[curBlockDepth] = &block{
|
||||
ifBlock: false,
|
||||
discard: *instr.NewStack,
|
||||
}
|
||||
continue
|
||||
case ops.Else:
|
||||
ifInstr := disassembly[instr.Block.ElseIfIndex] // the corresponding `if` instruction for this else
|
||||
if ifInstr.NewStack != nil && ifInstr.NewStack.StackTopDiff != 0 {
|
||||
// add code for jumping out of a taken if branch
|
||||
if ifInstr.NewStack.PreserveTop {
|
||||
buffer.WriteByte(OpDiscardPreserveTop)
|
||||
} else {
|
||||
buffer.WriteByte(OpDiscard)
|
||||
}
|
||||
binary.Write(buffer, binary.LittleEndian, ifInstr.NewStack.StackTopDiff)
|
||||
}
|
||||
buffer.WriteByte(OpJmp)
|
||||
ifBlockEndOffset := int64(buffer.Len())
|
||||
binary.Write(buffer, binary.LittleEndian, int64(0))
|
||||
|
||||
curOffset := int64(buffer.Len())
|
||||
ifBlock := blocks[curBlockDepth]
|
||||
code := buffer.Bytes()
|
||||
|
||||
buffer = patchOffset(code, ifBlock.elseAddrOffset, curOffset)
|
||||
// this is no longer an if block
|
||||
ifBlock.ifBlock = false
|
||||
ifBlock.patchOffsets = append(ifBlock.patchOffsets, ifBlockEndOffset)
|
||||
continue
|
||||
case ops.End:
|
||||
depth := curBlockDepth
|
||||
block := blocks[depth]
|
||||
|
||||
if instr.NewStack.StackTopDiff != 0 {
|
||||
// when exiting a block, discard elements to
|
||||
// restore stack height.
|
||||
if instr.NewStack.PreserveTop {
|
||||
// this is true when the block has a
|
||||
// signature, and therefore pushes
|
||||
// a value on to the stack
|
||||
buffer.WriteByte(OpDiscardPreserveTop)
|
||||
} else {
|
||||
buffer.WriteByte(OpDiscard)
|
||||
}
|
||||
binary.Write(buffer, binary.LittleEndian, instr.NewStack.StackTopDiff)
|
||||
}
|
||||
|
||||
if !block.loopBlock { // is a normal block
|
||||
block.offset = int64(buffer.Len())
|
||||
if block.ifBlock {
|
||||
code := buffer.Bytes()
|
||||
buffer = patchOffset(code, block.elseAddrOffset, int64(block.offset))
|
||||
}
|
||||
}
|
||||
|
||||
for _, offset := range block.patchOffsets {
|
||||
code := buffer.Bytes()
|
||||
buffer = patchOffset(code, offset, block.offset)
|
||||
}
|
||||
|
||||
for _, table := range block.branchTables {
|
||||
table.patchTable(table.blocksLen-depth-1, int64(block.offset))
|
||||
}
|
||||
|
||||
delete(blocks, curBlockDepth)
|
||||
curBlockDepth--
|
||||
continue
|
||||
case ops.Br:
|
||||
if instr.NewStack != nil && instr.NewStack.StackTopDiff != 0 {
|
||||
if instr.NewStack.PreserveTop {
|
||||
buffer.WriteByte(OpDiscardPreserveTop)
|
||||
} else {
|
||||
buffer.WriteByte(OpDiscard)
|
||||
}
|
||||
binary.Write(buffer, binary.LittleEndian, instr.NewStack.StackTopDiff)
|
||||
}
|
||||
buffer.WriteByte(OpJmp)
|
||||
label := int(instr.Immediates[0].(uint32))
|
||||
block := blocks[curBlockDepth-int(label)]
|
||||
block.patchOffsets = append(block.patchOffsets, int64(buffer.Len()))
|
||||
// write the jump address
|
||||
binary.Write(buffer, binary.LittleEndian, int64(0))
|
||||
continue
|
||||
case ops.BrIf:
|
||||
buffer.WriteByte(OpJmpNz)
|
||||
label := int(instr.Immediates[0].(uint32))
|
||||
block := blocks[curBlockDepth-int(label)]
|
||||
block.patchOffsets = append(block.patchOffsets, int64(buffer.Len()))
|
||||
// write the jump address
|
||||
binary.Write(buffer, binary.LittleEndian, int64(0))
|
||||
|
||||
var stackTopDiff int64
|
||||
// write whether we need to preserve the top
|
||||
if instr.NewStack == nil || !instr.NewStack.PreserveTop || instr.NewStack.StackTopDiff == 0 {
|
||||
buffer.WriteByte(byte(0))
|
||||
} else {
|
||||
stackTopDiff = instr.NewStack.StackTopDiff
|
||||
buffer.WriteByte(byte(1))
|
||||
}
|
||||
// write the number of elements on the stack we need to discard
|
||||
binary.Write(buffer, binary.LittleEndian, stackTopDiff)
|
||||
continue
|
||||
case ops.BrTable:
|
||||
branchTable := &BranchTable{
|
||||
// we subtract one for the implicit block created by
|
||||
// the function body
|
||||
blocksLen: len(blocks) - 1,
|
||||
}
|
||||
targetCount := instr.Immediates[0].(uint32)
|
||||
branchTable.Targets = make([]Target, targetCount)
|
||||
for i := range branchTable.Targets {
|
||||
// The first immediates is the number of targets, so we ignore that
|
||||
label := int64(instr.Immediates[i+1].(uint32))
|
||||
branchTable.Targets[i].Addr = label
|
||||
branch := instr.Branches[i]
|
||||
|
||||
branchTable.Targets[i].Return = branch.IsReturn
|
||||
branchTable.Targets[i].Discard = branch.StackTopDiff
|
||||
branchTable.Targets[i].PreserveTop = branch.PreserveTop
|
||||
}
|
||||
defaultLabel := int64(instr.Immediates[len(instr.Immediates)-1].(uint32))
|
||||
branchTable.DefaultTarget.Addr = defaultLabel
|
||||
defaultBranch := instr.Branches[targetCount]
|
||||
branchTable.DefaultTarget.Return = defaultBranch.IsReturn
|
||||
branchTable.DefaultTarget.Discard = defaultBranch.StackTopDiff
|
||||
branchTable.DefaultTarget.PreserveTop = defaultBranch.PreserveTop
|
||||
branchTables = append(branchTables, branchTable)
|
||||
for _, block := range blocks {
|
||||
block.branchTables = append(block.branchTables, branchTable)
|
||||
}
|
||||
|
||||
buffer.WriteByte(ops.BrTable)
|
||||
binary.Write(buffer, binary.LittleEndian, int64(len(branchTables)-1))
|
||||
}
|
||||
|
||||
buffer.WriteByte(instr.Op.Code)
|
||||
for _, imm := range instr.Immediates {
|
||||
err := binary.Write(buffer, binary.LittleEndian, imm)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// writing nop as the last instructions allows us to branch out of the
|
||||
// function (ie, return)
|
||||
addr := buffer.Len()
|
||||
buffer.WriteByte(ops.Nop)
|
||||
|
||||
// patch all references to the "root" block of the function body
|
||||
for _, offset := range blocks[-1].patchOffsets {
|
||||
code := buffer.Bytes()
|
||||
buffer = patchOffset(code, offset, int64(addr))
|
||||
}
|
||||
|
||||
for _, table := range branchTables {
|
||||
table.patchedAddrs = nil
|
||||
}
|
||||
return buffer.Bytes(), branchTables
|
||||
}
|
||||
|
||||
// replace the address starting at start with addr
|
||||
func patchOffset(code []byte, start int64, addr int64) *bytes.Buffer {
|
||||
var shift uint
|
||||
for i := int64(0); i < 8; i++ {
|
||||
code[start+i] = byte(addr >> shift)
|
||||
shift += 8
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
buf.Write(code)
|
||||
return buf
|
||||
}
|
||||
|
||||
func (table *BranchTable) patchTable(block int, addr int64) {
|
||||
if block < 0 {
|
||||
panic("Invalid block value")
|
||||
}
|
||||
|
||||
for i, target := range table.Targets {
|
||||
if !table.isAddr(target.Addr) && target.Addr == int64(block) {
|
||||
table.Targets[i].Addr = addr
|
||||
}
|
||||
}
|
||||
|
||||
if table.DefaultTarget.Addr == int64(block) {
|
||||
table.DefaultTarget.Addr = addr
|
||||
}
|
||||
table.patchedAddrs = append(table.patchedAddrs, addr)
|
||||
}
|
||||
|
||||
// Whether the given value is an instruction (or the block depth)
|
||||
func (table *BranchTable) isAddr(addr int64) bool {
|
||||
for _, t := range table.patchedAddrs {
|
||||
if t == addr {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
214
vendor/github.com/go-interpreter/wagon/exec/memory.go
generated
vendored
Normal file
214
vendor/github.com/go-interpreter/wagon/exec/memory.go
generated
vendored
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package exec
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
)
|
||||
|
||||
// ErrOutOfBoundsMemoryAccess is the error value used while trapping the VM
|
||||
// when it detects an out of bounds access to the linear memory.
|
||||
var ErrOutOfBoundsMemoryAccess = errors.New("exec: out of bounds memory access")
|
||||
|
||||
func (vm *VM) fetchBaseAddr() int {
|
||||
return int(vm.fetchUint32() + uint32(vm.popInt32()))
|
||||
}
|
||||
|
||||
// inBounds returns true when the next vm.fetchBaseAddr() + offset
|
||||
// indices are in bounds accesses to the linear memory.
|
||||
func (vm *VM) inBounds(offset int) bool {
|
||||
addr := endianess.Uint32(vm.ctx.code[vm.ctx.pc:]) + uint32(vm.ctx.stack[len(vm.ctx.stack)-1])
|
||||
return int(addr)+offset < len(vm.memory)
|
||||
}
|
||||
|
||||
// curMem returns a slice to the memeory segment pointed to by
|
||||
// the current base address on the bytecode stream.
|
||||
func (vm *VM) curMem() []byte {
|
||||
return vm.memory[vm.fetchBaseAddr():]
|
||||
}
|
||||
|
||||
func (vm *VM) i32Load() {
|
||||
if !vm.inBounds(3) {
|
||||
panic(ErrOutOfBoundsMemoryAccess)
|
||||
}
|
||||
vm.pushUint32(endianess.Uint32(vm.curMem()))
|
||||
}
|
||||
|
||||
func (vm *VM) i32Load8s() {
|
||||
if !vm.inBounds(0) {
|
||||
panic(ErrOutOfBoundsMemoryAccess)
|
||||
}
|
||||
vm.pushInt32(int32(int8(vm.memory[vm.fetchBaseAddr()])))
|
||||
}
|
||||
|
||||
func (vm *VM) i32Load8u() {
|
||||
if !vm.inBounds(0) {
|
||||
panic(ErrOutOfBoundsMemoryAccess)
|
||||
}
|
||||
vm.pushUint32(uint32(uint8(vm.memory[vm.fetchBaseAddr()])))
|
||||
}
|
||||
|
||||
func (vm *VM) i32Load16s() {
|
||||
if !vm.inBounds(1) {
|
||||
panic(ErrOutOfBoundsMemoryAccess)
|
||||
}
|
||||
vm.pushInt32(int32(int16(endianess.Uint16(vm.curMem()))))
|
||||
}
|
||||
|
||||
func (vm *VM) i32Load16u() {
|
||||
if !vm.inBounds(1) {
|
||||
panic(ErrOutOfBoundsMemoryAccess)
|
||||
}
|
||||
vm.pushUint32(uint32(endianess.Uint16(vm.curMem())))
|
||||
}
|
||||
|
||||
func (vm *VM) i64Load() {
|
||||
if !vm.inBounds(7) {
|
||||
panic(ErrOutOfBoundsMemoryAccess)
|
||||
}
|
||||
vm.pushUint64(endianess.Uint64(vm.curMem()))
|
||||
}
|
||||
|
||||
func (vm *VM) i64Load8s() {
|
||||
if !vm.inBounds(0) {
|
||||
panic(ErrOutOfBoundsMemoryAccess)
|
||||
}
|
||||
vm.pushInt64(int64(int8(vm.memory[vm.fetchBaseAddr()])))
|
||||
}
|
||||
|
||||
func (vm *VM) i64Load8u() {
|
||||
if !vm.inBounds(0) {
|
||||
panic(ErrOutOfBoundsMemoryAccess)
|
||||
}
|
||||
vm.pushUint64(uint64(uint8(vm.memory[vm.fetchBaseAddr()])))
|
||||
}
|
||||
|
||||
func (vm *VM) i64Load16s() {
|
||||
if !vm.inBounds(1) {
|
||||
panic(ErrOutOfBoundsMemoryAccess)
|
||||
}
|
||||
vm.pushInt64(int64(int16(endianess.Uint16(vm.curMem()))))
|
||||
}
|
||||
|
||||
func (vm *VM) i64Load16u() {
|
||||
if !vm.inBounds(1) {
|
||||
panic(ErrOutOfBoundsMemoryAccess)
|
||||
}
|
||||
vm.pushUint64(uint64(endianess.Uint16(vm.curMem())))
|
||||
}
|
||||
|
||||
func (vm *VM) i64Load32s() {
|
||||
if !vm.inBounds(3) {
|
||||
panic(ErrOutOfBoundsMemoryAccess)
|
||||
}
|
||||
vm.pushInt64(int64(int32(endianess.Uint32(vm.curMem()))))
|
||||
}
|
||||
|
||||
func (vm *VM) i64Load32u() {
|
||||
if !vm.inBounds(3) {
|
||||
panic(ErrOutOfBoundsMemoryAccess)
|
||||
}
|
||||
vm.pushUint64(uint64(endianess.Uint32(vm.curMem())))
|
||||
}
|
||||
|
||||
func (vm *VM) f32Store() {
|
||||
v := math.Float32bits(vm.popFloat32())
|
||||
if !vm.inBounds(3) {
|
||||
panic(ErrOutOfBoundsMemoryAccess)
|
||||
}
|
||||
endianess.PutUint32(vm.curMem(), v)
|
||||
}
|
||||
|
||||
func (vm *VM) f32Load() {
|
||||
if !vm.inBounds(3) {
|
||||
panic(ErrOutOfBoundsMemoryAccess)
|
||||
}
|
||||
vm.pushFloat32(math.Float32frombits(endianess.Uint32(vm.curMem())))
|
||||
}
|
||||
|
||||
func (vm *VM) f64Store() {
|
||||
v := math.Float64bits(vm.popFloat64())
|
||||
if !vm.inBounds(7) {
|
||||
panic(ErrOutOfBoundsMemoryAccess)
|
||||
}
|
||||
endianess.PutUint64(vm.curMem(), v)
|
||||
}
|
||||
|
||||
func (vm *VM) f64Load() {
|
||||
if !vm.inBounds(7) {
|
||||
panic(ErrOutOfBoundsMemoryAccess)
|
||||
}
|
||||
vm.pushFloat64(math.Float64frombits(endianess.Uint64(vm.curMem())))
|
||||
}
|
||||
|
||||
func (vm *VM) i32Store() {
|
||||
v := vm.popUint32()
|
||||
if !vm.inBounds(3) {
|
||||
panic(ErrOutOfBoundsMemoryAccess)
|
||||
}
|
||||
endianess.PutUint32(vm.curMem(), v)
|
||||
}
|
||||
|
||||
func (vm *VM) i32Store8() {
|
||||
v := byte(uint8(vm.popUint32()))
|
||||
if !vm.inBounds(0) {
|
||||
panic(ErrOutOfBoundsMemoryAccess)
|
||||
}
|
||||
vm.memory[vm.fetchBaseAddr()] = v
|
||||
}
|
||||
|
||||
func (vm *VM) i32Store16() {
|
||||
v := uint16(vm.popUint32())
|
||||
if !vm.inBounds(1) {
|
||||
panic(ErrOutOfBoundsMemoryAccess)
|
||||
}
|
||||
endianess.PutUint16(vm.curMem(), v)
|
||||
}
|
||||
|
||||
func (vm *VM) i64Store() {
|
||||
v := vm.popUint64()
|
||||
if !vm.inBounds(7) {
|
||||
panic(ErrOutOfBoundsMemoryAccess)
|
||||
}
|
||||
endianess.PutUint64(vm.curMem(), v)
|
||||
}
|
||||
|
||||
func (vm *VM) i64Store8() {
|
||||
v := byte(uint8(vm.popUint64()))
|
||||
if !vm.inBounds(0) {
|
||||
panic(ErrOutOfBoundsMemoryAccess)
|
||||
}
|
||||
vm.memory[vm.fetchBaseAddr()] = v
|
||||
}
|
||||
|
||||
func (vm *VM) i64Store16() {
|
||||
v := uint16(vm.popUint64())
|
||||
if !vm.inBounds(1) {
|
||||
panic(ErrOutOfBoundsMemoryAccess)
|
||||
}
|
||||
endianess.PutUint16(vm.curMem(), v)
|
||||
}
|
||||
|
||||
func (vm *VM) i64Store32() {
|
||||
v := uint32(vm.popUint64())
|
||||
if !vm.inBounds(3) {
|
||||
panic(ErrOutOfBoundsMemoryAccess)
|
||||
}
|
||||
endianess.PutUint32(vm.curMem(), v)
|
||||
}
|
||||
|
||||
func (vm *VM) currentMemory() {
|
||||
_ = vm.fetchInt8() // reserved (https://github.com/WebAssembly/design/blob/27ac254c854994103c24834a994be16f74f54186/BinaryEncoding.md#memory-related-operators-described-here)
|
||||
vm.pushInt32(int32(len(vm.memory) / wasmPageSize))
|
||||
}
|
||||
|
||||
func (vm *VM) growMemory() {
|
||||
_ = vm.fetchInt8() // reserved (https://github.com/WebAssembly/design/blob/27ac254c854994103c24834a994be16f74f54186/BinaryEncoding.md#memory-related-operators-described-here)
|
||||
curLen := len(vm.memory) / wasmPageSize
|
||||
n := vm.popInt32()
|
||||
vm.memory = append(vm.memory, make([]byte, n*wasmPageSize)...)
|
||||
vm.pushInt32(int32(curLen))
|
||||
}
|
||||
508
vendor/github.com/go-interpreter/wagon/exec/num.go
generated
vendored
Normal file
508
vendor/github.com/go-interpreter/wagon/exec/num.go
generated
vendored
Normal file
|
|
@ -0,0 +1,508 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package exec
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// int32 operators
|
||||
|
||||
func (vm *VM) i32Clz() {
|
||||
vm.pushUint64(uint64(bits.LeadingZeros32(vm.popUint32())))
|
||||
}
|
||||
|
||||
func (vm *VM) i32Ctz() {
|
||||
vm.pushUint64(uint64(bits.TrailingZeros32(vm.popUint32())))
|
||||
}
|
||||
|
||||
func (vm *VM) i32Popcnt() {
|
||||
vm.pushUint64(uint64(bits.OnesCount32(vm.popUint32())))
|
||||
}
|
||||
|
||||
func (vm *VM) i32Add() {
|
||||
vm.pushUint32(vm.popUint32() + vm.popUint32())
|
||||
}
|
||||
|
||||
func (vm *VM) i32Mul() {
|
||||
vm.pushUint32(vm.popUint32() * vm.popUint32())
|
||||
}
|
||||
|
||||
func (vm *VM) i32DivS() {
|
||||
v2 := vm.popInt32()
|
||||
v1 := vm.popInt32()
|
||||
vm.pushInt32(v1 / v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i32DivU() {
|
||||
v2 := vm.popUint32()
|
||||
v1 := vm.popUint32()
|
||||
vm.pushUint32(v1 / v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i32RemS() {
|
||||
v2 := vm.popInt32()
|
||||
v1 := vm.popInt32()
|
||||
vm.pushInt32(v1 % v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i32RemU() {
|
||||
v2 := vm.popUint32()
|
||||
v1 := vm.popUint32()
|
||||
vm.pushUint32(v1 % v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i32Sub() {
|
||||
v2 := vm.popUint32()
|
||||
v1 := vm.popUint32()
|
||||
vm.pushUint32(v1 - v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i32And() {
|
||||
vm.pushUint32(vm.popUint32() & vm.popUint32())
|
||||
}
|
||||
|
||||
func (vm *VM) i32Or() {
|
||||
vm.pushUint32(vm.popUint32() | vm.popUint32())
|
||||
}
|
||||
|
||||
func (vm *VM) i32Xor() {
|
||||
vm.pushUint32(vm.popUint32() ^ vm.popUint32())
|
||||
}
|
||||
|
||||
func (vm *VM) i32Shl() {
|
||||
v2 := vm.popUint32()
|
||||
v1 := vm.popUint32()
|
||||
vm.pushUint32(v1 << v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i32ShrU() {
|
||||
v2 := vm.popUint32()
|
||||
v1 := vm.popUint32()
|
||||
vm.pushUint32(v1 >> v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i32ShrS() {
|
||||
v2 := vm.popUint32()
|
||||
v1 := vm.popInt32()
|
||||
vm.pushInt32(v1 >> v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i32Rotl() {
|
||||
v2 := vm.popUint32()
|
||||
v1 := vm.popUint32()
|
||||
vm.pushUint32(bits.RotateLeft32(v1, int(v2)))
|
||||
}
|
||||
|
||||
func (vm *VM) i32Rotr() {
|
||||
v2 := vm.popUint32()
|
||||
v1 := vm.popUint32()
|
||||
vm.pushUint32(bits.RotateLeft32(v1, -int(v2)))
|
||||
}
|
||||
|
||||
func (vm *VM) i32LeS() {
|
||||
v2 := vm.popInt32()
|
||||
v1 := vm.popInt32()
|
||||
vm.pushBool(v1 <= v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i32LeU() {
|
||||
v2 := vm.popUint32()
|
||||
v1 := vm.popUint32()
|
||||
vm.pushBool(v1 <= v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i32LtS() {
|
||||
v2 := vm.popInt32()
|
||||
v1 := vm.popInt32()
|
||||
vm.pushBool(v1 < v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i32LtU() {
|
||||
v2 := vm.popUint32()
|
||||
v1 := vm.popUint32()
|
||||
vm.pushBool(v1 < v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i32GtS() {
|
||||
v2 := vm.popInt32()
|
||||
v1 := vm.popInt32()
|
||||
vm.pushBool(v1 > v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i32GeS() {
|
||||
v2 := vm.popInt32()
|
||||
v1 := vm.popInt32()
|
||||
vm.pushBool(v1 >= v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i32GtU() {
|
||||
v2 := vm.popUint32()
|
||||
v1 := vm.popUint32()
|
||||
vm.pushBool(v1 > v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i32GeU() {
|
||||
v2 := vm.popUint32()
|
||||
v1 := vm.popUint32()
|
||||
vm.pushBool(v1 >= v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i32Eqz() {
|
||||
vm.pushBool(vm.popUint32() == 0)
|
||||
}
|
||||
|
||||
func (vm *VM) i32Eq() {
|
||||
vm.pushBool(vm.popUint32() == vm.popUint32())
|
||||
}
|
||||
|
||||
func (vm *VM) i32Ne() {
|
||||
vm.pushBool(vm.popUint32() != vm.popUint32())
|
||||
}
|
||||
|
||||
// int64 operators
|
||||
|
||||
func (vm *VM) i64Clz() {
|
||||
vm.pushUint64(uint64(bits.LeadingZeros64(vm.popUint64())))
|
||||
}
|
||||
|
||||
func (vm *VM) i64Ctz() {
|
||||
vm.pushUint64(uint64(bits.TrailingZeros64(vm.popUint64())))
|
||||
}
|
||||
|
||||
func (vm *VM) i64Popcnt() {
|
||||
vm.pushUint64(uint64(bits.OnesCount64(vm.popUint64())))
|
||||
}
|
||||
|
||||
func (vm *VM) i64Add() {
|
||||
vm.pushUint64(vm.popUint64() + vm.popUint64())
|
||||
}
|
||||
|
||||
func (vm *VM) i64Sub() {
|
||||
v2 := vm.popUint64()
|
||||
v1 := vm.popUint64()
|
||||
vm.pushUint64(v1 - v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i64Mul() {
|
||||
vm.pushUint64(vm.popUint64() * vm.popUint64())
|
||||
}
|
||||
|
||||
func (vm *VM) i64DivS() {
|
||||
v2 := vm.popInt64()
|
||||
v1 := vm.popInt64()
|
||||
vm.pushInt64(v1 / v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i64DivU() {
|
||||
v2 := vm.popUint64()
|
||||
v1 := vm.popUint64()
|
||||
vm.pushUint64(v1 / v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i64RemS() {
|
||||
v2 := vm.popInt64()
|
||||
v1 := vm.popInt64()
|
||||
vm.pushInt64(v1 % v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i64RemU() {
|
||||
v2 := vm.popUint64()
|
||||
v1 := vm.popUint64()
|
||||
vm.pushUint64(v1 % v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i64And() {
|
||||
vm.pushUint64(vm.popUint64() & vm.popUint64())
|
||||
}
|
||||
|
||||
func (vm *VM) i64Or() {
|
||||
vm.pushUint64(vm.popUint64() | vm.popUint64())
|
||||
}
|
||||
|
||||
func (vm *VM) i64Xor() {
|
||||
vm.pushUint64(vm.popUint64() ^ vm.popUint64())
|
||||
}
|
||||
|
||||
func (vm *VM) i64Shl() {
|
||||
v2 := vm.popUint64()
|
||||
v1 := vm.popUint64()
|
||||
vm.pushUint64(v1 << v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i64ShrS() {
|
||||
v2 := vm.popUint64()
|
||||
v1 := vm.popInt64()
|
||||
vm.pushInt64(v1 >> v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i64ShrU() {
|
||||
v2 := vm.popUint64()
|
||||
v1 := vm.popUint64()
|
||||
vm.pushUint64(v1 >> v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i64Rotl() {
|
||||
v2 := vm.popInt64()
|
||||
v1 := vm.popUint64()
|
||||
vm.pushUint64(bits.RotateLeft64(v1, int(v2)))
|
||||
}
|
||||
|
||||
func (vm *VM) i64Rotr() {
|
||||
v2 := vm.popInt64()
|
||||
v1 := vm.popUint64()
|
||||
vm.pushUint64(bits.RotateLeft64(v1, -int(v2)))
|
||||
}
|
||||
|
||||
func (vm *VM) i64Eq() {
|
||||
vm.pushBool(vm.popUint64() == vm.popUint64())
|
||||
}
|
||||
|
||||
func (vm *VM) i64Eqz() {
|
||||
vm.pushBool(vm.popUint64() == 0)
|
||||
}
|
||||
|
||||
func (vm *VM) i64Ne() {
|
||||
vm.pushBool(vm.popUint64() != vm.popUint64())
|
||||
}
|
||||
|
||||
func (vm *VM) i64LtS() {
|
||||
v2 := vm.popInt64()
|
||||
v1 := vm.popInt64()
|
||||
vm.pushBool(v1 < v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i64LtU() {
|
||||
v2 := vm.popUint64()
|
||||
v1 := vm.popUint64()
|
||||
vm.pushBool(v1 < v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i64GtS() {
|
||||
v2 := vm.popInt64()
|
||||
v1 := vm.popInt64()
|
||||
vm.pushBool(v1 > v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i64GtU() {
|
||||
v2 := vm.popUint64()
|
||||
v1 := vm.popUint64()
|
||||
vm.pushBool(v1 > v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i64LeU() {
|
||||
v2 := vm.popUint64()
|
||||
v1 := vm.popUint64()
|
||||
vm.pushBool(v1 <= v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i64LeS() {
|
||||
v2 := vm.popInt64()
|
||||
v1 := vm.popInt64()
|
||||
vm.pushBool(v1 <= v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i64GeS() {
|
||||
v2 := vm.popInt64()
|
||||
v1 := vm.popInt64()
|
||||
vm.pushBool(v1 >= v2)
|
||||
}
|
||||
|
||||
func (vm *VM) i64GeU() {
|
||||
v2 := vm.popUint64()
|
||||
v1 := vm.popUint64()
|
||||
vm.pushBool(v1 >= v2)
|
||||
}
|
||||
|
||||
// float32 operators
|
||||
|
||||
func (vm *VM) f32Abs() {
|
||||
vm.pushFloat32(float32(math.Abs(float64(vm.popFloat32()))))
|
||||
}
|
||||
|
||||
func (vm *VM) f32Neg() {
|
||||
vm.pushFloat32(-vm.popFloat32())
|
||||
}
|
||||
|
||||
func (vm *VM) f32Ceil() {
|
||||
vm.pushFloat32(float32(math.Ceil(float64(vm.popFloat32()))))
|
||||
}
|
||||
|
||||
func (vm *VM) f32Floor() {
|
||||
vm.pushFloat32(float32(math.Floor(float64(vm.popFloat32()))))
|
||||
}
|
||||
|
||||
func (vm *VM) f32Trunc() {
|
||||
vm.pushFloat32(float32(math.Trunc(float64(vm.popFloat32()))))
|
||||
}
|
||||
|
||||
func (vm *VM) f32Nearest() {
|
||||
f := vm.popFloat32()
|
||||
vm.pushFloat32(float32(int32(f + float32(math.Copysign(0.5, float64(f))))))
|
||||
}
|
||||
|
||||
func (vm *VM) f32Sqrt() {
|
||||
vm.pushFloat32(float32(math.Sqrt(float64(vm.popFloat32()))))
|
||||
}
|
||||
|
||||
func (vm *VM) f32Add() {
|
||||
vm.pushFloat32(vm.popFloat32() + vm.popFloat32())
|
||||
}
|
||||
|
||||
func (vm *VM) f32Sub() {
|
||||
v2 := vm.popFloat32()
|
||||
v1 := vm.popFloat32()
|
||||
vm.pushFloat32(v1 - v2)
|
||||
}
|
||||
|
||||
func (vm *VM) f32Mul() {
|
||||
vm.pushFloat32(vm.popFloat32() * vm.popFloat32())
|
||||
}
|
||||
|
||||
func (vm *VM) f32Div() {
|
||||
v2 := vm.popFloat32()
|
||||
v1 := vm.popFloat32()
|
||||
vm.pushFloat32(v1 / v2)
|
||||
}
|
||||
|
||||
func (vm *VM) f32Min() {
|
||||
vm.pushFloat32(float32(math.Min(float64(vm.popFloat32()), float64(vm.popFloat32()))))
|
||||
}
|
||||
|
||||
func (vm *VM) f32Max() {
|
||||
vm.pushFloat32(float32(math.Max(float64(vm.popFloat32()), float64(vm.popFloat32()))))
|
||||
}
|
||||
|
||||
func (vm *VM) f32Copysign() {
|
||||
vm.pushFloat32(float32(math.Copysign(float64(vm.popFloat32()), float64(vm.popFloat32()))))
|
||||
}
|
||||
|
||||
func (vm *VM) f32Eq() {
|
||||
vm.pushBool(vm.popFloat32() == vm.popFloat32())
|
||||
}
|
||||
|
||||
func (vm *VM) f32Ne() {
|
||||
vm.pushBool(vm.popFloat32() != vm.popFloat32())
|
||||
}
|
||||
|
||||
func (vm *VM) f32Lt() {
|
||||
v2 := vm.popFloat32()
|
||||
v1 := vm.popFloat32()
|
||||
vm.pushBool(v1 < v2)
|
||||
}
|
||||
|
||||
func (vm *VM) f32Gt() {
|
||||
v2 := vm.popFloat32()
|
||||
v1 := vm.popFloat32()
|
||||
vm.pushBool(v1 > v2)
|
||||
}
|
||||
|
||||
func (vm *VM) f32Le() {
|
||||
v2 := vm.popFloat32()
|
||||
v1 := vm.popFloat32()
|
||||
vm.pushBool(v1 <= v2)
|
||||
}
|
||||
|
||||
func (vm *VM) f32Ge() {
|
||||
v2 := vm.popFloat32()
|
||||
v1 := vm.popFloat32()
|
||||
vm.pushBool(v1 >= v2)
|
||||
}
|
||||
|
||||
// float64 operators
|
||||
|
||||
func (vm *VM) f64Abs() {
|
||||
vm.pushFloat64(math.Abs(vm.popFloat64()))
|
||||
}
|
||||
|
||||
func (vm *VM) f64Neg() {
|
||||
vm.pushFloat64(-vm.popFloat64())
|
||||
}
|
||||
|
||||
func (vm *VM) f64Ceil() {
|
||||
vm.pushFloat64(math.Ceil(vm.popFloat64()))
|
||||
}
|
||||
|
||||
func (vm *VM) f64Floor() {
|
||||
vm.pushFloat64(math.Floor(vm.popFloat64()))
|
||||
}
|
||||
|
||||
func (vm *VM) f64Trunc() {
|
||||
vm.pushFloat64(math.Trunc(vm.popFloat64()))
|
||||
}
|
||||
|
||||
func (vm *VM) f64Nearest() {
|
||||
f := vm.popFloat64()
|
||||
vm.pushFloat64(float64(int64(f + math.Copysign(0.5, f))))
|
||||
}
|
||||
|
||||
func (vm *VM) f64Sqrt() {
|
||||
vm.pushFloat64(math.Sqrt(vm.popFloat64()))
|
||||
}
|
||||
|
||||
func (vm *VM) f64Add() {
|
||||
vm.pushFloat64(vm.popFloat64() + vm.popFloat64())
|
||||
}
|
||||
|
||||
func (vm *VM) f64Sub() {
|
||||
v2 := vm.popFloat64()
|
||||
v1 := vm.popFloat64()
|
||||
vm.pushFloat64(v1 - v2)
|
||||
}
|
||||
|
||||
func (vm *VM) f64Mul() {
|
||||
vm.pushFloat64(vm.popFloat64() * vm.popFloat64())
|
||||
}
|
||||
|
||||
func (vm *VM) f64Div() {
|
||||
v2 := vm.popFloat64()
|
||||
v1 := vm.popFloat64()
|
||||
vm.pushFloat64(v1 / v2)
|
||||
}
|
||||
|
||||
func (vm *VM) f64Min() {
|
||||
vm.pushFloat64(math.Min(vm.popFloat64(), vm.popFloat64()))
|
||||
}
|
||||
|
||||
func (vm *VM) f64Max() {
|
||||
vm.pushFloat64(math.Max(vm.popFloat64(), vm.popFloat64()))
|
||||
}
|
||||
|
||||
func (vm *VM) f64Copysign() {
|
||||
vm.pushFloat64(math.Copysign(vm.popFloat64(), vm.popFloat64()))
|
||||
}
|
||||
|
||||
func (vm *VM) f64Eq() {
|
||||
vm.pushBool(vm.popFloat64() == vm.popFloat64())
|
||||
}
|
||||
|
||||
func (vm *VM) f64Ne() {
|
||||
vm.pushBool(vm.popFloat64() != vm.popFloat64())
|
||||
}
|
||||
|
||||
func (vm *VM) f64Lt() {
|
||||
v2 := vm.popFloat64()
|
||||
v1 := vm.popFloat64()
|
||||
vm.pushBool(v1 < v2)
|
||||
}
|
||||
|
||||
func (vm *VM) f64Gt() {
|
||||
v2 := vm.popFloat64()
|
||||
v1 := vm.popFloat64()
|
||||
vm.pushBool(v1 > v2)
|
||||
}
|
||||
|
||||
func (vm *VM) f64Le() {
|
||||
v2 := vm.popFloat64()
|
||||
v1 := vm.popFloat64()
|
||||
vm.pushBool(v1 <= v2)
|
||||
}
|
||||
|
||||
func (vm *VM) f64Ge() {
|
||||
v2 := vm.popFloat64()
|
||||
v1 := vm.popFloat64()
|
||||
vm.pushBool(v1 >= v2)
|
||||
}
|
||||
21
vendor/github.com/go-interpreter/wagon/exec/parametric.go
generated
vendored
Normal file
21
vendor/github.com/go-interpreter/wagon/exec/parametric.go
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package exec
|
||||
|
||||
func (vm *VM) drop() {
|
||||
vm.ctx.stack = vm.ctx.stack[:len(vm.ctx.stack)-1]
|
||||
}
|
||||
|
||||
func (vm *VM) selectOp() {
|
||||
c := vm.popUint32()
|
||||
val2 := vm.popUint64()
|
||||
val1 := vm.popUint64()
|
||||
|
||||
if c != 0 {
|
||||
vm.pushUint64(val1)
|
||||
} else {
|
||||
vm.pushUint64(val2)
|
||||
}
|
||||
}
|
||||
29
vendor/github.com/go-interpreter/wagon/exec/reinterp.go
generated
vendored
Normal file
29
vendor/github.com/go-interpreter/wagon/exec/reinterp.go
generated
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package exec
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
// these operations are essentially no-ops.
|
||||
// TODO(vibhavp): Add optimisations to package compiles that
|
||||
// removes them from the original bytecode.
|
||||
|
||||
func (vm *VM) i32ReinterpretF32() {
|
||||
vm.pushUint32(math.Float32bits(vm.popFloat32()))
|
||||
}
|
||||
|
||||
func (vm *VM) i64ReinterpretF64() {
|
||||
vm.pushUint64(math.Float64bits(vm.popFloat64()))
|
||||
}
|
||||
|
||||
func (vm *VM) f32ReinterpretI32() {
|
||||
vm.pushFloat32(math.Float32frombits(vm.popUint32()))
|
||||
}
|
||||
|
||||
func (vm *VM) f64ReinterpretI64() {
|
||||
vm.pushFloat64(math.Float64frombits(vm.popUint64()))
|
||||
}
|
||||
31
vendor/github.com/go-interpreter/wagon/exec/var.go
generated
vendored
Normal file
31
vendor/github.com/go-interpreter/wagon/exec/var.go
generated
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package exec
|
||||
|
||||
func (vm *VM) getLocal() {
|
||||
index := vm.fetchUint32()
|
||||
vm.pushUint64(vm.ctx.locals[int(index)])
|
||||
}
|
||||
|
||||
func (vm *VM) setLocal() {
|
||||
index := vm.fetchUint32()
|
||||
vm.ctx.locals[int(index)] = vm.popUint64()
|
||||
}
|
||||
|
||||
func (vm *VM) teeLocal() {
|
||||
index := vm.fetchUint32()
|
||||
val := vm.ctx.stack[len(vm.ctx.stack)-1]
|
||||
vm.ctx.locals[int(index)] = val
|
||||
}
|
||||
|
||||
func (vm *VM) getGlobal() {
|
||||
index := vm.fetchUint32()
|
||||
vm.pushUint64(vm.globals[int(index)])
|
||||
}
|
||||
|
||||
func (vm *VM) setGlobal() {
|
||||
index := vm.fetchUint32()
|
||||
vm.globals[int(index)] = vm.popUint64()
|
||||
}
|
||||
462
vendor/github.com/go-interpreter/wagon/exec/vm.go
generated
vendored
Normal file
462
vendor/github.com/go-interpreter/wagon/exec/vm.go
generated
vendored
Normal file
|
|
@ -0,0 +1,462 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package exec provides functions for executing WebAssembly bytecode.
|
||||
package exec
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
|
||||
"github.com/go-interpreter/wagon/disasm"
|
||||
"github.com/go-interpreter/wagon/exec/internal/compile"
|
||||
"github.com/go-interpreter/wagon/wasm"
|
||||
ops "github.com/go-interpreter/wagon/wasm/operators"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrMultipleLinearMemories is returned by (*VM).NewVM when the module
|
||||
// has more then one entries in the linear memory space.
|
||||
ErrMultipleLinearMemories = errors.New("exec: more than one linear memories in module")
|
||||
// ErrInvalidArgumentCount is returned by (*VM).ExecCode when an invalid
|
||||
// number of arguments to the WebAssembly function are passed to it.
|
||||
ErrInvalidArgumentCount = errors.New("exec: invalid number of arguments to function")
|
||||
)
|
||||
|
||||
// InvalidReturnTypeError is returned by (*VM).ExecCode when the module
|
||||
// specifies an invalid return type value for the executed function.
|
||||
type InvalidReturnTypeError int8
|
||||
|
||||
func (e InvalidReturnTypeError) Error() string {
|
||||
return fmt.Sprintf("Function has invalid return value_type: %d", int8(e))
|
||||
}
|
||||
|
||||
// InvalidFunctionIndexError is returned by (*VM).ExecCode when the function
|
||||
// index provided is invalid.
|
||||
type InvalidFunctionIndexError int64
|
||||
|
||||
func (e InvalidFunctionIndexError) Error() string {
|
||||
return fmt.Sprintf("Invalid index to function index space: %d", int64(e))
|
||||
}
|
||||
|
||||
type context struct {
|
||||
stack []uint64
|
||||
locals []uint64
|
||||
code []byte
|
||||
pc int64
|
||||
curFunc int64
|
||||
}
|
||||
|
||||
// VM is the execution context for executing WebAssembly bytecode.
|
||||
type VM struct {
|
||||
ctx context
|
||||
|
||||
module *wasm.Module
|
||||
globals []uint64
|
||||
memory []byte
|
||||
funcs []function
|
||||
|
||||
funcTable [256]func()
|
||||
|
||||
// RecoverPanic controls whether the `ExecCode` method
|
||||
// recovers from a panic and returns it as an error
|
||||
// instead.
|
||||
// A panic can occur either when executing an invalid VM
|
||||
// or encountering an invalid instruction, e.g. `unreachable`.
|
||||
RecoverPanic bool
|
||||
|
||||
abort bool // Flag for host functions to terminate execution
|
||||
}
|
||||
|
||||
// As per the WebAssembly spec: https://github.com/WebAssembly/design/blob/27ac254c854994103c24834a994be16f74f54186/Semantics.md#linear-memory
|
||||
const wasmPageSize = 65536 // (64 KB)
|
||||
|
||||
var endianess = binary.LittleEndian
|
||||
|
||||
// NewVM creates a new VM from a given module. If the module defines a
|
||||
// start function, it will be executed.
|
||||
func NewVM(module *wasm.Module) (*VM, error) {
|
||||
var vm VM
|
||||
|
||||
if module.Memory != nil && len(module.Memory.Entries) != 0 {
|
||||
if len(module.Memory.Entries) > 1 {
|
||||
return nil, ErrMultipleLinearMemories
|
||||
}
|
||||
vm.memory = make([]byte, uint(module.Memory.Entries[0].Limits.Initial)*wasmPageSize)
|
||||
copy(vm.memory, module.LinearMemoryIndexSpace[0])
|
||||
}
|
||||
|
||||
vm.funcs = make([]function, len(module.FunctionIndexSpace))
|
||||
vm.globals = make([]uint64, len(module.GlobalIndexSpace))
|
||||
vm.newFuncTable()
|
||||
vm.module = module
|
||||
|
||||
nNatives := 0
|
||||
for i, fn := range module.FunctionIndexSpace {
|
||||
// Skip native methods as they need not be
|
||||
// disassembled; simply add them at the end
|
||||
// of the `funcs` array as is, as specified
|
||||
// in the spec. See the "host functions"
|
||||
// section of:
|
||||
// https://webassembly.github.io/spec/core/exec/modules.html#allocation
|
||||
if fn.IsHost() {
|
||||
vm.funcs[i] = goFunction{
|
||||
typ: fn.Host.Type(),
|
||||
val: fn.Host,
|
||||
}
|
||||
nNatives++
|
||||
continue
|
||||
}
|
||||
|
||||
disassembly, err := disasm.Disassemble(fn, module)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
totalLocalVars := 0
|
||||
totalLocalVars += len(fn.Sig.ParamTypes)
|
||||
for _, entry := range fn.Body.Locals {
|
||||
totalLocalVars += int(entry.Count)
|
||||
}
|
||||
code, table := compile.Compile(disassembly.Code)
|
||||
vm.funcs[i] = compiledFunction{
|
||||
code: code,
|
||||
branchTables: table,
|
||||
maxDepth: disassembly.MaxDepth,
|
||||
totalLocalVars: totalLocalVars,
|
||||
args: len(fn.Sig.ParamTypes),
|
||||
returns: len(fn.Sig.ReturnTypes) != 0,
|
||||
}
|
||||
}
|
||||
|
||||
for i, global := range module.GlobalIndexSpace {
|
||||
val, err := module.ExecInitExpr(global.Init)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch v := val.(type) {
|
||||
case int32:
|
||||
vm.globals[i] = uint64(v)
|
||||
case int64:
|
||||
vm.globals[i] = uint64(v)
|
||||
case float32:
|
||||
vm.globals[i] = uint64(math.Float32bits(v))
|
||||
case float64:
|
||||
vm.globals[i] = uint64(math.Float64bits(v))
|
||||
}
|
||||
}
|
||||
|
||||
if module.Start != nil {
|
||||
_, err := vm.ExecCode(int64(module.Start.Index))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &vm, nil
|
||||
}
|
||||
|
||||
// Memory returns the linear memory space for the VM.
|
||||
func (vm *VM) Memory() []byte {
|
||||
return vm.memory
|
||||
}
|
||||
|
||||
func (vm *VM) pushBool(v bool) {
|
||||
if v {
|
||||
vm.pushUint64(1)
|
||||
} else {
|
||||
vm.pushUint64(0)
|
||||
}
|
||||
}
|
||||
|
||||
func (vm *VM) fetchBool() bool {
|
||||
return vm.fetchInt8() != 0
|
||||
}
|
||||
|
||||
func (vm *VM) fetchInt8() int8 {
|
||||
i := int8(vm.ctx.code[vm.ctx.pc])
|
||||
vm.ctx.pc++
|
||||
return i
|
||||
}
|
||||
|
||||
func (vm *VM) fetchUint32() uint32 {
|
||||
v := endianess.Uint32(vm.ctx.code[vm.ctx.pc:])
|
||||
vm.ctx.pc += 4
|
||||
return v
|
||||
}
|
||||
|
||||
func (vm *VM) fetchInt32() int32 {
|
||||
return int32(vm.fetchUint32())
|
||||
}
|
||||
|
||||
func (vm *VM) fetchFloat32() float32 {
|
||||
return math.Float32frombits(vm.fetchUint32())
|
||||
}
|
||||
|
||||
func (vm *VM) fetchUint64() uint64 {
|
||||
v := endianess.Uint64(vm.ctx.code[vm.ctx.pc:])
|
||||
vm.ctx.pc += 8
|
||||
return v
|
||||
}
|
||||
|
||||
func (vm *VM) fetchInt64() int64 {
|
||||
return int64(vm.fetchUint64())
|
||||
}
|
||||
|
||||
func (vm *VM) fetchFloat64() float64 {
|
||||
return math.Float64frombits(vm.fetchUint64())
|
||||
}
|
||||
|
||||
func (vm *VM) popUint64() uint64 {
|
||||
i := vm.ctx.stack[len(vm.ctx.stack)-1]
|
||||
vm.ctx.stack = vm.ctx.stack[:len(vm.ctx.stack)-1]
|
||||
return i
|
||||
}
|
||||
|
||||
func (vm *VM) popInt64() int64 {
|
||||
return int64(vm.popUint64())
|
||||
}
|
||||
|
||||
func (vm *VM) popFloat64() float64 {
|
||||
return math.Float64frombits(vm.popUint64())
|
||||
}
|
||||
|
||||
func (vm *VM) popUint32() uint32 {
|
||||
return uint32(vm.popUint64())
|
||||
}
|
||||
|
||||
func (vm *VM) popInt32() int32 {
|
||||
return int32(vm.popUint32())
|
||||
}
|
||||
|
||||
func (vm *VM) popFloat32() float32 {
|
||||
return math.Float32frombits(vm.popUint32())
|
||||
}
|
||||
|
||||
func (vm *VM) pushUint64(i uint64) {
|
||||
vm.ctx.stack = append(vm.ctx.stack, i)
|
||||
}
|
||||
|
||||
func (vm *VM) pushInt64(i int64) {
|
||||
vm.pushUint64(uint64(i))
|
||||
}
|
||||
|
||||
func (vm *VM) pushFloat64(f float64) {
|
||||
vm.pushUint64(math.Float64bits(f))
|
||||
}
|
||||
|
||||
func (vm *VM) pushUint32(i uint32) {
|
||||
vm.pushUint64(uint64(i))
|
||||
}
|
||||
|
||||
func (vm *VM) pushInt32(i int32) {
|
||||
vm.pushUint64(uint64(i))
|
||||
}
|
||||
|
||||
func (vm *VM) pushFloat32(f float32) {
|
||||
vm.pushUint32(math.Float32bits(f))
|
||||
}
|
||||
|
||||
// ExecCode calls the function with the given index and arguments.
|
||||
// fnIndex should be a valid index into the function index space of
|
||||
// the VM's module.
|
||||
func (vm *VM) ExecCode(fnIndex int64, args ...uint64) (rtrn interface{}, err error) {
|
||||
// If used as a library, client code should set vm.RecoverPanic to true
|
||||
// in order to have an error returned.
|
||||
if vm.RecoverPanic {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
switch e := r.(type) {
|
||||
case error:
|
||||
err = e
|
||||
default:
|
||||
err = fmt.Errorf("exec: %v", e)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
if int(fnIndex) > len(vm.funcs) {
|
||||
return nil, InvalidFunctionIndexError(fnIndex)
|
||||
}
|
||||
if len(vm.module.GetFunction(int(fnIndex)).Sig.ParamTypes) != len(args) {
|
||||
return nil, ErrInvalidArgumentCount
|
||||
}
|
||||
compiled, ok := vm.funcs[fnIndex].(compiledFunction)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("exec: function at index %d is not a compiled function", fnIndex))
|
||||
}
|
||||
if len(vm.ctx.stack) < compiled.maxDepth {
|
||||
vm.ctx.stack = make([]uint64, 0, compiled.maxDepth)
|
||||
}
|
||||
vm.ctx.locals = make([]uint64, compiled.totalLocalVars)
|
||||
vm.ctx.pc = 0
|
||||
vm.ctx.code = compiled.code
|
||||
vm.ctx.curFunc = fnIndex
|
||||
|
||||
for i, arg := range args {
|
||||
vm.ctx.locals[i] = arg
|
||||
}
|
||||
|
||||
res := vm.execCode(compiled)
|
||||
if compiled.returns {
|
||||
rtrnType := vm.module.GetFunction(int(fnIndex)).Sig.ReturnTypes[0]
|
||||
switch rtrnType {
|
||||
case wasm.ValueTypeI32:
|
||||
rtrn = uint32(res)
|
||||
case wasm.ValueTypeI64:
|
||||
rtrn = uint64(res)
|
||||
case wasm.ValueTypeF32:
|
||||
rtrn = math.Float32frombits(uint32(res))
|
||||
case wasm.ValueTypeF64:
|
||||
rtrn = math.Float64frombits(res)
|
||||
default:
|
||||
return nil, InvalidReturnTypeError(rtrnType)
|
||||
}
|
||||
}
|
||||
|
||||
return rtrn, nil
|
||||
}
|
||||
|
||||
func (vm *VM) execCode(compiled compiledFunction) uint64 {
|
||||
outer:
|
||||
for int(vm.ctx.pc) < len(vm.ctx.code) && !vm.abort {
|
||||
op := vm.ctx.code[vm.ctx.pc]
|
||||
vm.ctx.pc++
|
||||
switch op {
|
||||
case ops.Return:
|
||||
break outer
|
||||
case compile.OpJmp:
|
||||
vm.ctx.pc = vm.fetchInt64()
|
||||
continue
|
||||
case compile.OpJmpZ:
|
||||
target := vm.fetchInt64()
|
||||
if vm.popUint32() == 0 {
|
||||
vm.ctx.pc = target
|
||||
continue
|
||||
}
|
||||
case compile.OpJmpNz:
|
||||
target := vm.fetchInt64()
|
||||
preserveTop := vm.fetchBool()
|
||||
discard := vm.fetchInt64()
|
||||
if vm.popUint32() != 0 {
|
||||
vm.ctx.pc = target
|
||||
var top uint64
|
||||
if preserveTop {
|
||||
top = vm.ctx.stack[len(vm.ctx.stack)-1]
|
||||
}
|
||||
vm.ctx.stack = vm.ctx.stack[:len(vm.ctx.stack)-int(discard)]
|
||||
if preserveTop {
|
||||
vm.pushUint64(top)
|
||||
}
|
||||
continue
|
||||
}
|
||||
case ops.BrTable:
|
||||
index := vm.fetchInt64()
|
||||
label := vm.popInt32()
|
||||
cf, ok := vm.funcs[vm.ctx.curFunc].(compiledFunction)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("exec: function at index %d is not a compiled function", vm.ctx.curFunc))
|
||||
}
|
||||
table := cf.branchTables[index]
|
||||
var target compile.Target
|
||||
if label >= 0 && label < int32(len(table.Targets)) {
|
||||
target = table.Targets[int32(label)]
|
||||
} else {
|
||||
target = table.DefaultTarget
|
||||
}
|
||||
|
||||
if target.Return {
|
||||
break outer
|
||||
}
|
||||
vm.ctx.pc = target.Addr
|
||||
var top uint64
|
||||
if target.PreserveTop {
|
||||
top = vm.ctx.stack[len(vm.ctx.stack)-1]
|
||||
}
|
||||
vm.ctx.stack = vm.ctx.stack[:len(vm.ctx.stack)-int(target.Discard)]
|
||||
if target.PreserveTop {
|
||||
vm.pushUint64(top)
|
||||
}
|
||||
continue
|
||||
case compile.OpDiscard:
|
||||
place := vm.fetchInt64()
|
||||
vm.ctx.stack = vm.ctx.stack[:len(vm.ctx.stack)-int(place)]
|
||||
case compile.OpDiscardPreserveTop:
|
||||
top := vm.ctx.stack[len(vm.ctx.stack)-1]
|
||||
place := vm.fetchInt64()
|
||||
vm.ctx.stack = vm.ctx.stack[:len(vm.ctx.stack)-int(place)]
|
||||
vm.pushUint64(top)
|
||||
default:
|
||||
vm.funcTable[op]()
|
||||
}
|
||||
}
|
||||
|
||||
if compiled.returns {
|
||||
return vm.ctx.stack[len(vm.ctx.stack)-1]
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Process is a proxy passed to host functions in order to access
|
||||
// things such as memory and control.
|
||||
type Process struct {
|
||||
vm *VM
|
||||
}
|
||||
|
||||
// NewProcess creates a VM interface object for host functions
|
||||
func NewProcess(vm *VM) *Process {
|
||||
return &Process{vm: vm}
|
||||
}
|
||||
|
||||
// ReadAt implements the ReaderAt interface: it copies into p
|
||||
// the content of memory at offset off.
|
||||
func (proc *Process) ReadAt(p []byte, off int64) (int, error) {
|
||||
mem := proc.vm.Memory()
|
||||
|
||||
var length int
|
||||
if len(mem) < len(p)+int(off) {
|
||||
length = len(mem) - int(off)
|
||||
} else {
|
||||
length = len(p)
|
||||
}
|
||||
|
||||
copy(p, mem[off:off+int64(length)])
|
||||
|
||||
var err error
|
||||
if length < len(p) {
|
||||
err = io.ErrShortBuffer
|
||||
}
|
||||
|
||||
return length, err
|
||||
}
|
||||
|
||||
// WriteAt implements the WriterAt interface: it writes the content of p
|
||||
// into the VM memory at offset off.
|
||||
func (proc *Process) WriteAt(p []byte, off int64) (int, error) {
|
||||
mem := proc.vm.Memory()
|
||||
|
||||
var length int
|
||||
if len(mem) < len(p)+int(off) {
|
||||
length = len(mem) - int(off)
|
||||
} else {
|
||||
length = len(p)
|
||||
}
|
||||
|
||||
copy(mem[off:], p[:length])
|
||||
|
||||
var err error
|
||||
if length < len(p) {
|
||||
err = io.ErrShortWrite
|
||||
}
|
||||
|
||||
return length, err
|
||||
}
|
||||
|
||||
// Terminate stops the execution of the current module.
|
||||
func (proc *Process) Terminate() {
|
||||
proc.vm.abort = true
|
||||
}
|
||||
1
vendor/github.com/go-interpreter/wagon/go.mod
generated
vendored
Normal file
1
vendor/github.com/go-interpreter/wagon/go.mod
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
module github.com/go-interpreter/wagon
|
||||
40
vendor/github.com/go-interpreter/wagon/internal/stack/stack.go
generated
vendored
Normal file
40
vendor/github.com/go-interpreter/wagon/internal/stack/stack.go
generated
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package stack implements a growable uint64 stack
|
||||
package stack
|
||||
|
||||
type Stack struct {
|
||||
slice []uint64
|
||||
}
|
||||
|
||||
func (s *Stack) Push(b uint64) {
|
||||
s.slice = append(s.slice, b)
|
||||
}
|
||||
|
||||
func (s *Stack) Pop() uint64 {
|
||||
v := s.Top()
|
||||
s.slice = s.slice[:len(s.slice)-1]
|
||||
return v
|
||||
}
|
||||
|
||||
func (s *Stack) SetTop(v uint64) {
|
||||
s.slice[len(s.slice)-1] = v
|
||||
}
|
||||
|
||||
func (s *Stack) Top() uint64 {
|
||||
return s.slice[len(s.slice)-1]
|
||||
}
|
||||
|
||||
func (s *Stack) Get(i int) uint64 {
|
||||
return s.slice[i]
|
||||
}
|
||||
|
||||
func (s *Stack) Set(i int, v uint64) {
|
||||
s.slice[i] = v
|
||||
}
|
||||
|
||||
func (s *Stack) Len() int {
|
||||
return len(s.slice)
|
||||
}
|
||||
6
vendor/github.com/go-interpreter/wagon/wasm/doc.go
generated
vendored
Normal file
6
vendor/github.com/go-interpreter/wagon/wasm/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package wasm provides functions for reading and parsing WebAssembly modules.
|
||||
package wasm
|
||||
70
vendor/github.com/go-interpreter/wagon/wasm/encode.go
generated
vendored
Normal file
70
vendor/github.com/go-interpreter/wagon/wasm/encode.go
generated
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
// Copyright 2018 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package wasm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
"github.com/go-interpreter/wagon/wasm/leb128"
|
||||
)
|
||||
|
||||
const currentVersion = 0x01
|
||||
|
||||
// EncodeModule writes a provided module to w using WASM binary encoding.
|
||||
func EncodeModule(w io.Writer, m *Module) error {
|
||||
if err := writeU32(w, Magic); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeU32(w, currentVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
sections := m.Sections
|
||||
buf := new(bytes.Buffer)
|
||||
for _, s := range sections {
|
||||
if _, err := leb128.WriteVarUint32(w, uint32(s.SectionID())); err != nil {
|
||||
return err
|
||||
}
|
||||
buf.Reset()
|
||||
if err := s.WritePayload(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := leb128.WriteVarUint32(w, uint32(buf.Len())); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := buf.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeStringUint(w io.Writer, s string) error {
|
||||
return writeBytesUint(w, []byte(s))
|
||||
}
|
||||
|
||||
func writeBytesUint(w io.Writer, p []byte) error {
|
||||
_, err := leb128.WriteVarUint32(w, uint32(len(p)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(p)
|
||||
return err
|
||||
}
|
||||
|
||||
func writeU32(w io.Writer, n uint32) error {
|
||||
var buf [4]byte
|
||||
binary.LittleEndian.PutUint32(buf[:], n)
|
||||
_, err := w.Write(buf[:])
|
||||
return err
|
||||
}
|
||||
|
||||
func writeU64(w io.Writer, n uint64) error {
|
||||
var buf [8]byte
|
||||
binary.LittleEndian.PutUint64(buf[:], n)
|
||||
_, err := w.Write(buf[:])
|
||||
return err
|
||||
}
|
||||
200
vendor/github.com/go-interpreter/wagon/wasm/imports.go
generated
vendored
Normal file
200
vendor/github.com/go-interpreter/wagon/wasm/imports.go
generated
vendored
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package wasm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/go-interpreter/wagon/wasm/leb128"
|
||||
)
|
||||
|
||||
// Import is an interface implemented by types that can be imported by a WebAssembly module.
|
||||
type Import interface {
|
||||
Kind() External
|
||||
Marshaler
|
||||
isImport()
|
||||
}
|
||||
|
||||
// ImportEntry describes an import statement in a Wasm module.
|
||||
type ImportEntry struct {
|
||||
ModuleName string // module name string
|
||||
FieldName string // field name string
|
||||
|
||||
// If Kind is Function, Type is a FuncImport containing the type index of the function signature
|
||||
// If Kind is Table, Type is a TableImport containing the type of the imported table
|
||||
// If Kind is Memory, Type is a MemoryImport containing the type of the imported memory
|
||||
// If the Kind is Global, Type is a GlobalVarImport
|
||||
Type Import
|
||||
}
|
||||
|
||||
type FuncImport struct {
|
||||
Type uint32
|
||||
}
|
||||
|
||||
func (FuncImport) isImport() {}
|
||||
func (FuncImport) Kind() External {
|
||||
return ExternalFunction
|
||||
}
|
||||
func (f FuncImport) MarshalWASM(w io.Writer) error {
|
||||
_, err := leb128.WriteVarUint32(w, uint32(f.Type))
|
||||
return err
|
||||
}
|
||||
|
||||
type TableImport struct {
|
||||
Type Table
|
||||
}
|
||||
|
||||
func (TableImport) isImport() {}
|
||||
func (TableImport) Kind() External {
|
||||
return ExternalTable
|
||||
}
|
||||
func (t TableImport) MarshalWASM(w io.Writer) error {
|
||||
return t.Type.MarshalWASM(w)
|
||||
}
|
||||
|
||||
type MemoryImport struct {
|
||||
Type Memory
|
||||
}
|
||||
|
||||
func (MemoryImport) isImport() {}
|
||||
func (MemoryImport) Kind() External {
|
||||
return ExternalMemory
|
||||
}
|
||||
func (t MemoryImport) MarshalWASM(w io.Writer) error {
|
||||
return t.Type.MarshalWASM(w)
|
||||
}
|
||||
|
||||
type GlobalVarImport struct {
|
||||
Type GlobalVar
|
||||
}
|
||||
|
||||
func (GlobalVarImport) isImport() {}
|
||||
func (GlobalVarImport) Kind() External {
|
||||
return ExternalGlobal
|
||||
}
|
||||
func (t GlobalVarImport) MarshalWASM(w io.Writer) error {
|
||||
return t.Type.MarshalWASM(w)
|
||||
}
|
||||
|
||||
var (
|
||||
ErrImportMutGlobal = errors.New("wasm: cannot import global mutable variable")
|
||||
ErrNoExportsInImportedModule = errors.New("wasm: imported module has no exports")
|
||||
)
|
||||
|
||||
type InvalidExternalError uint8
|
||||
|
||||
func (e InvalidExternalError) Error() string {
|
||||
return fmt.Sprintf("wasm: invalid external_kind value %d", uint8(e))
|
||||
}
|
||||
|
||||
type ExportNotFoundError struct {
|
||||
ModuleName string
|
||||
FieldName string
|
||||
}
|
||||
|
||||
type KindMismatchError struct {
|
||||
ModuleName string
|
||||
FieldName string
|
||||
Import External
|
||||
Export External
|
||||
}
|
||||
|
||||
func (e KindMismatchError) Error() string {
|
||||
return fmt.Sprintf("wasm: Mismatching import and export external kind values for %s.%s (%v, %v)", e.FieldName, e.ModuleName, e.Import, e.Export)
|
||||
}
|
||||
|
||||
func (e ExportNotFoundError) Error() string {
|
||||
return fmt.Sprintf("wasm: couldn't find export with name %s in module %s", e.FieldName, e.ModuleName)
|
||||
}
|
||||
|
||||
type InvalidFunctionIndexError uint32
|
||||
|
||||
func (e InvalidFunctionIndexError) Error() string {
|
||||
return fmt.Sprintf("wasm: Invalid index to function index space: %#x", uint32(e))
|
||||
}
|
||||
|
||||
func (module *Module) resolveImports(resolve ResolveFunc) error {
|
||||
if module.Import == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
modules := make(map[string]*Module)
|
||||
|
||||
var funcs uint32
|
||||
for _, importEntry := range module.Import.Entries {
|
||||
importedModule, ok := modules[importEntry.ModuleName]
|
||||
if !ok {
|
||||
var err error
|
||||
importedModule, err = resolve(importEntry.ModuleName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
modules[importEntry.ModuleName] = importedModule
|
||||
}
|
||||
|
||||
if importedModule.Export == nil {
|
||||
return ErrNoExportsInImportedModule
|
||||
}
|
||||
|
||||
exportEntry, ok := importedModule.Export.Entries[importEntry.FieldName]
|
||||
if !ok {
|
||||
return ExportNotFoundError{importEntry.ModuleName, importEntry.FieldName}
|
||||
}
|
||||
|
||||
if exportEntry.Kind != importEntry.Type.Kind() {
|
||||
return KindMismatchError{
|
||||
FieldName: importEntry.FieldName,
|
||||
ModuleName: importEntry.ModuleName,
|
||||
Import: importEntry.Type.Kind(),
|
||||
Export: exportEntry.Kind,
|
||||
}
|
||||
}
|
||||
|
||||
index := exportEntry.Index
|
||||
|
||||
switch exportEntry.Kind {
|
||||
case ExternalFunction:
|
||||
fn := importedModule.GetFunction(int(index))
|
||||
if fn == nil {
|
||||
return InvalidFunctionIndexError(index)
|
||||
}
|
||||
module.FunctionIndexSpace = append(module.FunctionIndexSpace, *fn)
|
||||
module.Code.Bodies = append(module.Code.Bodies, *fn.Body)
|
||||
module.imports.Funcs = append(module.imports.Funcs, funcs)
|
||||
funcs++
|
||||
case ExternalGlobal:
|
||||
glb := importedModule.GetGlobal(int(index))
|
||||
if glb == nil {
|
||||
return InvalidGlobalIndexError(index)
|
||||
}
|
||||
if glb.Type.Mutable {
|
||||
return ErrImportMutGlobal
|
||||
}
|
||||
module.GlobalIndexSpace = append(module.GlobalIndexSpace, *glb)
|
||||
module.imports.Globals++
|
||||
|
||||
// In both cases below, index should be always 0 (according to the MVP)
|
||||
// We check it against the length of the index space anyway.
|
||||
case ExternalTable:
|
||||
if int(index) >= len(importedModule.TableIndexSpace) {
|
||||
return InvalidTableIndexError(index)
|
||||
}
|
||||
module.TableIndexSpace[0] = importedModule.TableIndexSpace[0]
|
||||
module.imports.Tables++
|
||||
case ExternalMemory:
|
||||
if int(index) >= len(importedModule.LinearMemoryIndexSpace) {
|
||||
return InvalidLinearMemoryIndexError(index)
|
||||
}
|
||||
module.LinearMemoryIndexSpace[0] = importedModule.LinearMemoryIndexSpace[0]
|
||||
module.imports.Memories++
|
||||
default:
|
||||
return InvalidExternalError(exportEntry.Kind)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
180
vendor/github.com/go-interpreter/wagon/wasm/index.go
generated
vendored
Normal file
180
vendor/github.com/go-interpreter/wagon/wasm/index.go
generated
vendored
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package wasm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type InvalidTableIndexError uint32
|
||||
|
||||
func (e InvalidTableIndexError) Error() string {
|
||||
return fmt.Sprintf("wasm: Invalid table to table index space: %d", uint32(e))
|
||||
}
|
||||
|
||||
type InvalidValueTypeInitExprError struct {
|
||||
Wanted reflect.Kind
|
||||
Got reflect.Kind
|
||||
}
|
||||
|
||||
func (e InvalidValueTypeInitExprError) Error() string {
|
||||
return fmt.Sprintf("wasm: Wanted initializer expression to return %v value, got %v", e.Wanted, e.Got)
|
||||
}
|
||||
|
||||
type InvalidLinearMemoryIndexError uint32
|
||||
|
||||
func (e InvalidLinearMemoryIndexError) Error() string {
|
||||
return fmt.Sprintf("wasm: Invalid linear memory index: %d", uint32(e))
|
||||
}
|
||||
|
||||
// Functions for populating and looking up entries in a module's index space.
|
||||
// More info: http://webassembly.org/docs/modules/#function-index-space
|
||||
|
||||
func (m *Module) populateFunctions() error {
|
||||
if m.Types == nil || m.Function == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for codeIndex, typeIndex := range m.Function.Types {
|
||||
if int(typeIndex) >= len(m.Types.Entries) {
|
||||
return InvalidFunctionIndexError(typeIndex)
|
||||
}
|
||||
|
||||
fn := Function{
|
||||
Sig: &m.Types.Entries[typeIndex],
|
||||
Body: &m.Code.Bodies[codeIndex],
|
||||
}
|
||||
|
||||
m.FunctionIndexSpace = append(m.FunctionIndexSpace, fn)
|
||||
}
|
||||
|
||||
funcs := make([]uint32, 0, len(m.Function.Types)+len(m.imports.Funcs))
|
||||
funcs = append(funcs, m.imports.Funcs...)
|
||||
funcs = append(funcs, m.Function.Types...)
|
||||
m.Function.Types = funcs
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFunction returns a *Function, based on the function's index in
|
||||
// the function index space. Returns nil when the index is invalid
|
||||
func (m *Module) GetFunction(i int) *Function {
|
||||
if i >= len(m.FunctionIndexSpace) || i < 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &m.FunctionIndexSpace[i]
|
||||
}
|
||||
|
||||
func (m *Module) populateGlobals() error {
|
||||
if m.Global == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
m.GlobalIndexSpace = append(m.GlobalIndexSpace, m.Global.Globals...)
|
||||
logger.Printf("There are %d entries in the global index spaces.", len(m.GlobalIndexSpace))
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetGlobal returns a *GlobalEntry, based on the global index space.
|
||||
// Returns nil when the index is invalid
|
||||
func (m *Module) GetGlobal(i int) *GlobalEntry {
|
||||
if i >= len(m.GlobalIndexSpace) || i < 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &m.GlobalIndexSpace[i]
|
||||
}
|
||||
|
||||
func (m *Module) populateTables() error {
|
||||
if m.Table == nil || len(m.Table.Entries) == 0 || m.Elements == nil || len(m.Elements.Entries) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, elem := range m.Elements.Entries {
|
||||
// the MVP dictates that index should always be zero, we shuold
|
||||
// probably check this
|
||||
if int(elem.Index) >= len(m.TableIndexSpace) {
|
||||
return InvalidTableIndexError(elem.Index)
|
||||
}
|
||||
|
||||
val, err := m.ExecInitExpr(elem.Offset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
offset, ok := val.(int32)
|
||||
if !ok {
|
||||
return InvalidValueTypeInitExprError{reflect.Int32, reflect.TypeOf(val).Kind()}
|
||||
}
|
||||
|
||||
table := m.TableIndexSpace[int(elem.Index)]
|
||||
if int(offset)+len(elem.Elems) > len(table) {
|
||||
data := make([]uint32, int(offset)+len(elem.Elems))
|
||||
copy(data[offset:], elem.Elems)
|
||||
copy(data, table)
|
||||
m.TableIndexSpace[int(elem.Index)] = data
|
||||
} else {
|
||||
copy(table[int(offset):], elem.Elems)
|
||||
m.TableIndexSpace[int(elem.Index)] = table
|
||||
}
|
||||
}
|
||||
|
||||
logger.Printf("There are %d entries in the table index space.", len(m.TableIndexSpace))
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTableElement returns an element from the tableindex space indexed
|
||||
// by the integer index. It returns an error if index is invalid.
|
||||
func (m *Module) GetTableElement(index int) (uint32, error) {
|
||||
if index >= len(m.TableIndexSpace[0]) {
|
||||
return 0, InvalidTableIndexError(index)
|
||||
}
|
||||
|
||||
return m.TableIndexSpace[0][index], nil
|
||||
}
|
||||
|
||||
func (m *Module) populateLinearMemory() error {
|
||||
if m.Data == nil || len(m.Data.Entries) == 0 {
|
||||
return nil
|
||||
}
|
||||
// each module can only have a single linear memory in the MVP
|
||||
|
||||
for _, entry := range m.Data.Entries {
|
||||
if entry.Index != 0 {
|
||||
return InvalidLinearMemoryIndexError(entry.Index)
|
||||
}
|
||||
|
||||
val, err := m.ExecInitExpr(entry.Offset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
offset, ok := val.(int32)
|
||||
if !ok {
|
||||
return InvalidValueTypeInitExprError{reflect.Int32, reflect.TypeOf(val).Kind()}
|
||||
}
|
||||
|
||||
memory := m.LinearMemoryIndexSpace[int(entry.Index)]
|
||||
if int(offset)+len(entry.Data) > len(memory) {
|
||||
data := make([]byte, int(offset)+len(entry.Data))
|
||||
copy(data[offset:], entry.Data)
|
||||
copy(data, memory)
|
||||
m.LinearMemoryIndexSpace[int(entry.Index)] = data
|
||||
} else {
|
||||
copy(memory[int(offset):], entry.Data)
|
||||
m.LinearMemoryIndexSpace[int(entry.Index)] = memory
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Module) GetLinearMemoryData(index int) (byte, error) {
|
||||
if index >= len(m.LinearMemoryIndexSpace[0]) {
|
||||
return 0, InvalidLinearMemoryIndexError(uint32(index))
|
||||
|
||||
}
|
||||
|
||||
return m.LinearMemoryIndexSpace[0][index], nil
|
||||
}
|
||||
172
vendor/github.com/go-interpreter/wagon/wasm/init_expr.go
generated
vendored
Normal file
172
vendor/github.com/go-interpreter/wagon/wasm/init_expr.go
generated
vendored
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package wasm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
|
||||
"github.com/go-interpreter/wagon/wasm/leb128"
|
||||
)
|
||||
|
||||
const (
|
||||
i32Const byte = 0x41
|
||||
i64Const byte = 0x42
|
||||
f32Const byte = 0x43
|
||||
f64Const byte = 0x44
|
||||
getGlobal byte = 0x23
|
||||
end byte = 0x0b
|
||||
)
|
||||
|
||||
var ErrEmptyInitExpr = errors.New("wasm: Initializer expression produces no value")
|
||||
|
||||
type InvalidInitExprOpError byte
|
||||
|
||||
func (e InvalidInitExprOpError) Error() string {
|
||||
return fmt.Sprintf("wasm: Invalid opcode in initializer expression: %#x", byte(e))
|
||||
}
|
||||
|
||||
type InvalidGlobalIndexError uint32
|
||||
|
||||
func (e InvalidGlobalIndexError) Error() string {
|
||||
return fmt.Sprintf("wasm: Invalid index to global index space: %#x", uint32(e))
|
||||
}
|
||||
|
||||
func readInitExpr(r io.Reader) ([]byte, error) {
|
||||
b := make([]byte, 1)
|
||||
buf := new(bytes.Buffer)
|
||||
r = io.TeeReader(r, buf)
|
||||
|
||||
outer:
|
||||
for {
|
||||
_, err := io.ReadFull(r, b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch b[0] {
|
||||
case i32Const:
|
||||
_, err := leb128.ReadVarint32(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case i64Const:
|
||||
_, err := leb128.ReadVarint64(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case f32Const:
|
||||
if _, err := readU32(r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case f64Const:
|
||||
if _, err := readU64(r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case getGlobal:
|
||||
_, err := leb128.ReadVarUint32(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case end:
|
||||
break outer
|
||||
default:
|
||||
return nil, InvalidInitExprOpError(b[0])
|
||||
}
|
||||
}
|
||||
|
||||
if buf.Len() == 0 {
|
||||
return nil, ErrEmptyInitExpr
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// ExecInitExpr executes an initializer expression and returns an interface{} value
|
||||
// which can either be int32, int64, float32 or float64.
|
||||
// It returns an error if the expression is invalid, and nil when the expression
|
||||
// yields no value.
|
||||
func (m *Module) ExecInitExpr(expr []byte) (interface{}, error) {
|
||||
var stack []uint64
|
||||
var lastVal ValueType
|
||||
r := bytes.NewReader(expr)
|
||||
|
||||
if r.Len() == 0 {
|
||||
return nil, ErrEmptyInitExpr
|
||||
}
|
||||
|
||||
for {
|
||||
b, err := r.ReadByte()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch b {
|
||||
case i32Const:
|
||||
i, err := leb128.ReadVarint32(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stack = append(stack, uint64(i))
|
||||
lastVal = ValueTypeI32
|
||||
case i64Const:
|
||||
i, err := leb128.ReadVarint64(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stack = append(stack, uint64(i))
|
||||
lastVal = ValueTypeI64
|
||||
case f32Const:
|
||||
i, err := readU32(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stack = append(stack, uint64(i))
|
||||
lastVal = ValueTypeF32
|
||||
case f64Const:
|
||||
i, err := readU64(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stack = append(stack, i)
|
||||
lastVal = ValueTypeF64
|
||||
case getGlobal:
|
||||
index, err := leb128.ReadVarUint32(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
globalVar := m.GetGlobal(int(index))
|
||||
if globalVar == nil {
|
||||
return nil, InvalidGlobalIndexError(index)
|
||||
}
|
||||
lastVal = globalVar.Type.Type
|
||||
case end:
|
||||
break
|
||||
default:
|
||||
return nil, InvalidInitExprOpError(b)
|
||||
}
|
||||
}
|
||||
|
||||
if len(stack) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
v := stack[len(stack)-1]
|
||||
switch lastVal {
|
||||
case ValueTypeI32:
|
||||
return int32(v), nil
|
||||
case ValueTypeI64:
|
||||
return int64(v), nil
|
||||
case ValueTypeF32:
|
||||
return math.Float32frombits(uint32(v)), nil
|
||||
case ValueTypeF64:
|
||||
return math.Float64frombits(uint64(v)), nil
|
||||
default:
|
||||
panic(fmt.Sprintf("Invalid value type produced by initializer expression: %d", int8(lastVal)))
|
||||
}
|
||||
}
|
||||
30
vendor/github.com/go-interpreter/wagon/wasm/internal/readpos/readpos.go
generated
vendored
Normal file
30
vendor/github.com/go-interpreter/wagon/wasm/internal/readpos/readpos.go
generated
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package readpos
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// ReadPos implements io.Reader and stores the current number of bytes read from
|
||||
// the reader
|
||||
type ReadPos struct {
|
||||
R io.Reader
|
||||
CurPos int64
|
||||
}
|
||||
|
||||
// Read implements the io.Reader interface
|
||||
func (r *ReadPos) Read(p []byte) (int, error) {
|
||||
n, err := r.R.Read(p)
|
||||
r.CurPos += int64(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// ReadByte implements the io.ByteReader interface
|
||||
func (r *ReadPos) ReadByte() (byte, error) {
|
||||
p := make([]byte, 1)
|
||||
_, err := r.R.Read(p)
|
||||
return p[0], err
|
||||
}
|
||||
92
vendor/github.com/go-interpreter/wagon/wasm/leb128/read.go
generated
vendored
Normal file
92
vendor/github.com/go-interpreter/wagon/wasm/leb128/read.go
generated
vendored
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package leb128 provides functions for reading integer values encoded in the
|
||||
// Little Endian Base 128 (LEB128) format: https://en.wikipedia.org/wiki/LEB128
|
||||
package leb128
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// ReadVarUint32Size reads a LEB128 encoded unsigned 32-bit integer from r.
|
||||
// It returns the integer value, the size of the encoded value (in bytes), and
|
||||
// the error (if any).
|
||||
func ReadVarUint32Size(r io.Reader) (res uint32, size uint, err error) {
|
||||
b := make([]byte, 1)
|
||||
var shift uint
|
||||
for {
|
||||
if _, err = io.ReadFull(r, b); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
size++
|
||||
|
||||
cur := uint32(b[0])
|
||||
res |= (cur & 0x7f) << (shift)
|
||||
if cur&0x80 == 0 {
|
||||
return res, size, nil
|
||||
}
|
||||
shift += 7
|
||||
}
|
||||
}
|
||||
|
||||
// ReadVarUint32 reads a LEB128 encoded unsigned 32-bit integer from r, and
|
||||
// returns the integer value, and the error (if any).
|
||||
func ReadVarUint32(r io.Reader) (uint32, error) {
|
||||
n, _, err := ReadVarUint32Size(r)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// ReadVarint32Size reads a LEB128 encoded signed 32-bit integer from r, and
|
||||
// returns the integer value, the size of the encoded value, and the error
|
||||
// (if any)
|
||||
func ReadVarint32Size(r io.Reader) (res int32, size uint, err error) {
|
||||
res64, size, err := ReadVarint64Size(r)
|
||||
res = int32(res64)
|
||||
return
|
||||
}
|
||||
|
||||
// ReadVarint32 reads a LEB128 encoded signed 32-bit integer from r, and
|
||||
// returns the integer value, and the error (if any).
|
||||
func ReadVarint32(r io.Reader) (int32, error) {
|
||||
n, _, err := ReadVarint32Size(r)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// ReadVarint64Size reads a LEB128 encoded signed 64-bit integer from r, and
|
||||
// returns the integer value, the size of the encoded value, and the error
|
||||
// (if any)
|
||||
func ReadVarint64Size(r io.Reader) (res int64, size uint, err error) {
|
||||
var shift uint
|
||||
var sign int64 = -1
|
||||
b := make([]byte, 1)
|
||||
|
||||
for {
|
||||
if _, err = io.ReadFull(r, b); err != nil {
|
||||
return
|
||||
}
|
||||
size++
|
||||
|
||||
cur := int64(b[0])
|
||||
res |= (cur & 0x7f) << shift
|
||||
shift += 7
|
||||
sign <<= 7
|
||||
if cur&0x80 == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if ((sign >> 1) & res) != 0 {
|
||||
res |= sign
|
||||
}
|
||||
return res, size, nil
|
||||
}
|
||||
|
||||
// ReadVarint64 reads a LEB128 encoded signed 64-bit integer from r, and
|
||||
// returns the integer value, and the error (if any).
|
||||
func ReadVarint64(r io.Reader) (int64, error) {
|
||||
n, _, err := ReadVarint64Size(r)
|
||||
return n, err
|
||||
}
|
||||
60
vendor/github.com/go-interpreter/wagon/wasm/leb128/write.go
generated
vendored
Normal file
60
vendor/github.com/go-interpreter/wagon/wasm/leb128/write.go
generated
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright 2018 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package leb128
|
||||
|
||||
import "io"
|
||||
|
||||
// Copied from cmd/internal/dwarf/dwarf.go
|
||||
|
||||
// AppendUleb128 appends v to b using unsigned LEB128 encoding.
|
||||
func AppendUleb128(b []byte, v uint64) []byte {
|
||||
for {
|
||||
c := uint8(v & 0x7f)
|
||||
v >>= 7
|
||||
if v != 0 {
|
||||
c |= 0x80
|
||||
}
|
||||
b = append(b, c)
|
||||
if c&0x80 == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// AppendSleb128 appends v to b using signed LEB128 encoding.
|
||||
func AppendSleb128(b []byte, v int64) []byte {
|
||||
for {
|
||||
c := uint8(v & 0x7f)
|
||||
s := uint8(v & 0x40)
|
||||
v >>= 7
|
||||
if (v != -1 || s == 0) && (v != 0 || s != 0) {
|
||||
c |= 0x80
|
||||
}
|
||||
b = append(b, c)
|
||||
if c&0x80 == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// WriteVarUint32 writes a LEB128 encoded unsigned 32-bit integer to w.
|
||||
// It returns the integer value, the size of the encoded value (in bytes), and
|
||||
// the error (if any).
|
||||
func WriteVarUint32(w io.Writer, cur uint32) (int, error) {
|
||||
var buf []byte
|
||||
buf = AppendUleb128(buf, uint64(cur))
|
||||
return w.Write(buf)
|
||||
}
|
||||
|
||||
// WriteVarint64 writes a LEB128 encoded signed 64-bit integer to w, and
|
||||
// returns the integer value, the size of the encoded value, and the error
|
||||
// (if any)
|
||||
func WriteVarint64(w io.Writer, cur int64) (int, error) {
|
||||
var buf []byte
|
||||
buf = AppendSleb128(buf, cur)
|
||||
return w.Write(buf)
|
||||
}
|
||||
25
vendor/github.com/go-interpreter/wagon/wasm/log.go
generated
vendored
Normal file
25
vendor/github.com/go-interpreter/wagon/wasm/log.go
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package wasm
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
var logger *log.Logger
|
||||
|
||||
func init() {
|
||||
SetDebugMode(false)
|
||||
}
|
||||
|
||||
func SetDebugMode(dbg bool) {
|
||||
w := ioutil.Discard
|
||||
if dbg {
|
||||
w = os.Stderr
|
||||
}
|
||||
logger = log.New(w, "", log.Lshortfile)
|
||||
}
|
||||
163
vendor/github.com/go-interpreter/wagon/wasm/module.go
generated
vendored
Normal file
163
vendor/github.com/go-interpreter/wagon/wasm/module.go
generated
vendored
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package wasm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"reflect"
|
||||
|
||||
"github.com/go-interpreter/wagon/wasm/internal/readpos"
|
||||
)
|
||||
|
||||
var ErrInvalidMagic = errors.New("wasm: Invalid magic number")
|
||||
|
||||
const (
|
||||
Magic uint32 = 0x6d736100
|
||||
Version uint32 = 0x1
|
||||
)
|
||||
|
||||
// Function represents an entry in the function index space of a module.
|
||||
type Function struct {
|
||||
Sig *FunctionSig
|
||||
Body *FunctionBody
|
||||
Host reflect.Value
|
||||
}
|
||||
|
||||
// IsHost indicates whether this function is a host function as defined in:
|
||||
// https://webassembly.github.io/spec/core/exec/modules.html#host-functions
|
||||
func (fct *Function) IsHost() bool {
|
||||
return fct.Host != reflect.Value{}
|
||||
}
|
||||
|
||||
// Module represents a parsed WebAssembly module:
|
||||
// http://webassembly.org/docs/modules/
|
||||
type Module struct {
|
||||
Version uint32
|
||||
Sections []Section
|
||||
|
||||
Types *SectionTypes
|
||||
Import *SectionImports
|
||||
Function *SectionFunctions
|
||||
Table *SectionTables
|
||||
Memory *SectionMemories
|
||||
Global *SectionGlobals
|
||||
Export *SectionExports
|
||||
Start *SectionStartFunction
|
||||
Elements *SectionElements
|
||||
Code *SectionCode
|
||||
Data *SectionData
|
||||
Customs []*SectionCustom
|
||||
|
||||
// The function index space of the module
|
||||
FunctionIndexSpace []Function
|
||||
GlobalIndexSpace []GlobalEntry
|
||||
|
||||
// function indices into the global function space
|
||||
// the limit of each table is its capacity (cap)
|
||||
TableIndexSpace [][]uint32
|
||||
LinearMemoryIndexSpace [][]byte
|
||||
|
||||
imports struct {
|
||||
Funcs []uint32
|
||||
Globals int
|
||||
Tables int
|
||||
Memories int
|
||||
}
|
||||
}
|
||||
|
||||
// Custom returns a custom section with a specific name, if it exists.
|
||||
func (m *Module) Custom(name string) *SectionCustom {
|
||||
for _, s := range m.Customs {
|
||||
if s.Name == name {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewModule creates a new empty module
|
||||
func NewModule() *Module {
|
||||
return &Module{
|
||||
Types: &SectionTypes{},
|
||||
Import: &SectionImports{},
|
||||
Table: &SectionTables{},
|
||||
Memory: &SectionMemories{},
|
||||
Global: &SectionGlobals{},
|
||||
Export: &SectionExports{},
|
||||
Start: &SectionStartFunction{},
|
||||
Elements: &SectionElements{},
|
||||
Data: &SectionData{},
|
||||
}
|
||||
}
|
||||
|
||||
// ResolveFunc is a function that takes a module name and
|
||||
// returns a valid resolved module.
|
||||
type ResolveFunc func(name string) (*Module, error)
|
||||
|
||||
// DecodeModule is the same as ReadModule, but it only decodes the module without
|
||||
// initializing the index space or resolving imports.
|
||||
func DecodeModule(r io.Reader) (*Module, error) {
|
||||
reader := &readpos.ReadPos{
|
||||
R: r,
|
||||
CurPos: 0,
|
||||
}
|
||||
m := &Module{}
|
||||
magic, err := readU32(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if magic != Magic {
|
||||
return nil, ErrInvalidMagic
|
||||
}
|
||||
if m.Version, err = readU32(reader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for {
|
||||
done, err := m.readSection(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if done {
|
||||
return m, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ReadModule reads a module from the reader r. resolvePath must take a string
|
||||
// and a return a reader to the module pointed to by the string.
|
||||
func ReadModule(r io.Reader, resolvePath ResolveFunc) (*Module, error) {
|
||||
m, err := DecodeModule(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m.LinearMemoryIndexSpace = make([][]byte, 1)
|
||||
if m.Table != nil {
|
||||
m.TableIndexSpace = make([][]uint32, int(len(m.Table.Entries)))
|
||||
}
|
||||
|
||||
if m.Import != nil && resolvePath != nil {
|
||||
err := m.resolveImports(resolvePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, fn := range []func() error{
|
||||
m.populateGlobals,
|
||||
m.populateFunctions,
|
||||
m.populateTables,
|
||||
m.populateLinearMemory,
|
||||
} {
|
||||
if err := fn(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
logger.Printf("There are %d entries in the function index space.", len(m.FunctionIndexSpace))
|
||||
return m, nil
|
||||
}
|
||||
10
vendor/github.com/go-interpreter/wagon/wasm/operators/call.go
generated
vendored
Normal file
10
vendor/github.com/go-interpreter/wagon/wasm/operators/call.go
generated
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package operators
|
||||
|
||||
var (
|
||||
Call = newPolymorphicOp(0x10, "call")
|
||||
CallIndirect = newPolymorphicOp(0x11, "call_indirect")
|
||||
)
|
||||
46
vendor/github.com/go-interpreter/wagon/wasm/operators/comp.go
generated
vendored
Normal file
46
vendor/github.com/go-interpreter/wagon/wasm/operators/comp.go
generated
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package operators
|
||||
|
||||
import (
|
||||
"github.com/go-interpreter/wagon/wasm"
|
||||
)
|
||||
|
||||
var (
|
||||
I32Eqz = newOp(0x45, "i32.eqz", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32Eq = newOp(0x46, "i32.eq", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32Ne = newOp(0x47, "i32.ne", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32LtS = newOp(0x48, "i32.lt_s", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32LtU = newOp(0x49, "i32.lt_u", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32GtS = newOp(0x4a, "i32.gt_s", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32GtU = newOp(0x4b, "i32.gt_u", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32LeS = newOp(0x4c, "i32.le_s", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32LeU = newOp(0x4d, "i32.le_u", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32GeS = newOp(0x4e, "i32.ge_s", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32GeU = newOp(0x4f, "i32.ge_u", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I64Eqz = newOp(0x50, "i64.eqz", []wasm.ValueType{wasm.ValueTypeI64}, wasm.ValueTypeI32)
|
||||
I64Eq = newOp(0x51, "i64.eq", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI32)
|
||||
I64Ne = newOp(0x52, "i64.ne", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI32)
|
||||
I64LtS = newOp(0x53, "i64.lt_s", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI32)
|
||||
I64LtU = newOp(0x54, "i64.lt_u", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI32)
|
||||
I64GtS = newOp(0x55, "i64.gt_s", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI32)
|
||||
I64GtU = newOp(0x56, "i64.gt_u", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI32)
|
||||
I64LeS = newOp(0x57, "i64.le_s", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI32)
|
||||
I64LeU = newOp(0x58, "i64.le_u", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI32)
|
||||
I64GeS = newOp(0x59, "i64.ge_s", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI32)
|
||||
I64GeU = newOp(0x5a, "i64.ge_u", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI32)
|
||||
F32Eq = newOp(0x5b, "f32.eq", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeF32}, wasm.ValueTypeI32)
|
||||
F32Ne = newOp(0x5c, "f32.ne", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeF32}, wasm.ValueTypeI32)
|
||||
F32Lt = newOp(0x5d, "f32.lt", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeF32}, wasm.ValueTypeI32)
|
||||
F32Gt = newOp(0x5e, "f32.gt", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeF32}, wasm.ValueTypeI32)
|
||||
F32Le = newOp(0x5f, "f32.le", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeF32}, wasm.ValueTypeI32)
|
||||
F32Ge = newOp(0x60, "f32.ge", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeF32}, wasm.ValueTypeI32)
|
||||
F64Eq = newOp(0x61, "f64.eq", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeF64}, wasm.ValueTypeI32)
|
||||
F64Ne = newOp(0x62, "f64.ne", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeF64}, wasm.ValueTypeI32)
|
||||
F64Lt = newOp(0x63, "f64.lt", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeF64}, wasm.ValueTypeI32)
|
||||
F64Gt = newOp(0x64, "f64.gt", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeF64}, wasm.ValueTypeI32)
|
||||
F64Le = newOp(0x65, "f64.le", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeF64}, wasm.ValueTypeI32)
|
||||
F64Ge = newOp(0x66, "f64.ge", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeF64}, wasm.ValueTypeI32)
|
||||
)
|
||||
16
vendor/github.com/go-interpreter/wagon/wasm/operators/const.go
generated
vendored
Normal file
16
vendor/github.com/go-interpreter/wagon/wasm/operators/const.go
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package operators
|
||||
|
||||
import (
|
||||
"github.com/go-interpreter/wagon/wasm"
|
||||
)
|
||||
|
||||
var (
|
||||
I32Const = newOp(0x41, "i32.const", nil, wasm.ValueTypeI32)
|
||||
I64Const = newOp(0x42, "i64.const", nil, wasm.ValueTypeI64)
|
||||
F32Const = newOp(0x43, "f32.const", nil, wasm.ValueTypeF32)
|
||||
F64Const = newOp(0x44, "f64.const", nil, wasm.ValueTypeF64)
|
||||
)
|
||||
23
vendor/github.com/go-interpreter/wagon/wasm/operators/control.go
generated
vendored
Normal file
23
vendor/github.com/go-interpreter/wagon/wasm/operators/control.go
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package operators
|
||||
|
||||
import (
|
||||
"github.com/go-interpreter/wagon/wasm"
|
||||
)
|
||||
|
||||
var (
|
||||
Unreachable = newOp(0x00, "unreachable", nil, noReturn)
|
||||
Nop = newOp(0x01, "nop", nil, noReturn)
|
||||
Block = newOp(0x02, "block", nil, noReturn)
|
||||
Loop = newOp(0x03, "loop", nil, noReturn)
|
||||
If = newOp(0x04, "if", []wasm.ValueType{wasm.ValueTypeI32}, noReturn)
|
||||
Else = newOp(0x05, "else", nil, noReturn)
|
||||
End = newOp(0x0b, "end", nil, noReturn)
|
||||
Br = newPolymorphicOp(0x0c, "br")
|
||||
BrIf = newOp(0x0d, "br_if", []wasm.ValueType{wasm.ValueTypeI32}, noReturn)
|
||||
BrTable = newPolymorphicOp(0x0e, "br_table")
|
||||
Return = newPolymorphicOp(0x0f, "return")
|
||||
)
|
||||
64
vendor/github.com/go-interpreter/wagon/wasm/operators/conv.go
generated
vendored
Normal file
64
vendor/github.com/go-interpreter/wagon/wasm/operators/conv.go
generated
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package operators
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
"github.com/go-interpreter/wagon/wasm"
|
||||
)
|
||||
|
||||
var reCvrtOp = regexp.MustCompile(`(.+)\.(?:[a-z]|\_)+\/(.+)`)
|
||||
|
||||
func valType(s string) wasm.ValueType {
|
||||
switch s {
|
||||
case "i32":
|
||||
return wasm.ValueTypeI32
|
||||
case "i64":
|
||||
return wasm.ValueTypeI64
|
||||
case "f32":
|
||||
return wasm.ValueTypeF32
|
||||
case "f64":
|
||||
return wasm.ValueTypeF64
|
||||
default:
|
||||
panic("Invalid value type string: " + s)
|
||||
}
|
||||
}
|
||||
|
||||
func newConversionOp(code byte, name string) byte {
|
||||
matches := reCvrtOp.FindStringSubmatch(name)
|
||||
if len(matches) == 0 {
|
||||
panic(name + " is not a conversion operator")
|
||||
}
|
||||
|
||||
returns := valType(matches[1])
|
||||
param := valType(matches[2])
|
||||
|
||||
return newOp(code, name, []wasm.ValueType{param}, returns)
|
||||
}
|
||||
|
||||
var (
|
||||
I32WrapI64 = newConversionOp(0xa7, "i32.wrap/i64")
|
||||
I32TruncSF32 = newConversionOp(0xa8, "i32.trunc_s/f32")
|
||||
I32TruncUF32 = newConversionOp(0xa9, "i32.trunc_u/f32")
|
||||
I32TruncSF64 = newConversionOp(0xaa, "i32.trunc_s/f64")
|
||||
I32TruncUF64 = newConversionOp(0xab, "i32.trunc_u/f64")
|
||||
I64ExtendSI32 = newConversionOp(0xac, "i64.extend_s/i32")
|
||||
I64ExtendUI32 = newConversionOp(0xad, "i64.extend_u/i32")
|
||||
I64TruncSF32 = newConversionOp(0xae, "i64.trunc_s/f32")
|
||||
I64TruncUF32 = newConversionOp(0xaf, "i64.trunc_u/f32")
|
||||
I64TruncSF64 = newConversionOp(0xb0, "i64.trunc_s/f64")
|
||||
I64TruncUF64 = newConversionOp(0xb1, "i64.trunc_u/f64")
|
||||
F32ConvertSI32 = newConversionOp(0xb2, "f32.convert_s/i32")
|
||||
F32ConvertUI32 = newConversionOp(0xb3, "f32.convert_u/i32")
|
||||
F32ConvertSI64 = newConversionOp(0xb4, "f32.convert_s/i64")
|
||||
F32ConvertUI64 = newConversionOp(0xb5, "f32.convert_u/i64")
|
||||
F32DemoteF64 = newConversionOp(0xb6, "f32.demote/f64")
|
||||
F64ConvertSI32 = newConversionOp(0xb7, "f64.convert_s/i32")
|
||||
F64ConvertUI32 = newConversionOp(0xb8, "f64.convert_u/i32")
|
||||
F64ConvertSI64 = newConversionOp(0xb9, "f64.convert_s/i64")
|
||||
F64ConvertUI64 = newConversionOp(0xba, "f64.convert_u/i64")
|
||||
F64PromoteF32 = newConversionOp(0xbb, "f64.promote/f32")
|
||||
)
|
||||
39
vendor/github.com/go-interpreter/wagon/wasm/operators/memory.go
generated
vendored
Normal file
39
vendor/github.com/go-interpreter/wagon/wasm/operators/memory.go
generated
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package operators
|
||||
|
||||
import (
|
||||
"github.com/go-interpreter/wagon/wasm"
|
||||
)
|
||||
|
||||
var (
|
||||
I32Load = newOp(0x28, "i32.load", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I64Load = newOp(0x29, "i64.load", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI64)
|
||||
F32Load = newOp(0x2a, "f32.load", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeF32)
|
||||
F64Load = newOp(0x2b, "f64.load", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeF64)
|
||||
I32Load8s = newOp(0x2c, "i32.load8_s", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32Load8u = newOp(0x2d, "i32.load8_u", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32Load16s = newOp(0x2e, "i32.load16_s", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32Load16u = newOp(0x2f, "i32.load16_u", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I64Load8s = newOp(0x30, "i64.load8_s", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI64)
|
||||
I64Load8u = newOp(0x31, "i64.load8_u", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI64)
|
||||
I64Load16s = newOp(0x32, "i64.load16_s", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI64)
|
||||
I64Load16u = newOp(0x33, "i64.load16_u", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI64)
|
||||
I64Load32s = newOp(0x34, "i64.load32_s", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI64)
|
||||
I64Load32u = newOp(0x35, "i64.load32_u", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI64)
|
||||
|
||||
I32Store = newOp(0x36, "i32.store", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, noReturn)
|
||||
I64Store = newOp(0x37, "i64.store", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI32}, noReturn)
|
||||
F32Store = newOp(0x38, "f32.store", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeI32}, noReturn)
|
||||
F64Store = newOp(0x39, "f64.store", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeI32}, noReturn)
|
||||
I32Store8 = newOp(0x3a, "i32.store8", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, noReturn)
|
||||
I32Store16 = newOp(0x3b, "i32.store16", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, noReturn)
|
||||
I64Store8 = newOp(0x3c, "i64.store8", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI32}, noReturn)
|
||||
I64Store16 = newOp(0x3d, "i64.store16", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI32}, noReturn)
|
||||
I64Store32 = newOp(0x3e, "i64.store32", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI32}, noReturn)
|
||||
|
||||
CurrentMemory = newOp(0x3f, "current_memory", nil, wasm.ValueTypeI32)
|
||||
GrowMemory = newOp(0x40, "grow_memory", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
)
|
||||
76
vendor/github.com/go-interpreter/wagon/wasm/operators/num.go
generated
vendored
Normal file
76
vendor/github.com/go-interpreter/wagon/wasm/operators/num.go
generated
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package operators
|
||||
|
||||
import (
|
||||
"github.com/go-interpreter/wagon/wasm"
|
||||
)
|
||||
|
||||
var (
|
||||
I32Clz = newOp(0x67, "i32.clz", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32Ctz = newOp(0x68, "i32.ctz", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32Popcnt = newOp(0x69, "i32.popcnt", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32Add = newOp(0x6a, "i32.add", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32Sub = newOp(0x6b, "i32.sub", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32Mul = newOp(0x6c, "i32.mul", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32DivS = newOp(0x6d, "i32.div_s", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32DivU = newOp(0x6e, "i32.div_u", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32RemS = newOp(0x6f, "i32.rem_s", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32RemU = newOp(0x70, "i32.rem_u", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32And = newOp(0x71, "i32.and", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32Or = newOp(0x72, "i32.or", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32Xor = newOp(0x73, "i32.xor", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32Shl = newOp(0x74, "i32.shl", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32ShrS = newOp(0x75, "i32.shr_s", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32ShrU = newOp(0x76, "i32.shr_u", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32Rotl = newOp(0x77, "i32.rotl", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I32Rotr = newOp(0x78, "i32.rotr", []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32}, wasm.ValueTypeI32)
|
||||
I64Clz = newOp(0x79, "i64.clz", []wasm.ValueType{wasm.ValueTypeI64}, wasm.ValueTypeI64)
|
||||
I64Ctz = newOp(0x7a, "i64.ctz", []wasm.ValueType{wasm.ValueTypeI64}, wasm.ValueTypeI64)
|
||||
I64Popcnt = newOp(0x7b, "i64.popcnt", []wasm.ValueType{wasm.ValueTypeI64}, wasm.ValueTypeI64)
|
||||
I64Add = newOp(0x7c, "i64.add", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
|
||||
I64Sub = newOp(0x7d, "i64.sub", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
|
||||
I64Mul = newOp(0x7e, "i64.mul", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
|
||||
I64DivS = newOp(0x7f, "i64.div_s", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
|
||||
I64DivU = newOp(0x80, "i64.div_u", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
|
||||
I64RemS = newOp(0x81, "i64.div_u", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
|
||||
I64RemU = newOp(0x82, "i64.rem_u", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
|
||||
I64And = newOp(0x83, "i64.and", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
|
||||
I64Or = newOp(0x84, "i64.or", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
|
||||
I64Xor = newOp(0x85, "i64.xor", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
|
||||
I64Shl = newOp(0x86, "i64.shl", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
|
||||
I64ShrS = newOp(0x87, "i64.shr_s", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
|
||||
I64ShrU = newOp(0x88, "i64.shr_u", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
|
||||
I64Rotl = newOp(0x89, "i64.rotl", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
|
||||
I64Rotr = newOp(0x8a, "i64.rotr", []wasm.ValueType{wasm.ValueTypeI64, wasm.ValueTypeI64}, wasm.ValueTypeI64)
|
||||
F32Abs = newOp(0x8b, "f32.abs", []wasm.ValueType{wasm.ValueTypeF32}, wasm.ValueTypeF32)
|
||||
F32Neg = newOp(0x8c, "f32.neg", []wasm.ValueType{wasm.ValueTypeF32}, wasm.ValueTypeF32)
|
||||
F32Ceil = newOp(0x8d, "f32.ceil", []wasm.ValueType{wasm.ValueTypeF32}, wasm.ValueTypeF32)
|
||||
F32Floor = newOp(0x8e, "f32.floor", []wasm.ValueType{wasm.ValueTypeF32}, wasm.ValueTypeF32)
|
||||
F32Trunc = newOp(0x8f, "f32.trunc", []wasm.ValueType{wasm.ValueTypeF32}, wasm.ValueTypeF32)
|
||||
F32Nearest = newOp(0x90, "f32.nearest", []wasm.ValueType{wasm.ValueTypeF32}, wasm.ValueTypeF32)
|
||||
F32Sqrt = newOp(0x91, "f32.sqrt", []wasm.ValueType{wasm.ValueTypeF32}, wasm.ValueTypeF32)
|
||||
F32Add = newOp(0x92, "f32.add", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeF32}, wasm.ValueTypeF32)
|
||||
F32Sub = newOp(0x93, "f32.sub", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeF32}, wasm.ValueTypeF32)
|
||||
F32Mul = newOp(0x94, "f32.mul", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeF32}, wasm.ValueTypeF32)
|
||||
F32Div = newOp(0x95, "f32.div", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeF32}, wasm.ValueTypeF32)
|
||||
F32Min = newOp(0x96, "f32.min", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeF32}, wasm.ValueTypeF32)
|
||||
F32Max = newOp(0x97, "f32.max", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeF32}, wasm.ValueTypeF32)
|
||||
F32Copysign = newOp(0x98, "f32.copysign", []wasm.ValueType{wasm.ValueTypeF32, wasm.ValueTypeF32}, wasm.ValueTypeF32)
|
||||
F64Abs = newOp(0x99, "f64.abs", []wasm.ValueType{wasm.ValueTypeF64}, wasm.ValueTypeF64)
|
||||
F64Neg = newOp(0x9a, "f64.neg", []wasm.ValueType{wasm.ValueTypeF64}, wasm.ValueTypeF64)
|
||||
F64Ceil = newOp(0x9b, "f64.ceil", []wasm.ValueType{wasm.ValueTypeF64}, wasm.ValueTypeF64)
|
||||
F64Floor = newOp(0x9c, "f64.floor", []wasm.ValueType{wasm.ValueTypeF64}, wasm.ValueTypeF64)
|
||||
F64Trunc = newOp(0x9d, "f64.trunc", []wasm.ValueType{wasm.ValueTypeF64}, wasm.ValueTypeF64)
|
||||
F64Nearest = newOp(0x9e, "f64.nearest", []wasm.ValueType{wasm.ValueTypeF64}, wasm.ValueTypeF64)
|
||||
F64Sqrt = newOp(0x9f, "f64.sqrt", []wasm.ValueType{wasm.ValueTypeF64}, wasm.ValueTypeF64)
|
||||
F64Add = newOp(0xa0, "f64.add", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeF64}, wasm.ValueTypeF64)
|
||||
F64Sub = newOp(0xa1, "f64.sub", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeF64}, wasm.ValueTypeF64)
|
||||
F64Mul = newOp(0xa2, "f64.mul", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeF64}, wasm.ValueTypeF64)
|
||||
F64Div = newOp(0xa3, "f64.div", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeF64}, wasm.ValueTypeF64)
|
||||
F64Min = newOp(0xa4, "f64.min", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeF64}, wasm.ValueTypeF64)
|
||||
F64Max = newOp(0xa5, "f64.max", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeF64}, wasm.ValueTypeF64)
|
||||
F64Copysign = newOp(0xa6, "f64.copysign", []wasm.ValueType{wasm.ValueTypeF64, wasm.ValueTypeF64}, wasm.ValueTypeF64)
|
||||
)
|
||||
86
vendor/github.com/go-interpreter/wagon/wasm/operators/op.go
generated
vendored
Normal file
86
vendor/github.com/go-interpreter/wagon/wasm/operators/op.go
generated
vendored
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package operators provides all operators used by WebAssembly bytecode,
|
||||
// together with their parameter and return type(s).
|
||||
package operators
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-interpreter/wagon/wasm"
|
||||
)
|
||||
|
||||
var (
|
||||
ops [256]Op // an array of Op values mapped by wasm opcodes, used by New().
|
||||
noReturn = wasm.ValueType(wasm.BlockTypeEmpty)
|
||||
)
|
||||
|
||||
// Op describes a WASM operator.
|
||||
type Op struct {
|
||||
Code byte // The single-byte opcode
|
||||
Name string // The name of the operator
|
||||
|
||||
// Whether this operator is polymorphic.
|
||||
// A polymorphic operator has a variable arity. call, call_indirect, and
|
||||
// drop are examples of polymorphic operators.
|
||||
Polymorphic bool
|
||||
Args []wasm.ValueType // an array of value types used by the operator as arguments, is nil for polymorphic operators
|
||||
Returns wasm.ValueType // the value returned (pushed) by the operator, is 0 for polymorphic operators
|
||||
}
|
||||
|
||||
func (o Op) IsValid() bool {
|
||||
return o.Name != ""
|
||||
}
|
||||
|
||||
func newOp(code byte, name string, args []wasm.ValueType, returns wasm.ValueType) byte {
|
||||
if ops[code].IsValid() {
|
||||
panic(fmt.Errorf("Opcode %#x is already assigned to %s", code, ops[code].Name))
|
||||
}
|
||||
|
||||
op := Op{
|
||||
Code: code,
|
||||
Name: name,
|
||||
Polymorphic: false,
|
||||
Args: args,
|
||||
Returns: returns,
|
||||
}
|
||||
ops[code] = op
|
||||
return code
|
||||
}
|
||||
|
||||
func newPolymorphicOp(code byte, name string) byte {
|
||||
if ops[code].IsValid() {
|
||||
panic(fmt.Errorf("Opcode %#x is already assigned to %s", code, ops[code].Name))
|
||||
}
|
||||
|
||||
op := Op{
|
||||
Code: code,
|
||||
Name: name,
|
||||
Polymorphic: true,
|
||||
}
|
||||
ops[code] = op
|
||||
return code
|
||||
}
|
||||
|
||||
type InvalidOpcodeError byte
|
||||
|
||||
func (e InvalidOpcodeError) Error() string {
|
||||
return fmt.Sprintf("Invalid opcode: %#x", byte(e))
|
||||
}
|
||||
|
||||
// New returns the Op object for a valid given opcode.
|
||||
// If code is invalid, an ErrInvalidOpcode is returned.
|
||||
func New(code byte) (Op, error) {
|
||||
var op Op
|
||||
if int(code) >= len(ops) {
|
||||
return op, InvalidOpcodeError(code)
|
||||
}
|
||||
|
||||
op = ops[code]
|
||||
if !op.IsValid() {
|
||||
return op, InvalidOpcodeError(code)
|
||||
}
|
||||
return op, nil
|
||||
}
|
||||
10
vendor/github.com/go-interpreter/wagon/wasm/operators/parametric.go
generated
vendored
Normal file
10
vendor/github.com/go-interpreter/wagon/wasm/operators/parametric.go
generated
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package operators
|
||||
|
||||
var (
|
||||
Drop = newPolymorphicOp(0x1a, "drop")
|
||||
Select = newPolymorphicOp(0x1b, "select")
|
||||
)
|
||||
16
vendor/github.com/go-interpreter/wagon/wasm/operators/reinterp.go
generated
vendored
Normal file
16
vendor/github.com/go-interpreter/wagon/wasm/operators/reinterp.go
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package operators
|
||||
|
||||
import (
|
||||
"github.com/go-interpreter/wagon/wasm"
|
||||
)
|
||||
|
||||
var (
|
||||
I32ReinterpretF32 = newOp(0xbc, "i32.reinterpret/f32", []wasm.ValueType{wasm.ValueTypeF32}, wasm.ValueTypeI32)
|
||||
I64ReinterpretF64 = newOp(0xbd, "i64.reinterpret/f64", []wasm.ValueType{wasm.ValueTypeF64}, wasm.ValueTypeI64)
|
||||
F32ReinterpretI32 = newOp(0xbe, "f32.reinterpret/i32", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeF32)
|
||||
F64ReinterpretI64 = newOp(0xbf, "f64.reinterpret/i64", []wasm.ValueType{wasm.ValueTypeI64}, wasm.ValueTypeF64)
|
||||
)
|
||||
13
vendor/github.com/go-interpreter/wagon/wasm/operators/var.go
generated
vendored
Normal file
13
vendor/github.com/go-interpreter/wagon/wasm/operators/var.go
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package operators
|
||||
|
||||
var (
|
||||
GetLocal = newPolymorphicOp(0x20, "get_local")
|
||||
SetLocal = newPolymorphicOp(0x21, "set_local")
|
||||
TeeLocal = newPolymorphicOp(0x22, "tee_local")
|
||||
GetGlobal = newPolymorphicOp(0x23, "get_global")
|
||||
SetGlobal = newPolymorphicOp(0x24, "set_global")
|
||||
)
|
||||
64
vendor/github.com/go-interpreter/wagon/wasm/read.go
generated
vendored
Normal file
64
vendor/github.com/go-interpreter/wagon/wasm/read.go
generated
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package wasm
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
"github.com/go-interpreter/wagon/wasm/leb128"
|
||||
)
|
||||
|
||||
func readBytes(r io.Reader, n int) ([]byte, error) {
|
||||
bytes := make([]byte, n)
|
||||
_, err := io.ReadFull(r, bytes)
|
||||
if err != nil {
|
||||
return bytes, err
|
||||
}
|
||||
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
func readBytesUint(r io.Reader) ([]byte, error) {
|
||||
n, err := leb128.ReadVarUint32(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return readBytes(r, int(n))
|
||||
}
|
||||
|
||||
func readString(r io.Reader, n int) (string, error) {
|
||||
bytes, err := readBytes(r, n)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
func readStringUint(r io.Reader) (string, error) {
|
||||
n, err := leb128.ReadVarUint32(r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return readString(r, int(n))
|
||||
}
|
||||
|
||||
func readU32(r io.Reader) (uint32, error) {
|
||||
var buf [4]byte
|
||||
_, err := io.ReadFull(r, buf[:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return binary.LittleEndian.Uint32(buf[:]), nil
|
||||
}
|
||||
|
||||
func readU64(r io.Reader) (uint64, error) {
|
||||
var buf [8]byte
|
||||
_, err := io.ReadFull(r, buf[:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return binary.LittleEndian.Uint64(buf[:]), nil
|
||||
}
|
||||
1203
vendor/github.com/go-interpreter/wagon/wasm/section.go
generated
vendored
Normal file
1203
vendor/github.com/go-interpreter/wagon/wasm/section.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
352
vendor/github.com/go-interpreter/wagon/wasm/types.go
generated
vendored
Normal file
352
vendor/github.com/go-interpreter/wagon/wasm/types.go
generated
vendored
Normal file
|
|
@ -0,0 +1,352 @@
|
|||
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package wasm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/go-interpreter/wagon/wasm/leb128"
|
||||
)
|
||||
|
||||
type Marshaler interface {
|
||||
// MarshalWASM encodes an object into w using WASM binary encoding.
|
||||
MarshalWASM(w io.Writer) error
|
||||
}
|
||||
|
||||
type Unmarshaler interface {
|
||||
// UnmarshalWASM decodes an object from r using WASM binary encoding.
|
||||
UnmarshalWASM(r io.Reader) error
|
||||
}
|
||||
|
||||
// ValueType represents the type of a valid value in Wasm
|
||||
type ValueType int8
|
||||
|
||||
const (
|
||||
ValueTypeI32 ValueType = -0x01
|
||||
ValueTypeI64 ValueType = -0x02
|
||||
ValueTypeF32 ValueType = -0x03
|
||||
ValueTypeF64 ValueType = -0x04
|
||||
)
|
||||
|
||||
var valueTypeStrMap = map[ValueType]string{
|
||||
ValueTypeI32: "i32",
|
||||
ValueTypeI64: "i64",
|
||||
ValueTypeF32: "f32",
|
||||
ValueTypeF64: "f64",
|
||||
}
|
||||
|
||||
func (t ValueType) String() string {
|
||||
str, ok := valueTypeStrMap[t]
|
||||
if !ok {
|
||||
str = fmt.Sprintf("<unknown value_type %d>", int8(t))
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// TypeFunc represents the value type of a function
|
||||
const TypeFunc int = -0x20
|
||||
|
||||
func (t *ValueType) UnmarshalWASM(r io.Reader) error {
|
||||
v, err := leb128.ReadVarint32(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*t = ValueType(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t ValueType) MarshalWASM(w io.Writer) error {
|
||||
_, err := leb128.WriteVarint64(w, int64(t))
|
||||
return err
|
||||
}
|
||||
|
||||
// BlockType represents the signature of a structured block
|
||||
type BlockType ValueType // varint7
|
||||
const BlockTypeEmpty BlockType = -0x40
|
||||
|
||||
func (b BlockType) String() string {
|
||||
if b == BlockTypeEmpty {
|
||||
return "<empty block>"
|
||||
}
|
||||
return ValueType(b).String()
|
||||
}
|
||||
|
||||
// ElemType describes the type of a table's elements
|
||||
type ElemType int // varint7
|
||||
// ElemTypeAnyFunc descibres an any_func value
|
||||
const ElemTypeAnyFunc ElemType = -0x10
|
||||
|
||||
func (t *ElemType) UnmarshalWASM(r io.Reader) error {
|
||||
b, err := leb128.ReadVarint32(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*t = ElemType(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t ElemType) MarshalWASM(w io.Writer) error {
|
||||
_, err := leb128.WriteVarint64(w, int64(t))
|
||||
return err
|
||||
}
|
||||
|
||||
func (t ElemType) String() string {
|
||||
if t == ElemTypeAnyFunc {
|
||||
return "anyfunc"
|
||||
}
|
||||
|
||||
return "<unknown elem_type>"
|
||||
}
|
||||
|
||||
// FunctionSig describes the signature of a declared function in a WASM module
|
||||
type FunctionSig struct {
|
||||
// value for the 'func` type constructor
|
||||
Form int8
|
||||
// The parameter types of the function
|
||||
ParamTypes []ValueType
|
||||
ReturnTypes []ValueType
|
||||
}
|
||||
|
||||
func (f FunctionSig) String() string {
|
||||
return fmt.Sprintf("<func %v -> %v>", f.ParamTypes, f.ReturnTypes)
|
||||
}
|
||||
|
||||
type InvalidTypeConstructorError struct {
|
||||
Wanted int
|
||||
Got int
|
||||
}
|
||||
|
||||
func (e InvalidTypeConstructorError) Error() string {
|
||||
return fmt.Sprintf("wasm: invalid type constructor: wanted %d, got %d", e.Wanted, e.Got)
|
||||
}
|
||||
|
||||
func (f *FunctionSig) UnmarshalWASM(r io.Reader) error {
|
||||
form, err := leb128.ReadVarint32(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.Form = int8(form)
|
||||
|
||||
paramCount, err := leb128.ReadVarUint32(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.ParamTypes = make([]ValueType, paramCount)
|
||||
|
||||
for i := range f.ParamTypes {
|
||||
err = f.ParamTypes[i].UnmarshalWASM(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
returnCount, err := leb128.ReadVarUint32(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f.ReturnTypes = make([]ValueType, returnCount)
|
||||
for i := range f.ReturnTypes {
|
||||
err = f.ReturnTypes[i].UnmarshalWASM(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FunctionSig) MarshalWASM(w io.Writer) error {
|
||||
_, err := leb128.WriteVarint64(w, int64(f.Form))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = leb128.WriteVarUint32(w, uint32(len(f.ParamTypes)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, p := range f.ParamTypes {
|
||||
err = p.MarshalWASM(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = leb128.WriteVarUint32(w, uint32(len(f.ReturnTypes)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, p := range f.ReturnTypes {
|
||||
err = p.MarshalWASM(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GlobalVar describes the type and mutability of a declared global variable
|
||||
type GlobalVar struct {
|
||||
Type ValueType // Type of the value stored by the variable
|
||||
Mutable bool // Whether the value of the variable can be changed by the set_global operator
|
||||
}
|
||||
|
||||
func (g *GlobalVar) UnmarshalWASM(r io.Reader) error {
|
||||
*g = GlobalVar{}
|
||||
|
||||
err := g.Type.UnmarshalWASM(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m, err := leb128.ReadVarUint32(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
g.Mutable = m == 1
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *GlobalVar) MarshalWASM(w io.Writer) error {
|
||||
if err := g.Type.MarshalWASM(w); err != nil {
|
||||
return err
|
||||
}
|
||||
var m uint32
|
||||
if g.Mutable {
|
||||
m = 1
|
||||
}
|
||||
if _, err := leb128.WriteVarUint32(w, m); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Table describes a table in a Wasm module.
|
||||
type Table struct {
|
||||
// The type of elements
|
||||
ElementType ElemType
|
||||
Limits ResizableLimits
|
||||
}
|
||||
|
||||
func (t *Table) UnmarshalWASM(r io.Reader) error {
|
||||
err := t.ElementType.UnmarshalWASM(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = t.Limits.UnmarshalWASM(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *Table) MarshalWASM(w io.Writer) error {
|
||||
if err := t.ElementType.MarshalWASM(w); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := t.Limits.MarshalWASM(w); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Memory struct {
|
||||
Limits ResizableLimits
|
||||
}
|
||||
|
||||
func (m *Memory) UnmarshalWASM(r io.Reader) error {
|
||||
return m.Limits.UnmarshalWASM(r)
|
||||
}
|
||||
|
||||
func (m *Memory) MarshalWASM(w io.Writer) error {
|
||||
return m.Limits.MarshalWASM(w)
|
||||
}
|
||||
|
||||
// External describes the kind of the entry being imported or exported.
|
||||
type External uint8
|
||||
|
||||
const (
|
||||
ExternalFunction External = 0
|
||||
ExternalTable External = 1
|
||||
ExternalMemory External = 2
|
||||
ExternalGlobal External = 3
|
||||
)
|
||||
|
||||
func (e External) String() string {
|
||||
switch e {
|
||||
case ExternalFunction:
|
||||
return "function"
|
||||
case ExternalTable:
|
||||
return "table"
|
||||
case ExternalMemory:
|
||||
return "memory"
|
||||
case ExternalGlobal:
|
||||
return "global"
|
||||
default:
|
||||
return "<unknown external_kind>"
|
||||
}
|
||||
}
|
||||
func (e *External) UnmarshalWASM(r io.Reader) error {
|
||||
bytes, err := readBytes(r, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*e = External(bytes[0])
|
||||
return nil
|
||||
}
|
||||
func (e External) MarshalWASM(w io.Writer) error {
|
||||
_, err := w.Write([]byte{byte(e)})
|
||||
return err
|
||||
}
|
||||
|
||||
// ResizableLimits describe the limit of a table or linear memory.
|
||||
type ResizableLimits struct {
|
||||
Flags uint32 // 1 if the Maximum field is valid
|
||||
Initial uint32 // initial length (in units of table elements or wasm pages)
|
||||
Maximum uint32 // If flags is 1, it describes the maximum size of the table or memory
|
||||
}
|
||||
|
||||
func (lim *ResizableLimits) UnmarshalWASM(r io.Reader) error {
|
||||
*lim = ResizableLimits{}
|
||||
f, err := leb128.ReadVarUint32(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lim.Flags = f
|
||||
|
||||
lim.Initial, err = leb128.ReadVarUint32(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if lim.Flags&0x1 != 0 {
|
||||
m, err := leb128.ReadVarUint32(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lim.Maximum = m
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lim *ResizableLimits) MarshalWASM(w io.Writer) error {
|
||||
if _, err := leb128.WriteVarUint32(w, uint32(lim.Flags)); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := leb128.WriteVarUint32(w, uint32(lim.Initial)); err != nil {
|
||||
return err
|
||||
}
|
||||
if lim.Flags&0x1 != 0 {
|
||||
if _, err := leb128.WriteVarUint32(w, uint32(lim.Maximum)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
54
vendor/vendor.json
vendored
54
vendor/vendor.json
vendored
|
|
@ -128,6 +128,60 @@
|
|||
"revision": "991cd3d3809135dc24daf6188dc6edcaf3d7d2d9",
|
||||
"revisionTime": "2017-01-17T22:23:42Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "O8hj2TjWxwLgPDVJeEwvuQFm664=",
|
||||
"path": "github.com/go-interpreter/wagon",
|
||||
"revision": "876a973424d4032ac0f0af8c78c0d22dc9b0249e",
|
||||
"revisionTime": "2018-08-07T20:59:56Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "zMJhaMOsF8AoZD1nF/geiD0EEiU=",
|
||||
"path": "github.com/go-interpreter/wagon/disasm",
|
||||
"revision": "876a973424d4032ac0f0af8c78c0d22dc9b0249e",
|
||||
"revisionTime": "2018-08-07T20:59:56Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "liSTT+2c8gy/07ueALb5+d5+cY8=",
|
||||
"path": "github.com/go-interpreter/wagon/exec",
|
||||
"revision": "876a973424d4032ac0f0af8c78c0d22dc9b0249e",
|
||||
"revisionTime": "2018-08-07T20:59:56Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "4yRxeKi6k5yIf87EwOXzbSTlTRU=",
|
||||
"path": "github.com/go-interpreter/wagon/exec/internal/compile",
|
||||
"revision": "876a973424d4032ac0f0af8c78c0d22dc9b0249e",
|
||||
"revisionTime": "2018-08-07T20:59:56Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "6zjjuEauIj/855KLJGKT2cgjVio=",
|
||||
"path": "github.com/go-interpreter/wagon/internal/stack",
|
||||
"revision": "876a973424d4032ac0f0af8c78c0d22dc9b0249e",
|
||||
"revisionTime": "2018-08-07T20:59:56Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "m8mlG4hbjWqwiwxUijPXDXq9wBc=",
|
||||
"path": "github.com/go-interpreter/wagon/wasm",
|
||||
"revision": "876a973424d4032ac0f0af8c78c0d22dc9b0249e",
|
||||
"revisionTime": "2018-08-07T20:59:56Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "kRHgYIzF9xTBn1Ai9h5H0wcPfp8=",
|
||||
"path": "github.com/go-interpreter/wagon/wasm/internal/readpos",
|
||||
"revision": "876a973424d4032ac0f0af8c78c0d22dc9b0249e",
|
||||
"revisionTime": "2018-08-07T20:59:56Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "PwuCAZewDWGOeIK2Ya8vh2ux3ik=",
|
||||
"path": "github.com/go-interpreter/wagon/wasm/leb128",
|
||||
"revision": "876a973424d4032ac0f0af8c78c0d22dc9b0249e",
|
||||
"revisionTime": "2018-08-07T20:59:56Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "eaISibWdQ9N489Att4HG3Xbg+jw=",
|
||||
"path": "github.com/go-interpreter/wagon/wasm/operators",
|
||||
"revision": "876a973424d4032ac0f0af8c78c0d22dc9b0249e",
|
||||
"revisionTime": "2018-08-07T20:59:56Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "gxV/cPPLkByTdY8y172t7v4qcZA=",
|
||||
"path": "github.com/go-ole/go-ole",
|
||||
|
|
|
|||
Loading…
Reference in a new issue