aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordm4 <dm4@skymizer.com>2019-03-21 02:46:07 +0800
committerhydai <hydai@skymizer.com>2019-04-11 18:16:52 +0800
commit8d0abcaefbd5f6ae5c8a3eae7446aaabcade11bf (patch)
tree67e0a39e7263a43edced3665059690b08de78018
parent27b5f88f0f8d3d6e3e478e43180ed79e3f8ae28e (diff)
downloaddexon-8d0abcaefbd5f6ae5c8a3eae7446aaabcade11bf.tar.gz
dexon-8d0abcaefbd5f6ae5c8a3eae7446aaabcade11bf.tar.zst
dexon-8d0abcaefbd5f6ae5c8a3eae7446aaabcade11bf.zip
core/vm: add dvm, a wasm-based virtual machine
DVM follows DEXON VM interface. It checks bytecode and executes it in a WASM VM.
-rw-r--r--core/vm/dvm/dvm.go606
-rw-r--r--core/vm/dvm/eei.go1007
-rw-r--r--core/vm/dvm/linker.go119
-rw-r--r--core/vm/vm.go3
-rw-r--r--vendor/github.com/go-interpreter/wagon/LICENSE24
-rw-r--r--vendor/github.com/go-interpreter/wagon/README.md28
-rw-r--r--vendor/github.com/go-interpreter/wagon/disasm/asm.go63
-rw-r--r--vendor/github.com/go-interpreter/wagon/disasm/disasm.go492
-rw-r--r--vendor/github.com/go-interpreter/wagon/disasm/log.go33
-rw-r--r--vendor/github.com/go-interpreter/wagon/doc.go6
-rw-r--r--vendor/github.com/go-interpreter/wagon/exec/call.go57
-rw-r--r--vendor/github.com/go-interpreter/wagon/exec/const.go21
-rw-r--r--vendor/github.com/go-interpreter/wagon/exec/control.go17
-rw-r--r--vendor/github.com/go-interpreter/wagon/exec/conv.go93
-rw-r--r--vendor/github.com/go-interpreter/wagon/exec/func.go109
-rw-r--r--vendor/github.com/go-interpreter/wagon/exec/func_table.go186
-rw-r--r--vendor/github.com/go-interpreter/wagon/exec/internal/compile/compile.go377
-rw-r--r--vendor/github.com/go-interpreter/wagon/exec/memory.go214
-rw-r--r--vendor/github.com/go-interpreter/wagon/exec/num.go508
-rw-r--r--vendor/github.com/go-interpreter/wagon/exec/parametric.go21
-rw-r--r--vendor/github.com/go-interpreter/wagon/exec/reinterp.go29
-rw-r--r--vendor/github.com/go-interpreter/wagon/exec/var.go31
-rw-r--r--vendor/github.com/go-interpreter/wagon/exec/vm.go462
-rw-r--r--vendor/github.com/go-interpreter/wagon/go.mod1
-rw-r--r--vendor/github.com/go-interpreter/wagon/internal/stack/stack.go40
-rw-r--r--vendor/github.com/go-interpreter/wagon/validate/error.go74
-rw-r--r--vendor/github.com/go-interpreter/wagon/validate/log.go26
-rw-r--r--vendor/github.com/go-interpreter/wagon/validate/operand.go13
-rw-r--r--vendor/github.com/go-interpreter/wagon/validate/validate.go374
-rw-r--r--vendor/github.com/go-interpreter/wagon/validate/vm.go222
-rw-r--r--vendor/github.com/go-interpreter/wagon/wasm/doc.go6
-rw-r--r--vendor/github.com/go-interpreter/wagon/wasm/encode.go70
-rw-r--r--vendor/github.com/go-interpreter/wagon/wasm/imports.go226
-rw-r--r--vendor/github.com/go-interpreter/wagon/wasm/index.go180
-rw-r--r--vendor/github.com/go-interpreter/wagon/wasm/init_expr.go172
-rw-r--r--vendor/github.com/go-interpreter/wagon/wasm/internal/readpos/readpos.go30
-rw-r--r--vendor/github.com/go-interpreter/wagon/wasm/leb128/read.go72
-rw-r--r--vendor/github.com/go-interpreter/wagon/wasm/leb128/write.go60
-rw-r--r--vendor/github.com/go-interpreter/wagon/wasm/log.go25
-rw-r--r--vendor/github.com/go-interpreter/wagon/wasm/module.go167
-rw-r--r--vendor/github.com/go-interpreter/wagon/wasm/operators/call.go10
-rw-r--r--vendor/github.com/go-interpreter/wagon/wasm/operators/comp.go46
-rw-r--r--vendor/github.com/go-interpreter/wagon/wasm/operators/const.go16
-rw-r--r--vendor/github.com/go-interpreter/wagon/wasm/operators/control.go23
-rw-r--r--vendor/github.com/go-interpreter/wagon/wasm/operators/conv.go64
-rw-r--r--vendor/github.com/go-interpreter/wagon/wasm/operators/memory.go41
-rw-r--r--vendor/github.com/go-interpreter/wagon/wasm/operators/num.go76
-rw-r--r--vendor/github.com/go-interpreter/wagon/wasm/operators/op.go86
-rw-r--r--vendor/github.com/go-interpreter/wagon/wasm/operators/parametric.go10
-rw-r--r--vendor/github.com/go-interpreter/wagon/wasm/operators/reinterp.go16
-rw-r--r--vendor/github.com/go-interpreter/wagon/wasm/operators/var.go13
-rw-r--r--vendor/github.com/go-interpreter/wagon/wasm/read.go64
-rw-r--r--vendor/github.com/go-interpreter/wagon/wasm/section.go1205
-rw-r--r--vendor/github.com/go-interpreter/wagon/wasm/types.go352
-rw-r--r--vendor/github.com/go-interpreter/wagon/wast/write.go493
-rw-r--r--vendor/vendor.json60
56 files changed, 8838 insertions, 1 deletions
diff --git a/core/vm/dvm/dvm.go b/core/vm/dvm/dvm.go
new file mode 100644
index 000000000..44f5434da
--- /dev/null
+++ b/core/vm/dvm/dvm.go
@@ -0,0 +1,606 @@
+// 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 dvm
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "math/big"
+
+ "github.com/dexon-foundation/dexon/common"
+ "github.com/dexon-foundation/dexon/core"
+ "github.com/dexon-foundation/dexon/core/vm"
+ "github.com/dexon-foundation/dexon/crypto"
+ "github.com/dexon-foundation/dexon/params"
+
+ "github.com/go-interpreter/wagon/exec"
+ "github.com/go-interpreter/wagon/wasm"
+)
+
+type terminationType int
+
+// List of termination reasons
+const (
+ TerminateFinish = iota
+ TerminateRevert
+ TerminateSuicide
+ TerminateInvalid
+)
+
+// emptyCodeHash is used by create to ensure deployment is disallowed to already
+// deployed contract addresses (relevant after the account abstraction).
+var (
+ emptyCodeHash = crypto.Keccak256Hash(nil)
+ errExecutionReverted = errors.New("dvm: execution reverted")
+ errMaxCodeSizeExceeded = errors.New("dvm: max code size exceeded")
+ u256Len = 32
+ u128Len = 16
+ maxCallDepth = 1024
+ sentinelContractAddress = "0x000000000000000000000000000000000000000a"
+)
+
+type DVM struct {
+ // Context provides auxiliary blockchain related information
+ vm.Context
+ // StateDB gives access to the underlying state
+ statedb vm.StateDB
+ // Depth is the current call stack
+ depth int
+
+ // chainConfig contains information about the current chain
+ chainConfig *params.ChainConfig
+ // chain rules contains the chain rules for the current epoch
+ chainRules params.Rules
+ // abort is used to abort the SQLVM calling operations
+ // NOTE: must be set atomically
+ abort int32
+ // callGasTemp holds the gas available for the current call. This is needed because the
+ // available gas is calculated in gasCall* according to the 63/64 rule and later
+ // applied in opCall*.
+ callGasTemp uint64
+
+ // vm is wasm VM to execute bytecode
+ vm *exec.VM
+
+ gasTable params.GasTable
+ contract *vm.Contract
+ returnData []byte
+ terminationType terminationType
+ staticMode bool
+
+ metering bool
+ meteringContract *vm.Contract
+ meteringModule *wasm.Module
+ meteringStartIndex int64
+}
+
+type Config struct {
+ Metering bool
+}
+
+func init() {
+ vm.Register(vm.DVM, &DVM{})
+}
+
+// NewDVM creates a new wagon-based ewasm vm.
+func NewDVM(statedb vm.StateDB, config Config) *DVM {
+ ctx := vm.Context{
+ CanTransfer: core.CanTransfer,
+ Transfer: core.Transfer,
+ }
+ dvm := &DVM{
+ Context: ctx,
+ statedb: statedb,
+ metering: config.Metering,
+ }
+
+ if dvm.metering {
+ meteringContractAddress := common.HexToAddress(sentinelContractAddress)
+ meteringCode := dvm.StateDB().GetCode(meteringContractAddress)
+
+ var err error
+ dvm.meteringModule, err = wasm.ReadModule(bytes.NewReader(meteringCode), WrappedModuleResolver(dvm))
+ if err != nil {
+ panic(fmt.Sprintf("Error loading the metering contract: %v", err))
+ }
+ // TODO when the metering contract abides by that rule, check that it
+ // only exports "main" and "memory".
+ dvm.meteringStartIndex = int64(dvm.meteringModule.Export.Entries["main"].Index)
+ mainSig := dvm.meteringModule.FunctionIndexSpace[dvm.meteringStartIndex].Sig
+ if len(mainSig.ParamTypes) != 0 || len(mainSig.ReturnTypes) != 0 {
+ panic(fmt.Sprintf("Invalid main function for the metering contract: index=%d sig=%v", dvm.meteringStartIndex, mainSig))
+ }
+ }
+
+ return dvm
+}
+
+func (dvm *DVM) StateDB() vm.StateDB {
+ return dvm.statedb
+}
+
+func (dvm *DVM) Create(caller vm.ContractRef, code []byte, gas uint64, value *big.Int, in vm.Interpreter) ([]byte, common.Address, uint64, error) {
+ contractAddr := crypto.CreateAddress(caller.Address(), in.(*DVM).statedb.GetNonce(caller.Address()))
+ return in.(*DVM).create(caller, &vm.CodeAndHash{Code: code}, gas, value, contractAddr)
+}
+
+func (dvm *DVM) Create2(caller vm.ContractRef, code []byte, gas uint64, endowment *big.Int, salt *big.Int, in vm.Interpreter) ([]byte, common.Address, uint64, error) {
+ codeAndHash := &vm.CodeAndHash{Code: code}
+ contractAddr := crypto.CreateAddress2(caller.Address(), common.BigToHash(salt), codeAndHash.Hash().Bytes())
+ return dvm.create(caller, codeAndHash, gas, endowment, contractAddr)
+}
+
+// Call executes the contract associated with the addr with the given input as
+// parameters. It also handles any necessary value transfer required and takes
+// the necessary steps to create accounts and reverses the state in case of an
+// execution error or failed value transfer.
+func (dvm *DVM) Call(caller vm.ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int, in vm.Interpreter) ([]byte, uint64, error) {
+ // TODO: do we need these checks?
+ // if evm.vmConfig.NoRecursion && evm.depth > 0 {
+ // return nil, gas, nil
+ // }
+
+ // Fail if we're trying to execute above the call depth limit
+ if dvm.depth > int(params.CallCreateDepth) {
+ return nil, gas, vm.ErrDepth
+ }
+ // Fail if we're trying to transfer more than the available balance
+ if !dvm.Context.CanTransfer(dvm.StateDB(), caller.Address(), value) {
+ return nil, gas, vm.ErrInsufficientBalance
+ }
+
+ var (
+ to = vm.AccountRef(addr)
+ snapshot = dvm.StateDB().Snapshot()
+ )
+ if !dvm.StateDB().Exist(addr) {
+ precompiles := vm.PrecompiledContractsByzantium
+ if precompiles[addr] == nil && value.Sign() == 0 {
+ return nil, gas, nil
+ }
+ dvm.StateDB().CreateAccount(addr)
+ }
+ dvm.Transfer(dvm.StateDB(), caller.Address(), to.Address(), value)
+
+ // Initialise a new contract and set the code that is to be used by the EVM.
+ // The contract is a scoped environment for this execution context only.
+ contract := vm.NewContract(caller, to, value, gas)
+ code := dvm.StateDB().GetCode(addr)
+ if len(code) > 0 && code[0] == vm.DVM && vm.MULTIVM {
+ code = code[1:]
+ }
+ codeAndHash := vm.CodeAndHash{Code: code}
+ contract.SetCodeOptionalHash(&addr, &codeAndHash)
+
+ // Even if the account has no code, we need to continue because it might be a precompile
+ ret, err := dvm.run(contract, input, false)
+
+ // When an error was returned by the DVM or when setting the creation code
+ // above we revert to the snapshot and consume any gas remaining. Additionally
+ // when we're in homestead this also counts for code storage gas errors.
+ if err != nil {
+ dvm.StateDB().RevertToSnapshot(snapshot)
+ if err != errExecutionReverted {
+ contract.UseGas(contract.Gas)
+ }
+ }
+ return ret, contract.Gas, err
+}
+
+// CallCode executes the contract associated with the addr with the given input
+// as parameters. It also handles any necessary value transfer required and takes
+// the necessary steps to create accounts and reverses the state in case of an
+// execution error or failed value transfer.
+//
+// CallCode differs from Call in the sense that it executes the given address'
+// code with the caller as context.
+func (dvm *DVM) CallCode(caller vm.ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int, in vm.Interpreter) ([]byte, uint64, error) {
+ // TODO: do we need these checks?
+ // if evm.vmConfig.NoRecursion && evm.depth > 0 {
+ // return nil, gas, nil
+ // }
+
+ // Fail if we're trying to execute above the call depth limit
+ if dvm.depth > int(params.CallCreateDepth) {
+ return nil, gas, vm.ErrDepth
+ }
+ // Fail if we're trying to transfer more than the available balance
+ if !dvm.Context.CanTransfer(dvm.StateDB(), caller.Address(), value) {
+ return nil, gas, vm.ErrInsufficientBalance
+ }
+
+ var (
+ snapshot = dvm.StateDB().Snapshot()
+ to = vm.AccountRef(caller.Address())
+ )
+
+ // initialise a new contract and set the code that is to be used by the
+ // DVM. The contract is a scoped environment for this execution context
+ // only.
+ contract := vm.NewContract(caller, to, value, gas)
+ code := dvm.StateDB().GetCode(addr)
+ if len(code) > 0 && vm.MULTIVM {
+ code = code[1:]
+ }
+ codeAndHash := vm.CodeAndHash{Code: code}
+ contract.SetCodeOptionalHash(&addr, &codeAndHash)
+
+ ret, err := dvm.run(contract, input, false)
+ if err != nil {
+ dvm.StateDB().RevertToSnapshot(snapshot)
+ if err != errExecutionReverted {
+ contract.UseGas(contract.Gas)
+ }
+ }
+ return ret, contract.Gas, err
+}
+
+// DelegateCall executes the contract associated with the addr with the given input
+// as parameters. It reverses the state in case of an execution error.
+//
+// DelegateCall differs from CallCode in the sense that it executes the given address'
+// code with the caller as context and the caller is set to the caller of the caller.
+func (dvm *DVM) DelegateCall(caller vm.ContractRef, addr common.Address, input []byte, gas uint64, in vm.Interpreter) ([]byte, uint64, error) {
+ // TODO: do we need these checks?
+ // if evm.vmConfig.NoRecursion && evm.depth > 0 {
+ // return nil, gas, nil
+ // }
+
+ // Fail if we're trying to execute above the call depth limit
+ if dvm.depth > int(params.CallCreateDepth) {
+ return nil, gas, vm.ErrDepth
+ }
+
+ var (
+ snapshot = dvm.StateDB().Snapshot()
+ to = vm.AccountRef(caller.Address())
+ )
+
+ // Initialise a new contract and make initialise the delegate values
+ contract := vm.NewContract(caller, to, nil, gas).AsDelegate()
+ code := dvm.StateDB().GetCode(addr)
+ if len(code) > 0 && vm.MULTIVM {
+ code = code[1:]
+ }
+ codeAndHash := vm.CodeAndHash{Code: code}
+ contract.SetCodeOptionalHash(&addr, &codeAndHash)
+
+ ret, err := dvm.run(contract, input, false)
+ if err != nil {
+ dvm.StateDB().RevertToSnapshot(snapshot)
+ if err != errExecutionReverted {
+ contract.UseGas(contract.Gas)
+ }
+ }
+ return ret, contract.Gas, err
+}
+
+// StaticCall executes the contract associated with the addr with the given input
+// as parameters while disallowing any modifications to the state during the call.
+// Opcodes that attempt to perform such modifications will result in exceptions
+// instead of performing the modifications.
+func (dvm *DVM) StaticCall(caller vm.ContractRef, addr common.Address, input []byte, gas uint64, in vm.Interpreter) ([]byte, uint64, error) {
+ // TODO: do we need these checks?
+ // if evm.vmConfig.NoRecursion && evm.depth > 0 {
+ // return nil, gas, nil
+ // }
+
+ // Fail if we're trying to execute above the call depth limit
+ if dvm.depth > int(params.CallCreateDepth) {
+ return nil, gas, vm.ErrDepth
+ }
+
+ var (
+ to = vm.AccountRef(addr)
+ snapshot = dvm.StateDB().Snapshot()
+ )
+
+ // Initialise a new contract and set the code that is to be used by the
+ // DVM. The contract is a scoped environment for this execution context
+ // only.
+ contract := vm.NewContract(caller, to, new(big.Int), gas)
+ code := dvm.StateDB().GetCode(addr)
+ if len(code) > 0 && vm.MULTIVM {
+ code = code[1:]
+ }
+ codeAndHash := vm.CodeAndHash{Code: code}
+ contract.SetCodeOptionalHash(&addr, &codeAndHash)
+
+ // We do an AddBalance of zero here, just in order to trigger a touch.
+ // This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
+ // but is the correct thing to do and matters on other networks, in tests, and potential
+ // future scenarios
+ dvm.StateDB().AddBalance(addr, new(big.Int))
+
+ // When an error was returned by the DVM or when setting the creation code
+ // above we revert to the snapshot and consume any gas remaining. Additionally
+ // when we're in Homestead this also counts for code storage gas errors.
+ ret, err := dvm.run(contract, input, true)
+ if err != nil {
+ dvm.StateDB().RevertToSnapshot(snapshot)
+ if err != errExecutionReverted {
+ contract.UseGas(contract.Gas)
+ }
+ }
+ return ret, contract.Gas, err
+}
+
+func (dvm *DVM) create(caller vm.ContractRef, codeAndHash *vm.CodeAndHash, gas uint64, value *big.Int, address common.Address) ([]byte, common.Address, uint64, error) {
+ // Depth check execution. Fail if we're trying to execute above the
+ // limit.
+ if dvm.depth > int(params.CallCreateDepth) {
+ return nil, common.Address{}, gas, vm.ErrDepth
+ }
+ if !dvm.Context.CanTransfer(dvm.statedb, caller.Address(), value) {
+ return nil, common.Address{}, gas, vm.ErrInsufficientBalance
+ }
+ nonce := dvm.statedb.GetNonce(caller.Address())
+ dvm.statedb.SetNonce(caller.Address(), nonce+1)
+
+ // Ensure there's no existing contract already at the designated address
+ contractHash := dvm.statedb.GetCodeHash(address)
+ if dvm.statedb.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) {
+ return nil, common.Address{}, 0, vm.ErrContractAddressCollision
+ }
+
+ // Create a new account on the state
+ snapshot := dvm.statedb.Snapshot()
+ dvm.statedb.CreateAccount(address)
+ dvm.statedb.SetNonce(address, 1)
+ dvm.Context.Transfer(dvm.statedb, caller.Address(), address, value)
+
+ // initialise a new contract and set the code that is to be used by the
+ // DVM. The contract is a scoped environment for this execution context
+ // only.
+ contract := vm.NewContract(caller, vm.AccountRef(address), value, gas)
+ meteredCode, err := dvm.PreContractCreation(codeAndHash.Code, contract)
+ if err != nil {
+ return nil, address, gas, nil
+ }
+ codeAndHash.Code = meteredCode
+ contract.SetCodeOptionalHash(&address, codeAndHash)
+
+ // TODO: do we need these checks?
+ // if evm.vmConfig.NoRecursion && evm.depth > 0 {
+ // return nil, address, gas, nil
+ // }
+
+ ret, err := dvm.run(contract, nil, false)
+
+ // The new contract needs to be metered after it has executed the constructor
+ if err != nil {
+ if dvm.CanRun(contract.Code) {
+ ret, err = dvm.PostContractCreation(ret)
+ }
+ }
+
+ // check whether the max code size has been exceeded
+ maxCodeSizeExceeded := len(ret) > params.MaxCodeSize
+ // if the contract creation ran successfully and no errors were returned
+ // calculate the gas required to store the code. If the code could not
+ // be stored due to not enough gas set an error and let it be handled
+ // by the error checking condition below.
+ if err == nil && !maxCodeSizeExceeded {
+ createDataGas := uint64(len(ret)) * params.CreateDataGas
+ if contract.UseGas(createDataGas) {
+ dvm.statedb.SetCode(address, ret)
+ } else {
+ err = vm.ErrCodeStoreOutOfGas
+ }
+ }
+
+ // When an error was returned by the DVM or when setting the creation code
+ // above we revert to the snapshot and consume any gas remaining. Additionally
+ // when we're in homestead this also counts for code storage gas errors.
+ if maxCodeSizeExceeded || err != nil {
+ dvm.statedb.RevertToSnapshot(snapshot)
+ if err != errExecutionReverted {
+ contract.UseGas(contract.Gas)
+ }
+ }
+ // Assign err if contract code size exceeds the max while the err is still empty.
+ if maxCodeSizeExceeded && err == nil {
+ err = errMaxCodeSizeExceeded
+ }
+
+ return ret, address, contract.Gas, err
+}
+
+// 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 (dvm *DVM) run(contract *vm.Contract, input []byte, ro bool) ([]byte, error) {
+ // Take care of running precompile contracts
+ if contract.CodeAddr != nil {
+ precompiles := vm.PrecompiledContractsByzantium
+ if p := precompiles[*contract.CodeAddr]; p != nil {
+ return vm.RunPrecompiledContract(p, input, contract)
+ }
+ }
+
+ // Increment the call depth which is restricted to 1024
+ dvm.depth++
+ defer func() { dvm.depth-- }()
+
+ fmt.Printf("contract.Code %v\n", contract.Code)
+ dvm.contract = contract
+ dvm.contract.Input = input
+
+ module, err := wasm.ReadModule(bytes.NewReader(contract.Code), WrappedModuleResolver(dvm))
+ if err != nil {
+ dvm.terminationType = TerminateInvalid
+ return nil, fmt.Errorf("Error decoding module at address %s: %v", contract.Address().Hex(), err)
+ }
+
+ wavm, err := exec.NewVM(module)
+ if err != nil {
+ dvm.terminationType = TerminateInvalid
+ return nil, fmt.Errorf("could not create the vm: %v", err)
+ }
+ wavm.RecoverPanic = true
+ dvm.vm = wavm
+
+ mainIndex, err := validateModule(module)
+ if err != nil {
+ dvm.terminationType = TerminateInvalid
+ return nil, err
+ }
+
+ // Check input and output types
+ sig := module.FunctionIndexSpace[mainIndex].Sig
+ if len(sig.ParamTypes) == 0 && len(sig.ReturnTypes) == 0 {
+ _, err = wavm.ExecCode(int64(mainIndex))
+
+ if err != nil && err != errExecutionReverted {
+ dvm.terminationType = TerminateInvalid
+ }
+
+ if dvm.StateDB().HasSuicided(contract.Address()) {
+ dvm.StateDB().AddRefund(params.SuicideRefundGas)
+ err = nil
+ }
+
+ return dvm.returnData, err
+ }
+
+ dvm.terminationType = TerminateInvalid
+ return nil, errors.New("Could not find a suitable 'main' function in that contract")
+}
+
+func validateModule(m *wasm.Module) (int, error) {
+ // A module should not have a start section
+ if m.Start != nil {
+ return -1, fmt.Errorf("Module has a start section")
+ }
+
+ // Only two exports are authorized: "main" and "memory"
+ if m.Export == nil {
+ return -1, fmt.Errorf("Module has no exports instead of 2")
+ }
+ if len(m.Export.Entries) != 2 {
+ return -1, fmt.Errorf("Module has %d exports instead of 2", len(m.Export.Entries))
+ }
+
+ mainIndex := -1
+ for name, entry := range m.Export.Entries {
+ switch name {
+ case "main":
+ if entry.Kind != wasm.ExternalFunction {
+ return -1, fmt.Errorf("Main is not a function in module")
+ }
+ mainIndex = int(entry.Index)
+ case "memory":
+ if entry.Kind != wasm.ExternalMemory {
+ return -1, fmt.Errorf("'memory' is not a memory in module")
+ }
+ default:
+ return -1, fmt.Errorf("A symbol named %s has been exported. Only main and memory should exist", name)
+ }
+ }
+
+ if m.Import != nil {
+ OUTER:
+ for _, entry := range m.Import.Entries {
+ if entry.ModuleName == "ethereum" {
+ if entry.Type.Kind() == wasm.ExternalFunction {
+ for _, name := range eeiFunctionList {
+ if name == entry.FieldName {
+ continue OUTER
+ }
+ }
+ return -1, fmt.Errorf("%s could not be found in the list of ethereum-provided functions", entry.FieldName)
+ }
+ }
+ }
+ }
+
+ return mainIndex, nil
+}
+
+// CanRun checks the binary for a WASM header and accepts the binary blob
+// if it matches.
+func (dvm *DVM) CanRun(file []byte) bool {
+ // Check the header
+ if len(file) < 4 || string(file[:4]) != "\000asm" {
+ return false
+ }
+
+ return true
+}
+
+// PreContractCreation meters the contract's its init code before it
+// is run.
+func (dvm *DVM) PreContractCreation(code []byte, contract *vm.Contract) ([]byte, error) {
+ savedContract := dvm.contract
+ dvm.contract = contract
+
+ defer func() {
+ dvm.contract = savedContract
+ }()
+
+ if dvm.metering {
+ metered, _, err := sentinel(dvm, code)
+ if len(metered) < 5 || err != nil {
+ return nil, fmt.Errorf("Error metering the init contract code, err=%v", err)
+ }
+ return metered, nil
+ }
+ return code, nil
+}
+
+// PostContractCreation meters the contract once its init code has
+// been run. It also validates the module's format before it is to
+// be committed to disk.
+func (dvm *DVM) PostContractCreation(code []byte) ([]byte, error) {
+ // If a REVERT has been encountered, then return the code and
+ if dvm.terminationType == TerminateRevert {
+ return nil, errExecutionReverted
+ }
+
+ if dvm.CanRun(code) {
+ if dvm.metering {
+ meteredCode, _, err := sentinel(dvm, code)
+ code = meteredCode
+ if len(code) < 5 || err != nil {
+ return nil, fmt.Errorf("Error metering the generated contract code, err=%v", err)
+ }
+
+ if len(code) < 8 {
+ return nil, fmt.Errorf("Invalid contract code")
+ }
+ }
+
+ if len(code) > 8 {
+ // Check the validity of the module
+ m, err := wasm.DecodeModule(bytes.NewReader(code))
+ if err != nil {
+ return nil, fmt.Errorf("Error decoding the module produced by init code: %v", err)
+ }
+
+ _, err = validateModule(m)
+ if err != nil {
+ dvm.terminationType = TerminateInvalid
+ return nil, err
+ }
+ }
+ }
+
+ return code, nil
+}
diff --git a/core/vm/dvm/eei.go b/core/vm/dvm/eei.go
new file mode 100644
index 000000000..df97438b6
--- /dev/null
+++ b/core/vm/dvm/eei.go
@@ -0,0 +1,1007 @@
+// 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 dvm
+
+import (
+ "fmt"
+ "math/big"
+ "reflect"
+
+ "github.com/dexon-foundation/dexon/common"
+ "github.com/dexon-foundation/dexon/core/types"
+ "github.com/dexon-foundation/dexon/core/vm"
+ "github.com/dexon-foundation/dexon/crypto"
+
+ "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
+ GasCostNewAccount = 25000
+ 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 *DVM) gasAccounting(cost uint64) {
+ if in.contract == nil {
+ panic("nil contract")
+ }
+ if cost > in.contract.Gas {
+ panic(fmt.Sprintf("out of gas %d > %d", cost, in.contract.Gas))
+ }
+ in.contract.Gas -= cost
+}
+
+func getDebugFuncs(in *DVM) []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 *DVM, 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 *DVM, 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 *DVM) []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[1],
+ 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[1],
+ 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[7],
+ 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 useGas(p *exec.Process, in *DVM, amount int64) {
+ in.gasAccounting(uint64(amount))
+}
+
+func getAddress(p *exec.Process, in *DVM, resultOffset int32) {
+ in.gasAccounting(GasCostBase)
+ contractBytes := in.contract.CodeAddr.Bytes()
+ p.WriteAt(contractBytes, int64(resultOffset))
+}
+
+func getExternalBalance(p *exec.Process, in *DVM, 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 *DVM, number int64, resultOffset int32) int32 {
+ in.gasAccounting(GasCostBlockHash)
+ n := big.NewInt(number)
+ n.Sub(in.Context.BlockNumber, n)
+ if n.Cmp(big.NewInt(256)) > 0 || n.Cmp(big.NewInt(0)) <= 0 {
+ return 1
+ }
+ h := in.GetHash(uint64(number))
+ p.WriteAt(h.Bytes(), int64(resultOffset))
+ return 0
+}
+
+func callCommon(in *DVM, contract, targetContract *vm.Contract, input []byte, value *big.Int, snapshot int, gas int64, ro bool) int32 {
+ if in.depth > maxCallDepth {
+ return ErrEEICallFailure
+ }
+
+ 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 *DVM, gas int64, addressOffset int32, valueOffset int32, dataOffset int32, dataLength int32) int32 {
+ 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.
+ // To be compatible with hera, one must read a u256 value, then check that this is a u128.
+ value := big.NewInt(0).SetBytes(swapEndian(readSize(p, valueOffset, u256Len)))
+ check128bits := big.NewInt(1)
+ check128bits.Lsh(check128bits, 128)
+ if value.Cmp(check128bits) > 0 {
+ return ErrEEICallFailure
+ }
+
+ // Fail if the account's balance is greater than 128bits as discussed
+ // in https://github.com/ewasm/hera/issues/456
+ if in.StateDB().GetBalance(contract.Address()).Cmp(check128bits) > 0 {
+ in.gasAccounting(contract.Gas)
+ return ErrEEICallRevert
+ }
+
+ if in.staticMode && value.Cmp(big.NewInt(0)) != 0 {
+ in.gasAccounting(in.contract.Gas)
+ return ErrEEICallFailure
+ }
+
+ in.gasAccounting(GasCostCall)
+
+ if in.depth > maxCallDepth {
+ return ErrEEICallFailure
+ }
+
+ if value.Cmp(big.NewInt(0)) != 0 {
+ in.gasAccounting(GasCostCallValue)
+ }
+
+ // 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(contract.Address()).Cmp(value) < 0 {
+ return ErrEEICallFailure
+ }
+
+ // Check that the contract exists
+ if !in.StateDB().Exist(addr) {
+ in.gasAccounting(GasCostNewAccount)
+ in.StateDB().CreateAccount(addr)
+ }
+
+ var calleeGas uint64
+ if uint64(gas) > ((63 * contract.Gas) / 64) {
+ calleeGas = contract.Gas - (contract.Gas / 64)
+ } else {
+ calleeGas = uint64(gas)
+ }
+ in.gasAccounting(calleeGas)
+
+ if value.Cmp(big.NewInt(0)) != 0 {
+ calleeGas += GasCostCallStipend
+ }
+
+ // TODO tracing
+
+ // Add amount to recipient
+ in.Transfer(in.StateDB(), contract.Address(), addr, value)
+
+ // Load the contract code in a new VM structure
+ targetContract := vm.NewContract(contract, vm.AccountRef(addr), value, calleeGas)
+ code := in.StateDB().GetCode(addr)
+ if len(code) == 0 {
+ in.contract.Gas += calleeGas
+ return EEICallSuccess
+ }
+ targetContract.SetCallCode(&addr, in.StateDB().GetCodeHash(addr), code)
+
+ savedVM := in.vm
+
+ in.run(targetContract, input, false)
+
+ in.vm = savedVM
+ in.contract = contract
+
+ // Add leftover gas
+ in.contract.Gas += targetContract.Gas
+ defer func() { in.terminationType = TerminateFinish }()
+
+ 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 callDataCopy(p *exec.Process, in *DVM, 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 *DVM) int32 {
+ in.gasAccounting(GasCostBase)
+ return int32(len(in.contract.Input))
+}
+
+func callCode(p *exec.Process, in *DVM, 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 := vm.NewContract(vm.AccountRef(contract.Caller()), vm.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 *DVM, 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 := vm.NewContract(vm.AccountRef(contract.Address()), vm.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 *DVM, gas int64, addressOffset int32, dataOffset int32, dataLength int32) int32 {
+ 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()
+
+ in.gasAccounting(GasCostCall)
+
+ if in.depth > maxCallDepth {
+ return ErrEEICallFailure
+ }
+
+ // Check that the contract exists
+ if !in.StateDB().Exist(addr) {
+ in.gasAccounting(GasCostNewAccount)
+ in.StateDB().CreateAccount(addr)
+ }
+
+ calleeGas := uint64(gas)
+ if calleeGas > ((63 * contract.Gas) / 64) {
+ calleeGas -= ((63 * contract.Gas) / 64)
+ }
+ in.gasAccounting(calleeGas)
+
+ // TODO tracing
+
+ // Add amount to recipient
+ in.Transfer(in.StateDB(), contract.Address(), addr, value)
+
+ // Load the contract code in a new VM structure
+ targetContract := vm.NewContract(contract, vm.AccountRef(addr), value, calleeGas)
+ code := in.StateDB().GetCode(addr)
+ if len(code) == 0 {
+ in.contract.Gas += calleeGas
+ return EEICallSuccess
+ }
+ targetContract.SetCallCode(&addr, in.StateDB().GetCodeHash(addr), code)
+
+ savedVM := in.vm
+ saveStatic := in.staticMode
+ in.staticMode = true
+ defer func() { in.staticMode = saveStatic }()
+
+ in.run(targetContract, input, false)
+
+ in.vm = savedVM
+ in.contract = contract
+
+ // Add leftover gas
+ in.contract.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 storageStore(p *exec.Process, interpreter *DVM, pathOffset int32, valueOffset int32) {
+ if interpreter.staticMode {
+ panic("Static mode violation in storageStore")
+ }
+
+ loc := common.BytesToHash(readSize(p, pathOffset, u256Len))
+ val := common.BytesToHash(readSize(p, valueOffset, u256Len))
+
+ 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 *DVM, pathOffset int32, resultOffset int32) {
+ interpreter.gasAccounting(interpreter.gasTable.SLoad)
+ loc := common.BytesToHash(readSize(p, pathOffset, u256Len))
+ valBytes := interpreter.StateDB().GetState(interpreter.contract.Address(), loc).Bytes()
+ p.WriteAt(valBytes, int64(resultOffset))
+}
+
+func getCaller(p *exec.Process, in *DVM, resultOffset int32) {
+ callerAddress := in.contract.CallerAddress
+ in.gasAccounting(GasCostBase)
+ p.WriteAt(callerAddress.Bytes(), int64(resultOffset))
+}
+
+func getCallValue(p *exec.Process, in *DVM, resultOffset int32) {
+ in.gasAccounting(GasCostBase)
+ p.WriteAt(swapEndian(in.contract.Value.Bytes()), int64(resultOffset))
+}
+
+func codeCopy(p *exec.Process, in *DVM, 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 *DVM) int32 {
+ in.gasAccounting(GasCostBase)
+ code := in.StateDB().GetCode(*in.contract.CodeAddr)
+ return int32(len(code))
+}
+
+func getBlockCoinbase(p *exec.Process, in *DVM, resultOffset int32) {
+ in.gasAccounting(GasCostBase)
+ p.WriteAt(in.Coinbase.Bytes(), int64(resultOffset))
+}
+
+func sentinel(in *DVM, input []byte) ([]byte, uint64, error) {
+ savedContract := in.contract
+ savedVM := in.vm
+ defer func() {
+ in.contract = savedContract
+ in.vm = savedVM
+ }()
+ meteringContractAddress := common.HexToAddress(sentinelContractAddress)
+ meteringCode := in.StateDB().GetCode(meteringContractAddress)
+ in.contract = vm.NewContract(in.contract, vm.AccountRef(meteringContractAddress), &big.Int{}, in.contract.Gas)
+ in.contract.SetCallCode(&meteringContractAddress, crypto.Keccak256Hash(meteringCode), meteringCode)
+ vm, err := exec.NewVM(in.meteringModule)
+ vm.RecoverPanic = true
+ in.vm = vm
+ if err != nil {
+ panic(fmt.Sprintf("Error allocating metering VM: %v", err))
+ }
+ in.contract.Input = input
+ meteredCode, err := in.vm.ExecCode(in.meteringStartIndex)
+ if meteredCode == nil {
+ meteredCode = in.returnData
+ }
+
+ var asBytes []byte
+ if err == nil {
+ asBytes = meteredCode.([]byte)
+ }
+
+ return asBytes, savedContract.Gas - in.contract.Gas, err
+}
+
+func create(p *exec.Process, in *DVM, valueOffset uint32, codeOffset uint32, length uint32, resultOffset uint32) int32 {
+ in.gasAccounting(GasCostCreate)
+ savedVM := in.vm
+ savedContract := in.contract
+ defer func() {
+ in.vm = savedVM
+ in.contract = savedContract
+ }()
+ 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))
+
+ in.terminationType = TerminateFinish
+
+ // 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)
+
+ /* Meter the contract code if metering is enabled */
+ if in.metering {
+ input, _, _ = sentinel(in, input)
+ if len(input) < 5 {
+ return ErrEEICallFailure
+ }
+ }
+
+ _, addr, gasLeft, _ := in.Create(in.contract, input, gas, big.NewInt(0).SetBytes(value), in)
+
+ switch in.terminationType {
+ case TerminateFinish:
+ savedContract.Gas += gasLeft
+ p.WriteAt(addr.Bytes(), int64(resultOffset))
+ return EEICallSuccess
+ case TerminateRevert:
+ savedContract.Gas += gas
+ return ErrEEICallRevert
+ default:
+ savedContract.Gas += gasLeft
+ return ErrEEICallFailure
+ }
+}
+
+func getBlockDifficulty(p *exec.Process, in *DVM, resultOffset int32) {
+ in.gasAccounting(GasCostBase)
+ p.WriteAt(swapEndian(in.Difficulty.Bytes()), int64(resultOffset))
+}
+
+func externalCodeCopy(p *exec.Process, in *DVM, 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 *DVM, 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 *DVM) int64 {
+ in.gasAccounting(GasCostBase)
+ return int64(in.contract.Gas)
+}
+
+func getBlockGasLimit(p *exec.Process, in *DVM) int64 {
+ in.gasAccounting(GasCostBase)
+ return int64(in.GasLimit)
+}
+
+func getTxGasPrice(p *exec.Process, in *DVM, valueOffset int32) {
+ in.gasAccounting(GasCostBase)
+ p.WriteAt(in.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 *DVM, 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(readSize(p, topic1, u256Len)))
+ }
+ if numberOfTopics > 1 {
+ if uint64(len(in.vm.Memory())) <= uint64(topic2) {
+ panic("out of memory")
+ }
+ topics[1] = common.BigToHash(big.NewInt(0).SetBytes(readSize(p, topic2, u256Len)))
+ }
+ if numberOfTopics > 2 {
+ if uint64(len(in.vm.Memory())) <= uint64(topic3) {
+ panic("out of memory")
+ }
+ topics[2] = common.BigToHash(big.NewInt(0).SetBytes(readSize(p, topic3, u256Len)))
+ }
+ if numberOfTopics > 3 {
+ if uint64(len(in.vm.Memory())) <= uint64(topic3) {
+ panic("out of memory")
+ }
+ topics[3] = common.BigToHash(big.NewInt(0).SetBytes(readSize(p, topic4, u256Len)))
+ }
+
+ in.StateDB().AddLog(&types.Log{
+ Address: in.contract.Address(),
+ Topics: topics,
+ Data: data,
+ BlockNumber: in.BlockNumber.Uint64(),
+ })
+}
+
+func getBlockNumber(p *exec.Process, in *DVM) int64 {
+ in.gasAccounting(GasCostBase)
+ return in.BlockNumber.Int64()
+}
+
+func getTxOrigin(p *exec.Process, in *DVM, resultOffset int32) {
+ in.gasAccounting(GasCostBase)
+ p.WriteAt(in.Origin.Big().Bytes(), int64(resultOffset))
+}
+
+func unWindContract(p *exec.Process, in *DVM, dataOffset int32, length int32) {
+ in.returnData = make([]byte, length)
+ p.ReadAt(in.returnData, int64(dataOffset))
+}
+
+func finish(p *exec.Process, in *DVM, dataOffset int32, length int32) {
+ unWindContract(p, in, dataOffset, length)
+
+ in.terminationType = TerminateFinish
+ p.Terminate()
+}
+
+func revert(p *exec.Process, in *DVM, dataOffset int32, length int32) {
+ unWindContract(p, in, dataOffset, length)
+
+ in.terminationType = TerminateRevert
+ p.Terminate()
+}
+
+func getReturnDataSize(p *exec.Process, in *DVM) int32 {
+ in.gasAccounting(GasCostBase)
+ return int32(len(in.returnData))
+}
+
+func returnDataCopy(p *exec.Process, in *DVM, 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 *DVM, addressOffset int32) {
+ contract := in.contract
+ mem := in.vm.Memory()
+
+ balance := in.StateDB().GetBalance(contract.Address())
+
+ addr := common.BytesToAddress(mem[addressOffset : addressOffset+common.AddressLength])
+
+ 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().AddBalance(addr, balance)
+ in.StateDB().Suicide(contract.Address())
+
+ // Same as for `revert` and `return`, I need to forcefully terminate
+ // the execution of the contract.
+ in.terminationType = TerminateSuicide
+ p.Terminate()
+}
+
+func getBlockTimestamp(p *exec.Process, in *DVM) int64 {
+ in.gasAccounting(GasCostBase)
+ return in.Time.Int64()
+}
diff --git a/core/vm/dvm/linker.go b/core/vm/dvm/linker.go
new file mode 100644
index 000000000..eb57f8b79
--- /dev/null
+++ b/core/vm/dvm/linker.go
@@ -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 dvm
+
+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(dvm *DVM, name string) (*wasm.Module, error) {
+ if name == "debug" {
+ debugModule := wasm.NewModule()
+ debugModule.Types = eeiTypes
+ debugModule.FunctionIndexSpace = getDebugFuncs(dvm)
+ 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 != "ethereum" {
+ return nil, fmt.Errorf("Unknown module name: %s", name)
+ }
+
+ m := wasm.NewModule()
+ m.Types = eeiTypes
+ m.FunctionIndexSpace = eeiFuncs(dvm)
+
+ 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(dvm *DVM) wasm.ResolveFunc {
+ return func(name string) (*wasm.Module, error) {
+ return ModuleResolver(dvm, name)
+ }
+}
diff --git a/core/vm/vm.go b/core/vm/vm.go
index 99ab2ed90..04e1b1520 100644
--- a/core/vm/vm.go
+++ b/core/vm/vm.go
@@ -9,6 +9,7 @@ import (
const (
EVM = byte(iota)
SQLVM
+ DVM
)
var (
@@ -93,7 +94,7 @@ func StaticCall(caller ContractRef, addr common.Address, input []byte,
func getVMAndCode(code []byte) (byte, []byte) {
if MULTIVM && len(code) > 0 {
switch code[0] {
- case EVM, SQLVM:
+ case EVM, SQLVM, DVM:
return code[0], code[1:]
default:
return EVM, code
diff --git a/vendor/github.com/go-interpreter/wagon/LICENSE b/vendor/github.com/go-interpreter/wagon/LICENSE
new file mode 100644
index 000000000..e46a6de2c
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/LICENSE
@@ -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.
+
diff --git a/vendor/github.com/go-interpreter/wagon/README.md b/vendor/github.com/go-interpreter/wagon/README.md
new file mode 100644
index 000000000..3d2558e3d
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/README.md
@@ -0,0 +1,28 @@
+wagon
+=====
+
+[![Build Status](https://travis-ci.org/go-interpreter/wagon.svg?branch=master)](https://travis-ci.org/go-interpreter/wagon)
+[![codecov](https://codecov.io/gh/go-interpreter/wagon/branch/master/graph/badge.svg)](https://codecov.io/gh/go-interpreter/wagon)
+[![GoDoc](https://godoc.org/github.com/go-interpreter/wagon?status.svg)](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`.
diff --git a/vendor/github.com/go-interpreter/wagon/disasm/asm.go b/vendor/github.com/go-interpreter/wagon/disasm/asm.go
new file mode 100644
index 000000000..2f888c724
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/disasm/asm.go
@@ -0,0 +1,63 @@
+// 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"
+ "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.Immediates[0].(wasm.BlockType)))
+ 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
+}
diff --git a/vendor/github.com/go-interpreter/wagon/disasm/disasm.go b/vendor/github.com/go-interpreter/wagon/disasm/disasm.go
new file mode 100644
index 000000000..dffe23732
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/disasm/disasm.go
@@ -0,0 +1,492 @@
+// 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")
+
+// NewDisassembly disassembles the given function. It also takes the function's
+// parent module as an argument for locating any other functions referenced by
+// fn.
+func NewDisassembly(fn wasm.Function, module *wasm.Module) (*Disassembly, error) {
+ code := fn.Body.Code
+ instrs, err := Disassemble(code)
+ if err != nil {
+ return nil, err
+ }
+ 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 _, instr := range instrs {
+ logger.Printf("stack top is %d", stackDepths.Top())
+ opStr := instr.Op
+ op := opStr.Code
+ 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(stackDepths.Top())
+ blockIndices.Push(uint64(curIndex))
+ if !instr.Unreachable {
+ blockPolymorphicOps = append(blockPolymorphicOps, []int{})
+ }
+ }
+
+ case ops.Block, ops.Loop, ops.If:
+ sig := uint32(instr.Immediates[0].(wasm.BlockType))
+ 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))
+ case ops.Br, ops.BrIf:
+ depth := instr.Immediates[0].(uint32)
+ 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 := instr.Immediates[0].(uint32)
+ for i := uint32(0); i < targetCount; i++ {
+ entry := instr.Immediates[i+1].(uint32)
+
+ 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 := instr.Immediates[targetCount+1].(uint32)
+
+ 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 := instr.Immediates[0].(uint32)
+ 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:
+ 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
+ }
+ }
+ }
+
+ 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
+}
+
+// Disassemble disassembles a given function body into a set of instructions. It won't check operations for validity.
+func Disassemble(code []byte) ([]Instr, error) {
+ reader := bytes.NewReader(code)
+ var out []Instr
+ for {
+ op, err := reader.ReadByte()
+ if err == io.EOF {
+ break
+ } else if err != nil {
+ return nil, err
+ }
+
+ opStr, err := ops.New(op)
+ if err != nil {
+ return nil, err
+ }
+ instr := Instr{
+ Op: opStr,
+ }
+
+ switch op {
+ case ops.Block, ops.Loop, ops.If:
+ sig, err := leb128.ReadVarint32(reader)
+ if err != nil {
+ return nil, err
+ }
+ 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)
+ case ops.BrTable:
+ 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)
+ }
+
+ defaultTarget, err := leb128.ReadVarUint32(reader)
+ if err != nil {
+ return nil, err
+ }
+ instr.Immediates = append(instr.Immediates, defaultTarget)
+ 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)
+ }
+ 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)
+ 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))
+ }
+ out = append(out, instr)
+ }
+ return out, nil
+}
diff --git a/vendor/github.com/go-interpreter/wagon/disasm/log.go b/vendor/github.com/go-interpreter/wagon/disasm/log.go
new file mode 100644
index 000000000..4b6912ccf
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/disasm/log.go
@@ -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)
+}
diff --git a/vendor/github.com/go-interpreter/wagon/doc.go b/vendor/github.com/go-interpreter/wagon/doc.go
new file mode 100644
index 000000000..cdcbfc75c
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/doc.go
@@ -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
diff --git a/vendor/github.com/go-interpreter/wagon/exec/call.go b/vendor/github.com/go-interpreter/wagon/exec/call.go
new file mode 100644
index 000000000..0250364e3
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/exec/call.go
@@ -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))
+}
diff --git a/vendor/github.com/go-interpreter/wagon/exec/const.go b/vendor/github.com/go-interpreter/wagon/exec/const.go
new file mode 100644
index 000000000..08ebe2ba5
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/exec/const.go
@@ -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())
+}
diff --git a/vendor/github.com/go-interpreter/wagon/exec/control.go b/vendor/github.com/go-interpreter/wagon/exec/control.go
new file mode 100644
index 000000000..89545a3c6
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/exec/control.go
@@ -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() {}
diff --git a/vendor/github.com/go-interpreter/wagon/exec/conv.go b/vendor/github.com/go-interpreter/wagon/exec/conv.go
new file mode 100644
index 000000000..ce7e704a8
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/exec/conv.go
@@ -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()))
+}
diff --git a/vendor/github.com/go-interpreter/wagon/exec/func.go b/vendor/github.com/go-interpreter/wagon/exec/func.go
new file mode 100644
index 000000000..3aa5974af
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/exec/func.go
@@ -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)
+ }
+}
diff --git a/vendor/github.com/go-interpreter/wagon/exec/func_table.go b/vendor/github.com/go-interpreter/wagon/exec/func_table.go
new file mode 100644
index 000000000..d265671b8
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/exec/func_table.go
@@ -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
+}
diff --git a/vendor/github.com/go-interpreter/wagon/exec/internal/compile/compile.go b/vendor/github.com/go-interpreter/wagon/exec/internal/compile/compile.go
new file mode 100644
index 000000000..3441cbe3a
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/exec/internal/compile/compile.go
@@ -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
+}
diff --git a/vendor/github.com/go-interpreter/wagon/exec/memory.go b/vendor/github.com/go-interpreter/wagon/exec/memory.go
new file mode 100644
index 000000000..cc8ab7454
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/exec/memory.go
@@ -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))
+}
diff --git a/vendor/github.com/go-interpreter/wagon/exec/num.go b/vendor/github.com/go-interpreter/wagon/exec/num.go
new file mode 100644
index 000000000..b069d0cf3
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/exec/num.go
@@ -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)
+}
diff --git a/vendor/github.com/go-interpreter/wagon/exec/parametric.go b/vendor/github.com/go-interpreter/wagon/exec/parametric.go
new file mode 100644
index 000000000..f29c63c91
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/exec/parametric.go
@@ -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)
+ }
+}
diff --git a/vendor/github.com/go-interpreter/wagon/exec/reinterp.go b/vendor/github.com/go-interpreter/wagon/exec/reinterp.go
new file mode 100644
index 000000000..888c02589
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/exec/reinterp.go
@@ -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()))
+}
diff --git a/vendor/github.com/go-interpreter/wagon/exec/var.go b/vendor/github.com/go-interpreter/wagon/exec/var.go
new file mode 100644
index 000000000..fe761984d
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/exec/var.go
@@ -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()
+}
diff --git a/vendor/github.com/go-interpreter/wagon/exec/vm.go b/vendor/github.com/go-interpreter/wagon/exec/vm.go
new file mode 100644
index 000000000..04b56adc1
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/exec/vm.go
@@ -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.NewDisassembly(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
+}
diff --git a/vendor/github.com/go-interpreter/wagon/go.mod b/vendor/github.com/go-interpreter/wagon/go.mod
new file mode 100644
index 000000000..280ba086d
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/go.mod
@@ -0,0 +1 @@
+module github.com/go-interpreter/wagon
diff --git a/vendor/github.com/go-interpreter/wagon/internal/stack/stack.go b/vendor/github.com/go-interpreter/wagon/internal/stack/stack.go
new file mode 100644
index 000000000..4481da022
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/internal/stack/stack.go
@@ -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)
+}
diff --git a/vendor/github.com/go-interpreter/wagon/validate/error.go b/vendor/github.com/go-interpreter/wagon/validate/error.go
new file mode 100644
index 000000000..3bbe1eb2b
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/validate/error.go
@@ -0,0 +1,74 @@
+// 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 validate
+
+import (
+ "errors"
+ "fmt"
+
+ "github.com/go-interpreter/wagon/wasm"
+ ops "github.com/go-interpreter/wagon/wasm/operators"
+)
+
+type Error struct {
+ Offset int // Byte offset in the bytecode vector where the error occurs.
+ Function int // Index into the function index space for the offending function.
+ Err error
+}
+
+func (e Error) Error() string {
+ return fmt.Sprintf("error while validating function %d at offset %d: %v", e.Function, e.Offset, e.Err)
+}
+
+var ErrStackUnderflow = errors.New("validate: stack underflow")
+
+type InvalidImmediateError struct {
+ ImmType string
+ OpName string
+}
+
+func (e InvalidImmediateError) Error() string {
+ return fmt.Sprintf("invalid immediate for op %s at (should be %s)", e.OpName, e.ImmType)
+}
+
+type UnmatchedOpError byte
+
+func (e UnmatchedOpError) Error() string {
+ n1, _ := ops.New(byte(e))
+ return fmt.Sprintf("encountered unmatched %s", n1.Name)
+}
+
+type InvalidLabelError uint32
+
+func (e InvalidLabelError) Error() string {
+ return fmt.Sprintf("invalid nesting depth %d", uint32(e))
+}
+
+type InvalidLocalIndexError uint32
+
+func (e InvalidLocalIndexError) Error() string {
+ return fmt.Sprintf("invalid index for local variable %d", uint32(e))
+}
+
+type InvalidTypeError struct {
+ Wanted wasm.ValueType
+ Got wasm.ValueType
+}
+
+func (e InvalidTypeError) Error() string {
+ return fmt.Sprintf("invalid type, got: %v, wanted: %v", e.Got, e.Wanted)
+}
+
+type InvalidElementIndexError uint32
+
+func (e InvalidElementIndexError) Error() string {
+ return fmt.Sprintf("invalid element index %d", uint32(e))
+}
+
+type NoSectionError wasm.SectionID
+
+func (e NoSectionError) Error() string {
+ return fmt.Sprintf("reference to non existant section (id %d) in module", wasm.SectionID(e))
+}
diff --git a/vendor/github.com/go-interpreter/wagon/validate/log.go b/vendor/github.com/go-interpreter/wagon/validate/log.go
new file mode 100644
index 000000000..447166624
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/validate/log.go
@@ -0,0 +1,26 @@
+// 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 validate
+
+import (
+ "io/ioutil"
+ "log"
+ "os"
+)
+
+var PrintDebugInfo = false
+
+var logger *log.Logger
+
+func init() {
+ w := ioutil.Discard
+
+ if PrintDebugInfo {
+ w = os.Stderr
+ }
+
+ logger = log.New(w, "", log.Lshortfile)
+ log.SetFlags(log.Lshortfile)
+}
diff --git a/vendor/github.com/go-interpreter/wagon/validate/operand.go b/vendor/github.com/go-interpreter/wagon/validate/operand.go
new file mode 100644
index 000000000..6b951211c
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/validate/operand.go
@@ -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 validate
+
+import (
+ "github.com/go-interpreter/wagon/wasm"
+)
+
+type operand struct {
+ Type wasm.ValueType
+}
diff --git a/vendor/github.com/go-interpreter/wagon/validate/validate.go b/vendor/github.com/go-interpreter/wagon/validate/validate.go
new file mode 100644
index 000000000..dac219bb1
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/validate/validate.go
@@ -0,0 +1,374 @@
+// 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 validate provides functions for validating WebAssembly modules.
+package validate
+
+import (
+ "bytes"
+ "io"
+
+ "github.com/go-interpreter/wagon/wasm"
+ ops "github.com/go-interpreter/wagon/wasm/operators"
+)
+
+// vibhavp: TODO: We do not verify whether blocks don't access for the parent block, do that.
+func verifyBody(fn *wasm.FunctionSig, body *wasm.FunctionBody, module *wasm.Module) (*mockVM, error) {
+ vm := &mockVM{
+ stack: []operand{},
+ stackTop: 0,
+
+ code: bytes.NewReader(body.Code),
+ origLength: len(body.Code),
+
+ polymorphic: false,
+ blocks: []block{},
+ curFunc: fn,
+ }
+
+ localVariables := []operand{}
+
+ // Paramters count as local variables too
+ // This comment explains how local variables work: https://github.com/WebAssembly/design/issues/1037#issuecomment-293505798
+ for _, entry := range fn.ParamTypes {
+ localVariables = append(localVariables, operand{entry})
+ }
+
+ for _, entry := range body.Locals {
+ vars := make([]operand, entry.Count)
+ for i := uint32(0); i < entry.Count; i++ {
+ vars[i].Type = entry.Type
+ logger.Printf("Var %v", entry.Type)
+ }
+ localVariables = append(localVariables, vars...)
+ }
+
+ for {
+ op, err := vm.code.ReadByte()
+ if err == io.EOF {
+ break
+ } else if err != nil {
+ return vm, err
+ }
+
+ opStruct, err := ops.New(op)
+ if err != nil {
+ return vm, err
+ }
+
+ logger.Printf("PC: %d OP: %s polymorphic: %v", vm.pc(), opStruct.Name, vm.isPolymorphic())
+
+ if !opStruct.Polymorphic {
+ if err := vm.adjustStack(opStruct); err != nil {
+ return vm, err
+ }
+ }
+
+ switch op {
+ case ops.If, ops.Block, ops.Loop:
+ sig, err := vm.fetchVarInt()
+ if err != nil {
+ return vm, err
+ }
+
+ switch wasm.ValueType(sig) {
+ case wasm.ValueTypeI32, wasm.ValueTypeI64, wasm.ValueTypeF32, wasm.ValueTypeF64, wasm.ValueType(wasm.BlockTypeEmpty):
+ vm.pushBlock(op, wasm.BlockType(sig))
+ default:
+ if !vm.isPolymorphic() {
+ return vm, InvalidImmediateError{"block_type", opStruct.Name}
+ }
+ }
+
+ case ops.Else:
+ block := vm.topBlock()
+ if block == nil || block.op != ops.If {
+ return vm, UnmatchedOpError(op)
+ }
+
+ if block.blockType != wasm.BlockTypeEmpty {
+ top, under := vm.topOperand()
+ if !vm.isPolymorphic() && (under || top.Type != wasm.ValueType(block.blockType)) {
+ return vm, InvalidTypeError{wasm.ValueType(block.blockType), top.Type}
+ }
+ vm.pushOperand(wasm.ValueType(block.blockType))
+ }
+ vm.stackTop = block.stackTop
+ case ops.End:
+ isPolymorphic := vm.isPolymorphic()
+
+ block := vm.popBlock()
+ if block == nil {
+ return vm, UnmatchedOpError(op)
+ }
+
+ if block.blockType != wasm.BlockTypeEmpty {
+ top, under := vm.topOperand()
+ if !isPolymorphic && (under || top.Type != wasm.ValueType(block.blockType)) {
+ return vm, InvalidTypeError{wasm.ValueType(block.blockType), top.Type}
+ }
+ vm.stackTop = block.stackTop
+ vm.pushOperand(wasm.ValueType(block.blockType))
+ vm.stackTop = block.stackTop + 1 // as we pushed an element
+ } else {
+ vm.stackTop = block.stackTop
+ }
+
+ case ops.BrIf, ops.Br:
+ depth, err := vm.fetchVarUint()
+ if err != nil {
+ return vm, err
+ }
+ if err = vm.canBranch(int(depth)); !vm.isPolymorphic() && err != nil {
+ return vm, err
+ }
+ if op == ops.Br {
+ vm.setPolymorphic()
+ }
+ case ops.BrTable:
+ operand, under := vm.popOperand()
+ if !vm.isPolymorphic() && (under || operand.Type != wasm.ValueTypeI32) {
+ return vm, InvalidTypeError{wasm.ValueTypeI32, operand.Type}
+ }
+ // read table entries
+ targetCount, err := vm.fetchVarUint()
+ if err != nil {
+ return vm, err
+ }
+
+ var targetTable []uint32
+ for i := uint32(0); i < targetCount; i++ {
+ entry, err := vm.fetchVarUint()
+ if err != nil {
+ return vm, err
+ }
+ if err = vm.canBranch(int(entry)); !vm.isPolymorphic() && err != nil {
+ return vm, err
+ }
+ targetTable = append(targetTable, entry)
+ }
+
+ defaultTarget, err := vm.fetchVarUint()
+ if err != nil {
+ return vm, err
+ }
+ if err = vm.canBranch(int(defaultTarget)); !vm.isPolymorphic() && err != nil {
+ return vm, err
+ }
+ vm.setPolymorphic()
+
+ case ops.Return:
+ if len(fn.ReturnTypes) > 1 {
+ panic("not implemented")
+ }
+ if len(fn.ReturnTypes) != 0 {
+ // only single returns supported for now
+ top, under := vm.popOperand()
+ if !vm.isPolymorphic() && (under || top.Type != fn.ReturnTypes[0]) {
+ return vm, InvalidTypeError{fn.ReturnTypes[0], top.Type}
+ }
+ }
+ vm.setPolymorphic()
+
+ case ops.Unreachable:
+ vm.setPolymorphic()
+
+ case ops.I32Const:
+ _, err := vm.fetchVarUint()
+ if err != nil {
+ return vm, err
+ }
+ case ops.I64Const:
+ _, err := vm.fetchVarInt64()
+ if err != nil {
+ return vm, err
+ }
+ case ops.F32Const:
+ _, err := vm.fetchUint32()
+ if err != nil {
+ return vm, err
+ }
+ case ops.F64Const:
+ _, err := vm.fetchUint64()
+ if err != nil {
+ return vm, err
+ }
+ case ops.GetLocal, ops.SetLocal, ops.TeeLocal:
+ i, err := vm.fetchVarUint()
+ if err != nil {
+ return vm, err
+ }
+ if int(i) >= len(localVariables) {
+ return vm, InvalidLocalIndexError(i)
+ }
+
+ v := localVariables[i]
+
+ if op == ops.GetLocal {
+ vm.pushOperand(v.Type)
+ } else { // == set_local or tee_local
+ top, under := vm.popOperand()
+ if !vm.isPolymorphic() && (under || top.Type != v.Type) {
+ return vm, InvalidTypeError{v.Type, top.Type}
+ }
+ if op == ops.TeeLocal {
+ vm.pushOperand(v.Type)
+ }
+ }
+
+ case ops.GetGlobal, ops.SetGlobal:
+ index, err := vm.fetchVarUint()
+ if err != nil {
+ return vm, err
+ }
+
+ gv := module.GetGlobal(int(index))
+ if gv == nil {
+ return vm, wasm.InvalidGlobalIndexError(index)
+ }
+ if op == ops.GetGlobal {
+ vm.pushOperand(gv.Type.Type)
+ } else {
+ val, under := vm.popOperand()
+ if !vm.isPolymorphic() && (under || val.Type != gv.Type.Type) {
+ return vm, InvalidTypeError{gv.Type.Type, val.Type}
+ }
+ }
+
+ 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 := vm.fetchVarUint()
+ if err != nil {
+ return vm, err
+ }
+ // offset
+ _, err = vm.fetchVarUint()
+ if err != nil {
+ return vm, err
+ }
+ case ops.CurrentMemory, ops.GrowMemory:
+ _, err := vm.fetchVarUint()
+ if err != nil {
+ return vm, err
+ }
+
+ case ops.Call:
+ index, err := vm.fetchVarUint()
+ if err != nil {
+ return vm, err
+ }
+
+ fn := module.GetFunction(int(index))
+ if fn == nil {
+ return vm, wasm.InvalidFunctionIndexError(index)
+ }
+
+ logger.Printf("Function being called: %v", fn)
+ for index := range fn.Sig.ParamTypes {
+ argType := fn.Sig.ParamTypes[len(fn.Sig.ParamTypes)-index-1]
+ operand, under := vm.popOperand()
+ if !vm.isPolymorphic() && (under || operand.Type != argType) {
+ return vm, InvalidTypeError{argType, operand.Type}
+ }
+ }
+
+ if len(fn.Sig.ReturnTypes) > 0 {
+ vm.pushOperand(fn.Sig.ReturnTypes[0])
+ }
+
+ case ops.CallIndirect:
+ if module.Table == nil || len(module.Table.Entries) == 0 {
+ return vm, NoSectionError(wasm.SectionIDTable)
+ }
+ // The call_indirect process consists of getting two i32 values
+ // off (first from the bytecode stream, and the second from
+ // the stack) and using first as an index into the "Types" section
+ // of the module, while the the second one into the function index
+ // space. The signature of the two elements are then compared
+ // to see if they match, and the call proceeds as normal if they
+ // do.
+ // Since this is possible only during program execution, we only
+ // perform the static check for the function index mentioned
+ // in the bytecode stream here.
+
+ // type index
+ index, err := vm.fetchVarUint()
+ if err != nil {
+ return vm, err
+ }
+
+ fnExpectSig := module.Types.Entries[index]
+
+ if operand, under := vm.popOperand(); !vm.isPolymorphic() && (under || operand.Type != wasm.ValueTypeI32) {
+ return vm, InvalidTypeError{wasm.ValueTypeI32, operand.Type}
+ }
+
+ for index := range fnExpectSig.ParamTypes {
+ argType := fnExpectSig.ParamTypes[len(fnExpectSig.ParamTypes)-index-1]
+ operand, under := vm.popOperand()
+ if !vm.isPolymorphic() && (under || (operand.Type != argType)) {
+ return vm, InvalidTypeError{argType, operand.Type}
+ }
+ }
+
+ if len(fnExpectSig.ReturnTypes) > 0 {
+ vm.pushOperand(fnExpectSig.ReturnTypes[0])
+ }
+
+ case ops.Drop:
+ if _, under := vm.popOperand(); !vm.isPolymorphic() && under {
+ return vm, ErrStackUnderflow
+ }
+
+ case ops.Select:
+ if vm.isPolymorphic() {
+ continue
+ }
+ operands := make([]operand, 2)
+ c, under := vm.popOperand()
+ if under || c.Type != wasm.ValueTypeI32 {
+ return vm, InvalidTypeError{wasm.ValueTypeI32, c.Type}
+ }
+
+ for i := 0; i < 2; i++ {
+ operand, under := vm.popOperand()
+ if !vm.isPolymorphic() && under {
+ return vm, ErrStackUnderflow
+ }
+ operands[i] = operand
+ }
+
+ // last 2 popped values should be of the same type
+ if operands[0].Type != operands[1].Type {
+ return vm, InvalidTypeError{operands[1].Type, operands[2].Type}
+ }
+
+ vm.pushOperand(operands[1].Type)
+ }
+ }
+
+ return vm, nil
+}
+
+// VerifyModule verifies the given module according to WebAssembly verification
+// specs.
+func VerifyModule(module *wasm.Module) error {
+ if module.Function == nil || module.Types == nil || len(module.Types.Entries) == 0 {
+ return nil
+ }
+ if module.Code == nil {
+ return NoSectionError(wasm.SectionIDCode)
+ }
+
+ logger.Printf("There are %d functions", len(module.Function.Types))
+ for i, fn := range module.FunctionIndexSpace {
+ if vm, err := verifyBody(fn.Sig, fn.Body, module); err != nil {
+ return Error{vm.pc(), i, err}
+ }
+ logger.Printf("No errors in function %d", i)
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/go-interpreter/wagon/validate/vm.go b/vendor/github.com/go-interpreter/wagon/validate/vm.go
new file mode 100644
index 000000000..fec06068e
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/validate/vm.go
@@ -0,0 +1,222 @@
+// 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 validate
+
+import (
+ "bytes"
+ "encoding/binary"
+ "io"
+
+ "github.com/go-interpreter/wagon/wasm"
+ "github.com/go-interpreter/wagon/wasm/leb128"
+ ops "github.com/go-interpreter/wagon/wasm/operators"
+)
+
+// mockVM is a minimal implementation of a virtual machine to
+// validate WebAssembly code
+type mockVM struct {
+ stack []operand
+ stackTop int // the top of the operand stack
+ origLength int // the original length of the bytecode stream
+
+ code *bytes.Reader
+
+ polymorphic bool // whether the base implict block has a polymorphic stack
+ blocks []block // a stack of encountered blocks
+
+ curFunc *wasm.FunctionSig
+}
+
+// a block reprsents an instruction sequence preceeded by a control flow operator
+// it is used to verify that the block signature set by the operator is the correct
+// one when the block ends
+type block struct {
+ pc int // the pc where the control flow operator starting the block is located
+ stackTop int // stack top when the block started
+ blockType wasm.BlockType // block_type signature of the control operator
+ op byte // opcode for the operator starting the new block
+ polymorphic bool // whether the block has a polymorphic stack
+ loop bool // whether the block is the body of a loop instruction
+}
+
+func (vm *mockVM) fetchVarUint() (uint32, error) {
+ return leb128.ReadVarUint32(vm.code)
+}
+
+func (vm *mockVM) fetchVarInt() (int32, error) {
+ return leb128.ReadVarint32(vm.code)
+}
+
+func (vm *mockVM) fetchVarInt64() (int64, error) {
+ return leb128.ReadVarint64(vm.code)
+}
+
+func (vm *mockVM) fetchUint32() (uint32, error) {
+ var buf [4]byte
+ _, err := io.ReadFull(vm.code, buf[:])
+ if err != nil {
+ return 0, err
+ }
+ return binary.LittleEndian.Uint32(buf[:]), nil
+}
+
+func (vm *mockVM) fetchUint64() (uint64, error) {
+ var buf [8]byte
+ _, err := io.ReadFull(vm.code, buf[:])
+ if err != nil {
+ return 0, err
+ }
+ return binary.LittleEndian.Uint64(buf[:]), nil
+}
+
+func (vm *mockVM) pushBlock(op byte, blockType wasm.BlockType) {
+ logger.Printf("Pushing block %v", blockType)
+ vm.blocks = append(vm.blocks, block{
+ pc: vm.pc(),
+ stackTop: vm.stackTop,
+ blockType: blockType,
+ polymorphic: vm.isPolymorphic(),
+ op: op,
+ loop: op == ops.Loop,
+ })
+}
+
+// Get a block from it's relative nesting depth
+func (vm *mockVM) getBlockFromDepth(depth int) *block {
+ if depth >= len(vm.blocks) {
+ return nil
+ }
+
+ return &vm.blocks[len(vm.blocks)-1-depth]
+}
+
+// Returns nil if depth is a valid nesting depth value that can be
+// branched to.
+func (vm *mockVM) canBranch(depth int) error {
+ blockType := wasm.BlockTypeEmpty
+
+ block := vm.getBlockFromDepth(depth)
+ // jumping to the start of a loop block doesn't push a value
+ // on the stack.
+ if block == nil {
+ if depth == len(vm.blocks) {
+ //equivalent to a `return', as the function
+ //body is an "implicit" block
+ if len(vm.curFunc.ReturnTypes) != 0 {
+ blockType = wasm.BlockType(vm.curFunc.ReturnTypes[0])
+ }
+ } else {
+ return InvalidLabelError(uint32(depth))
+ }
+ } else if !block.loop {
+ blockType = block.blockType
+ }
+
+ if blockType != wasm.BlockTypeEmpty {
+ top, under := vm.topOperand()
+ if under || top.Type != wasm.ValueType(blockType) {
+ return InvalidTypeError{wasm.ValueType(blockType), top.Type}
+ }
+ }
+
+ return nil
+}
+
+// returns nil in case of an underflow
+func (vm *mockVM) popBlock() *block {
+ if len(vm.blocks) == 0 {
+ return nil
+ }
+
+ stackTop := len(vm.blocks) - 1
+ block := vm.blocks[stackTop]
+ vm.blocks = append(vm.blocks[:stackTop], vm.blocks[stackTop+1:]...)
+
+ return &block
+}
+
+func (vm *mockVM) topBlock() *block {
+ if len(vm.blocks) == 0 {
+ return nil
+ }
+
+ return &vm.blocks[len(vm.blocks)-1]
+}
+
+func (vm *mockVM) topOperand() (o operand, under bool) {
+ stackTop := vm.stackTop - 1
+ if stackTop == -1 {
+ under = true
+ return
+ }
+ o = vm.stack[stackTop]
+ return
+}
+
+func (vm *mockVM) popOperand() (operand, bool) {
+ var o operand
+ stackTop := vm.stackTop - 1
+ if stackTop == -1 {
+ return o, true
+ }
+ o = vm.stack[stackTop]
+ vm.stackTop--
+
+ logger.Printf("Stack after pop is %v. Popped %v", vm.stack[:vm.stackTop], o)
+ return o, false
+}
+
+func (vm *mockVM) pushOperand(t wasm.ValueType) {
+ o := operand{t}
+ logger.Printf("Stack top: %d, Len of stack :%d", vm.stackTop, len(vm.stack))
+ if vm.stackTop == len(vm.stack) {
+ vm.stack = append(vm.stack, o)
+ } else {
+ vm.stack[vm.stackTop] = o
+ }
+ vm.stackTop++
+
+ logger.Printf("Stack after push is %v. Pushed %v", vm.stack[:vm.stackTop], o)
+}
+
+func (vm *mockVM) adjustStack(op ops.Op) error {
+ for _, t := range op.Args {
+ op, under := vm.popOperand()
+ if !vm.isPolymorphic() && (under || op.Type != t) {
+ return InvalidTypeError{t, op.Type}
+ }
+ }
+
+ if op.Returns != wasm.ValueType(wasm.BlockTypeEmpty) {
+ vm.pushOperand(op.Returns)
+ }
+
+ return nil
+}
+
+// setPolymorphic sets the current block as having a polymorphic stack
+// blocks created under it will be polymorphic too. All type-checking
+// is ignored in a polymorhpic stack.
+// (See https://github.com/WebAssembly/design/blob/27ac254c854994103c24834a994be16f74f54186/Semantics.md#validation)
+func (vm *mockVM) setPolymorphic() {
+ if len(vm.blocks) == 0 {
+ vm.polymorphic = true
+ } else {
+
+ vm.blocks[len(vm.blocks)-1].polymorphic = true
+ }
+}
+
+func (vm *mockVM) isPolymorphic() bool {
+ if len(vm.blocks) == 0 {
+ return vm.polymorphic
+ }
+
+ return vm.topBlock().polymorphic
+}
+
+func (vm *mockVM) pc() int {
+ return vm.origLength - vm.code.Len()
+}
diff --git a/vendor/github.com/go-interpreter/wagon/wasm/doc.go b/vendor/github.com/go-interpreter/wagon/wasm/doc.go
new file mode 100644
index 000000000..c782f9b6e
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/wasm/doc.go
@@ -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
diff --git a/vendor/github.com/go-interpreter/wagon/wasm/encode.go b/vendor/github.com/go-interpreter/wagon/wasm/encode.go
new file mode 100644
index 000000000..be54869e8
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/wasm/encode.go
@@ -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
+}
diff --git a/vendor/github.com/go-interpreter/wagon/wasm/imports.go b/vendor/github.com/go-interpreter/wagon/wasm/imports.go
new file mode 100644
index 000000000..95c2db04e
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/wasm/imports.go
@@ -0,0 +1,226 @@
+// 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))
+}
+
+// InvalidImportError is returned when the export of a resolved module doesn't
+// match the signature of its import declaration.
+type InvalidImportError struct {
+ ModuleName string
+ FieldName string
+ TypeIndex uint32
+}
+
+func (e InvalidImportError) Error() string {
+ return fmt.Sprintf("wasm: invalid signature for import %#x with name '%s' in module %s", e.TypeIndex, e.FieldName, e.ModuleName)
+}
+
+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)
+ }
+
+ importIndex := importEntry.Type.(FuncImport).Type
+ if len(fn.Sig.ReturnTypes) != len(module.Types.Entries[importIndex].ReturnTypes) || len(fn.Sig.ParamTypes) != len(module.Types.Entries[importIndex].ParamTypes) {
+ return InvalidImportError{importEntry.ModuleName, importEntry.FieldName, importIndex}
+ }
+ for i, typ := range fn.Sig.ReturnTypes {
+ if typ != module.Types.Entries[importIndex].ReturnTypes[i] {
+ return InvalidImportError{importEntry.ModuleName, importEntry.FieldName, importIndex}
+ }
+ }
+ for i, typ := range fn.Sig.ParamTypes {
+ if typ != module.Types.Entries[importIndex].ParamTypes[i] {
+ return InvalidImportError{importEntry.ModuleName, importEntry.FieldName, importIndex}
+ }
+ }
+ 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
+}
diff --git a/vendor/github.com/go-interpreter/wagon/wasm/index.go b/vendor/github.com/go-interpreter/wagon/wasm/index.go
new file mode 100644
index 000000000..d2b22ec0f
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/wasm/index.go
@@ -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, memory)
+ copy(data[offset:], entry.Data)
+ 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
+}
diff --git a/vendor/github.com/go-interpreter/wagon/wasm/init_expr.go b/vendor/github.com/go-interpreter/wagon/wasm/init_expr.go
new file mode 100644
index 000000000..3a59fb3a3
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/wasm/init_expr.go
@@ -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)))
+ }
+}
diff --git a/vendor/github.com/go-interpreter/wagon/wasm/internal/readpos/readpos.go b/vendor/github.com/go-interpreter/wagon/wasm/internal/readpos/readpos.go
new file mode 100644
index 000000000..709fbe18f
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/wasm/internal/readpos/readpos.go
@@ -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
+}
diff --git a/vendor/github.com/go-interpreter/wagon/wasm/leb128/read.go b/vendor/github.com/go-interpreter/wagon/wasm/leb128/read.go
new file mode 100644
index 000000000..2584da37e
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/wasm/leb128/read.go
@@ -0,0 +1,72 @@
+// 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"
+)
+
+// 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) {
+ var (
+ b = make([]byte, 1)
+ shift uint
+ res uint32
+ err error
+ )
+ for {
+ if _, err = io.ReadFull(r, b); err != nil {
+ return res, err
+ }
+
+ cur := uint32(b[0])
+ res |= (cur & 0x7f) << (shift)
+ if cur&0x80 == 0 {
+ return res, nil
+ }
+ shift += 7
+ }
+}
+
+// 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 := ReadVarint64(r)
+ return int32(n), err
+}
+
+// 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) {
+ var (
+ b = make([]byte, 1)
+ shift uint
+ sign int64 = -1
+ res int64
+ err error
+ )
+
+ for {
+ if _, err = io.ReadFull(r, b); err != nil {
+ return res, err
+ }
+
+ 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, nil
+}
diff --git a/vendor/github.com/go-interpreter/wagon/wasm/leb128/write.go b/vendor/github.com/go-interpreter/wagon/wasm/leb128/write.go
new file mode 100644
index 000000000..9e96cb0f5
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/wasm/leb128/write.go
@@ -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)
+}
diff --git a/vendor/github.com/go-interpreter/wagon/wasm/log.go b/vendor/github.com/go-interpreter/wagon/wasm/log.go
new file mode 100644
index 000000000..bbc9d938c
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/wasm/log.go
@@ -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)
+}
diff --git a/vendor/github.com/go-interpreter/wagon/wasm/module.go b/vendor/github.com/go-interpreter/wagon/wasm/module.go
new file mode 100644
index 000000000..b17e50a53
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/wasm/module.go
@@ -0,0 +1,167 @@
+// 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 {
+ if m.Code == nil {
+ m.Code = &SectionCode{}
+ }
+
+ 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
+}
diff --git a/vendor/github.com/go-interpreter/wagon/wasm/operators/call.go b/vendor/github.com/go-interpreter/wagon/wasm/operators/call.go
new file mode 100644
index 000000000..bd4c3399a
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/wasm/operators/call.go
@@ -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")
+)
diff --git a/vendor/github.com/go-interpreter/wagon/wasm/operators/comp.go b/vendor/github.com/go-interpreter/wagon/wasm/operators/comp.go
new file mode 100644
index 000000000..850e7f264
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/wasm/operators/comp.go
@@ -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)
+)
diff --git a/vendor/github.com/go-interpreter/wagon/wasm/operators/const.go b/vendor/github.com/go-interpreter/wagon/wasm/operators/const.go
new file mode 100644
index 000000000..8bbc0367f
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/wasm/operators/const.go
@@ -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)
+)
diff --git a/vendor/github.com/go-interpreter/wagon/wasm/operators/control.go b/vendor/github.com/go-interpreter/wagon/wasm/operators/control.go
new file mode 100644
index 000000000..b37822773
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/wasm/operators/control.go
@@ -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")
+)
diff --git a/vendor/github.com/go-interpreter/wagon/wasm/operators/conv.go b/vendor/github.com/go-interpreter/wagon/wasm/operators/conv.go
new file mode 100644
index 000000000..7c252fe3a
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/wasm/operators/conv.go
@@ -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")
+)
diff --git a/vendor/github.com/go-interpreter/wagon/wasm/operators/memory.go b/vendor/github.com/go-interpreter/wagon/wasm/operators/memory.go
new file mode 100644
index 000000000..f0a53cb5e
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/wasm/operators/memory.go
@@ -0,0 +1,41 @@
+// 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)
+
+ // TODO: rename operations accordingly
+
+ CurrentMemory = newOp(0x3f, "memory.size", nil, wasm.ValueTypeI32)
+ GrowMemory = newOp(0x40, "memory.grow", []wasm.ValueType{wasm.ValueTypeI32}, wasm.ValueTypeI32)
+)
diff --git a/vendor/github.com/go-interpreter/wagon/wasm/operators/num.go b/vendor/github.com/go-interpreter/wagon/wasm/operators/num.go
new file mode 100644
index 000000000..4f604dd33
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/wasm/operators/num.go
@@ -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.rem_s", []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)
+)
diff --git a/vendor/github.com/go-interpreter/wagon/wasm/operators/op.go b/vendor/github.com/go-interpreter/wagon/wasm/operators/op.go
new file mode 100644
index 000000000..9a8c263ad
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/wasm/operators/op.go
@@ -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
+}
diff --git a/vendor/github.com/go-interpreter/wagon/wasm/operators/parametric.go b/vendor/github.com/go-interpreter/wagon/wasm/operators/parametric.go
new file mode 100644
index 000000000..ded32da3e
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/wasm/operators/parametric.go
@@ -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")
+)
diff --git a/vendor/github.com/go-interpreter/wagon/wasm/operators/reinterp.go b/vendor/github.com/go-interpreter/wagon/wasm/operators/reinterp.go
new file mode 100644
index 000000000..1b4dd377f
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/wasm/operators/reinterp.go
@@ -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)
+)
diff --git a/vendor/github.com/go-interpreter/wagon/wasm/operators/var.go b/vendor/github.com/go-interpreter/wagon/wasm/operators/var.go
new file mode 100644
index 000000000..b3cb54937
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/wasm/operators/var.go
@@ -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")
+)
diff --git a/vendor/github.com/go-interpreter/wagon/wasm/read.go b/vendor/github.com/go-interpreter/wagon/wasm/read.go
new file mode 100644
index 000000000..4625b05fd
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/wasm/read.go
@@ -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
+}
diff --git a/vendor/github.com/go-interpreter/wagon/wasm/section.go b/vendor/github.com/go-interpreter/wagon/wasm/section.go
new file mode 100644
index 000000000..ac6acafc1
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/wasm/section.go
@@ -0,0 +1,1205 @@
+// 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"
+ "io/ioutil"
+ "sort"
+
+ "github.com/go-interpreter/wagon/wasm/internal/readpos"
+ "github.com/go-interpreter/wagon/wasm/leb128"
+)
+
+// Section is a generic WASM section interface.
+type Section interface {
+ // SectionID returns a section ID for WASM encoding. Should be unique across types.
+ SectionID() SectionID
+ // GetRawSection Returns an embedded RawSection pointer to populate generic fields.
+ GetRawSection() *RawSection
+ // ReadPayload reads a section payload, assuming the size was already read, and reader is limited to it.
+ ReadPayload(r io.Reader) error
+ // WritePayload writes a section payload without the size.
+ // Caller should calculate written size and add it before the payload.
+ WritePayload(w io.Writer) error
+}
+
+// SectionID is a 1-byte code that encodes the section code of both known and custom sections.
+type SectionID uint8
+
+const (
+ SectionIDCustom SectionID = 0
+ SectionIDType SectionID = 1
+ SectionIDImport SectionID = 2
+ SectionIDFunction SectionID = 3
+ SectionIDTable SectionID = 4
+ SectionIDMemory SectionID = 5
+ SectionIDGlobal SectionID = 6
+ SectionIDExport SectionID = 7
+ SectionIDStart SectionID = 8
+ SectionIDElement SectionID = 9
+ SectionIDCode SectionID = 10
+ SectionIDData SectionID = 11
+)
+
+func (s SectionID) String() string {
+ n, ok := map[SectionID]string{
+ SectionIDCustom: "custom",
+ SectionIDType: "type",
+ SectionIDImport: "import",
+ SectionIDFunction: "function",
+ SectionIDTable: "table",
+ SectionIDMemory: "memory",
+ SectionIDGlobal: "global",
+ SectionIDExport: "export",
+ SectionIDStart: "start",
+ SectionIDElement: "element",
+ SectionIDCode: "code",
+ SectionIDData: "data",
+ }[s]
+ if !ok {
+ return "unknown"
+ }
+ return n
+}
+
+// RawSection is a declared section in a WASM module.
+type RawSection struct {
+ Start int64
+ End int64
+
+ ID SectionID
+ Bytes []byte
+}
+
+func (s *RawSection) SectionID() SectionID {
+ return s.ID
+}
+
+func (s *RawSection) GetRawSection() *RawSection {
+ return s
+}
+
+type InvalidSectionIDError SectionID
+
+func (e InvalidSectionIDError) Error() string {
+ return fmt.Sprintf("wasm: invalid section ID %d", e)
+}
+
+type InvalidCodeIndexError int
+
+func (e InvalidCodeIndexError) Error() string {
+ return fmt.Sprintf("wasm: invalid index to code section: %d", int(e))
+}
+
+var ErrUnsupportedSection = errors.New("wasm: unsupported section")
+
+type MissingSectionError SectionID
+
+func (e MissingSectionError) Error() string {
+ return fmt.Sprintf("wasm: missing section %s", SectionID(e).String())
+}
+
+// reads a valid section from r. The first return value is true if and only if
+// the module has been completely read.
+func (m *Module) readSection(r *readpos.ReadPos) (bool, error) {
+ var err error
+ var id uint32
+
+ logger.Println("Reading section ID")
+ id, err = leb128.ReadVarUint32(r)
+ if err == io.EOF {
+ return true, nil
+ } else if err != nil {
+ return false, err
+ }
+ s := RawSection{ID: SectionID(id)}
+
+ logger.Println("Reading payload length")
+
+ payloadDataLen, err := leb128.ReadVarUint32(r)
+ if err != nil {
+ return false, err
+ }
+
+ logger.Printf("Section payload length: %d", payloadDataLen)
+
+ s.Start = r.CurPos
+
+ sectionBytes := new(bytes.Buffer)
+ sectionBytes.Grow(int(payloadDataLen))
+ sectionReader := io.LimitReader(io.TeeReader(r, sectionBytes), int64(payloadDataLen))
+
+ var sec Section
+ switch s.ID {
+ case SectionIDCustom:
+ logger.Println("section custom")
+ cs := &SectionCustom{}
+ m.Customs = append(m.Customs, cs)
+ sec = cs
+ case SectionIDType:
+ logger.Println("section type")
+ m.Types = &SectionTypes{}
+ sec = m.Types
+ case SectionIDImport:
+ logger.Println("section import")
+ m.Import = &SectionImports{}
+ sec = m.Import
+ case SectionIDFunction:
+ logger.Println("section function")
+ m.Function = &SectionFunctions{}
+ sec = m.Function
+ case SectionIDTable:
+ logger.Println("section table")
+ m.Table = &SectionTables{}
+ sec = m.Table
+ case SectionIDMemory:
+ logger.Println("section memory")
+ m.Memory = &SectionMemories{}
+ sec = m.Memory
+ case SectionIDGlobal:
+ logger.Println("section global")
+ m.Global = &SectionGlobals{}
+ sec = m.Global
+ case SectionIDExport:
+ logger.Println("section export")
+ m.Export = &SectionExports{}
+ sec = m.Export
+ case SectionIDStart:
+ logger.Println("section start")
+ m.Start = &SectionStartFunction{}
+ sec = m.Start
+ case SectionIDElement:
+ logger.Println("section element")
+ m.Elements = &SectionElements{}
+ sec = m.Elements
+ case SectionIDCode:
+ logger.Println("section code")
+ m.Code = &SectionCode{}
+ sec = m.Code
+ case SectionIDData:
+ logger.Println("section data")
+ m.Data = &SectionData{}
+ sec = m.Data
+ default:
+ return false, InvalidSectionIDError(s.ID)
+ }
+ err = sec.ReadPayload(sectionReader)
+ if err != nil {
+ logger.Println(err)
+ return false, err
+ }
+ s.End = r.CurPos
+ s.Bytes = sectionBytes.Bytes()
+ *sec.GetRawSection() = s
+ switch s.ID {
+ case SectionIDCode:
+ s := m.Code
+ if m.Function == nil || len(m.Function.Types) == 0 {
+ return false, MissingSectionError(SectionIDFunction)
+ }
+ if len(m.Function.Types) != len(s.Bodies) {
+ return false, errors.New("The number of entries in the function and code section are unequal")
+ }
+ if m.Types == nil {
+ return false, MissingSectionError(SectionIDType)
+ }
+ for i := range s.Bodies {
+ s.Bodies[i].Module = m
+ }
+ }
+ m.Sections = append(m.Sections, sec)
+ return false, nil
+}
+
+var _ Section = (*SectionCustom)(nil)
+
+type SectionCustom struct {
+ RawSection
+ Name string
+ Data []byte
+}
+
+func (s *SectionCustom) SectionID() SectionID {
+ return SectionIDCustom
+}
+
+func (s *SectionCustom) ReadPayload(r io.Reader) error {
+ var err error
+ s.Name, err = readStringUint(r)
+ if err != nil {
+ return err
+ }
+ data, err := ioutil.ReadAll(r)
+ if err != nil {
+ return err
+ }
+ s.Data = data
+ return nil
+}
+
+func (s *SectionCustom) WritePayload(w io.Writer) error {
+ if err := writeStringUint(w, s.Name); err != nil {
+ return err
+ }
+ _, err := w.Write(s.Data)
+ return err
+}
+
+var _ Section = (*SectionTypes)(nil)
+
+// SectionTypes declares all function signatures that will be used in a module.
+type SectionTypes struct {
+ RawSection
+ Entries []FunctionSig
+}
+
+func (*SectionTypes) SectionID() SectionID {
+ return SectionIDType
+}
+
+func (s *SectionTypes) ReadPayload(r io.Reader) error {
+ count, err := leb128.ReadVarUint32(r)
+ if err != nil {
+ return err
+ }
+ s.Entries = make([]FunctionSig, int(count))
+ for i := range s.Entries {
+ if err = s.Entries[i].UnmarshalWASM(r); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (s *SectionTypes) WritePayload(w io.Writer) error {
+ _, err := leb128.WriteVarUint32(w, uint32(len(s.Entries)))
+ if err != nil {
+ return err
+ }
+ for _, f := range s.Entries {
+ if err = f.MarshalWASM(w); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+var _ Section = (*SectionImports)(nil)
+
+// SectionImports declares all imports that will be used in the module.
+type SectionImports struct {
+ RawSection
+ Entries []ImportEntry
+}
+
+func (*SectionImports) SectionID() SectionID {
+ return SectionIDImport
+}
+
+func (s *SectionImports) ReadPayload(r io.Reader) error {
+ count, err := leb128.ReadVarUint32(r)
+ if err != nil {
+ return err
+ }
+ s.Entries = make([]ImportEntry, count)
+ for i := range s.Entries {
+ err = s.Entries[i].UnmarshalWASM(r)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (s *SectionImports) WritePayload(w io.Writer) error {
+ _, err := leb128.WriteVarUint32(w, uint32(len(s.Entries)))
+ if err != nil {
+ return err
+ }
+ for _, e := range s.Entries {
+ err = writeImportEntry(w, e)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (i *ImportEntry) UnmarshalWASM(r io.Reader) error {
+ var err error
+ i.ModuleName, err = readStringUint(r)
+ if err != nil {
+ return err
+ }
+ i.FieldName, err = readStringUint(r)
+ if err != nil {
+ return err
+ }
+ var kind External
+ err = kind.UnmarshalWASM(r)
+ if err != nil {
+ return err
+ }
+
+ switch kind {
+ case ExternalFunction:
+ logger.Println("importing function")
+ var t uint32
+ t, err = leb128.ReadVarUint32(r)
+ i.Type = FuncImport{t}
+ case ExternalTable:
+ logger.Println("importing table")
+ var table Table
+
+ err = table.UnmarshalWASM(r)
+ if err == nil {
+ i.Type = TableImport{table}
+ }
+ case ExternalMemory:
+ logger.Println("importing memory")
+ var mem Memory
+
+ err = mem.UnmarshalWASM(r)
+ if err == nil {
+ i.Type = MemoryImport{mem}
+ }
+ case ExternalGlobal:
+ logger.Println("importing global var")
+ var gl GlobalVar
+
+ err = gl.UnmarshalWASM(r)
+ if err == nil {
+ i.Type = GlobalVarImport{gl}
+ }
+ default:
+ return InvalidExternalError(kind)
+ }
+
+ return err
+}
+
+func writeImportEntry(w io.Writer, i ImportEntry) error {
+ if err := writeStringUint(w, i.ModuleName); err != nil {
+ return err
+ }
+ if err := writeStringUint(w, i.FieldName); err != nil {
+ return err
+ }
+ if err := i.Type.Kind().MarshalWASM(w); err != nil {
+ return err
+ }
+ return i.Type.MarshalWASM(w)
+}
+
+// SectionFunction declares the signature of all functions defined in the module (in the code section)
+type SectionFunctions struct {
+ RawSection
+ // Sequences of indices into (FunctionSignatues).Entries
+ Types []uint32
+}
+
+func (*SectionFunctions) SectionID() SectionID {
+ return SectionIDFunction
+}
+
+func (s *SectionFunctions) ReadPayload(r io.Reader) error {
+ count, err := leb128.ReadVarUint32(r)
+ if err != nil {
+ return err
+ }
+ s.Types = make([]uint32, count)
+ for i := range s.Types {
+ t, err := leb128.ReadVarUint32(r)
+ if err != nil {
+ return err
+ }
+ s.Types[i] = t
+ }
+ return nil
+}
+
+func (s *SectionFunctions) WritePayload(w io.Writer) error {
+ if _, err := leb128.WriteVarUint32(w, uint32(len(s.Types))); err != nil {
+ return err
+ }
+ for _, t := range s.Types {
+ if _, err := leb128.WriteVarUint32(w, uint32(t)); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// SectionTables describes all tables declared by a module.
+type SectionTables struct {
+ RawSection
+ Entries []Table
+}
+
+func (*SectionTables) SectionID() SectionID {
+ return SectionIDTable
+}
+
+func (s *SectionTables) ReadPayload(r io.Reader) error {
+ count, err := leb128.ReadVarUint32(r)
+ if err != nil {
+ return err
+ }
+ s.Entries = make([]Table, count)
+ for i := range s.Entries {
+ err = s.Entries[i].UnmarshalWASM(r)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (s *SectionTables) WritePayload(w io.Writer) error {
+ if _, err := leb128.WriteVarUint32(w, uint32(len(s.Entries))); err != nil {
+ return err
+ }
+ for _, e := range s.Entries {
+ if err := e.MarshalWASM(w); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// SectionMemories describes all linaer memories used by a module.
+type SectionMemories struct {
+ RawSection
+ Entries []Memory
+}
+
+func (*SectionMemories) SectionID() SectionID {
+ return SectionIDMemory
+}
+
+func (s *SectionMemories) ReadPayload(r io.Reader) error {
+ count, err := leb128.ReadVarUint32(r)
+ if err != nil {
+ return err
+ }
+ s.Entries = make([]Memory, count)
+ for i := range s.Entries {
+ err = s.Entries[i].UnmarshalWASM(r)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (s *SectionMemories) WritePayload(w io.Writer) error {
+ if _, err := leb128.WriteVarUint32(w, uint32(len(s.Entries))); err != nil {
+ return err
+ }
+ for _, e := range s.Entries {
+ if err := e.MarshalWASM(w); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// SectionGlobals defines the value of all global variables declared in a module.
+type SectionGlobals struct {
+ RawSection
+ Globals []GlobalEntry
+}
+
+func (*SectionGlobals) SectionID() SectionID {
+ return SectionIDGlobal
+}
+
+func (s *SectionGlobals) ReadPayload(r io.Reader) error {
+ count, err := leb128.ReadVarUint32(r)
+ if err != nil {
+ return err
+ }
+ s.Globals = make([]GlobalEntry, count)
+ logger.Printf("%d global entries\n", count)
+ for i := range s.Globals {
+ err = s.Globals[i].UnmarshalWASM(r)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (s *SectionGlobals) WritePayload(w io.Writer) error {
+ if _, err := leb128.WriteVarUint32(w, uint32(len(s.Globals))); err != nil {
+ return err
+ }
+ for _, g := range s.Globals {
+ if err := g.MarshalWASM(w); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// GlobalEntry declares a global variable.
+type GlobalEntry struct {
+ Type GlobalVar // Type holds information about the value type and mutability of the variable
+ Init []byte // Init is an initializer expression that computes the initial value of the variable
+}
+
+func (g *GlobalEntry) UnmarshalWASM(r io.Reader) error {
+ err := g.Type.UnmarshalWASM(r)
+ if err != nil {
+ return err
+ }
+
+ // init_expr is delimited by opcode "end" (0x0b)
+ g.Init, err = readInitExpr(r)
+ return err
+}
+
+func (g *GlobalEntry) MarshalWASM(w io.Writer) error {
+ if err := g.Type.MarshalWASM(w); err != nil {
+ return err
+ }
+ _, err := w.Write(g.Init)
+ return err
+}
+
+// SectionExports declares the export section of a module
+type SectionExports struct {
+ RawSection
+ Entries map[string]ExportEntry
+ Names []string
+}
+
+func (*SectionExports) SectionID() SectionID {
+ return SectionIDExport
+}
+
+func (s *SectionExports) ReadPayload(r io.Reader) error {
+ count, err := leb128.ReadVarUint32(r)
+ if err != nil {
+ return err
+ }
+ s.Entries = make(map[string]ExportEntry, count)
+ for i := uint32(0); i < count; i++ {
+ var entry ExportEntry
+ err = entry.UnmarshalWASM(r)
+ if err != nil {
+ return err
+ }
+
+ if _, exists := s.Entries[entry.FieldStr]; exists {
+ return DuplicateExportError(entry.FieldStr)
+ }
+ s.Entries[entry.FieldStr] = entry
+ s.Names = append(s.Names, entry.FieldStr)
+ }
+ return nil
+}
+
+func (s *SectionExports) WritePayload(w io.Writer) error {
+ if _, err := leb128.WriteVarUint32(w, uint32(len(s.Entries))); err != nil {
+ return err
+ }
+ entries := make([]ExportEntry, 0, len(s.Entries))
+ for _, e := range s.Entries {
+ entries = append(entries, e)
+ }
+ sort.Slice(entries, func(i, j int) bool {
+ return entries[i].Index < entries[j].Index
+ })
+ for _, e := range entries {
+ if err := e.MarshalWASM(w); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+type DuplicateExportError string
+
+func (e DuplicateExportError) Error() string {
+ return fmt.Sprintf("Duplicate export entry: %s", e)
+}
+
+// ExportEntry represents an exported entry by the module
+type ExportEntry struct {
+ FieldStr string
+ Kind External
+ Index uint32
+}
+
+func (e *ExportEntry) UnmarshalWASM(r io.Reader) error {
+ var err error
+ e.FieldStr, err = readStringUint(r)
+ if err != nil {
+ return err
+ }
+
+ if err := e.Kind.UnmarshalWASM(r); err != nil {
+ return err
+ }
+
+ e.Index, err = leb128.ReadVarUint32(r)
+
+ return err
+}
+
+func (e *ExportEntry) MarshalWASM(w io.Writer) error {
+ if err := writeStringUint(w, e.FieldStr); err != nil {
+ return err
+ }
+ if err := e.Kind.MarshalWASM(w); err != nil {
+ return err
+ }
+ if _, err := leb128.WriteVarUint32(w, e.Index); err != nil {
+ return err
+ }
+ return nil
+}
+
+// SectionStartFunction represents the start function section.
+type SectionStartFunction struct {
+ RawSection
+ Index uint32 // The index of the start function into the global index space.
+}
+
+func (*SectionStartFunction) SectionID() SectionID {
+ return SectionIDStart
+}
+
+func (s *SectionStartFunction) ReadPayload(r io.Reader) error {
+ var err error
+ s.Index, err = leb128.ReadVarUint32(r)
+ return err
+}
+
+func (s *SectionStartFunction) WritePayload(w io.Writer) error {
+ _, err := leb128.WriteVarUint32(w, s.Index)
+ return err
+}
+
+// SectionElements describes the initial contents of a table's elements.
+type SectionElements struct {
+ RawSection
+ Entries []ElementSegment
+}
+
+func (*SectionElements) SectionID() SectionID {
+ return SectionIDElement
+}
+
+func (s *SectionElements) ReadPayload(r io.Reader) error {
+ count, err := leb128.ReadVarUint32(r)
+ if err != nil {
+ return err
+ }
+ s.Entries = make([]ElementSegment, count)
+ for i := range s.Entries {
+ err = s.Entries[i].UnmarshalWASM(r)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (s *SectionElements) WritePayload(w io.Writer) error {
+ if _, err := leb128.WriteVarUint32(w, uint32(len(s.Entries))); err != nil {
+ return err
+ }
+ for _, e := range s.Entries {
+ if err := e.MarshalWASM(w); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// ElementSegment describes a group of repeated elements that begin at a specified offset
+type ElementSegment struct {
+ Index uint32 // The index into the global table space, should always be 0 in the MVP.
+ Offset []byte // initializer expression for computing the offset for placing elements, should return an i32 value
+ Elems []uint32
+}
+
+func (s *ElementSegment) UnmarshalWASM(r io.Reader) error {
+ var err error
+
+ if s.Index, err = leb128.ReadVarUint32(r); err != nil {
+ return err
+ }
+ if s.Offset, err = readInitExpr(r); err != nil {
+ return err
+ }
+
+ numElems, err := leb128.ReadVarUint32(r)
+ if err != nil {
+ return err
+ }
+ s.Elems = make([]uint32, numElems)
+
+ for i := range s.Elems {
+ e, err := leb128.ReadVarUint32(r)
+ if err != nil {
+ return err
+ }
+ s.Elems[i] = e
+ }
+
+ return nil
+}
+
+func (s *ElementSegment) MarshalWASM(w io.Writer) error {
+ if _, err := leb128.WriteVarUint32(w, s.Index); err != nil {
+ return err
+ }
+ if _, err := w.Write(s.Offset); err != nil {
+ return err
+ }
+
+ if _, err := leb128.WriteVarUint32(w, uint32(len(s.Elems))); err != nil {
+ return err
+ }
+ for _, e := range s.Elems {
+ if _, err := leb128.WriteVarUint32(w, e); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// SectionCode describes the body for every function declared inside a module.
+type SectionCode struct {
+ RawSection
+ Bodies []FunctionBody
+}
+
+func (*SectionCode) SectionID() SectionID {
+ return SectionIDCode
+}
+
+func (s *SectionCode) ReadPayload(r io.Reader) error {
+ count, err := leb128.ReadVarUint32(r)
+ if err != nil {
+ return err
+ }
+ s.Bodies = make([]FunctionBody, count)
+ logger.Printf("%d function bodies\n", count)
+
+ for i := range s.Bodies {
+ logger.Printf("Reading function %d\n", i)
+ if err = s.Bodies[i].UnmarshalWASM(r); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (s *SectionCode) WritePayload(w io.Writer) error {
+ if _, err := leb128.WriteVarUint32(w, uint32(len(s.Bodies))); err != nil {
+ return err
+ }
+ for _, b := range s.Bodies {
+ if err := b.MarshalWASM(w); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+var ErrFunctionNoEnd = errors.New("Function body does not end with 0x0b (end)")
+
+type FunctionBody struct {
+ Module *Module // The parent module containing this function body, for execution purposes
+ Locals []LocalEntry
+ Code []byte
+}
+
+func (f *FunctionBody) UnmarshalWASM(r io.Reader) error {
+
+ bodySize, err := leb128.ReadVarUint32(r)
+ if err != nil {
+ return err
+ }
+
+ body := make([]byte, bodySize)
+
+ if _, err = io.ReadFull(r, body); err != nil {
+ return err
+ }
+
+ bytesReader := bytes.NewBuffer(body)
+
+ localCount, err := leb128.ReadVarUint32(bytesReader)
+ if err != nil {
+ return err
+ }
+ f.Locals = make([]LocalEntry, localCount)
+
+ for i := range f.Locals {
+ if err = f.Locals[i].UnmarshalWASM(bytesReader); err != nil {
+ return err
+ }
+ }
+
+ logger.Printf("bodySize: %d, localCount: %d\n", bodySize, localCount)
+
+ code := bytesReader.Bytes()
+ logger.Printf("Read %d bytes for function body", len(code))
+
+ if code[len(code)-1] != end {
+ return ErrFunctionNoEnd
+ }
+
+ f.Code = code[:len(code)-1]
+
+ return nil
+}
+
+func (f *FunctionBody) MarshalWASM(w io.Writer) error {
+ body := new(bytes.Buffer)
+ if _, err := leb128.WriteVarUint32(body, uint32(len(f.Locals))); err != nil {
+ return err
+ }
+ for _, l := range f.Locals {
+ if err := l.MarshalWASM(body); err != nil {
+ return err
+ }
+ }
+ if _, err := body.Write(f.Code); err != nil {
+ return err
+ }
+ body.WriteByte(end)
+ return writeBytesUint(w, body.Bytes())
+}
+
+type LocalEntry struct {
+ Count uint32 // The total number of local variables of the given Type used in the function body
+ Type ValueType // The type of value stored by the variable
+}
+
+func (l *LocalEntry) UnmarshalWASM(r io.Reader) error {
+ var err error
+
+ l.Count, err = leb128.ReadVarUint32(r)
+ if err != nil {
+ return err
+ }
+
+ err = l.Type.UnmarshalWASM(r)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (l *LocalEntry) MarshalWASM(w io.Writer) error {
+ if _, err := leb128.WriteVarUint32(w, l.Count); err != nil {
+ return err
+ }
+ if err := l.Type.MarshalWASM(w); err != nil {
+ return err
+ }
+ return nil
+}
+
+// SectionData describes the intial values of a module's linear memory
+type SectionData struct {
+ RawSection
+ Entries []DataSegment
+}
+
+func (*SectionData) SectionID() SectionID {
+ return SectionIDData
+}
+
+func (s *SectionData) ReadPayload(r io.Reader) error {
+ count, err := leb128.ReadVarUint32(r)
+ if err != nil {
+ return err
+ }
+ s.Entries = make([]DataSegment, count)
+ for i := range s.Entries {
+ if err = s.Entries[i].UnmarshalWASM(r); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (s *SectionData) WritePayload(w io.Writer) error {
+ if _, err := leb128.WriteVarUint32(w, uint32(len(s.Entries))); err != nil {
+ return err
+ }
+ for _, e := range s.Entries {
+ if err := e.MarshalWASM(w); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// DataSegment describes a group of repeated elements that begin at a specified offset in the linear memory
+type DataSegment struct {
+ Index uint32 // The index into the global linear memory space, should always be 0 in the MVP.
+ Offset []byte // initializer expression for computing the offset for placing elements, should return an i32 value
+ Data []byte
+}
+
+func (s *DataSegment) UnmarshalWASM(r io.Reader) error {
+ var err error
+
+ if s.Index, err = leb128.ReadVarUint32(r); err != nil {
+ return err
+ }
+ if s.Offset, err = readInitExpr(r); err != nil {
+ return err
+ }
+ s.Data, err = readBytesUint(r)
+ return err
+}
+
+func (s *DataSegment) MarshalWASM(w io.Writer) error {
+ if _, err := leb128.WriteVarUint32(w, s.Index); err != nil {
+ return err
+ }
+ if _, err := w.Write(s.Offset); err != nil {
+ return err
+ }
+ return writeBytesUint(w, s.Data)
+}
+
+// A list of well-known custom sections
+const (
+ CustomSectionName = "name"
+)
+
+var (
+ _ Marshaler = (*NameSection)(nil)
+ _ Unmarshaler = (*NameSection)(nil)
+)
+
+// NameType is the type of name subsection.
+type NameType byte
+
+const (
+ NameModule = NameType(0)
+ NameFunction = NameType(1)
+ NameLocal = NameType(2)
+)
+
+// NameSection is a custom section that stores names of modules, functions and locals for debugging purposes.
+// See https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md#name-section for more details.
+type NameSection struct {
+ Types map[NameType][]byte
+}
+
+func (s *NameSection) UnmarshalWASM(r io.Reader) error {
+ s.Types = make(map[NameType][]byte)
+ for {
+ typ, err := leb128.ReadVarUint32(r)
+ if err == io.EOF {
+ return nil
+ } else if err != nil {
+ return err
+ }
+ data, err := readBytesUint(r)
+ if err != nil {
+ return err
+ }
+ s.Types[NameType(typ)] = data
+ }
+}
+
+func (s *NameSection) MarshalWASM(w io.Writer) error {
+ keys := make([]NameType, 0, len(s.Types))
+ for k := range s.Types {
+ keys = append(keys, k)
+ }
+ sort.Slice(keys, func(i, j int) bool {
+ return keys[i] < keys[j]
+ })
+ for _, k := range keys {
+ data := s.Types[k]
+ if _, err := leb128.WriteVarUint32(w, uint32(k)); err != nil {
+ return err
+ }
+ if err := writeBytesUint(w, data); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// Decode finds a specific subsection type and decodes it.
+func (s *NameSection) Decode(typ NameType) (NameSubsection, error) {
+ var sub NameSubsection
+ switch typ {
+ case NameModule:
+ sub = &ModuleName{}
+ case NameFunction:
+ sub = &FunctionNames{}
+ case NameLocal:
+ sub = &LocalNames{}
+ default:
+ return nil, fmt.Errorf("unsupported name subsection: %x", typ)
+ }
+ data, ok := s.Types[typ]
+ if !ok {
+ return nil, nil
+ }
+ if err := sub.UnmarshalWASM(bytes.NewReader(data)); err != nil {
+ return nil, err
+ }
+ return sub, nil
+}
+
+// NameSubsection is an interface for subsections of NameSection.
+//
+// Valid types:
+// * ModuleName
+// * FunctionNames
+// * LocalNames
+type NameSubsection interface {
+ Marshaler
+ Unmarshaler
+ isNameSubsection()
+}
+
+// ModuleName is the name of a module.
+type ModuleName struct {
+ Name string
+}
+
+func (*ModuleName) isNameSubsection() {}
+
+func (s *ModuleName) UnmarshalWASM(r io.Reader) error {
+ var err error
+ s.Name, err = readStringUint(r)
+ return err
+}
+
+func (s *ModuleName) MarshalWASM(w io.Writer) error {
+ return writeStringUint(w, s.Name)
+}
+
+// FunctionNames is a set of names for functions.
+type FunctionNames struct {
+ Names NameMap
+}
+
+func (*FunctionNames) isNameSubsection() {}
+
+func (s *FunctionNames) UnmarshalWASM(r io.Reader) error {
+ s.Names = make(NameMap)
+ return s.Names.UnmarshalWASM(r)
+}
+
+func (s *FunctionNames) MarshalWASM(w io.Writer) error {
+ return s.Names.MarshalWASM(w)
+}
+
+// LocalNames is a set of local variable names for functions.
+type LocalNames struct {
+ // Funcs maps a function index to a set of variable names.
+ Funcs map[uint32]NameMap
+}
+
+func (*LocalNames) isNameSubsection() {}
+
+func (s *LocalNames) UnmarshalWASM(r io.Reader) error {
+ s.Funcs = make(map[uint32]NameMap)
+ size, err := leb128.ReadVarUint32(r)
+ if err != nil {
+ return err
+ }
+ for i := 0; i < int(size); i++ {
+ ind, err := leb128.ReadVarUint32(r)
+ if err != nil {
+ return err
+ }
+ m := make(NameMap)
+ if err := m.UnmarshalWASM(r); err != nil {
+ return err
+ }
+ s.Funcs[ind] = m
+ }
+ return nil
+}
+
+func (s *LocalNames) MarshalWASM(w io.Writer) error {
+ keys := make([]uint32, 0, len(s.Funcs))
+ for k := range s.Funcs {
+ keys = append(keys, k)
+ }
+ sort.Slice(keys, func(i, j int) bool {
+ return keys[i] < keys[j]
+ })
+ for _, k := range keys {
+ m := s.Funcs[k]
+ if _, err := leb128.WriteVarUint32(w, k); err != nil {
+ return err
+ }
+ if err := m.MarshalWASM(w); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+var (
+ _ Marshaler = (NameMap)(nil)
+ _ Unmarshaler = (NameMap)(nil)
+)
+
+// NameMap maps an index of the entry to a name.
+type NameMap map[uint32]string
+
+func (m NameMap) UnmarshalWASM(r io.Reader) error {
+ size, err := leb128.ReadVarUint32(r)
+ if err != nil {
+ return err
+ }
+ for i := 0; i < int(size); i++ {
+ ind, err := leb128.ReadVarUint32(r)
+ if err != nil {
+ return err
+ }
+ name, err := readStringUint(r)
+ if err != nil {
+ return err
+ }
+ m[ind] = name
+ }
+ return nil
+}
+func (m NameMap) MarshalWASM(w io.Writer) error {
+ keys := make([]uint32, 0, len(m))
+ for k := range m {
+ keys = append(keys, k)
+ }
+ sort.Slice(keys, func(i, j int) bool {
+ return keys[i] < keys[j]
+ })
+ for _, k := range keys {
+ name := m[k]
+ if _, err := leb128.WriteVarUint32(w, k); err != nil {
+ return err
+ }
+ if err := writeStringUint(w, name); err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/go-interpreter/wagon/wasm/types.go b/vendor/github.com/go-interpreter/wagon/wasm/types.go
new file mode 100644
index 000000000..e73f45393
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/wasm/types.go
@@ -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
+}
diff --git a/vendor/github.com/go-interpreter/wagon/wast/write.go b/vendor/github.com/go-interpreter/wagon/wast/write.go
new file mode 100644
index 000000000..6c3b521c0
--- /dev/null
+++ b/vendor/github.com/go-interpreter/wagon/wast/write.go
@@ -0,0 +1,493 @@
+// 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 wast implements a WebAssembly text format.
+package wast
+
+// See https://webassembly.github.io/spec/core/text/
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ "math"
+ "strconv"
+
+ "github.com/go-interpreter/wagon/disasm"
+ "github.com/go-interpreter/wagon/wasm"
+ "github.com/go-interpreter/wagon/wasm/operators"
+)
+
+const tab = ` `
+
+// WriteTo writes a WASM module in a text representation.
+func WriteTo(w io.Writer, m *wasm.Module) error {
+ wr, err := newWriter(w, m)
+ if err != nil {
+ return err
+ }
+ return wr.writeModule()
+}
+
+type writer struct {
+ bw *bufio.Writer
+ m *wasm.Module
+
+ fnames wasm.NameMap
+ funcOff int
+ err error
+}
+
+func newWriter(w io.Writer, m *wasm.Module) (*writer, error) {
+ wr := &writer{bw: bufio.NewWriter(w), m: m}
+ if s := m.Custom(wasm.CustomSectionName); s != nil {
+ var names wasm.NameSection
+ _ = names.UnmarshalWASM(bytes.NewReader(s.Data))
+ sub, _ := names.Decode(wasm.NameFunction)
+ funcs, ok := sub.(*wasm.FunctionNames)
+ if ok {
+ wr.fnames = funcs.Names
+ }
+ }
+ return wr, nil
+}
+
+func (w *writer) writeModule() error {
+ bw := w.bw
+ bw.WriteString("(module")
+
+ w.writeTypes()
+ w.writeImports()
+ w.writeFunctions()
+ w.writeGlobals()
+ w.writeTables()
+ w.writeMemory()
+ w.writeExports()
+ w.writeElements()
+ w.writeData()
+
+ bw.WriteString(")\n")
+ if err := bw.Flush(); err != nil {
+ return err
+ }
+ return w.err
+}
+
+func (w *writer) writeTypes() {
+ if w.m.Types == nil {
+ return
+ }
+ w.WriteString("\n")
+ for i, t := range w.m.Types.Entries {
+ if i != 0 {
+ w.WriteString("\n")
+ }
+ w.Print(tab+"(type (;%d;) ", i)
+ w.writeFuncSignature(t)
+ w.WriteString(")")
+ }
+}
+
+func (w *writer) writeFuncSignature(t wasm.FunctionSig) error {
+ w.WriteString("(func")
+ defer w.WriteString(")")
+ return w.writeFuncType(t)
+}
+
+func (w *writer) writeFuncType(t wasm.FunctionSig) error {
+ if len(t.ParamTypes) != 0 {
+ w.WriteString(" (param")
+ for _, p := range t.ParamTypes {
+ w.WriteString(" ")
+ w.WriteString(p.String())
+ }
+ w.WriteString(")")
+ }
+ if len(t.ReturnTypes) != 0 {
+ w.WriteString(" (result")
+ for _, p := range t.ReturnTypes {
+ w.WriteString(" ")
+ w.WriteString(p.String())
+ }
+ w.WriteString(")")
+ }
+ return nil
+}
+
+func (w *writer) writeImports() {
+ w.funcOff = 0
+ if w.m.Import == nil {
+ return
+ }
+ w.WriteString("\n")
+ for i, e := range w.m.Import.Entries {
+ if i != 0 {
+ w.WriteString("\n")
+ }
+ w.WriteString(tab + "(import ")
+ w.Print("%q %q ", e.ModuleName, e.FieldName)
+ switch im := e.Type.(type) {
+ case wasm.FuncImport:
+ w.Print("(func (;%d;) (type %d))", w.funcOff, im.Type)
+ if w.fnames == nil {
+ w.fnames = make(wasm.NameMap)
+ }
+ w.fnames[uint32(w.funcOff)] = e.ModuleName + "." + e.FieldName
+ w.funcOff++
+ case wasm.TableImport:
+ // TODO
+ case wasm.MemoryImport:
+ // TODO
+ case wasm.GlobalVarImport:
+ // TODO
+ }
+ w.WriteString(")")
+ }
+}
+
+func (w *writer) writeFunctions() {
+ if w.m.Function == nil {
+ return
+ }
+ w.WriteString("\n")
+ for i, t := range w.m.Function.Types {
+ if i != 0 {
+ w.WriteString("\n")
+ }
+ ind := w.funcOff + i
+ w.bw.WriteString(tab + "(func ")
+ if name, ok := w.fnames[uint32(ind)]; ok {
+ w.bw.WriteString("$" + name)
+ } else {
+ fmt.Fprintf(w.bw, "(;%d;)", ind)
+ }
+ fmt.Fprintf(w.bw, " (type %d)", int(t))
+ if int(t) < len(w.m.Types.Entries) {
+ sig := w.m.Types.Entries[t]
+ w.writeFuncType(sig)
+ }
+ if w.m.Code != nil && i < len(w.m.Code.Bodies) {
+ b := w.m.Code.Bodies[i]
+ if len(b.Locals) > 0 {
+ w.WriteString("\n" + tab + tab + "(local")
+ for _, l := range b.Locals {
+ for i := 0; i < int(l.Count); i++ {
+ w.WriteString(" ")
+ w.WriteString(l.Type.String())
+ }
+ }
+ w.WriteString(")")
+ }
+ w.writeCode(b.Code, false)
+ }
+ w.WriteString(")")
+ }
+}
+
+func (w *writer) writeGlobals() {
+ if w.m.Global == nil {
+ return
+ }
+ for i, e := range w.m.Global.Globals {
+ w.WriteString("\n")
+ w.WriteString(tab + "(global ")
+ w.Print("(;%d;)", i)
+ if e.Type.Mutable {
+ w.WriteString(" (mut")
+ }
+ w.Print(" %v", e.Type.Type)
+ if e.Type.Mutable {
+ w.WriteString(")")
+ }
+ w.WriteString(" (")
+ w.writeCode(e.Init, true)
+ w.WriteString("))")
+ }
+}
+
+func (w *writer) writeTables() {
+ if w.m.Table == nil {
+ return
+ }
+ w.WriteString("\n")
+ for i, t := range w.m.Table.Entries {
+ w.WriteString(tab + "(table ")
+ w.Print("(;%d;)", i)
+ w.Print(" %d %d ", t.Limits.Initial, t.Limits.Maximum)
+ switch t.ElementType {
+ case wasm.ElemTypeAnyFunc:
+ w.WriteString("anyfunc")
+ }
+ w.WriteString(")")
+ }
+}
+
+func (w *writer) writeMemory() {
+ if w.m.Memory == nil {
+ return
+ }
+ w.WriteString("\n")
+ for i, e := range w.m.Memory.Entries {
+ w.WriteString(tab + "(memory ")
+ w.Print("(;%d;)", i)
+ w.Print(" %d", e.Limits.Initial)
+ if e.Limits.Maximum != 0 {
+ w.Print(" %d", e.Limits.Initial)
+ }
+ w.WriteString(")")
+ }
+}
+
+func (w *writer) writeExports() {
+ if w.m.Export == nil {
+ return
+ }
+ w.WriteString("\n")
+ for i, k := range w.m.Export.Names {
+ e := w.m.Export.Entries[k]
+ if i != 0 {
+ w.WriteString("\n")
+ }
+ w.Print(tab+"(export %q (", e.FieldStr)
+ switch e.Kind {
+ case wasm.ExternalFunction:
+ w.WriteString("func")
+ case wasm.ExternalMemory:
+ w.WriteString("memory")
+ case wasm.ExternalTable:
+ w.WriteString("table")
+ case wasm.ExternalGlobal:
+ w.WriteString("global")
+ }
+ w.Print(" %d))", e.Index)
+ }
+}
+
+func (w *writer) writeElements() {
+ if w.m.Elements == nil {
+ return
+ }
+ for _, d := range w.m.Elements.Entries {
+ w.WriteString("\n")
+ w.WriteString(tab + "(elem")
+ if d.Index != 0 {
+ w.Print(" %d", d.Index)
+ }
+ w.WriteString(" (")
+ w.writeCode(d.Offset, true)
+ w.WriteString(")")
+ for _, v := range d.Elems {
+ w.Print(" %d", v)
+ }
+ w.WriteString(")")
+ }
+}
+
+func (w *writer) writeData() {
+ if w.m.Data == nil {
+ return
+ }
+ for _, d := range w.m.Data.Entries {
+ w.WriteString("\n")
+ w.WriteString(tab + "(data")
+ if d.Index != 0 {
+ w.Print(" %d", d.Index)
+ }
+ w.WriteString(" (")
+ w.writeCode(d.Offset, true)
+ w.Print(") %s)", quoteData(d.Data))
+ }
+}
+
+func (w *writer) WriteString(s string) {
+ if w.err != nil {
+ return
+ }
+ _, w.err = w.bw.WriteString(s)
+}
+
+func (w *writer) Print(format string, args ...interface{}) {
+ if w.err != nil {
+ return
+ }
+ _, w.err = fmt.Fprintf(w.bw, format, args...)
+}
+
+func quoteData(p []byte) string {
+ buf := new(bytes.Buffer)
+ buf.WriteRune('"')
+ for _, b := range p {
+ if strconv.IsGraphic(rune(b)) && b < 0xa0 && b != '"' && b != '\\' {
+ buf.WriteByte(b)
+ } else {
+ s := strconv.FormatInt(int64(b), 16)
+ if len(s) == 1 {
+ s = "0" + s
+ }
+ buf.WriteString(`\` + s)
+ }
+ }
+ buf.WriteRune('"')
+ return buf.String()
+}
+
+func (w *writer) writeCode(code []byte, isInit bool) {
+ if w.err != nil {
+ return
+ }
+ instr, err := disasm.Disassemble(code)
+ if err != nil {
+ w.err = err
+ return
+ }
+ tabs := 2
+ block := 0
+ writeBlock := func(d int) {
+ w.Print(" %d (;@%d;)", d, block-d)
+ }
+ hadEnd := false
+ for i, ins := range instr {
+ if !isInit {
+ w.WriteString("\n")
+ }
+ switch ins.Op.Code {
+ case operators.End, operators.Else:
+ tabs--
+ block--
+ }
+ if isInit && !hadEnd && ins.Op.Code == operators.End {
+ hadEnd = true
+ continue
+ }
+ if isInit {
+ if i > 0 {
+ w.WriteString(" ")
+ }
+ } else {
+ for i := 0; i < tabs; i++ {
+ w.WriteString(tab)
+ }
+ }
+ w.WriteString(ins.Op.Name)
+ switch ins.Op.Code {
+ case operators.Else:
+ tabs++
+ block++
+ case operators.Block, operators.Loop, operators.If:
+ tabs++
+ block++
+ b := ins.Immediates[0].(wasm.BlockType)
+ if b != wasm.BlockTypeEmpty {
+ w.WriteString(" (result ")
+ w.WriteString(b.String())
+ w.WriteString(")")
+ }
+ w.Print(" ;; label = @%d", block)
+ continue
+ case operators.F32Const:
+ i1 := ins.Immediates[0].(float32)
+ w.WriteString(" " + formatFloat32(i1))
+ continue
+ case operators.F64Const:
+ i1 := ins.Immediates[0].(float64)
+ w.WriteString(" " + formatFloat64(i1))
+ continue
+ case operators.BrIf, operators.Br:
+ i1 := ins.Immediates[0].(uint32)
+ writeBlock(int(i1))
+ continue
+ case operators.BrTable:
+ n := ins.Immediates[0].(uint32)
+ for i := 0; i < int(n); i++ {
+ v := ins.Immediates[i+1].(uint32)
+ writeBlock(int(v))
+ }
+ def := ins.Immediates[n+1].(uint32)
+ writeBlock(int(def))
+ continue
+ case operators.Call:
+ i1 := ins.Immediates[0].(uint32)
+ if name, ok := w.fnames[i1]; ok {
+ w.WriteString(" $")
+ w.WriteString(name)
+ } else {
+ w.Print(" %v", i1)
+ }
+ continue
+ case operators.CallIndirect:
+ i1 := ins.Immediates[0].(uint32)
+ w.Print(" (type %d)", i1)
+ continue
+ case operators.CurrentMemory, operators.GrowMemory:
+ r := ins.Immediates[0].(uint8)
+ if r == 0 {
+ continue
+ }
+ case operators.I32Store, operators.I64Store,
+ operators.I32Store8, operators.I64Store8,
+ operators.I32Store16, operators.I64Store16,
+ operators.I64Store32,
+ operators.F32Store, operators.F64Store,
+ operators.I32Load, operators.I64Load,
+ operators.I32Load8u, operators.I32Load8s,
+ operators.I32Load16u, operators.I32Load16s,
+ operators.I64Load8u, operators.I64Load8s,
+ operators.I64Load16u, operators.I64Load16s,
+ operators.I64Load32u, operators.I64Load32s,
+ operators.F32Load, operators.F64Load:
+
+ i1 := ins.Immediates[0].(uint32)
+ i2 := ins.Immediates[1].(uint32)
+ dst := 0 // in log 2 (i8)
+ switch ins.Op.Code {
+ case operators.I64Load, operators.I64Store,
+ operators.F64Load, operators.F64Store:
+ dst = 3
+ case operators.I32Load, operators.I64Load32s, operators.I64Load32u,
+ operators.I32Store, operators.I64Store32,
+ operators.F32Load, operators.F32Store:
+ dst = 2
+ case operators.I32Load16u, operators.I32Load16s, operators.I64Load16u, operators.I64Load16s,
+ operators.I32Store16, operators.I64Store16:
+ dst = 1
+ case operators.I32Load8u, operators.I32Load8s, operators.I64Load8u, operators.I64Load8s,
+ operators.I32Store8, operators.I64Store8:
+ dst = 0
+ }
+ if i2 != 0 {
+ w.Print(" offset=%d", i2)
+ }
+ if int(i1) != dst {
+ w.Print(" align=%d", 1<<i1)
+ }
+ continue
+ }
+ for _, a := range ins.Immediates {
+ w.WriteString(" ")
+ w.Print("%v", a)
+ }
+ }
+}
+
+func formatFloat32(v float32) string {
+ s := ""
+ if v == float32(int32(v)) {
+ s = strconv.FormatInt(int64(v), 10)
+ } else {
+ s = strconv.FormatFloat(float64(v), 'g', -1, 32)
+ }
+ return fmt.Sprintf("%#0x (;=%s;)", math.Float32bits(v), s)
+}
+
+func formatFloat64(v float64) string {
+ // TODO: https://github.com/WebAssembly/wabt/blob/master/src/literal.cc (FloatWriter<T>::WriteHex)
+ s := ""
+ if v == float64(int64(v)) {
+ s = strconv.FormatInt(int64(v), 10)
+ } else {
+ s = strconv.FormatFloat(float64(v), 'g', -1, 64)
+ }
+ return fmt.Sprintf("%#0x (;=%v;)", math.Float64bits(v), s)
+}
diff --git a/vendor/vendor.json b/vendor/vendor.json
index fc554bfce..2b38e5487 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -293,6 +293,66 @@
"revisionTime": "2017-01-17T22:23:42Z"
},
{
+ "checksumSHA1": "4gQMKF4uGPHsuitzRwSxBo7/EBA=",
+ "path": "github.com/go-interpreter/wagon/disasm",
+ "revision": "a76146c83d210a8893392bcde44420be18db39f5",
+ "revisionTime": "2019-03-11T15:10:31Z"
+ },
+ {
+ "checksumSHA1": "+X9ssBMp1fralbG8OjI7Vc8zoE4=",
+ "path": "github.com/go-interpreter/wagon/exec",
+ "revision": "a76146c83d210a8893392bcde44420be18db39f5",
+ "revisionTime": "2019-03-11T15:10:31Z"
+ },
+ {
+ "checksumSHA1": "4yRxeKi6k5yIf87EwOXzbSTlTRU=",
+ "path": "github.com/go-interpreter/wagon/exec/internal/compile",
+ "revision": "a76146c83d210a8893392bcde44420be18db39f5",
+ "revisionTime": "2019-03-11T15:10:31Z"
+ },
+ {
+ "checksumSHA1": "6zjjuEauIj/855KLJGKT2cgjVio=",
+ "path": "github.com/go-interpreter/wagon/internal/stack",
+ "revision": "a76146c83d210a8893392bcde44420be18db39f5",
+ "revisionTime": "2019-03-11T15:10:31Z"
+ },
+ {
+ "checksumSHA1": "nKawEPA06hZUgof0kPD3pwkdDPo=",
+ "path": "github.com/go-interpreter/wagon/validate",
+ "revision": "a76146c83d210a8893392bcde44420be18db39f5",
+ "revisionTime": "2019-03-11T15:10:31Z"
+ },
+ {
+ "checksumSHA1": "NUIMs+zfyuHDzStjUq0qCr0uKcs=",
+ "path": "github.com/go-interpreter/wagon/wasm",
+ "revision": "a76146c83d210a8893392bcde44420be18db39f5",
+ "revisionTime": "2019-03-11T15:10:31Z"
+ },
+ {
+ "checksumSHA1": "kRHgYIzF9xTBn1Ai9h5H0wcPfp8=",
+ "path": "github.com/go-interpreter/wagon/wasm/internal/readpos",
+ "revision": "a76146c83d210a8893392bcde44420be18db39f5",
+ "revisionTime": "2019-03-11T15:10:31Z"
+ },
+ {
+ "checksumSHA1": "yRQe75b3JNu76jw2fHHaQzGAOEA=",
+ "path": "github.com/go-interpreter/wagon/wasm/leb128",
+ "revision": "a76146c83d210a8893392bcde44420be18db39f5",
+ "revisionTime": "2019-03-11T15:10:31Z"
+ },
+ {
+ "checksumSHA1": "Ic6GhILuFEnGnMq8pOM2iUrJq54=",
+ "path": "github.com/go-interpreter/wagon/wasm/operators",
+ "revision": "a76146c83d210a8893392bcde44420be18db39f5",
+ "revisionTime": "2019-03-11T15:10:31Z"
+ },
+ {
+ "checksumSHA1": "Jvjgz1mb1a/sdGK1K1lWEuElGV4=",
+ "path": "github.com/go-interpreter/wagon/wast",
+ "revision": "a76146c83d210a8893392bcde44420be18db39f5",
+ "revisionTime": "2019-03-11T15:10:31Z"
+ },
+ {
"checksumSHA1": "gxV/cPPLkByTdY8y172t7v4qcZA=",
"path": "github.com/go-ole/go-ole",
"revision": "a41e3c4b706f6ae8dfbff342b06e40fa4d2d0506",