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"]
|
[submodule "tests"]
|
||||||
path = tests/testdata
|
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
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Address returns the contracts address
|
// Address returns the contract's address
|
||||||
func (c *Contract) Address() common.Address {
|
func (c *Contract) Address() common.Address {
|
||||||
return c.self.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 {
|
func (c *Contract) Value() *big.Int {
|
||||||
return c.value
|
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.
|
in the Ethereum yellow paper.
|
||||||
*/
|
*/
|
||||||
package vm
|
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))
|
// evm.interpreters = append(evm.interpreters, NewEVMVCInterpreter(evm, vmConfig, options))
|
||||||
// } else {
|
// } else {
|
||||||
// evm.interpreters = append(evm.interpreters, NewEWASMInterpreter(evm, vmConfig))
|
evm.interpreters = append(evm.interpreters, NewEWASMInterpreter(evm, vmConfig))
|
||||||
// }
|
// }
|
||||||
panic("No supported ewasm interpreter yet.")
|
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
|
// 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.
|
// as we always want to have the built-in EVM as the failover option.
|
||||||
evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, vmConfig))
|
evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, vmConfig))
|
||||||
evm.interpreter = evm.interpreters[0]
|
evm.interpreter = evm.interpreters[len(evm.interpreters)-1]
|
||||||
|
|
||||||
return evm
|
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
|
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.miner.SetExtra(makeExtraData(config.MinerExtraData))
|
||||||
|
|
||||||
eth.APIBackend = &EthAPIBackend{eth, nil}
|
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")
|
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.
|
// SetEtherbase sets the mining reward address.
|
||||||
func (s *Ethereum) SetEtherbase(etherbase common.Address) {
|
func (s *Ethereum) SetEtherbase(etherbase common.Address) {
|
||||||
s.lock.Lock()
|
s.lock.Lock()
|
||||||
|
|
@ -420,7 +366,7 @@ func (s *Ethereum) StartMining(threads int) error {
|
||||||
s.lock.RUnlock()
|
s.lock.RUnlock()
|
||||||
s.txPool.SetGasPrice(price)
|
s.txPool.SetGasPrice(price)
|
||||||
|
|
||||||
// Configure the local mining address
|
// Configure the local mining addess
|
||||||
eb, err := s.Etherbase()
|
eb, err := s.Etherbase()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Cannot start mining without etherbase", "err", err)
|
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.
|
// 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) {
|
func withTrace(t *testing.T, gasLimit uint64, test func(vm.Config) error) {
|
||||||
err := test(vm.Config{})
|
err := test(vm.Config{})
|
||||||
|
|
@ -75,7 +75,7 @@ func withTrace(t *testing.T, gasLimit uint64, test func(vm.Config) error) {
|
||||||
}
|
}
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
if gasLimit > traceErrorLimit {
|
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
|
return
|
||||||
}
|
}
|
||||||
tracer := vm.NewStructLogger(nil)
|
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",
|
"revision": "991cd3d3809135dc24daf6188dc6edcaf3d7d2d9",
|
||||||
"revisionTime": "2017-01-17T22:23:42Z"
|
"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=",
|
"checksumSHA1": "gxV/cPPLkByTdY8y172t7v4qcZA=",
|
||||||
"path": "github.com/go-ole/go-ole",
|
"path": "github.com/go-ole/go-ole",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue