mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
Revert "cmd/evm: fix some issues with the evm run command (#28109)"
This reverts commit 0264a9ad74.
This commit is contained in:
parent
627678beea
commit
2755593ba0
9 changed files with 271 additions and 296 deletions
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/tests"
|
"github.com/ethereum/go-ethereum/tests"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
@ -40,7 +41,10 @@ func blockTestCmd(ctx *cli.Context) error {
|
||||||
if len(ctx.Args().First()) == 0 {
|
if len(ctx.Args().First()) == 0 {
|
||||||
return errors.New("path-to-test argument required")
|
return errors.New("path-to-test argument required")
|
||||||
}
|
}
|
||||||
|
// Configure the go-ethereum logger
|
||||||
|
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
|
||||||
|
glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name)))
|
||||||
|
log.Root().SetHandler(glogger)
|
||||||
var tracer vm.EVMLogger
|
var tracer vm.EVMLogger
|
||||||
// Configure the EVM logger
|
// Configure the EVM logger
|
||||||
if ctx.Bool(MachineFlag.Name) {
|
if ctx.Bool(MachineFlag.Name) {
|
||||||
|
|
|
||||||
185
cmd/evm/main.go
185
cmd/evm/main.go
|
|
@ -23,116 +23,107 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/cmd/evm/internal/t8ntool"
|
"github.com/ethereum/go-ethereum/cmd/evm/internal/t8ntool"
|
||||||
"github.com/ethereum/go-ethereum/internal/debug"
|
|
||||||
"github.com/ethereum/go-ethereum/internal/flags"
|
"github.com/ethereum/go-ethereum/internal/flags"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DebugFlag = &cli.BoolFlag{
|
DebugFlag = &cli.BoolFlag{
|
||||||
Name: "debug",
|
Name: "debug",
|
||||||
Usage: "output full trace logs",
|
Usage: "output full trace logs",
|
||||||
Category: flags.VMCategory,
|
}
|
||||||
|
MemProfileFlag = &cli.StringFlag{
|
||||||
|
Name: "memprofile",
|
||||||
|
Usage: "creates a memory profile at the given path",
|
||||||
|
}
|
||||||
|
CPUProfileFlag = &cli.StringFlag{
|
||||||
|
Name: "cpuprofile",
|
||||||
|
Usage: "creates a CPU profile at the given path",
|
||||||
}
|
}
|
||||||
StatDumpFlag = &cli.BoolFlag{
|
StatDumpFlag = &cli.BoolFlag{
|
||||||
Name: "statdump",
|
Name: "statdump",
|
||||||
Usage: "displays stack and heap memory information",
|
Usage: "displays stack and heap memory information",
|
||||||
Category: flags.VMCategory,
|
|
||||||
}
|
}
|
||||||
CodeFlag = &cli.StringFlag{
|
CodeFlag = &cli.StringFlag{
|
||||||
Name: "code",
|
Name: "code",
|
||||||
Usage: "EVM code",
|
Usage: "EVM code",
|
||||||
Category: flags.VMCategory,
|
|
||||||
}
|
}
|
||||||
CodeFileFlag = &cli.StringFlag{
|
CodeFileFlag = &cli.StringFlag{
|
||||||
Name: "codefile",
|
Name: "codefile",
|
||||||
Usage: "File containing EVM code. If '-' is specified, code is read from stdin ",
|
Usage: "File containing EVM code. If '-' is specified, code is read from stdin ",
|
||||||
Category: flags.VMCategory,
|
|
||||||
}
|
}
|
||||||
GasFlag = &cli.Uint64Flag{
|
GasFlag = &cli.Uint64Flag{
|
||||||
Name: "gas",
|
Name: "gas",
|
||||||
Usage: "gas limit for the evm",
|
Usage: "gas limit for the evm",
|
||||||
Value: 10000000000,
|
Value: 10000000000,
|
||||||
Category: flags.VMCategory,
|
|
||||||
}
|
}
|
||||||
PriceFlag = &flags.BigFlag{
|
PriceFlag = &flags.BigFlag{
|
||||||
Name: "price",
|
Name: "price",
|
||||||
Usage: "price set for the evm",
|
Usage: "price set for the evm",
|
||||||
Value: new(big.Int),
|
Value: new(big.Int),
|
||||||
Category: flags.VMCategory,
|
|
||||||
}
|
}
|
||||||
ValueFlag = &flags.BigFlag{
|
ValueFlag = &flags.BigFlag{
|
||||||
Name: "value",
|
Name: "value",
|
||||||
Usage: "value set for the evm",
|
Usage: "value set for the evm",
|
||||||
Value: new(big.Int),
|
Value: new(big.Int),
|
||||||
Category: flags.VMCategory,
|
|
||||||
}
|
}
|
||||||
DumpFlag = &cli.BoolFlag{
|
DumpFlag = &cli.BoolFlag{
|
||||||
Name: "dump",
|
Name: "dump",
|
||||||
Usage: "dumps the state after the run",
|
Usage: "dumps the state after the run",
|
||||||
Category: flags.VMCategory,
|
|
||||||
}
|
}
|
||||||
InputFlag = &cli.StringFlag{
|
InputFlag = &cli.StringFlag{
|
||||||
Name: "input",
|
Name: "input",
|
||||||
Usage: "input for the EVM",
|
Usage: "input for the EVM",
|
||||||
Category: flags.VMCategory,
|
|
||||||
}
|
}
|
||||||
InputFileFlag = &cli.StringFlag{
|
InputFileFlag = &cli.StringFlag{
|
||||||
Name: "inputfile",
|
Name: "inputfile",
|
||||||
Usage: "file containing input for the EVM",
|
Usage: "file containing input for the EVM",
|
||||||
Category: flags.VMCategory,
|
}
|
||||||
|
VerbosityFlag = &cli.IntFlag{
|
||||||
|
Name: "verbosity",
|
||||||
|
Usage: "sets the verbosity level",
|
||||||
}
|
}
|
||||||
BenchFlag = &cli.BoolFlag{
|
BenchFlag = &cli.BoolFlag{
|
||||||
Name: "bench",
|
Name: "bench",
|
||||||
Usage: "benchmark the execution",
|
Usage: "benchmark the execution",
|
||||||
Category: flags.VMCategory,
|
|
||||||
}
|
}
|
||||||
CreateFlag = &cli.BoolFlag{
|
CreateFlag = &cli.BoolFlag{
|
||||||
Name: "create",
|
Name: "create",
|
||||||
Usage: "indicates the action should be create rather than call",
|
Usage: "indicates the action should be create rather than call",
|
||||||
Category: flags.VMCategory,
|
|
||||||
}
|
}
|
||||||
GenesisFlag = &cli.StringFlag{
|
GenesisFlag = &cli.StringFlag{
|
||||||
Name: "prestate",
|
Name: "prestate",
|
||||||
Usage: "JSON file with prestate (genesis) config",
|
Usage: "JSON file with prestate (genesis) config",
|
||||||
Category: flags.VMCategory,
|
|
||||||
}
|
}
|
||||||
MachineFlag = &cli.BoolFlag{
|
MachineFlag = &cli.BoolFlag{
|
||||||
Name: "json",
|
Name: "json",
|
||||||
Usage: "output trace logs in machine readable format (json)",
|
Usage: "output trace logs in machine readable format (json)",
|
||||||
Category: flags.VMCategory,
|
|
||||||
}
|
}
|
||||||
SenderFlag = &cli.StringFlag{
|
SenderFlag = &cli.StringFlag{
|
||||||
Name: "sender",
|
Name: "sender",
|
||||||
Usage: "The transaction origin",
|
Usage: "The transaction origin",
|
||||||
Category: flags.VMCategory,
|
|
||||||
}
|
}
|
||||||
ReceiverFlag = &cli.StringFlag{
|
ReceiverFlag = &cli.StringFlag{
|
||||||
Name: "receiver",
|
Name: "receiver",
|
||||||
Usage: "The transaction receiver (execution context)",
|
Usage: "The transaction receiver (execution context)",
|
||||||
Category: flags.VMCategory,
|
|
||||||
}
|
}
|
||||||
DisableMemoryFlag = &cli.BoolFlag{
|
DisableMemoryFlag = &cli.BoolFlag{
|
||||||
Name: "nomemory",
|
Name: "nomemory",
|
||||||
Value: true,
|
Value: true,
|
||||||
Usage: "disable memory output",
|
Usage: "disable memory output",
|
||||||
Category: flags.VMCategory,
|
|
||||||
}
|
}
|
||||||
DisableStackFlag = &cli.BoolFlag{
|
DisableStackFlag = &cli.BoolFlag{
|
||||||
Name: "nostack",
|
Name: "nostack",
|
||||||
Usage: "disable stack output",
|
Usage: "disable stack output",
|
||||||
Category: flags.VMCategory,
|
|
||||||
}
|
}
|
||||||
DisableStorageFlag = &cli.BoolFlag{
|
DisableStorageFlag = &cli.BoolFlag{
|
||||||
Name: "nostorage",
|
Name: "nostorage",
|
||||||
Usage: "disable storage output",
|
Usage: "disable storage output",
|
||||||
Category: flags.VMCategory,
|
|
||||||
}
|
}
|
||||||
DisableReturnDataFlag = &cli.BoolFlag{
|
DisableReturnDataFlag = &cli.BoolFlag{
|
||||||
Name: "noreturndata",
|
Name: "noreturndata",
|
||||||
Value: true,
|
Value: true,
|
||||||
Usage: "enable return data output",
|
Usage: "enable return data output",
|
||||||
Category: flags.VMCategory,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -192,38 +183,34 @@ var blockBuilderCommand = &cli.Command{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// vmFlags contains flags related to running the EVM.
|
|
||||||
var vmFlags = []cli.Flag{
|
|
||||||
CodeFlag,
|
|
||||||
CodeFileFlag,
|
|
||||||
CreateFlag,
|
|
||||||
GasFlag,
|
|
||||||
PriceFlag,
|
|
||||||
ValueFlag,
|
|
||||||
InputFlag,
|
|
||||||
InputFileFlag,
|
|
||||||
GenesisFlag,
|
|
||||||
SenderFlag,
|
|
||||||
ReceiverFlag,
|
|
||||||
}
|
|
||||||
|
|
||||||
// traceFlags contains flags that configure tracing output.
|
|
||||||
var traceFlags = []cli.Flag{
|
|
||||||
BenchFlag,
|
|
||||||
DebugFlag,
|
|
||||||
DumpFlag,
|
|
||||||
MachineFlag,
|
|
||||||
StatDumpFlag,
|
|
||||||
DisableMemoryFlag,
|
|
||||||
DisableStackFlag,
|
|
||||||
DisableStorageFlag,
|
|
||||||
DisableReturnDataFlag,
|
|
||||||
}
|
|
||||||
|
|
||||||
var app = flags.NewApp("the evm command line interface")
|
var app = flags.NewApp("the evm command line interface")
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
app.Flags = flags.Merge(vmFlags, traceFlags, debug.Flags)
|
app.Flags = []cli.Flag{
|
||||||
|
BenchFlag,
|
||||||
|
CreateFlag,
|
||||||
|
DebugFlag,
|
||||||
|
VerbosityFlag,
|
||||||
|
CodeFlag,
|
||||||
|
CodeFileFlag,
|
||||||
|
GasFlag,
|
||||||
|
PriceFlag,
|
||||||
|
ValueFlag,
|
||||||
|
DumpFlag,
|
||||||
|
InputFlag,
|
||||||
|
InputFileFlag,
|
||||||
|
MemProfileFlag,
|
||||||
|
CPUProfileFlag,
|
||||||
|
StatDumpFlag,
|
||||||
|
GenesisFlag,
|
||||||
|
MachineFlag,
|
||||||
|
SenderFlag,
|
||||||
|
ReceiverFlag,
|
||||||
|
DisableMemoryFlag,
|
||||||
|
DisableStackFlag,
|
||||||
|
DisableStorageFlag,
|
||||||
|
DisableReturnDataFlag,
|
||||||
|
}
|
||||||
app.Commands = []*cli.Command{
|
app.Commands = []*cli.Command{
|
||||||
compileCommand,
|
compileCommand,
|
||||||
disasmCommand,
|
disasmCommand,
|
||||||
|
|
@ -234,14 +221,6 @@ func init() {
|
||||||
transactionCommand,
|
transactionCommand,
|
||||||
blockBuilderCommand,
|
blockBuilderCommand,
|
||||||
}
|
}
|
||||||
app.Before = func(ctx *cli.Context) error {
|
|
||||||
flags.MigrateGlobalFlags(ctx)
|
|
||||||
return debug.Setup(ctx)
|
|
||||||
}
|
|
||||||
app.After = func(ctx *cli.Context) error {
|
|
||||||
debug.Exit()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
goruntime "runtime"
|
goruntime "runtime"
|
||||||
|
"runtime/pprof"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -33,10 +34,12 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/core/vm/runtime"
|
"github.com/ethereum/go-ethereum/core/vm/runtime"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
||||||
"github.com/ethereum/go-ethereum/internal/flags"
|
"github.com/ethereum/go-ethereum/internal/flags"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"github.com/ethereum/go-ethereum/trie/triedb/hashdb"
|
"github.com/ethereum/go-ethereum/trie/triedb/hashdb"
|
||||||
|
|
@ -49,7 +52,6 @@ var runCommand = &cli.Command{
|
||||||
Usage: "run arbitrary evm binary",
|
Usage: "run arbitrary evm binary",
|
||||||
ArgsUsage: "<code>",
|
ArgsUsage: "<code>",
|
||||||
Description: `The run command runs arbitrary EVM code.`,
|
Description: `The run command runs arbitrary EVM code.`,
|
||||||
Flags: flags.Merge(vmFlags, traceFlags),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// readGenesis will read the given JSON format genesis file and return
|
// readGenesis will read the given JSON format genesis file and return
|
||||||
|
|
@ -107,6 +109,9 @@ func timedExec(bench bool, execFunc func() ([]byte, uint64, error)) (output []by
|
||||||
}
|
}
|
||||||
|
|
||||||
func runCmd(ctx *cli.Context) error {
|
func runCmd(ctx *cli.Context) error {
|
||||||
|
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
|
||||||
|
glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name)))
|
||||||
|
log.Root().SetHandler(glogger)
|
||||||
logconfig := &logger.Config{
|
logconfig := &logger.Config{
|
||||||
EnableMemory: !ctx.Bool(DisableMemoryFlag.Name),
|
EnableMemory: !ctx.Bool(DisableMemoryFlag.Name),
|
||||||
DisableStack: ctx.Bool(DisableStackFlag.Name),
|
DisableStack: ctx.Bool(DisableStackFlag.Name),
|
||||||
|
|
@ -116,14 +121,15 @@ func runCmd(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
tracer vm.EVMLogger
|
tracer vm.EVMLogger
|
||||||
debugLogger *logger.StructLogger
|
debugLogger *logger.StructLogger
|
||||||
statedb *state.StateDB
|
statedb *state.StateDB
|
||||||
chainConfig *params.ChainConfig
|
chainConfig *params.ChainConfig
|
||||||
sender = common.BytesToAddress([]byte("sender"))
|
sender = common.BytesToAddress([]byte("sender"))
|
||||||
receiver = common.BytesToAddress([]byte("receiver"))
|
receiver = common.BytesToAddress([]byte("receiver"))
|
||||||
preimages = ctx.Bool(DumpFlag.Name)
|
genesisConfig *core.Genesis
|
||||||
blobHashes []common.Hash // TODO (MariusVanDerWijden) implement blob hashes in state tests
|
preimages = ctx.Bool(DumpFlag.Name)
|
||||||
|
blobHashes []common.Hash // TODO (MariusVanDerWijden) implement blob hashes in state tests
|
||||||
)
|
)
|
||||||
if ctx.Bool(MachineFlag.Name) {
|
if ctx.Bool(MachineFlag.Name) {
|
||||||
tracer = logger.NewJSONLogger(logconfig, os.Stdout)
|
tracer = logger.NewJSONLogger(logconfig, os.Stdout)
|
||||||
|
|
@ -133,30 +139,30 @@ func runCmd(ctx *cli.Context) error {
|
||||||
} else {
|
} else {
|
||||||
debugLogger = logger.NewStructLogger(logconfig)
|
debugLogger = logger.NewStructLogger(logconfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
initialGas := ctx.Uint64(GasFlag.Name)
|
|
||||||
genesisConfig := new(core.Genesis)
|
|
||||||
genesisConfig.GasLimit = initialGas
|
|
||||||
if ctx.String(GenesisFlag.Name) != "" {
|
if ctx.String(GenesisFlag.Name) != "" {
|
||||||
genesisConfig = readGenesis(ctx.String(GenesisFlag.Name))
|
gen := readGenesis(ctx.String(GenesisFlag.Name))
|
||||||
if genesisConfig.GasLimit != 0 {
|
genesisConfig = gen
|
||||||
initialGas = genesisConfig.GasLimit
|
db := rawdb.NewMemoryDatabase()
|
||||||
}
|
triedb := trie.NewDatabase(db, &trie.Config{
|
||||||
|
Preimages: preimages,
|
||||||
|
HashDB: hashdb.Defaults,
|
||||||
|
})
|
||||||
|
defer triedb.Close()
|
||||||
|
genesis := gen.MustCommit(db, triedb)
|
||||||
|
sdb := state.NewDatabaseWithNodeDB(db, triedb)
|
||||||
|
statedb, _ = state.New(genesis.Root(), sdb, nil)
|
||||||
|
chainConfig = gen.Config
|
||||||
} else {
|
} else {
|
||||||
genesisConfig.Config = params.AllEthashProtocolChanges
|
db := rawdb.NewMemoryDatabase()
|
||||||
|
triedb := trie.NewDatabase(db, &trie.Config{
|
||||||
|
Preimages: preimages,
|
||||||
|
HashDB: hashdb.Defaults,
|
||||||
|
})
|
||||||
|
defer triedb.Close()
|
||||||
|
sdb := state.NewDatabaseWithNodeDB(db, triedb)
|
||||||
|
statedb, _ = state.New(types.EmptyRootHash, sdb, nil)
|
||||||
|
genesisConfig = new(core.Genesis)
|
||||||
}
|
}
|
||||||
|
|
||||||
db := rawdb.NewMemoryDatabase()
|
|
||||||
triedb := trie.NewDatabase(db, &trie.Config{
|
|
||||||
Preimages: preimages,
|
|
||||||
HashDB: hashdb.Defaults,
|
|
||||||
})
|
|
||||||
defer triedb.Close()
|
|
||||||
genesis := genesisConfig.MustCommit(db, triedb)
|
|
||||||
sdb := state.NewDatabaseWithNodeDB(db, triedb)
|
|
||||||
statedb, _ = state.New(genesis.Root(), sdb, nil)
|
|
||||||
chainConfig = genesisConfig.Config
|
|
||||||
|
|
||||||
if ctx.String(SenderFlag.Name) != "" {
|
if ctx.String(SenderFlag.Name) != "" {
|
||||||
sender = common.HexToAddress(ctx.String(SenderFlag.Name))
|
sender = common.HexToAddress(ctx.String(SenderFlag.Name))
|
||||||
}
|
}
|
||||||
|
|
@ -210,6 +216,10 @@ func runCmd(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
code = common.Hex2Bytes(bin)
|
code = common.Hex2Bytes(bin)
|
||||||
}
|
}
|
||||||
|
initialGas := ctx.Uint64(GasFlag.Name)
|
||||||
|
if genesisConfig.GasLimit != 0 {
|
||||||
|
initialGas = genesisConfig.GasLimit
|
||||||
|
}
|
||||||
runtimeConfig := runtime.Config{
|
runtimeConfig := runtime.Config{
|
||||||
Origin: sender,
|
Origin: sender,
|
||||||
State: statedb,
|
State: statedb,
|
||||||
|
|
@ -226,6 +236,19 @@ func runCmd(ctx *cli.Context) error {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cpuProfilePath := ctx.String(CPUProfileFlag.Name); cpuProfilePath != "" {
|
||||||
|
f, err := os.Create(cpuProfilePath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("could not create CPU profile: ", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if err := pprof.StartCPUProfile(f); err != nil {
|
||||||
|
fmt.Println("could not start CPU profile: ", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
defer pprof.StopCPUProfile()
|
||||||
|
}
|
||||||
|
|
||||||
if chainConfig != nil {
|
if chainConfig != nil {
|
||||||
runtimeConfig.ChainConfig = chainConfig
|
runtimeConfig.ChainConfig = chainConfig
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -273,6 +296,19 @@ func runCmd(ctx *cli.Context) error {
|
||||||
fmt.Println(string(statedb.Dump(nil)))
|
fmt.Println(string(statedb.Dump(nil)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if memProfilePath := ctx.String(MemProfileFlag.Name); memProfilePath != "" {
|
||||||
|
f, err := os.Create(memProfilePath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("could not create memory profile: ", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if err := pprof.WriteHeapProfile(f); err != nil {
|
||||||
|
fmt.Println("could not write memory profile: ", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
|
||||||
if ctx.Bool(DebugFlag.Name) {
|
if ctx.Bool(DebugFlag.Name) {
|
||||||
if debugLogger != nil {
|
if debugLogger != nil {
|
||||||
fmt.Fprintln(os.Stderr, "#### TRACE ####")
|
fmt.Fprintln(os.Stderr, "#### TRACE ####")
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/tests"
|
"github.com/ethereum/go-ethereum/tests"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
@ -51,6 +52,11 @@ type StatetestResult struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func stateTestCmd(ctx *cli.Context) error {
|
func stateTestCmd(ctx *cli.Context) error {
|
||||||
|
// Configure the go-ethereum logger
|
||||||
|
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
|
||||||
|
glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name)))
|
||||||
|
log.Root().SetHandler(glogger)
|
||||||
|
|
||||||
// Configure the EVM logger
|
// Configure the EVM logger
|
||||||
config := &logger.Config{
|
config := &logger.Config{
|
||||||
EnableMemory: !ctx.Bool(DisableMemoryFlag.Name),
|
EnableMemory: !ctx.Bool(DisableMemoryFlag.Name),
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,6 @@
|
||||||
package asm
|
package asm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -32,7 +30,7 @@ import (
|
||||||
// and holds the tokens for the program.
|
// and holds the tokens for the program.
|
||||||
type Compiler struct {
|
type Compiler struct {
|
||||||
tokens []token
|
tokens []token
|
||||||
out []byte
|
binary []interface{}
|
||||||
|
|
||||||
labels map[string]int
|
labels map[string]int
|
||||||
|
|
||||||
|
|
@ -52,10 +50,12 @@ func NewCompiler(debug bool) *Compiler {
|
||||||
// Feed feeds tokens in to ch and are interpreted by
|
// Feed feeds tokens in to ch and are interpreted by
|
||||||
// the compiler.
|
// the compiler.
|
||||||
//
|
//
|
||||||
// feed is the first pass in the compile stage as it collects the used labels in the
|
// feed is the first pass in the compile stage as it
|
||||||
// program and keeps a program counter which is used to determine the locations of the
|
// collects the used labels in the program and keeps a
|
||||||
// jump dests. The labels can than be used in the second stage to push labels and
|
// program counter which is used to determine the locations
|
||||||
// determine the right position.
|
// of the jump dests. The labels can than be used in the
|
||||||
|
// second stage to push labels and determine the right
|
||||||
|
// position.
|
||||||
func (c *Compiler) Feed(ch <-chan token) {
|
func (c *Compiler) Feed(ch <-chan token) {
|
||||||
var prev token
|
var prev token
|
||||||
for i := range ch {
|
for i := range ch {
|
||||||
|
|
@ -79,6 +79,7 @@ func (c *Compiler) Feed(ch <-chan token) {
|
||||||
c.pc++
|
c.pc++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.tokens = append(c.tokens, i)
|
c.tokens = append(c.tokens, i)
|
||||||
prev = i
|
prev = i
|
||||||
}
|
}
|
||||||
|
|
@ -87,11 +88,12 @@ func (c *Compiler) Feed(ch <-chan token) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile compiles the current tokens and returns a binary string that can be interpreted
|
// Compile compiles the current tokens and returns a
|
||||||
// by the EVM and an error if it failed.
|
// binary string that can be interpreted by the EVM
|
||||||
|
// and an error if it failed.
|
||||||
//
|
//
|
||||||
// compile is the second stage in the compile phase which compiles the tokens to EVM
|
// compile is the second stage in the compile phase
|
||||||
// instructions.
|
// which compiles the tokens to EVM instructions.
|
||||||
func (c *Compiler) Compile() (string, []error) {
|
func (c *Compiler) Compile() (string, []error) {
|
||||||
var errors []error
|
var errors []error
|
||||||
// continue looping over the tokens until
|
// continue looping over the tokens until
|
||||||
|
|
@ -103,8 +105,16 @@ func (c *Compiler) Compile() (string, []error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// turn the binary to hex
|
// turn the binary to hex
|
||||||
h := hex.EncodeToString(c.out)
|
var bin strings.Builder
|
||||||
return h, errors
|
for _, v := range c.binary {
|
||||||
|
switch v := v.(type) {
|
||||||
|
case vm.OpCode:
|
||||||
|
bin.WriteString(fmt.Sprintf("%x", []byte{byte(v)}))
|
||||||
|
case []byte:
|
||||||
|
bin.WriteString(fmt.Sprintf("%x", v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bin.String(), errors
|
||||||
}
|
}
|
||||||
|
|
||||||
// next returns the next token and increments the
|
// next returns the next token and increments the
|
||||||
|
|
@ -146,114 +156,87 @@ func (c *Compiler) compileLine() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseNumber compiles the number to bytes
|
// compileNumber compiles the number to bytes
|
||||||
func parseNumber(tok token) ([]byte, error) {
|
func (c *Compiler) compileNumber(element token) {
|
||||||
if tok.typ != number {
|
num := math.MustParseBig256(element.text).Bytes()
|
||||||
panic("parseNumber of non-number token")
|
if len(num) == 0 {
|
||||||
|
num = []byte{0}
|
||||||
}
|
}
|
||||||
num, ok := math.ParseBig256(tok.text)
|
c.pushBin(num)
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("invalid number")
|
|
||||||
}
|
|
||||||
bytes := num.Bytes()
|
|
||||||
if len(bytes) == 0 {
|
|
||||||
bytes = []byte{0}
|
|
||||||
}
|
|
||||||
return bytes, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// compileElement compiles the element (push & label or both)
|
// compileElement compiles the element (push & label or both)
|
||||||
// to a binary representation and may error if incorrect statements
|
// to a binary representation and may error if incorrect statements
|
||||||
// where fed.
|
// where fed.
|
||||||
func (c *Compiler) compileElement(element token) error {
|
func (c *Compiler) compileElement(element token) error {
|
||||||
switch {
|
// check for a jump. jumps must be read and compiled
|
||||||
case isJump(element.text):
|
// from right to left.
|
||||||
return c.compileJump(element.text)
|
if isJump(element.text) {
|
||||||
case isPush(element.text):
|
rvalue := c.next()
|
||||||
return c.compilePush()
|
switch rvalue.typ {
|
||||||
default:
|
case number:
|
||||||
c.outputOpcode(toBinary(element.text))
|
// TODO figure out how to return the error properly
|
||||||
|
c.compileNumber(rvalue)
|
||||||
|
case stringValue:
|
||||||
|
// strings are quoted, remove them.
|
||||||
|
c.pushBin(rvalue.text[1 : len(rvalue.text)-2])
|
||||||
|
case label:
|
||||||
|
c.pushBin(vm.PUSH4)
|
||||||
|
pos := big.NewInt(int64(c.labels[rvalue.text])).Bytes()
|
||||||
|
pos = append(make([]byte, 4-len(pos)), pos...)
|
||||||
|
c.pushBin(pos)
|
||||||
|
case lineEnd:
|
||||||
|
c.pos--
|
||||||
|
default:
|
||||||
|
return compileErr(rvalue, rvalue.text, "number, string or label")
|
||||||
|
}
|
||||||
|
// push the operation
|
||||||
|
c.pushBin(toBinary(element.text))
|
||||||
return nil
|
return nil
|
||||||
}
|
} else if isPush(element.text) {
|
||||||
}
|
// handle pushes. pushes are read from left to right.
|
||||||
|
var value []byte
|
||||||
|
|
||||||
func (c *Compiler) compileJump(jumpType string) error {
|
rvalue := c.next()
|
||||||
rvalue := c.next()
|
switch rvalue.typ {
|
||||||
switch rvalue.typ {
|
case number:
|
||||||
case number:
|
value = math.MustParseBig256(rvalue.text).Bytes()
|
||||||
numBytes, err := parseNumber(rvalue)
|
if len(value) == 0 {
|
||||||
if err != nil {
|
value = []byte{0}
|
||||||
return err
|
}
|
||||||
|
case stringValue:
|
||||||
|
value = []byte(rvalue.text[1 : len(rvalue.text)-1])
|
||||||
|
case label:
|
||||||
|
value = big.NewInt(int64(c.labels[rvalue.text])).Bytes()
|
||||||
|
value = append(make([]byte, 4-len(value)), value...)
|
||||||
|
default:
|
||||||
|
return compileErr(rvalue, rvalue.text, "number, string or label")
|
||||||
}
|
}
|
||||||
c.outputBytes(numBytes)
|
|
||||||
|
|
||||||
case stringValue:
|
if len(value) > 32 {
|
||||||
// strings are quoted, remove them.
|
return fmt.Errorf("%d type error: unsupported string or number with size > 32", rvalue.lineno)
|
||||||
str := rvalue.text[1 : len(rvalue.text)-2]
|
|
||||||
c.outputBytes([]byte(str))
|
|
||||||
|
|
||||||
case label:
|
|
||||||
c.outputOpcode(vm.PUSH4)
|
|
||||||
pos := big.NewInt(int64(c.labels[rvalue.text])).Bytes()
|
|
||||||
pos = append(make([]byte, 4-len(pos)), pos...)
|
|
||||||
c.outputBytes(pos)
|
|
||||||
|
|
||||||
case lineEnd:
|
|
||||||
// push without argument is supported, it just takes the destination from the stack.
|
|
||||||
c.pos--
|
|
||||||
|
|
||||||
default:
|
|
||||||
return compileErr(rvalue, rvalue.text, "number, string or label")
|
|
||||||
}
|
|
||||||
// push the operation
|
|
||||||
c.outputOpcode(toBinary(jumpType))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Compiler) compilePush() error {
|
|
||||||
// handle pushes. pushes are read from left to right.
|
|
||||||
var value []byte
|
|
||||||
rvalue := c.next()
|
|
||||||
switch rvalue.typ {
|
|
||||||
case number:
|
|
||||||
value = math.MustParseBig256(rvalue.text).Bytes()
|
|
||||||
if len(value) == 0 {
|
|
||||||
value = []byte{0}
|
|
||||||
}
|
}
|
||||||
case stringValue:
|
|
||||||
value = []byte(rvalue.text[1 : len(rvalue.text)-1])
|
c.pushBin(vm.OpCode(int(vm.PUSH1) - 1 + len(value)))
|
||||||
case label:
|
c.pushBin(value)
|
||||||
value = big.NewInt(int64(c.labels[rvalue.text])).Bytes()
|
} else {
|
||||||
value = append(make([]byte, 4-len(value)), value...)
|
c.pushBin(toBinary(element.text))
|
||||||
default:
|
|
||||||
return compileErr(rvalue, rvalue.text, "number, string or label")
|
|
||||||
}
|
}
|
||||||
if len(value) > 32 {
|
|
||||||
return fmt.Errorf("%d: string or number size > 32 bytes", rvalue.lineno+1)
|
|
||||||
}
|
|
||||||
c.outputOpcode(vm.OpCode(int(vm.PUSH1) - 1 + len(value)))
|
|
||||||
c.outputBytes(value)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// compileLabel pushes a jumpdest to the binary slice.
|
// compileLabel pushes a jumpdest to the binary slice.
|
||||||
func (c *Compiler) compileLabel() {
|
func (c *Compiler) compileLabel() {
|
||||||
c.outputOpcode(vm.JUMPDEST)
|
c.pushBin(vm.JUMPDEST)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) outputOpcode(op vm.OpCode) {
|
// pushBin pushes the value v to the binary stack.
|
||||||
|
func (c *Compiler) pushBin(v interface{}) {
|
||||||
if c.debug {
|
if c.debug {
|
||||||
fmt.Printf("%d: %v\n", len(c.out), op)
|
fmt.Printf("%d: %v\n", len(c.binary), v)
|
||||||
}
|
}
|
||||||
c.out = append(c.out, byte(op))
|
c.binary = append(c.binary, v)
|
||||||
}
|
|
||||||
|
|
||||||
// output pushes the value v to the binary stack.
|
|
||||||
func (c *Compiler) outputBytes(b []byte) {
|
|
||||||
if c.debug {
|
|
||||||
fmt.Printf("%d: %x\n", len(c.out), b)
|
|
||||||
}
|
|
||||||
c.out = append(c.out, b...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// isPush returns whether the string op is either any of
|
// isPush returns whether the string op is either any of
|
||||||
|
|
@ -280,13 +263,13 @@ type compileError struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (err compileError) Error() string {
|
func (err compileError) Error() string {
|
||||||
return fmt.Sprintf("%d: syntax error: unexpected %v, expected %v", err.lineno, err.got, err.want)
|
return fmt.Sprintf("%d syntax error: unexpected %v, expected %v", err.lineno, err.got, err.want)
|
||||||
}
|
}
|
||||||
|
|
||||||
func compileErr(c token, got, want string) error {
|
func compileErr(c token, got, want string) error {
|
||||||
return compileError{
|
return compileError{
|
||||||
got: got,
|
got: got,
|
||||||
want: want,
|
want: want,
|
||||||
lineno: c.lineno + 1,
|
lineno: c.lineno,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,14 +54,6 @@ func TestCompiler(t *testing.T) {
|
||||||
`,
|
`,
|
||||||
output: "6300000006565b",
|
output: "6300000006565b",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
input: `
|
|
||||||
JUMP @label
|
|
||||||
label: ;; comment
|
|
||||||
ADD ;; comment
|
|
||||||
`,
|
|
||||||
output: "6300000006565b01",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
ch := Lex([]byte(test.input), false)
|
ch := Lex([]byte(test.input), false)
|
||||||
|
|
|
||||||
|
|
@ -72,16 +72,6 @@ func TestLexer(t *testing.T) {
|
||||||
input: "@label123",
|
input: "@label123",
|
||||||
tokens: []token{{typ: lineStart}, {typ: label, text: "label123"}, {typ: eof}},
|
tokens: []token{{typ: lineStart}, {typ: label, text: "label123"}, {typ: eof}},
|
||||||
},
|
},
|
||||||
// comment after label
|
|
||||||
{
|
|
||||||
input: "@label123 ;; comment",
|
|
||||||
tokens: []token{{typ: lineStart}, {typ: label, text: "label123"}, {typ: eof}},
|
|
||||||
},
|
|
||||||
// comment after instruction
|
|
||||||
{
|
|
||||||
input: "push 3 ;; comment\nadd",
|
|
||||||
tokens: []token{{typ: lineStart}, {typ: element, text: "push"}, {typ: number, text: "3"}, {typ: lineEnd, text: "\n"}, {typ: lineStart, lineno: 1}, {typ: element, lineno: 1, text: "add"}, {typ: eof, lineno: 1}},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
|
|
||||||
|
|
@ -42,8 +42,6 @@ type token struct {
|
||||||
// is able to parse and return.
|
// is able to parse and return.
|
||||||
type tokenType int
|
type tokenType int
|
||||||
|
|
||||||
//go:generate go run golang.org/x/tools/cmd/stringer -type tokenType
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
eof tokenType = iota // end of file
|
eof tokenType = iota // end of file
|
||||||
lineStart // emitted when a line starts
|
lineStart // emitted when a line starts
|
||||||
|
|
@ -54,13 +52,31 @@ const (
|
||||||
labelDef // label definition is emitted when a new label is found
|
labelDef // label definition is emitted when a new label is found
|
||||||
number // number is emitted when a number is found
|
number // number is emitted when a number is found
|
||||||
stringValue // stringValue is emitted when a string has been found
|
stringValue // stringValue is emitted when a string has been found
|
||||||
|
|
||||||
|
Numbers = "1234567890" // characters representing any decimal number
|
||||||
|
HexadecimalNumbers = Numbers + "aAbBcCdDeEfF" // characters representing any hexadecimal
|
||||||
|
Alpha = "abcdefghijklmnopqrstuwvxyzABCDEFGHIJKLMNOPQRSTUWVXYZ" // characters representing alphanumeric
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
// String implements stringer
|
||||||
decimalNumbers = "1234567890" // characters representing any decimal number
|
func (it tokenType) String() string {
|
||||||
hexNumbers = decimalNumbers + "aAbBcCdDeEfF" // characters representing any hexadecimal
|
if int(it) > len(stringtokenTypes) {
|
||||||
alpha = "abcdefghijklmnopqrstuwvxyzABCDEFGHIJKLMNOPQRSTUWVXYZ" // characters representing alphanumeric
|
return "invalid"
|
||||||
)
|
}
|
||||||
|
return stringtokenTypes[it]
|
||||||
|
}
|
||||||
|
|
||||||
|
var stringtokenTypes = []string{
|
||||||
|
eof: "EOF",
|
||||||
|
lineStart: "new line",
|
||||||
|
lineEnd: "end of line",
|
||||||
|
invalidStatement: "invalid statement",
|
||||||
|
element: "element",
|
||||||
|
label: "label",
|
||||||
|
labelDef: "label definition",
|
||||||
|
number: "number",
|
||||||
|
stringValue: "string",
|
||||||
|
}
|
||||||
|
|
||||||
// lexer is the basic construct for parsing
|
// lexer is the basic construct for parsing
|
||||||
// source code and turning them in to tokens.
|
// source code and turning them in to tokens.
|
||||||
|
|
@ -184,6 +200,7 @@ func lexLine(l *lexer) stateFn {
|
||||||
l.emit(lineEnd)
|
l.emit(lineEnd)
|
||||||
l.ignore()
|
l.ignore()
|
||||||
l.lineno++
|
l.lineno++
|
||||||
|
|
||||||
l.emit(lineStart)
|
l.emit(lineStart)
|
||||||
case r == ';' && l.peek() == ';':
|
case r == ';' && l.peek() == ';':
|
||||||
return lexComment
|
return lexComment
|
||||||
|
|
@ -208,7 +225,6 @@ func lexLine(l *lexer) stateFn {
|
||||||
// of the line and discards the text.
|
// of the line and discards the text.
|
||||||
func lexComment(l *lexer) stateFn {
|
func lexComment(l *lexer) stateFn {
|
||||||
l.acceptRunUntil('\n')
|
l.acceptRunUntil('\n')
|
||||||
l.backup()
|
|
||||||
l.ignore()
|
l.ignore()
|
||||||
|
|
||||||
return lexLine
|
return lexLine
|
||||||
|
|
@ -218,7 +234,7 @@ func lexComment(l *lexer) stateFn {
|
||||||
// the lex text state function to advance the parsing
|
// the lex text state function to advance the parsing
|
||||||
// process.
|
// process.
|
||||||
func lexLabel(l *lexer) stateFn {
|
func lexLabel(l *lexer) stateFn {
|
||||||
l.acceptRun(alpha + "_" + decimalNumbers)
|
l.acceptRun(Alpha + "_" + Numbers)
|
||||||
|
|
||||||
l.emit(label)
|
l.emit(label)
|
||||||
|
|
||||||
|
|
@ -237,9 +253,9 @@ func lexInsideString(l *lexer) stateFn {
|
||||||
}
|
}
|
||||||
|
|
||||||
func lexNumber(l *lexer) stateFn {
|
func lexNumber(l *lexer) stateFn {
|
||||||
acceptance := decimalNumbers
|
acceptance := Numbers
|
||||||
if l.accept("xX") {
|
if l.accept("xX") {
|
||||||
acceptance = hexNumbers
|
acceptance = HexadecimalNumbers
|
||||||
}
|
}
|
||||||
l.acceptRun(acceptance)
|
l.acceptRun(acceptance)
|
||||||
|
|
||||||
|
|
@ -249,7 +265,7 @@ func lexNumber(l *lexer) stateFn {
|
||||||
}
|
}
|
||||||
|
|
||||||
func lexElement(l *lexer) stateFn {
|
func lexElement(l *lexer) stateFn {
|
||||||
l.acceptRun(alpha + "_" + decimalNumbers)
|
l.acceptRun(Alpha + "_" + Numbers)
|
||||||
|
|
||||||
if l.peek() == ':' {
|
if l.peek() == ':' {
|
||||||
l.emit(labelDef)
|
l.emit(labelDef)
|
||||||
|
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
// Code generated by "stringer -type tokenType"; DO NOT EDIT.
|
|
||||||
|
|
||||||
package asm
|
|
||||||
|
|
||||||
import "strconv"
|
|
||||||
|
|
||||||
func _() {
|
|
||||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
|
||||||
// Re-run the stringer command to generate them again.
|
|
||||||
var x [1]struct{}
|
|
||||||
_ = x[eof-0]
|
|
||||||
_ = x[lineStart-1]
|
|
||||||
_ = x[lineEnd-2]
|
|
||||||
_ = x[invalidStatement-3]
|
|
||||||
_ = x[element-4]
|
|
||||||
_ = x[label-5]
|
|
||||||
_ = x[labelDef-6]
|
|
||||||
_ = x[number-7]
|
|
||||||
_ = x[stringValue-8]
|
|
||||||
}
|
|
||||||
|
|
||||||
const _tokenType_name = "eoflineStartlineEndinvalidStatementelementlabellabelDefnumberstringValue"
|
|
||||||
|
|
||||||
var _tokenType_index = [...]uint8{0, 3, 12, 19, 35, 42, 47, 55, 61, 72}
|
|
||||||
|
|
||||||
func (i tokenType) String() string {
|
|
||||||
if i < 0 || i >= tokenType(len(_tokenType_index)-1) {
|
|
||||||
return "tokenType(" + strconv.FormatInt(int64(i), 10) + ")"
|
|
||||||
}
|
|
||||||
return _tokenType_name[_tokenType_index[i]:_tokenType_index[i+1]]
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue