Update compiler (#22)
* refactor to use ast.Walk for recursive converting * added lots of test cases * added a new way to handle jump labels * function calls with multiple arguments * binary expression (LOR LAND) * struct types + method receives * cleaner opcode dumps, side by side diff for debugging test cases
This commit is contained in:
parent
b257a06f3e
commit
8fe079ec8e
19 changed files with 1298 additions and 1064 deletions
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
0.10.0
|
0.11.0
|
||||||
|
|
|
@ -1,12 +1,7 @@
|
||||||
package smartcontract
|
package smartcontract
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/compiler"
|
"github.com/CityOfZion/neo-go/pkg/vm/compiler"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
|
@ -43,39 +38,19 @@ func contractCompile(ctx *cli.Context) error {
|
||||||
return errors.New("not enough arguments")
|
return errors.New("not enough arguments")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
o := &compiler.Options{
|
||||||
|
Outfile: ctx.String("out"),
|
||||||
|
Debug: true,
|
||||||
|
}
|
||||||
|
|
||||||
src := ctx.Args()[0]
|
src := ctx.Args()[0]
|
||||||
c := compiler.New()
|
return compiler.CompileAndSave(src, o)
|
||||||
if err := c.CompileSource(src); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
filename := strings.Split(src, ".")[0]
|
|
||||||
filename = filename + ".avm"
|
|
||||||
|
|
||||||
out := ctx.String("out")
|
|
||||||
if len(out) > 0 {
|
|
||||||
filename = out
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := os.Create(out)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
hx := hex.EncodeToString(c.Buffer().Bytes())
|
|
||||||
fmt.Println(hx)
|
|
||||||
|
|
||||||
_, err = io.Copy(f, c.Buffer())
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func contractDumpOpcode(ctx *cli.Context) error {
|
func contractDumpOpcode(ctx *cli.Context) error {
|
||||||
src := ctx.Args()[0]
|
if len(ctx.Args()) == 0 {
|
||||||
|
return errors.New("not enough arguments")
|
||||||
c := compiler.New()
|
|
||||||
if err := c.CompileSource(src); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
c.DumpOpcode()
|
src := ctx.Args()[0]
|
||||||
return nil
|
return compiler.DumpOpcode(src)
|
||||||
}
|
}
|
||||||
|
|
35
pkg/vm/compiler/README.md
Normal file
35
pkg/vm/compiler/README.md
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
# NEO-GO Compiler
|
||||||
|
|
||||||
|
The neo-go compiler compiles Go programs to bytecode that the NEO virtual machine can understand.
|
||||||
|
|
||||||
|
> The neo-go compiler is under very active development and will be updated on a weekly basis.
|
||||||
|
|
||||||
|
## Currently supported
|
||||||
|
- type checker
|
||||||
|
- multiple assigns
|
||||||
|
- types int, string and bool
|
||||||
|
- struct types + method receives
|
||||||
|
- functions
|
||||||
|
- composite literals `[]int, []string`
|
||||||
|
- basic if statements
|
||||||
|
- binary expressions.
|
||||||
|
- return statements
|
||||||
|
|
||||||
|
## Not yet implemented
|
||||||
|
- for loops
|
||||||
|
- ranges
|
||||||
|
- builtins (append, len, ..)
|
||||||
|
- blockchain helpers (sha256, storage, ..)
|
||||||
|
- import packages
|
||||||
|
|
||||||
|
## Not supported
|
||||||
|
Due to the limitations of the NEO virtual machine, features listed below will not be supported.
|
||||||
|
- channels
|
||||||
|
- goroutines
|
||||||
|
- multiple returns
|
||||||
|
|
||||||
|
## How to report bugs
|
||||||
|
1. Make a proper testcase (example testcases can be found in the tests folder)
|
||||||
|
2. Create an issue on Github
|
||||||
|
3. Make a PR with a reference to the created issue, containing the testcase that proves the bug
|
||||||
|
4. Either you fix the bug yourself or wait for patch that solves the problem
|
472
pkg/vm/compiler/codegen.go
Normal file
472
pkg/vm/compiler/codegen.go
Normal file
|
@ -0,0 +1,472 @@
|
||||||
|
package compiler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"go/ast"
|
||||||
|
"go/constant"
|
||||||
|
"go/token"
|
||||||
|
"go/types"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/vm"
|
||||||
|
)
|
||||||
|
|
||||||
|
const mainIdent = "Main"
|
||||||
|
|
||||||
|
type codegen struct {
|
||||||
|
prog *bytes.Buffer
|
||||||
|
|
||||||
|
// Type information
|
||||||
|
typeInfo *types.Info
|
||||||
|
|
||||||
|
// a mapping of func identifiers with its scope.
|
||||||
|
funcs map[string]*funcScope
|
||||||
|
|
||||||
|
// current function being generated
|
||||||
|
fctx *funcScope
|
||||||
|
|
||||||
|
// Label table for recording jump destinations.
|
||||||
|
l []int
|
||||||
|
}
|
||||||
|
|
||||||
|
// newLabel creates a new label to jump to
|
||||||
|
func (c *codegen) newLabel() (l int) {
|
||||||
|
l = len(c.l)
|
||||||
|
c.l = append(c.l, -1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *codegen) setLabel(l int) {
|
||||||
|
c.l[l] = c.pc() + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// pc return the program offset off the last instruction.
|
||||||
|
func (c *codegen) pc() int {
|
||||||
|
return c.prog.Len() - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *codegen) emitLoadConst(t types.TypeAndValue) {
|
||||||
|
switch typ := t.Type.(type) {
|
||||||
|
case *types.Basic:
|
||||||
|
switch typ.Kind() {
|
||||||
|
case types.Int:
|
||||||
|
val, _ := constant.Int64Val(t.Value)
|
||||||
|
emitInt(c.prog, val)
|
||||||
|
case types.String:
|
||||||
|
val := constant.StringVal(t.Value)
|
||||||
|
emitString(c.prog, val)
|
||||||
|
case types.Bool, types.UntypedBool:
|
||||||
|
val := constant.BoolVal(t.Value)
|
||||||
|
emitBool(c.prog, val)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.Fatalf("compiler don't know how to convert this constant: %v", t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *codegen) emitLoadLocal(name string) {
|
||||||
|
pos := c.fctx.loadLocal(name)
|
||||||
|
if pos < 0 {
|
||||||
|
log.Fatalf("cannot load local variable with position: %d", pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
emitOpcode(c.prog, vm.Ofromaltstack)
|
||||||
|
emitOpcode(c.prog, vm.Odup)
|
||||||
|
emitOpcode(c.prog, vm.Otoaltstack)
|
||||||
|
|
||||||
|
emitInt(c.prog, int64(pos))
|
||||||
|
emitOpcode(c.prog, vm.Opickitem)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *codegen) emitLoadStructField(sName, fName string) {
|
||||||
|
strct := c.fctx.loadStruct(sName)
|
||||||
|
pos := strct.loadField(fName)
|
||||||
|
emitInt(c.prog, int64(pos))
|
||||||
|
emitOpcode(c.prog, vm.Opickitem)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *codegen) emitStoreLocal(pos int) {
|
||||||
|
emitOpcode(c.prog, vm.Ofromaltstack)
|
||||||
|
emitOpcode(c.prog, vm.Odup)
|
||||||
|
emitOpcode(c.prog, vm.Otoaltstack)
|
||||||
|
|
||||||
|
if pos < 0 {
|
||||||
|
log.Fatalf("invalid position to store local: %d", pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
emitInt(c.prog, int64(pos))
|
||||||
|
emitInt(c.prog, 2)
|
||||||
|
emitOpcode(c.prog, vm.Oroll)
|
||||||
|
emitOpcode(c.prog, vm.Osetitem)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *codegen) emitStoreStructField(sName, fName string) {
|
||||||
|
strct := c.fctx.loadStruct(sName)
|
||||||
|
pos := strct.loadField(fName)
|
||||||
|
emitInt(c.prog, int64(pos))
|
||||||
|
emitOpcode(c.prog, vm.Orot)
|
||||||
|
emitOpcode(c.prog, vm.Osetitem)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *codegen) convertFuncDecl(decl *ast.FuncDecl) {
|
||||||
|
var (
|
||||||
|
f *funcScope
|
||||||
|
ok bool
|
||||||
|
)
|
||||||
|
|
||||||
|
f, ok = c.funcs[decl.Name.Name]
|
||||||
|
if ok {
|
||||||
|
c.setLabel(f.label)
|
||||||
|
} else {
|
||||||
|
f = c.newFunc(decl)
|
||||||
|
}
|
||||||
|
c.fctx = f
|
||||||
|
|
||||||
|
emitInt(c.prog, f.stackSize())
|
||||||
|
emitOpcode(c.prog, vm.Onewarray)
|
||||||
|
emitOpcode(c.prog, vm.Otoaltstack)
|
||||||
|
|
||||||
|
// We need to handle methods, which in Go, is just syntactic sugar.
|
||||||
|
// The method receiver will be passed in as first argument.
|
||||||
|
// We check if this declaration has a receiver and load it into scope.
|
||||||
|
//
|
||||||
|
// FIXME: For now we will hard cast this to a struct. We can later finetune this
|
||||||
|
// to support other types.
|
||||||
|
if decl.Recv != nil {
|
||||||
|
for _, arg := range decl.Recv.List {
|
||||||
|
strct := c.fctx.newStruct()
|
||||||
|
|
||||||
|
ident := arg.Names[0]
|
||||||
|
strct.initializeFields(ident, c.typeInfo)
|
||||||
|
l := c.fctx.newLocal(ident.Name)
|
||||||
|
c.emitStoreLocal(l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the arguments in scope.
|
||||||
|
for _, arg := range decl.Type.Params.List {
|
||||||
|
name := arg.Names[0].Name // for now.
|
||||||
|
l := c.fctx.newLocal(name)
|
||||||
|
c.emitStoreLocal(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
ast.Walk(c, decl.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
|
switch n := node.(type) {
|
||||||
|
|
||||||
|
case *ast.AssignStmt:
|
||||||
|
for i := 0; i < len(n.Lhs); i++ {
|
||||||
|
// resolve the whole right hand side.
|
||||||
|
ast.Walk(c, n.Rhs[i])
|
||||||
|
// check if we are assigning to a struct or an identifier
|
||||||
|
switch t := n.Lhs[i].(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
l := c.fctx.loadLocal(t.Name)
|
||||||
|
c.emitStoreLocal(l)
|
||||||
|
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
switch n := t.X.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
c.emitLoadLocal(n.Name) // load the struct
|
||||||
|
c.emitStoreStructField(n.Name, t.Sel.Name) // store the field
|
||||||
|
default:
|
||||||
|
log.Fatal("nested selector assigns not supported yet")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case *ast.ReturnStmt:
|
||||||
|
if len(n.Results) > 1 {
|
||||||
|
log.Fatal("multiple returns not supported.")
|
||||||
|
}
|
||||||
|
|
||||||
|
emitOpcode(c.prog, vm.Ojmp)
|
||||||
|
emitOpcode(c.prog, vm.Opcode(0x03))
|
||||||
|
emitOpcode(c.prog, vm.Opush0)
|
||||||
|
|
||||||
|
if len(n.Results) > 0 {
|
||||||
|
ast.Walk(c, n.Results[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
emitOpcode(c.prog, vm.Onop)
|
||||||
|
emitOpcode(c.prog, vm.Ofromaltstack)
|
||||||
|
emitOpcode(c.prog, vm.Odrop)
|
||||||
|
emitOpcode(c.prog, vm.Oret)
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case *ast.IfStmt:
|
||||||
|
lIf := c.newLabel()
|
||||||
|
lElse := c.newLabel()
|
||||||
|
if n.Cond != nil {
|
||||||
|
ast.Walk(c, n.Cond)
|
||||||
|
emitJmp(c.prog, vm.Ojmpifnot, int16(lElse))
|
||||||
|
}
|
||||||
|
|
||||||
|
c.setLabel(lIf)
|
||||||
|
ast.Walk(c, n.Body)
|
||||||
|
|
||||||
|
if n.Else != nil {
|
||||||
|
// TODO: handle else statements.
|
||||||
|
// emitJmp(c.prog, vm.Ojmp, int16(lEnd))
|
||||||
|
}
|
||||||
|
c.setLabel(lElse)
|
||||||
|
if n.Else != nil {
|
||||||
|
ast.Walk(c, n.Else)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case *ast.BasicLit:
|
||||||
|
c.emitLoadConst(c.getTypeInfo(n))
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case *ast.Ident:
|
||||||
|
if isIdentBool(n) {
|
||||||
|
c.emitLoadConst(makeBoolFromIdent(n, c.typeInfo))
|
||||||
|
} else {
|
||||||
|
c.emitLoadLocal(n.Name)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case *ast.CompositeLit:
|
||||||
|
switch t := n.Type.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
typ := c.typeInfo.ObjectOf(t).Type().Underlying()
|
||||||
|
switch typ.(type) {
|
||||||
|
case *types.Struct:
|
||||||
|
c.convertStruct(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
ln := len(n.Elts)
|
||||||
|
for i := ln - 1; i >= 0; i-- {
|
||||||
|
c.emitLoadConst(c.getTypeInfo(n.Elts[i]))
|
||||||
|
}
|
||||||
|
emitInt(c.prog, int64(ln))
|
||||||
|
emitOpcode(c.prog, vm.Opack)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case *ast.BinaryExpr:
|
||||||
|
switch n.Op {
|
||||||
|
case token.LAND:
|
||||||
|
ast.Walk(c, n.X)
|
||||||
|
emitJmp(c.prog, vm.Ojmpifnot, int16(len(c.l)-1))
|
||||||
|
ast.Walk(c, n.Y)
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case token.LOR:
|
||||||
|
ast.Walk(c, n.X)
|
||||||
|
emitJmp(c.prog, vm.Ojmpif, int16(len(c.l)-2))
|
||||||
|
ast.Walk(c, n.Y)
|
||||||
|
return nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
// The AST package will try to resolve all basic literals for us.
|
||||||
|
// If the typeinfo.Value is not nil we know that the expr is resolved
|
||||||
|
// and needs no further action. e.g. x := 2 + 2 + 2 will be resolved to 6.
|
||||||
|
if tinfo := c.getTypeInfo(n); tinfo.Value != nil {
|
||||||
|
c.emitLoadConst(tinfo)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
ast.Walk(c, n.X)
|
||||||
|
ast.Walk(c, n.Y)
|
||||||
|
|
||||||
|
switch n.Op {
|
||||||
|
case token.ADD:
|
||||||
|
emitOpcode(c.prog, vm.Oadd)
|
||||||
|
case token.SUB:
|
||||||
|
emitOpcode(c.prog, vm.Osub)
|
||||||
|
case token.MUL:
|
||||||
|
emitOpcode(c.prog, vm.Omul)
|
||||||
|
case token.QUO:
|
||||||
|
emitOpcode(c.prog, vm.Odiv)
|
||||||
|
case token.LSS:
|
||||||
|
emitOpcode(c.prog, vm.Olt)
|
||||||
|
case token.LEQ:
|
||||||
|
emitOpcode(c.prog, vm.Olte)
|
||||||
|
case token.GTR:
|
||||||
|
emitOpcode(c.prog, vm.Ogt)
|
||||||
|
case token.GEQ:
|
||||||
|
emitOpcode(c.prog, vm.Ogte)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.CallExpr:
|
||||||
|
var (
|
||||||
|
f *funcScope
|
||||||
|
ok bool
|
||||||
|
numArgs = len(n.Args)
|
||||||
|
)
|
||||||
|
|
||||||
|
switch fun := n.Fun.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
f, ok = c.funcs[fun.Name]
|
||||||
|
if !ok {
|
||||||
|
log.Fatalf("could not resolve function %s", fun.Name)
|
||||||
|
}
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
ast.Walk(c, fun.X)
|
||||||
|
f, ok = c.funcs[fun.Sel.Name]
|
||||||
|
if !ok {
|
||||||
|
log.Fatalf("could not resolve function %s", fun.Sel.Name)
|
||||||
|
}
|
||||||
|
// Dont forget to add 1 extra argument when its a method.
|
||||||
|
numArgs++
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, arg := range n.Args {
|
||||||
|
ast.Walk(c, arg)
|
||||||
|
}
|
||||||
|
if numArgs == 2 {
|
||||||
|
emitOpcode(c.prog, vm.Oswap)
|
||||||
|
}
|
||||||
|
if numArgs == 3 {
|
||||||
|
emitInt(c.prog, 2)
|
||||||
|
emitOpcode(c.prog, vm.Oxswap)
|
||||||
|
}
|
||||||
|
|
||||||
|
// c# compiler adds a NOP (0x61) before every function call. Dont think its relevant
|
||||||
|
// and we could easily removed it, but to be consistent with the original compiler I
|
||||||
|
// will put them in. ^^
|
||||||
|
emitOpcode(c.prog, vm.Onop)
|
||||||
|
emitCall(c.prog, vm.Ocall, int16(f.label))
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
switch t := n.X.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
c.emitLoadLocal(t.Name) // load the struct
|
||||||
|
c.emitLoadStructField(t.Name, n.Sel.Name) // load the field
|
||||||
|
default:
|
||||||
|
log.Fatal("nested selectors not supported yet")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *codegen) convertStruct(lit *ast.CompositeLit) {
|
||||||
|
emitOpcode(c.prog, vm.Onop)
|
||||||
|
emitInt(c.prog, int64(len(lit.Elts)))
|
||||||
|
emitOpcode(c.prog, vm.Onewstruct)
|
||||||
|
emitOpcode(c.prog, vm.Otoaltstack)
|
||||||
|
|
||||||
|
// Create a new struct scope to store the positions of its variables.
|
||||||
|
strct := c.fctx.newStruct()
|
||||||
|
|
||||||
|
for _, field := range lit.Elts {
|
||||||
|
f := field.(*ast.KeyValueExpr)
|
||||||
|
// Walk to resolve the expression of the value.
|
||||||
|
ast.Walk(c, f.Value)
|
||||||
|
l := strct.newField(f.Key.(*ast.Ident).Name)
|
||||||
|
c.emitStoreLocal(l)
|
||||||
|
}
|
||||||
|
emitOpcode(c.prog, vm.Ofromaltstack)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *codegen) newFunc(decl *ast.FuncDecl) *funcScope {
|
||||||
|
f := newFuncScope(decl, c.newLabel())
|
||||||
|
c.funcs[f.name] = f
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *codegen) getTypeInfo(expr ast.Expr) types.TypeAndValue {
|
||||||
|
return c.typeInfo.Types[expr]
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeBoolFromIdent(ident *ast.Ident, tinfo *types.Info) types.TypeAndValue {
|
||||||
|
var b bool
|
||||||
|
if ident.Name == "true" {
|
||||||
|
b = true
|
||||||
|
} else if ident.Name == "false" {
|
||||||
|
b = false
|
||||||
|
} else {
|
||||||
|
log.Fatalf("givent identifier cannot be converted to a boolean => %s", ident.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.TypeAndValue{
|
||||||
|
Type: tinfo.ObjectOf(ident).Type(),
|
||||||
|
Value: constant.MakeBool(b),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isIdentBool(ident *ast.Ident) bool {
|
||||||
|
return ident.Name == "true" || ident.Name == "false"
|
||||||
|
}
|
||||||
|
|
||||||
|
// CodeGen is the function that compiles the program to bytecode.
|
||||||
|
func CodeGen(f *ast.File, tInfo *types.Info) (*bytes.Buffer, error) {
|
||||||
|
c := &codegen{
|
||||||
|
prog: new(bytes.Buffer),
|
||||||
|
l: []int{},
|
||||||
|
funcs: map[string]*funcScope{},
|
||||||
|
typeInfo: tInfo,
|
||||||
|
}
|
||||||
|
|
||||||
|
var main *ast.FuncDecl
|
||||||
|
ast.Inspect(f, func(n ast.Node) bool {
|
||||||
|
switch t := n.(type) {
|
||||||
|
case *ast.FuncDecl:
|
||||||
|
if t.Name.Name == mainIdent {
|
||||||
|
main = t
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
if main == nil {
|
||||||
|
log.Fatal("could not find func main. did you forgot to declare it?")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.resolveFuncDecls(f)
|
||||||
|
c.convertFuncDecl(main)
|
||||||
|
|
||||||
|
for _, decl := range f.Decls {
|
||||||
|
switch n := decl.(type) {
|
||||||
|
case *ast.FuncDecl:
|
||||||
|
if n.Name.Name != mainIdent {
|
||||||
|
c.convertFuncDecl(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.writeJumps()
|
||||||
|
|
||||||
|
return c.prog, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *codegen) resolveFuncDecls(f *ast.File) {
|
||||||
|
for _, decl := range f.Decls {
|
||||||
|
switch n := decl.(type) {
|
||||||
|
case *ast.FuncDecl:
|
||||||
|
if n.Name.Name != mainIdent {
|
||||||
|
c.newFunc(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *codegen) writeJumps() {
|
||||||
|
b := c.prog.Bytes()
|
||||||
|
for i, op := range b {
|
||||||
|
j := i + 1
|
||||||
|
switch vm.Opcode(op) {
|
||||||
|
case vm.Ojmpifnot, vm.Ojmpif, vm.Ocall:
|
||||||
|
index := binary.LittleEndian.Uint16(b[j : j+2])
|
||||||
|
if int(index) > len(c.l) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
offset := uint16(c.l[index] - i)
|
||||||
|
if offset < 0 {
|
||||||
|
log.Fatalf("new offset is negative, table list %v", c.l)
|
||||||
|
}
|
||||||
|
binary.LittleEndian.PutUint16(b[j:j+2], offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,153 +2,44 @@ package compiler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/constant"
|
|
||||||
"go/importer"
|
"go/importer"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"strings"
|
||||||
|
"text/tabwriter"
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
"github.com/CityOfZion/neo-go/pkg/vm"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const fileExt = ".avm"
|
||||||
outputExt = ".avm"
|
|
||||||
// Identifier off the entry point function.
|
|
||||||
mainIdent = "Main"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CallContext represents more details on function calls in the program.
|
// Options contains all the parameters that affect the behaviour of the compiler.
|
||||||
// It stores the position off where the call happend along with the
|
type Options struct {
|
||||||
// function it called. The compiler will store all function calls, so
|
// The extension of the output file default set to .avm
|
||||||
// it can update them later.
|
Ext string
|
||||||
type CallContext struct {
|
|
||||||
pos int
|
// The name of the output file.
|
||||||
funcName string
|
Outfile string
|
||||||
|
|
||||||
|
// Debug will output an hex encoded string of the generated bytecode.
|
||||||
|
Debug bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// A VarContext holds the info about the context of a variable in the program.
|
// Compile compiles a Go program into bytecode that can run on the NEO virtual machine.
|
||||||
type VarContext struct {
|
func Compile(input io.Reader, o *Options) ([]byte, error) {
|
||||||
name string
|
|
||||||
tinfo types.TypeAndValue
|
|
||||||
pos int
|
|
||||||
}
|
|
||||||
|
|
||||||
func newVarContext(name string, tinfo types.TypeAndValue) *VarContext {
|
|
||||||
return &VarContext{
|
|
||||||
name: name,
|
|
||||||
pos: -1,
|
|
||||||
tinfo: tinfo,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compiler holds the output buffer of the compiled source.
|
|
||||||
type Compiler struct {
|
|
||||||
// Output extension of the file. Default .avm.
|
|
||||||
OutputExt string
|
|
||||||
|
|
||||||
// scriptBuilder is responsible for all opcode writes.
|
|
||||||
sb *ScriptBuilder
|
|
||||||
|
|
||||||
// map with all function contexts across the program.
|
|
||||||
funcs map[string]*FuncContext
|
|
||||||
|
|
||||||
// list of function calls
|
|
||||||
funcCalls []CallContext
|
|
||||||
|
|
||||||
// struct with info about decls, types, uses, ..
|
|
||||||
typeInfo *types.Info
|
|
||||||
}
|
|
||||||
|
|
||||||
// New returns a new compiler ready to compile smartcontracts.
|
|
||||||
func New() *Compiler {
|
|
||||||
return &Compiler{
|
|
||||||
OutputExt: outputExt,
|
|
||||||
sb: &ScriptBuilder{buf: new(bytes.Buffer)},
|
|
||||||
funcs: map[string]*FuncContext{},
|
|
||||||
funcCalls: []CallContext{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CompileSource will compile the source file into an avm format.
|
|
||||||
func (c *Compiler) CompileSource(src string) error {
|
|
||||||
file, err := os.Open(src)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return c.Compile(file)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadConst load a constant, if storeLocal is true it will store it on the position
|
|
||||||
// of the VarContext.
|
|
||||||
func (c *Compiler) loadConst(ctx *VarContext, storeLocal bool) {
|
|
||||||
switch t := ctx.tinfo.Type.(type) {
|
|
||||||
case *types.Basic:
|
|
||||||
switch t.Kind() {
|
|
||||||
case types.Int:
|
|
||||||
val, _ := constant.Int64Val(ctx.tinfo.Value)
|
|
||||||
c.sb.emitPushInt(val)
|
|
||||||
case types.String:
|
|
||||||
val := constant.StringVal(ctx.tinfo.Value)
|
|
||||||
c.sb.emitPushString(val)
|
|
||||||
case types.Bool, types.UntypedBool:
|
|
||||||
val := constant.BoolVal(ctx.tinfo.Value)
|
|
||||||
c.sb.emitPushBool(val)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
log.Fatalf("compiler don't know how to handle this => %v", ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
if storeLocal {
|
|
||||||
c.storeLocal(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load a local variable. The position of the VarContext is used to retrieve from
|
|
||||||
// that position.
|
|
||||||
func (c *Compiler) loadLocal(ctx *VarContext) {
|
|
||||||
pos := int64(ctx.pos)
|
|
||||||
if pos < 0 {
|
|
||||||
log.Fatalf("want to load local %v but got invalid position => %d <=", ctx, pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.sb.emitPush(vm.OpFromAltStack)
|
|
||||||
c.sb.emitPush(vm.OpDup)
|
|
||||||
c.sb.emitPush(vm.OpToAltStack)
|
|
||||||
|
|
||||||
// push it's index on the stack
|
|
||||||
c.sb.emitPushInt(pos)
|
|
||||||
c.sb.emitPush(vm.OpPickItem)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store a local variable on the stack. The position of the VarContext is used
|
|
||||||
// to store at that position.
|
|
||||||
func (c *Compiler) storeLocal(vctx *VarContext) {
|
|
||||||
c.sb.emitPush(vm.OpFromAltStack)
|
|
||||||
c.sb.emitPush(vm.OpDup)
|
|
||||||
c.sb.emitPush(vm.OpToAltStack)
|
|
||||||
|
|
||||||
pos := int64(vctx.pos)
|
|
||||||
if pos < 0 {
|
|
||||||
log.Fatalf("want to store local %v but got invalid positionl => %d", vctx, pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.sb.emitPushInt(pos)
|
|
||||||
c.sb.emitPushInt(2)
|
|
||||||
c.sb.emitPush(vm.OpRoll)
|
|
||||||
c.sb.emitPush(vm.OpSetItem)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compile will compile from r into an avm format.
|
|
||||||
func (c *Compiler) Compile(r io.Reader) error {
|
|
||||||
fset := token.NewFileSet()
|
fset := token.NewFileSet()
|
||||||
f, err := parser.ParseFile(fset, "", r, 0)
|
f, err := parser.ParseFile(fset, "", input, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
conf := types.Config{Importer: importer.Default()}
|
conf := types.Config{Importer: importer.Default()}
|
||||||
|
@ -161,343 +52,67 @@ func (c *Compiler) Compile(r io.Reader) error {
|
||||||
Scopes: make(map[ast.Node]*types.Scope),
|
Scopes: make(map[ast.Node]*types.Scope),
|
||||||
}
|
}
|
||||||
|
|
||||||
c.typeInfo = typeInfo
|
|
||||||
|
|
||||||
// Typechecker
|
// Typechecker
|
||||||
_, err = conf.Check("", fset, []*ast.File{f}, typeInfo)
|
_, err = conf.Check("", fset, []*ast.File{f}, typeInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var main *ast.FuncDecl
|
buf, err := CodeGen(f, typeInfo)
|
||||||
ast.Inspect(f, func(n ast.Node) bool {
|
if err != nil {
|
||||||
switch t := n.(type) {
|
return nil, err
|
||||||
case *ast.FuncDecl:
|
}
|
||||||
if t.Name.Name == mainIdent {
|
|
||||||
main = t
|
return buf.Bytes(), nil
|
||||||
return false
|
}
|
||||||
}
|
|
||||||
|
// CompileAndSave will compile and save the file to disk.
|
||||||
|
func CompileAndSave(src string, o *Options) error {
|
||||||
|
if len(o.Outfile) == 0 {
|
||||||
|
if !strings.HasSuffix(src, ".go") {
|
||||||
|
return errors.New("not a Go file")
|
||||||
}
|
}
|
||||||
return true
|
o.Outfile = strings.TrimSuffix(src, ".go")
|
||||||
})
|
}
|
||||||
if main == nil {
|
if len(o.Ext) == 0 {
|
||||||
log.Fatal("could not find func main. did you forgot to declare it?")
|
o.Ext = fileExt
|
||||||
|
}
|
||||||
|
b, err := ioutil.ReadFile(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b, err = Compile(bytes.NewReader(b), o)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if o.Debug {
|
||||||
|
log.Println(hex.EncodeToString(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
c.resolveFuncDecls(f)
|
out := fmt.Sprintf("%s.%s", o.Outfile, o.Ext)
|
||||||
c.convertFuncDecl(main)
|
return ioutil.WriteFile(out, b, os.ModePerm)
|
||||||
|
}
|
||||||
|
|
||||||
// Start building all declarations
|
// DumpOpcode compiles the program and dumps the opcode in a user friendly format.
|
||||||
for _, decl := range f.Decls {
|
func DumpOpcode(src string) error {
|
||||||
switch t := decl.(type) {
|
b, err := ioutil.ReadFile(src)
|
||||||
case *ast.GenDecl:
|
if err != nil {
|
||||||
case *ast.FuncDecl:
|
return err
|
||||||
if t.Name.Name != mainIdent {
|
}
|
||||||
c.convertFuncDecl(t)
|
b, err = Compile(bytes.NewReader(b), &Options{})
|
||||||
}
|
if err != nil {
|
||||||
}
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// update all local function calls.
|
w := tabwriter.NewWriter(os.Stdout, 0, 0, 4, ' ', 0)
|
||||||
c.updateFuncCalls()
|
fmt.Fprintln(w, "INDEX\tOPCODE\tDESC\t")
|
||||||
|
for i := 0; i < len(b); i++ {
|
||||||
|
fmt.Fprintf(w, "%d\t0x%2x\t%s\t\n", i, b[i], vm.Opcode(b[i]))
|
||||||
|
}
|
||||||
|
w.Flush()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateFuncCalls will update all local function calls that occured the program.
|
func init() {
|
||||||
func (c *Compiler) updateFuncCalls() {
|
log.SetFlags(0)
|
||||||
for _, ctx := range c.funcCalls {
|
|
||||||
fun, ok := c.funcs[ctx.funcName]
|
|
||||||
if !ok {
|
|
||||||
log.Fatalf("could not resolve function %s", ctx.funcName)
|
|
||||||
}
|
|
||||||
// pos is the position of the call op, we need to add 1 to get the
|
|
||||||
// start of the label.
|
|
||||||
// for calculating the correct offset we need to subtract the target label
|
|
||||||
// with the position the call occured.
|
|
||||||
offset := fun.label - int16(ctx.pos)
|
|
||||||
c.sb.updatePushCall(ctx.pos+1, offset)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Compiler) resolveFuncDecls(f *ast.File) {
|
|
||||||
for _, decl := range f.Decls {
|
|
||||||
switch t := decl.(type) {
|
|
||||||
case *ast.GenDecl:
|
|
||||||
case *ast.FuncDecl:
|
|
||||||
if t.Name.Name != mainIdent {
|
|
||||||
c.funcs[t.Name.Name] = newFuncContext(t, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Compiler) convertFuncDecl(decl *ast.FuncDecl) {
|
|
||||||
ctx := newFuncContext(decl, c.currentPos())
|
|
||||||
c.funcs[ctx.name] = ctx
|
|
||||||
|
|
||||||
// We need to write the the total stack size of the function first.
|
|
||||||
// That size is the number of arguments + body operations that will be
|
|
||||||
// pushed on the stack
|
|
||||||
c.sb.emitPushInt(ctx.numStackOps())
|
|
||||||
c.sb.emitPush(vm.OpNewArray)
|
|
||||||
c.sb.emitPush(vm.OpToAltStack)
|
|
||||||
|
|
||||||
// Load the arguments into scope.
|
|
||||||
for _, arg := range decl.Type.Params.List {
|
|
||||||
name := arg.Names[0].Name
|
|
||||||
ctx.args[name] = true
|
|
||||||
vctx := ctx.newConst(name, c.getTypeInfo(arg.Type), true)
|
|
||||||
c.storeLocal(vctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, stmt := range decl.Body.List {
|
|
||||||
c.convertStmt(ctx, stmt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Compiler) convertStmt(fctx *FuncContext, stmt ast.Stmt) {
|
|
||||||
switch t := stmt.(type) {
|
|
||||||
case *ast.AssignStmt:
|
|
||||||
for i := 0; i < len(t.Lhs); i++ {
|
|
||||||
lhs := t.Lhs[i].(*ast.Ident)
|
|
||||||
|
|
||||||
switch rhs := t.Rhs[i].(type) {
|
|
||||||
case *ast.BasicLit:
|
|
||||||
vctx := fctx.newConst(lhs.Name, c.getTypeInfo(t.Rhs[i]), true)
|
|
||||||
c.loadConst(vctx, true)
|
|
||||||
|
|
||||||
case *ast.CompositeLit:
|
|
||||||
// Write constants in reverse order on the stack.
|
|
||||||
n := len(rhs.Elts)
|
|
||||||
for i := n - 1; i >= 0; i-- {
|
|
||||||
vctx := fctx.newConst("", c.getTypeInfo(rhs.Elts[i]), false)
|
|
||||||
c.loadConst(vctx, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.sb.emitPushInt(int64(n))
|
|
||||||
c.sb.emitPush(vm.OpPack)
|
|
||||||
|
|
||||||
vctx := fctx.newConst(lhs.Name, c.getTypeInfo(rhs), true)
|
|
||||||
c.storeLocal(vctx)
|
|
||||||
|
|
||||||
case *ast.Ident:
|
|
||||||
if isIdentBool(rhs) {
|
|
||||||
vctx := fctx.newConst(lhs.Name, makeBoolFromIdent(rhs, c.typeInfo), true)
|
|
||||||
c.loadConst(vctx, true)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
knownCtx := fctx.getContext(rhs.Name)
|
|
||||||
c.loadLocal(knownCtx)
|
|
||||||
newCtx := fctx.newConst(lhs.Name, c.getTypeInfo(rhs), true)
|
|
||||||
c.storeLocal(newCtx)
|
|
||||||
|
|
||||||
default:
|
|
||||||
c.convertExpr(fctx, t.Rhs[i])
|
|
||||||
vctx := fctx.newConst(lhs.Name, c.getTypeInfo(t.Rhs[i]), true)
|
|
||||||
c.storeLocal(vctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Due to the design of the orginal VM, multiple return are not supported.
|
|
||||||
case *ast.ReturnStmt:
|
|
||||||
if len(t.Results) > 1 {
|
|
||||||
log.Fatal("multiple returns not supported.")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.sb.emitPush(vm.OpJMP)
|
|
||||||
c.sb.emitPush(vm.OpCode(0x03))
|
|
||||||
c.sb.emitPush(vm.OpPush0)
|
|
||||||
|
|
||||||
c.convertExpr(fctx, t.Results[0])
|
|
||||||
|
|
||||||
c.sb.emitPush(vm.OpNOP)
|
|
||||||
c.sb.emitPush(vm.OpFromAltStack)
|
|
||||||
c.sb.emitPush(vm.OpDrop)
|
|
||||||
c.sb.emitPush(vm.OpRET)
|
|
||||||
|
|
||||||
// TODO: this needs a rewrite ASAP.
|
|
||||||
case *ast.IfStmt:
|
|
||||||
c.convertExpr(fctx, t.Cond)
|
|
||||||
|
|
||||||
binExpr, ok := t.Cond.(*ast.BinaryExpr)
|
|
||||||
if ok && binExpr.Op != token.LAND && binExpr.Op != token.LOR {
|
|
||||||
// use a placeholder for the label.
|
|
||||||
c.sb.emitJump(vm.OpJMPIFNOT, int16(0))
|
|
||||||
// track our offset to update later subtract sizeOf int16.
|
|
||||||
offset := int(c.currentPos()) - 2
|
|
||||||
|
|
||||||
defer func(offset int) {
|
|
||||||
jumpTo := c.currentPos() + 1 - int16(offset)
|
|
||||||
c.sb.updateJmpLabel(jumpTo, offset)
|
|
||||||
}(offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
labelBeforeBlock := c.currentPos()
|
|
||||||
// Process the block.
|
|
||||||
for _, stmt := range t.Body.List {
|
|
||||||
c.convertStmt(fctx, stmt)
|
|
||||||
}
|
|
||||||
|
|
||||||
// if there are any labels we need to update.
|
|
||||||
if len(fctx.jumpLabels) > 0 {
|
|
||||||
for _, label := range fctx.jumpLabels {
|
|
||||||
var pos int16
|
|
||||||
if label.op == vm.OpJMPIF {
|
|
||||||
pos = labelBeforeBlock + 1
|
|
||||||
} else {
|
|
||||||
pos = c.currentPos() + 1
|
|
||||||
}
|
|
||||||
jumpTo := pos - int16(label.offset)
|
|
||||||
c.sb.updateJmpLabel(jumpTo, label.offset)
|
|
||||||
}
|
|
||||||
fctx.jumpLabels = []jumpLabel{}
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
log.Fatalf("compiler has not implemented this statement => %v", reflect.TypeOf(t))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Compiler) convertExpr(fctx *FuncContext, expr ast.Expr) {
|
|
||||||
switch t := expr.(type) {
|
|
||||||
case *ast.BasicLit:
|
|
||||||
vctx := fctx.newConst("", c.getTypeInfo(t), false)
|
|
||||||
c.loadConst(vctx, false)
|
|
||||||
|
|
||||||
case *ast.Ident:
|
|
||||||
if isIdentBool(t) {
|
|
||||||
vctx := fctx.newConst(t.Name, makeBoolFromIdent(t, c.typeInfo), false)
|
|
||||||
c.loadConst(vctx, false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if fctx.isArgument(t.Name) {
|
|
||||||
vctx := fctx.getContext(t.Name)
|
|
||||||
c.loadLocal(vctx)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
vctx := fctx.getContext(t.Name)
|
|
||||||
c.loadLocal(vctx)
|
|
||||||
|
|
||||||
case *ast.CallExpr:
|
|
||||||
fun := t.Fun.(*ast.Ident)
|
|
||||||
fctx, ok := c.funcs[fun.Name]
|
|
||||||
if !ok {
|
|
||||||
log.Fatalf("could not resolve func %s", fun.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle the passed arguments.
|
|
||||||
for _, arg := range t.Args {
|
|
||||||
vctx := fctx.newConst("", c.getTypeInfo(arg), false)
|
|
||||||
c.loadLocal(vctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// c# compiler adds a NOP (0x61) before every function call. Dont think its relevant
|
|
||||||
// and we could easily removed it, but to be consistent with the original compiler I
|
|
||||||
// will put them in. ^^
|
|
||||||
c.sb.emitPush(vm.OpNOP)
|
|
||||||
|
|
||||||
c.funcCalls = append(c.funcCalls, CallContext{int(c.currentPos()), fun.Name})
|
|
||||||
c.sb.emitPushCall(0) // placeholder, update later.
|
|
||||||
|
|
||||||
case *ast.BinaryExpr:
|
|
||||||
if t.Op == token.LAND || t.Op == token.LOR {
|
|
||||||
c.convertExpr(fctx, t.X)
|
|
||||||
|
|
||||||
opJMP := vm.OpJMPIFNOT
|
|
||||||
if t.Op == token.LOR {
|
|
||||||
opJMP = vm.OpJMPIF
|
|
||||||
}
|
|
||||||
|
|
||||||
if e, ok := t.X.(*ast.BinaryExpr); ok && e.Op != token.LAND && e.Op != token.LOR {
|
|
||||||
c.sb.emitJump(opJMP, int16(0))
|
|
||||||
fctx.addJump(opJMP, int(c.currentPos())-2)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.convertExpr(fctx, t.Y)
|
|
||||||
c.sb.emitJump(vm.OpJMPIFNOT, int16(0))
|
|
||||||
fctx.addJump(vm.OpJMPIFNOT, int(c.currentPos())-2)
|
|
||||||
c.convertToken(t.Op)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// The AST package resolves all basic literals for us. If the typeinfo.Value is not nil
|
|
||||||
// we know that the bin expr is resolved and needs no further action.
|
|
||||||
// e.g. x := 2 + 2 + 2 will be resolved to 6.
|
|
||||||
if tinfo := c.getTypeInfo(t); tinfo.Value != nil {
|
|
||||||
vctx := fctx.newConst("", tinfo, false)
|
|
||||||
c.loadConst(vctx, false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.convertExpr(fctx, t.X)
|
|
||||||
c.convertExpr(fctx, t.Y)
|
|
||||||
c.convertToken(t.Op)
|
|
||||||
|
|
||||||
default:
|
|
||||||
log.Fatalf("compiler has not implemented this expr => %v", reflect.TypeOf(t))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Compiler) convertToken(tok token.Token) {
|
|
||||||
switch tok {
|
|
||||||
case token.ADD:
|
|
||||||
c.sb.emitPush(vm.OpAdd)
|
|
||||||
case token.SUB:
|
|
||||||
c.sb.emitPush(vm.OpSub)
|
|
||||||
case token.MUL:
|
|
||||||
c.sb.emitPush(vm.OpMul)
|
|
||||||
case token.QUO:
|
|
||||||
c.sb.emitPush(vm.OpDiv)
|
|
||||||
case token.LSS:
|
|
||||||
c.sb.emitPush(vm.OpLT)
|
|
||||||
case token.LEQ:
|
|
||||||
c.sb.emitPush(vm.OpLTE)
|
|
||||||
case token.GTR:
|
|
||||||
c.sb.emitPush(vm.OpGT)
|
|
||||||
case token.GEQ:
|
|
||||||
c.sb.emitPush(vm.OpGTE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// getTypeInfo return TypeAndValue for the given expression. If it could not resolve
|
|
||||||
// the type value and type will be NIL.
|
|
||||||
func (c *Compiler) getTypeInfo(expr ast.Expr) types.TypeAndValue {
|
|
||||||
return c.typeInfo.Types[expr]
|
|
||||||
}
|
|
||||||
|
|
||||||
// currentPos return the current position (address) of the latest opcode.
|
|
||||||
func (c *Compiler) currentPos() int16 {
|
|
||||||
return int16(c.sb.buf.Len())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Buffer returns the buffer of the builder as a io.Reader.
|
|
||||||
func (c *Compiler) Buffer() *bytes.Buffer {
|
|
||||||
return c.sb.buf
|
|
||||||
}
|
|
||||||
|
|
||||||
// DumpOpcode dumps the current buffer, formatted with index, hex and opcode.
|
|
||||||
func (c *Compiler) DumpOpcode() {
|
|
||||||
c.sb.dumpOpcode()
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeBoolFromIdent(ident *ast.Ident, tinfo *types.Info) types.TypeAndValue {
|
|
||||||
var b bool
|
|
||||||
if ident.Name == "true" {
|
|
||||||
b = true
|
|
||||||
} else if ident.Name == "false" {
|
|
||||||
b = false
|
|
||||||
} else {
|
|
||||||
log.Fatalf("givent identifier cannot be converted to a boolean => %s", ident.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return types.TypeAndValue{
|
|
||||||
Type: tinfo.ObjectOf(ident).Type(),
|
|
||||||
Value: constant.MakeBool(b),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isIdentBool(ident *ast.Ident) bool {
|
|
||||||
return ident.Name == "true" || ident.Name == "false"
|
|
||||||
}
|
}
|
||||||
|
|
101
pkg/vm/compiler/emit.go
Normal file
101
pkg/vm/compiler/emit.go
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
package compiler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/vm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func emit(w *bytes.Buffer, op vm.Opcode, b []byte) error {
|
||||||
|
if err := w.WriteByte(byte(op)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err := w.Write(b)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func emitOpcode(w *bytes.Buffer, op vm.Opcode) error {
|
||||||
|
return w.WriteByte(byte(op))
|
||||||
|
}
|
||||||
|
|
||||||
|
func emitBool(w *bytes.Buffer, ok bool) error {
|
||||||
|
if ok {
|
||||||
|
return emitOpcode(w, vm.Opusht)
|
||||||
|
}
|
||||||
|
return emitOpcode(w, vm.Opushf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func emitInt(w *bytes.Buffer, i int64) error {
|
||||||
|
if i == -1 {
|
||||||
|
return emitOpcode(w, vm.Opushm1)
|
||||||
|
}
|
||||||
|
if i == 0 {
|
||||||
|
return emitOpcode(w, vm.Opushf)
|
||||||
|
}
|
||||||
|
if i > 0 && i < 16 {
|
||||||
|
val := vm.Opcode((int(vm.Opush1) - 1 + int(i)))
|
||||||
|
return emitOpcode(w, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
bInt := big.NewInt(i)
|
||||||
|
val := util.ToArrayReverse(bInt.Bytes())
|
||||||
|
return emitBytes(w, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func emitString(w *bytes.Buffer, s string) error {
|
||||||
|
return emitBytes(w, []byte(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func emitBytes(w *bytes.Buffer, b []byte) error {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
n = len(b)
|
||||||
|
)
|
||||||
|
|
||||||
|
if n == 0 {
|
||||||
|
return errors.New("cannot emit 0 bytes")
|
||||||
|
}
|
||||||
|
if n <= int(vm.Opushbytes75) {
|
||||||
|
return emit(w, vm.Opcode(n), b)
|
||||||
|
} else if n < 0x100 {
|
||||||
|
err = emit(w, vm.Opushdata1, []byte{byte(n)})
|
||||||
|
} else if n < 0x10000 {
|
||||||
|
buf := make([]byte, 2)
|
||||||
|
binary.LittleEndian.PutUint16(buf, uint16(n))
|
||||||
|
err = emit(w, vm.Opushdata2, buf)
|
||||||
|
} else {
|
||||||
|
buf := make([]byte, 4)
|
||||||
|
binary.LittleEndian.PutUint32(buf, uint32(n))
|
||||||
|
err = emit(w, vm.Opushdata4, buf)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = w.Write(b)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func emitCall(w *bytes.Buffer, op vm.Opcode, label int16) error {
|
||||||
|
return emitJmp(w, op, label)
|
||||||
|
}
|
||||||
|
|
||||||
|
func emitJmp(w *bytes.Buffer, op vm.Opcode, label int16) error {
|
||||||
|
if !isOpcodeJmp(op) {
|
||||||
|
return fmt.Errorf("opcode %s is not a jump or call type", op)
|
||||||
|
}
|
||||||
|
buf := make([]byte, 2)
|
||||||
|
binary.LittleEndian.PutUint16(buf, uint16(label))
|
||||||
|
return emit(w, op, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isOpcodeJmp(op vm.Opcode) bool {
|
||||||
|
if op == vm.Ojmp || op == vm.Ojmpifnot || op == vm.Ojmpif || op == vm.Ocall {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
|
@ -1,96 +0,0 @@
|
||||||
package compiler
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go/ast"
|
|
||||||
"go/types"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
|
||||||
)
|
|
||||||
|
|
||||||
type jumpLabel struct {
|
|
||||||
offset int
|
|
||||||
op vm.OpCode
|
|
||||||
}
|
|
||||||
|
|
||||||
// A FuncContext represents details about a function in the program along withs its variables.
|
|
||||||
type FuncContext struct {
|
|
||||||
// The declaration tree of this function.
|
|
||||||
decl *ast.FuncDecl
|
|
||||||
// Identifier (name of the function in the program).
|
|
||||||
name string
|
|
||||||
// The scope of the function.
|
|
||||||
scope map[string]*VarContext
|
|
||||||
// Arguments of the function.
|
|
||||||
args map[string]bool
|
|
||||||
// Address (label) where the compiler can find this function when someone calls it.
|
|
||||||
label int16
|
|
||||||
// Counter for stored local variables.
|
|
||||||
i int
|
|
||||||
// This needs refactor along with the (if stmt)
|
|
||||||
jumpLabels []jumpLabel
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FuncContext) addJump(op vm.OpCode, offset int) {
|
|
||||||
f.jumpLabels = append(f.jumpLabels, jumpLabel{offset, op})
|
|
||||||
}
|
|
||||||
|
|
||||||
func newFuncContext(decl *ast.FuncDecl, label int16) *FuncContext {
|
|
||||||
return &FuncContext{
|
|
||||||
decl: decl,
|
|
||||||
label: int16(label),
|
|
||||||
name: decl.Name.Name,
|
|
||||||
scope: map[string]*VarContext{},
|
|
||||||
args: map[string]bool{},
|
|
||||||
jumpLabels: []jumpLabel{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FuncContext) newConst(name string, t types.TypeAndValue, needStore bool) *VarContext {
|
|
||||||
ctx := &VarContext{
|
|
||||||
name: name,
|
|
||||||
tinfo: t,
|
|
||||||
}
|
|
||||||
if needStore {
|
|
||||||
f.storeContext(ctx)
|
|
||||||
}
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FuncContext) numStackOps() int64 {
|
|
||||||
ops := 0
|
|
||||||
ast.Inspect(f.decl, func(n ast.Node) bool {
|
|
||||||
switch n.(type) {
|
|
||||||
case *ast.AssignStmt, *ast.ReturnStmt, *ast.IfStmt:
|
|
||||||
ops++
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
numArgs := len(f.decl.Type.Params.List)
|
|
||||||
return int64(ops + numArgs)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FuncContext) storeContext(ctx *VarContext) {
|
|
||||||
ctx.pos = f.i
|
|
||||||
f.scope[ctx.name] = ctx
|
|
||||||
f.i++
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FuncContext) getContext(name string) *VarContext {
|
|
||||||
ctx, ok := f.scope[name]
|
|
||||||
if !ok {
|
|
||||||
log.Fatalf("could not resolve variable %s", name)
|
|
||||||
}
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FuncContext) isRegistered(ctx *VarContext) bool {
|
|
||||||
_, ok := f.scope[ctx.name]
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FuncContext) isArgument(name string) bool {
|
|
||||||
_, ok := f.args[name]
|
|
||||||
return ok
|
|
||||||
}
|
|
89
pkg/vm/compiler/func_scope.go
Normal file
89
pkg/vm/compiler/func_scope.go
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package compiler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A funcScope represents a scope within the function context.
|
||||||
|
// It holds al the local variables along with the initialized struct positions.
|
||||||
|
type funcScope struct {
|
||||||
|
// function identifier
|
||||||
|
name string
|
||||||
|
|
||||||
|
// The declaration of the function in the AST
|
||||||
|
decl *ast.FuncDecl
|
||||||
|
|
||||||
|
// program label of the function
|
||||||
|
label int
|
||||||
|
|
||||||
|
// local scope of the function
|
||||||
|
scope map[string]int
|
||||||
|
|
||||||
|
// mapping of structs positions with their scope
|
||||||
|
structs map[int]*structScope
|
||||||
|
|
||||||
|
// local variable counter
|
||||||
|
i int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFuncScope(decl *ast.FuncDecl, label int) *funcScope {
|
||||||
|
return &funcScope{
|
||||||
|
name: decl.Name.Name,
|
||||||
|
decl: decl,
|
||||||
|
label: label,
|
||||||
|
scope: map[string]int{},
|
||||||
|
structs: map[int]*structScope{},
|
||||||
|
i: -1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *funcScope) stackSize() int64 {
|
||||||
|
size := 0
|
||||||
|
ast.Inspect(c.decl, func(n ast.Node) bool {
|
||||||
|
switch n.(type) {
|
||||||
|
case *ast.AssignStmt, *ast.ReturnStmt, *ast.IfStmt:
|
||||||
|
size++
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
numArgs := len(c.decl.Type.Params.List)
|
||||||
|
// Also take care of struct methods recv: e.g. (t Token).Foo().
|
||||||
|
if c.decl.Recv != nil {
|
||||||
|
numArgs += len(c.decl.Recv.List)
|
||||||
|
}
|
||||||
|
return int64(size + numArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *funcScope) newStruct() *structScope {
|
||||||
|
strct := newStructScope()
|
||||||
|
c.structs[len(c.scope)] = strct
|
||||||
|
return strct
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *funcScope) loadStruct(name string) *structScope {
|
||||||
|
l := c.loadLocal(name)
|
||||||
|
strct, ok := c.structs[l]
|
||||||
|
if !ok {
|
||||||
|
log.Fatalf("could not resolve struct %s", name)
|
||||||
|
}
|
||||||
|
return strct
|
||||||
|
}
|
||||||
|
|
||||||
|
// newLocal creates a new local variable into the scope of the function.
|
||||||
|
func (c *funcScope) newLocal(name string) int {
|
||||||
|
c.i++
|
||||||
|
c.scope[name] = c.i
|
||||||
|
return c.i
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadLocal loads the position of a local variable inside the scope of the function.
|
||||||
|
func (c *funcScope) loadLocal(name string) int {
|
||||||
|
i, ok := c.scope[name]
|
||||||
|
if !ok {
|
||||||
|
// should emit a compiler warning.
|
||||||
|
return c.newLocal(name)
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
|
@ -1,152 +0,0 @@
|
||||||
package compiler
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"os"
|
|
||||||
"text/tabwriter"
|
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ScriptBuilder generates bytecode and will write all
|
|
||||||
// generated bytecode into its internal buffer.
|
|
||||||
type ScriptBuilder struct {
|
|
||||||
buf *bytes.Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sb *ScriptBuilder) emit(op vm.OpCode, b []byte) error {
|
|
||||||
if err := sb.buf.WriteByte(byte(op)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err := sb.buf.Write(b)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sb *ScriptBuilder) emitPush(op vm.OpCode) error {
|
|
||||||
return sb.buf.WriteByte(byte(op))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sb *ScriptBuilder) emitPushBool(b bool) error {
|
|
||||||
if b {
|
|
||||||
return sb.emitPush(vm.OpPushT)
|
|
||||||
}
|
|
||||||
return sb.emitPush(vm.OpPushF)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sb *ScriptBuilder) emitPushInt(i int64) error {
|
|
||||||
if i == -1 {
|
|
||||||
return sb.emitPush(vm.OpPushM1)
|
|
||||||
}
|
|
||||||
if i == 0 {
|
|
||||||
return sb.emitPush(vm.OpPushF)
|
|
||||||
}
|
|
||||||
if i > 0 && i < 16 {
|
|
||||||
val := vm.OpCode((int(vm.OpPush1) - 1 + int(i)))
|
|
||||||
return sb.emitPush(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
bInt := big.NewInt(i)
|
|
||||||
val := util.ToArrayReverse(bInt.Bytes())
|
|
||||||
return sb.emitPushArray(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sb *ScriptBuilder) emitPushArray(b []byte) error {
|
|
||||||
var (
|
|
||||||
err error
|
|
||||||
n = len(b)
|
|
||||||
)
|
|
||||||
|
|
||||||
if n == 0 {
|
|
||||||
return errors.New("0 bytes given in pushArray")
|
|
||||||
}
|
|
||||||
if n <= int(vm.OpPushBytes75) {
|
|
||||||
return sb.emit(vm.OpCode(n), b)
|
|
||||||
} else if n < 0x100 {
|
|
||||||
err = sb.emit(vm.OpPushData1, []byte{byte(n)})
|
|
||||||
} else if n < 0x10000 {
|
|
||||||
buf := make([]byte, 2)
|
|
||||||
binary.LittleEndian.PutUint16(buf, uint16(n))
|
|
||||||
err = sb.emit(vm.OpPushData2, buf)
|
|
||||||
} else {
|
|
||||||
buf := make([]byte, 4)
|
|
||||||
binary.LittleEndian.PutUint32(buf, uint32(n))
|
|
||||||
err = sb.emit(vm.OpPushData4, buf)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = sb.buf.Write(b)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sb *ScriptBuilder) emitPushString(str string) error {
|
|
||||||
return sb.emitPushArray([]byte(str))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sb *ScriptBuilder) emitSysCall(api string) error {
|
|
||||||
lenAPI := len(api)
|
|
||||||
if lenAPI == 0 {
|
|
||||||
return errors.New("syscall argument cant be 0")
|
|
||||||
}
|
|
||||||
if lenAPI > 252 {
|
|
||||||
return fmt.Errorf("invalid syscall argument: %s", api)
|
|
||||||
}
|
|
||||||
|
|
||||||
bapi := []byte(api)
|
|
||||||
args := make([]byte, lenAPI+1)
|
|
||||||
args[0] = byte(lenAPI)
|
|
||||||
copy(args, bapi[1:])
|
|
||||||
return sb.emit(vm.OpSysCall, args)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sb *ScriptBuilder) emitPushCall(offset int16) error {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
binary.Write(buf, binary.LittleEndian, offset)
|
|
||||||
return sb.emit(vm.OpCall, buf.Bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sb *ScriptBuilder) emitJump(op vm.OpCode, offset int16) error {
|
|
||||||
if op != vm.OpJMP && op != vm.OpJMPIF && op != vm.OpJMPIFNOT && op != vm.OpCall {
|
|
||||||
return fmt.Errorf("invalid jump opcode: %v", op)
|
|
||||||
}
|
|
||||||
buf := make([]byte, 2)
|
|
||||||
binary.LittleEndian.PutUint16(buf, uint16(offset))
|
|
||||||
return sb.emit(op, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sb *ScriptBuilder) updateJmpLabel(label int16, offset int) error {
|
|
||||||
sizeOfInt16 := 2
|
|
||||||
if sizeOfInt16+offset >= sb.buf.Len() {
|
|
||||||
return fmt.Errorf("cannot update label at offset %d", offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
b := make([]byte, sizeOfInt16)
|
|
||||||
binary.LittleEndian.PutUint16(b, uint16(label))
|
|
||||||
buf := sb.buf.Bytes()
|
|
||||||
copy(buf[offset:offset+sizeOfInt16], b)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sb *ScriptBuilder) updatePushCall(offset int, label int16) {
|
|
||||||
b := new(bytes.Buffer)
|
|
||||||
binary.Write(b, binary.LittleEndian, label)
|
|
||||||
|
|
||||||
buf := sb.buf.Bytes()
|
|
||||||
copy(buf[offset:offset+2], b.Bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sb *ScriptBuilder) dumpOpcode() {
|
|
||||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 4, ' ', 0)
|
|
||||||
buf := sb.buf.Bytes()
|
|
||||||
|
|
||||||
fmt.Fprintln(w, "INDEX\tOPCODE\tDESC")
|
|
||||||
for i := 0; i < len(buf); i++ {
|
|
||||||
fmt.Fprintf(w, "%d\t0x%2x\t%s\n", i, buf[i], vm.OpCode(buf[i]))
|
|
||||||
}
|
|
||||||
w.Flush()
|
|
||||||
}
|
|
|
@ -1,104 +0,0 @@
|
||||||
package compiler
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"math/big"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestEmitPush(t *testing.T) {
|
|
||||||
sb := &ScriptBuilder{buf: new(bytes.Buffer)}
|
|
||||||
|
|
||||||
if err := sb.emitPush(vm.OpPush1); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if sb.buf.Len() != 1 {
|
|
||||||
t.Fatalf("expect buffer len of 1 got %d", sb.buf.Len())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func TestEmitPushIntNeg(t *testing.T) {
|
|
||||||
sb := &ScriptBuilder{buf: new(bytes.Buffer)}
|
|
||||||
val := -1
|
|
||||||
if err := sb.emitPushInt(int64(val)); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if want, have := vm.OpPushM1, vm.OpCode(sb.buf.Bytes()[0]); want != have {
|
|
||||||
t.Fatalf("expected %v got %v", want, have)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmitPushInt0(t *testing.T) {
|
|
||||||
sb := &ScriptBuilder{buf: new(bytes.Buffer)}
|
|
||||||
val := 0
|
|
||||||
if err := sb.emitPushInt(int64(val)); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if want, have := vm.OpPushF, vm.OpCode(sb.buf.Bytes()[0]); want != have {
|
|
||||||
t.Fatalf("expected %v got %v", want, have)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmitPushInt1(t *testing.T) {
|
|
||||||
sb := &ScriptBuilder{buf: new(bytes.Buffer)}
|
|
||||||
val := 1
|
|
||||||
if err := sb.emitPushInt(int64(val)); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if want, have := vm.OpPush1, vm.OpCode(sb.buf.Bytes()[0]); want != have {
|
|
||||||
t.Fatalf("expected %v got %v", want, have)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmitPushInt100(t *testing.T) {
|
|
||||||
x := 100
|
|
||||||
bigx := big.NewInt(int64(x))
|
|
||||||
t.Log(bigx.Bytes())
|
|
||||||
|
|
||||||
sb := &ScriptBuilder{buf: new(bytes.Buffer)}
|
|
||||||
val := 100
|
|
||||||
if err := sb.emitPushInt(int64(val)); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
// len = 1
|
|
||||||
if want, have := byte(0x01), byte(sb.buf.Bytes()[0]); want != have {
|
|
||||||
t.Fatalf("expected %v got %v", want, have)
|
|
||||||
}
|
|
||||||
if want, have := byte(0x64), byte(sb.buf.Bytes()[1]); want != have {
|
|
||||||
t.Fatalf("expected %v got %v", want, have)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmitPush1000(t *testing.T) {
|
|
||||||
sb := &ScriptBuilder{buf: new(bytes.Buffer)}
|
|
||||||
val := 1000
|
|
||||||
if err := sb.emitPushInt(int64(val)); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
bInt := big.NewInt(int64(val))
|
|
||||||
if want, have := byte(len(bInt.Bytes())), byte(sb.buf.Bytes()[0]); want != have {
|
|
||||||
t.Fatalf("expected %v got %v", want, have)
|
|
||||||
}
|
|
||||||
want := util.ToArrayReverse(bInt.Bytes()) // reverse
|
|
||||||
have := sb.buf.Bytes()[1:]
|
|
||||||
if bytes.Compare(want, have) != 0 {
|
|
||||||
t.Fatalf("expected %v got %v", want, have)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmitPushString(t *testing.T) {
|
|
||||||
sb := &ScriptBuilder{buf: new(bytes.Buffer)}
|
|
||||||
str := "anthdm"
|
|
||||||
if err := sb.emitPushString(str); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if want, have := byte(len(str)), sb.buf.Bytes()[0]; want != have {
|
|
||||||
t.Fatalf("expected %v got %v", want, have)
|
|
||||||
}
|
|
||||||
want, have := []byte(str), sb.buf.Bytes()[1:]
|
|
||||||
if bytes.Compare(want, have) != 0 {
|
|
||||||
t.Fatalf("expected %v got %v", want, have)
|
|
||||||
}
|
|
||||||
}
|
|
51
pkg/vm/compiler/struct_scope.go
Normal file
51
pkg/vm/compiler/struct_scope.go
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package compiler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/types"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A structScope holds the positions for it's fields. Struct fields have different
|
||||||
|
// positions then local variables in any scope.
|
||||||
|
type structScope struct {
|
||||||
|
// identifier of the initialized struct in the program.
|
||||||
|
name string
|
||||||
|
|
||||||
|
// a mapping of field identifier and its position.
|
||||||
|
fields map[string]int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStructScope() *structScope {
|
||||||
|
return &structScope{
|
||||||
|
fields: map[string]int{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *structScope) newField(name string) int {
|
||||||
|
i := len(s.fields)
|
||||||
|
s.fields[name] = i
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *structScope) loadField(name string) int {
|
||||||
|
i, ok := s.fields[name]
|
||||||
|
if !ok {
|
||||||
|
log.Fatalf("could not resolve field name %s for struct %s", name, s.name)
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *structScope) initializeFields(ident *ast.Ident, tInfo *types.Info) {
|
||||||
|
def, ok := tInfo.Defs[ident]
|
||||||
|
if !ok {
|
||||||
|
log.Fatalf("could not initialize fields of %s: definitions not found in typeinfo", ident.Name)
|
||||||
|
}
|
||||||
|
t, ok := def.Type().Underlying().(*types.Struct)
|
||||||
|
if !ok {
|
||||||
|
log.Fatalf("%s is not of type struct", ident.Name)
|
||||||
|
}
|
||||||
|
for i := 0; i < t.NumFields(); i++ {
|
||||||
|
s.newField(t.Field(i).Name())
|
||||||
|
}
|
||||||
|
}
|
26
pkg/vm/compiler/tests/array_test.go
Normal file
26
pkg/vm/compiler/tests/array_test.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package compiler_test
|
||||||
|
|
||||||
|
var arrayTestCases = []testCase{
|
||||||
|
{
|
||||||
|
"assign int array",
|
||||||
|
`
|
||||||
|
package foo
|
||||||
|
func Main() []int {
|
||||||
|
x := []int{1, 2, 3}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
"52c56b53525153c16c766b00527ac46203006c766b00c3616c7566",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"assign string array",
|
||||||
|
`
|
||||||
|
package foo
|
||||||
|
func Main() []string {
|
||||||
|
x := []string{"foo", "bar", "foobar"}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
"52c56b06666f6f6261720362617203666f6f53c16c766b00527ac46203006c766b00c3616c7566",
|
||||||
|
},
|
||||||
|
}
|
31
pkg/vm/compiler/tests/assign_test.go
Normal file
31
pkg/vm/compiler/tests/assign_test.go
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package compiler_test
|
||||||
|
|
||||||
|
var assignTestCases = []testCase{
|
||||||
|
{
|
||||||
|
"chain define",
|
||||||
|
`
|
||||||
|
package foo
|
||||||
|
func Main() int {
|
||||||
|
x := 4
|
||||||
|
y := x
|
||||||
|
z := y
|
||||||
|
foo := z
|
||||||
|
bar := foo
|
||||||
|
return bar
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
"56c56b546c766b00527ac46c766b00c36c766b51527ac46c766b51c36c766b52527ac46c766b52c36c766b53527ac46c766b53c36c766b54527ac46203006c766b54c3616c7566",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"simple assign",
|
||||||
|
`
|
||||||
|
package foo
|
||||||
|
func Main() int {
|
||||||
|
x := 4
|
||||||
|
x = 8
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
"53c56b546c766b00527ac4586c766b00527ac46203006c766b00c3616c7566",
|
||||||
|
},
|
||||||
|
}
|
15
pkg/vm/compiler/tests/bool_test.go
Normal file
15
pkg/vm/compiler/tests/bool_test.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package compiler_test
|
||||||
|
|
||||||
|
var boolTestCases = []testCase{
|
||||||
|
{
|
||||||
|
"bool assign",
|
||||||
|
`
|
||||||
|
package foo
|
||||||
|
func Main() bool {
|
||||||
|
x := true
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
"52c56b516c766b00527ac46203006c766b00c3616c7566",
|
||||||
|
},
|
||||||
|
}
|
|
@ -21,14 +21,18 @@ type testCase struct {
|
||||||
|
|
||||||
func TestAllCases(t *testing.T) {
|
func TestAllCases(t *testing.T) {
|
||||||
testCases := []testCase{}
|
testCases := []testCase{}
|
||||||
|
testCases = append(testCases, assignTestCases...)
|
||||||
|
testCases = append(testCases, arrayTestCases...)
|
||||||
|
testCases = append(testCases, functionCallTestCases...)
|
||||||
|
testCases = append(testCases, boolTestCases...)
|
||||||
testCases = append(testCases, stringTestCases...)
|
testCases = append(testCases, stringTestCases...)
|
||||||
testCases = append(testCases, binaryExprTestCases...)
|
testCases = append(testCases, binaryExprTestCases...)
|
||||||
|
testCases = append(testCases, structTestCases...)
|
||||||
testCases = append(testCases, ifStatementTestCases...)
|
testCases = append(testCases, ifStatementTestCases...)
|
||||||
testCases = append(testCases, functionCallTestCases...)
|
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
c := compiler.New()
|
b, err := compiler.Compile(strings.NewReader(tc.src), &compiler.Options{})
|
||||||
if err := c.Compile(strings.NewReader(tc.src)); err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,10 +41,10 @@ func TestAllCases(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if bytes.Compare(c.Buffer().Bytes(), expectedResult) != 0 {
|
if bytes.Compare(b, expectedResult) != 0 {
|
||||||
t.Log(hex.EncodeToString(c.Buffer().Bytes()))
|
t.Log(hex.EncodeToString(b))
|
||||||
want, _ := hex.DecodeString(tc.result)
|
want, _ := hex.DecodeString(tc.result)
|
||||||
dumpOpCodeSideBySide(c.Buffer().Bytes(), want)
|
dumpOpCodeSideBySide(b, want)
|
||||||
t.Fatalf("compiling %s failed", tc.name)
|
t.Fatalf("compiling %s failed", tc.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,7 +63,7 @@ func dumpOpCodeSideBySide(have, want []byte) {
|
||||||
diff = "<<"
|
diff = "<<"
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, "%d\t0x%2x\t%s\t0x%2x\t%s\t%s\n",
|
fmt.Fprintf(w, "%d\t0x%2x\t%s\t0x%2x\t%s\t%s\n",
|
||||||
i, have[i], vm.OpCode(have[i]), want[i], vm.OpCode(want[i]), diff)
|
i, have[i], vm.Opcode(have[i]), want[i], vm.Opcode(want[i]), diff)
|
||||||
}
|
}
|
||||||
w.Flush()
|
w.Flush()
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,4 +58,19 @@ var functionCallTestCases = []testCase{
|
||||||
`,
|
`,
|
||||||
"53c56b5a6c766b00527ac46c766b00c3616516006c766b51527ac46203006c766b51c3616c756653c56b6c766b00527ac4586c766b51527ac46203006c766b00c36c766b51c393616c7566",
|
"53c56b5a6c766b00527ac46c766b00c3616516006c766b51527ac46203006c766b51c3616c756653c56b6c766b00527ac4586c766b51527ac46203006c766b00c36c766b51c393616c7566",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"function call with multiple arguments",
|
||||||
|
`
|
||||||
|
package testcase
|
||||||
|
func Main() int {
|
||||||
|
x := addIntegers(2, 4)
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
func addIntegers(x int, y int) int {
|
||||||
|
return x + y
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
"52c56b52547c616516006c766b00527ac46203006c766b00c3616c756653c56b6c766b00527ac46c766b51527ac46203006c766b00c36c766b51c393616c7566",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
157
pkg/vm/compiler/tests/struct_test.go
Normal file
157
pkg/vm/compiler/tests/struct_test.go
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
package compiler_test
|
||||||
|
|
||||||
|
var structTestCases = []testCase{
|
||||||
|
{
|
||||||
|
"struct field assign",
|
||||||
|
`
|
||||||
|
package foo
|
||||||
|
type token struct {
|
||||||
|
x int
|
||||||
|
y int
|
||||||
|
}
|
||||||
|
|
||||||
|
func Main() int {
|
||||||
|
t := token {
|
||||||
|
x: 2,
|
||||||
|
y: 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
age := t.x
|
||||||
|
return age
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
"53c56b6152c66b526c766b00527ac4546c766b51527ac46c6c766b00527ac46c766b00c300c36c766b51527ac46203006c766b51c3616c7566",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"struct field return",
|
||||||
|
`
|
||||||
|
package foo
|
||||||
|
type token struct {
|
||||||
|
x int
|
||||||
|
y int
|
||||||
|
}
|
||||||
|
|
||||||
|
func Main() int {
|
||||||
|
t := token {
|
||||||
|
x: 2,
|
||||||
|
y: 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.x
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
"52c56b6152c66b526c766b00527ac4546c766b51527ac46c6c766b00527ac46203006c766b00c300c3616c7566",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"struct field assign",
|
||||||
|
`
|
||||||
|
package foo
|
||||||
|
type token struct {
|
||||||
|
x int
|
||||||
|
y int
|
||||||
|
}
|
||||||
|
|
||||||
|
func Main() int {
|
||||||
|
t := token {
|
||||||
|
x: 2,
|
||||||
|
y: 4,
|
||||||
|
}
|
||||||
|
t.x = 10
|
||||||
|
return t.x
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
"53c56b6152c66b526c766b00527ac4546c766b51527ac46c6c766b00527ac45a6c766b00c3007bc46203006c766b00c300c3616c7566",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"complex struct",
|
||||||
|
`
|
||||||
|
package foo
|
||||||
|
type token struct {
|
||||||
|
x int
|
||||||
|
y int
|
||||||
|
}
|
||||||
|
|
||||||
|
func Main() int {
|
||||||
|
x := 10
|
||||||
|
|
||||||
|
t := token {
|
||||||
|
x: 2,
|
||||||
|
y: 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
y := x + t.x
|
||||||
|
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
"54c56b5a6c766b00527ac46152c66b526c766b00527ac4546c766b51527ac46c6c766b51527ac46c766b00c36c766b51c300c3936c766b52527ac46203006c766b52c3616c7566",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"initialize same struct twice",
|
||||||
|
`
|
||||||
|
package foo
|
||||||
|
type token struct {
|
||||||
|
x int
|
||||||
|
y int
|
||||||
|
}
|
||||||
|
|
||||||
|
func Main() int {
|
||||||
|
t1 := token {
|
||||||
|
x: 2,
|
||||||
|
y: 4,
|
||||||
|
}
|
||||||
|
t2 := token {
|
||||||
|
x: 2,
|
||||||
|
y: 4,
|
||||||
|
}
|
||||||
|
return t1.x + t2.y
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
"53c56b6152c66b526c766b00527ac4546c766b51527ac46c6c766b00527ac46152c66b526c766b00527ac4546c766b51527ac46c6c766b51527ac46203006c766b00c300c36c766b51c351c393616c7566",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"struct methods",
|
||||||
|
`
|
||||||
|
package foo
|
||||||
|
type token struct {
|
||||||
|
x int
|
||||||
|
}
|
||||||
|
|
||||||
|
func(t token) getInteger() int {
|
||||||
|
return t.x
|
||||||
|
}
|
||||||
|
|
||||||
|
func Main() int {
|
||||||
|
t := token {
|
||||||
|
x: 4,
|
||||||
|
}
|
||||||
|
someInt := t.getInteger()
|
||||||
|
return someInt
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
"53c56b6151c66b546c766b00527ac46c6c766b00527ac46c766b00c3616516006c766b51527ac46203006c766b51c3616c756652c56b6c766b00527ac46203006c766b00c300c3616c7566",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"struct methods with arguments",
|
||||||
|
`
|
||||||
|
package foo
|
||||||
|
type token struct {
|
||||||
|
x int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also tests if x conflicts with t.x
|
||||||
|
func(t token) addIntegers(x int, y int) int {
|
||||||
|
return t.x + x + y
|
||||||
|
}
|
||||||
|
|
||||||
|
func Main() int {
|
||||||
|
t := token {
|
||||||
|
x: 4,
|
||||||
|
}
|
||||||
|
someInt := t.addIntegers(2, 4)
|
||||||
|
return someInt
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
"53c56b6151c66b546c766b00527ac46c6c766b00527ac46c766b00c352545272616516006c766b51527ac46203006c766b51c3616c756654c56b6c766b00527ac46c766b51527ac46c766b52527ac46203006c766b00c300c36c766b51c3936c766b52c393616c7566",
|
||||||
|
},
|
||||||
|
}
|
214
pkg/vm/opcode.go
214
pkg/vm/opcode.go
|
@ -1,129 +1,129 @@
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
// OpCode is an single operational instruction for the GO NEO virtual machine.
|
// Opcode is an single operational instruction for the GO NEO virtual machine.
|
||||||
type OpCode byte
|
type Opcode byte
|
||||||
|
|
||||||
// List of supported opcodes.
|
// List of supported opcodes.
|
||||||
const (
|
const (
|
||||||
// Constants
|
// Constants
|
||||||
OpPush0 OpCode = 0x00 // An empty array of bytes is pushed onto the stack.
|
Opush0 Opcode = 0x00 // An empty array of bytes is pushed onto the stack.
|
||||||
OpPushF OpCode = OpPush0
|
Opushf Opcode = Opush0
|
||||||
OpPushBytes1 OpCode = 0x01 // 0x01-0x4B The next opcode bytes is data to be pushed onto the stack
|
Opushbytes1 Opcode = 0x01 // 0x01-0x4B The next opcode bytes is data to be pushed onto the stack
|
||||||
OpPushBytes75 OpCode = 0x4B
|
Opushbytes75 Opcode = 0x4B
|
||||||
OpPushData1 OpCode = 0x4C // The next byte contains the number of bytes to be pushed onto the stack.
|
Opushdata1 Opcode = 0x4C // The next byte contains the number of bytes to be pushed onto the stack.
|
||||||
OpPushData2 OpCode = 0x4D // The next two bytes contain the number of bytes to be pushed onto the stack.
|
Opushdata2 Opcode = 0x4D // The next two bytes contain the number of bytes to be pushed onto the stack.
|
||||||
OpPushData4 OpCode = 0x4E // The next four bytes contain the number of bytes to be pushed onto the stack.
|
Opushdata4 Opcode = 0x4E // The next four bytes contain the number of bytes to be pushed onto the stack.
|
||||||
OpPushM1 OpCode = 0x4F // The number -1 is pushed onto the stack.
|
Opushm1 Opcode = 0x4F // The number -1 is pushed onto the stack.
|
||||||
OpPush1 OpCode = 0x51
|
Opush1 Opcode = 0x51
|
||||||
OpPushT OpCode = OpPush1
|
Opusht Opcode = Opush1
|
||||||
OpPush2 OpCode = 0x52 // The number 2 is pushed onto the stack.
|
Opush2 Opcode = 0x52 // The number 2 is pushed onto the stack.
|
||||||
OpPush3 OpCode = 0x53 // The number 3 is pushed onto the stack.
|
Opush3 Opcode = 0x53 // The number 3 is pushed onto the stack.
|
||||||
OpPush4 OpCode = 0x54 // The number 4 is pushed onto the stack.
|
Opush4 Opcode = 0x54 // The number 4 is pushed onto the stack.
|
||||||
OpPush5 OpCode = 0x55 // The number 5 is pushed onto the stack.
|
Opush5 Opcode = 0x55 // The number 5 is pushed onto the stack.
|
||||||
OpPush6 OpCode = 0x56 // The number 6 is pushed onto the stack.
|
Opush6 Opcode = 0x56 // The number 6 is pushed onto the stack.
|
||||||
OpPush7 OpCode = 0x57 // The number 7 is pushed onto the stack.
|
Opush7 Opcode = 0x57 // The number 7 is pushed onto the stack.
|
||||||
OpPush8 OpCode = 0x58 // The number 8 is pushed onto the stack.
|
Opush8 Opcode = 0x58 // The number 8 is pushed onto the stack.
|
||||||
OpPush9 OpCode = 0x59 // The number 9 is pushed onto the stack.
|
Opush9 Opcode = 0x59 // The number 9 is pushed onto the stack.
|
||||||
OpPush10 OpCode = 0x5A // The number 10 is pushed onto the stack.
|
Opush10 Opcode = 0x5A // The number 10 is pushed onto the stack.
|
||||||
OpPush11 OpCode = 0x5B // The number 11 is pushed onto the stack.
|
Opush11 Opcode = 0x5B // The number 11 is pushed onto the stack.
|
||||||
OpPush12 OpCode = 0x5C // The number 12 is pushed onto the stack.
|
Opush12 Opcode = 0x5C // The number 12 is pushed onto the stack.
|
||||||
OpPush13 OpCode = 0x5D // The number 13 is pushed onto the stack.
|
Opush13 Opcode = 0x5D // The number 13 is pushed onto the stack.
|
||||||
OpPush14 OpCode = 0x5E // The number 14 is pushed onto the stack.
|
Opush14 Opcode = 0x5E // The number 14 is pushed onto the stack.
|
||||||
OpPush15 OpCode = 0x5F // The number 15 is pushed onto the stack.
|
Opush15 Opcode = 0x5F // The number 15 is pushed onto the stack.
|
||||||
OpPush16 OpCode = 0x60 // The number 16 is pushed onto the stack.
|
Opush16 Opcode = 0x60 // The number 16 is pushed onto the stack.
|
||||||
|
|
||||||
// Flow control
|
// Flow control
|
||||||
OpNOP OpCode = 0x61 // No operation.
|
Onop Opcode = 0x61 // No operation.
|
||||||
OpJMP OpCode = 0x62
|
Ojmp Opcode = 0x62
|
||||||
OpJMPIF OpCode = 0x63
|
Ojmpif Opcode = 0x63
|
||||||
OpJMPIFNOT OpCode = 0x64
|
Ojmpifnot Opcode = 0x64
|
||||||
OpCall OpCode = 0x65
|
Ocall Opcode = 0x65
|
||||||
OpRET OpCode = 0x66
|
Oret Opcode = 0x66
|
||||||
OpAppCall OpCode = 0x67
|
Opcall Opcode = 0x67
|
||||||
OpSysCall OpCode = 0x68
|
Osyscall Opcode = 0x68
|
||||||
OpTailCall OpCode = 0x69
|
Otailcall Opcode = 0x69
|
||||||
|
|
||||||
// The stack
|
// The stack
|
||||||
OpDupFromAltStack OpCode = 0x6A
|
Odupfromaltstack Opcode = 0x6A
|
||||||
OpToAltStack OpCode = 0x6B // Puts the input onto the top of the alt stack. Removes it from the main stack.
|
Otoaltstack Opcode = 0x6B // Puts the input onto the top of the alt stack. Removes it from the main stack.
|
||||||
OpFromAltStack OpCode = 0x6C // Puts the input onto the top of the main stack. Removes it from the alt stack.
|
Ofromaltstack Opcode = 0x6C // Puts the input onto the top of the main stack. Removes it from the alt stack.
|
||||||
OpXDrop OpCode = 0x6D
|
Oxdrop Opcode = 0x6D
|
||||||
OpXSwap OpCode = 0x72
|
Oxswap Opcode = 0x72
|
||||||
OpXTuck OpCode = 0x73
|
Oxtuck Opcode = 0x73
|
||||||
OpDepth OpCode = 0x74 // Puts the number of stack items onto the stack.
|
Odepth Opcode = 0x74 // Puts the number of stack items onto the stack.
|
||||||
OpDrop OpCode = 0x75 // Removes the top stack item.
|
Odrop Opcode = 0x75 // Removes the top stack item.
|
||||||
OpDup OpCode = 0x76 // Duplicates the top stack item.
|
Odup Opcode = 0x76 // Duplicates the top stack item.
|
||||||
OpNip OpCode = 0x77 // Removes the second-to-top stack item.
|
Onip Opcode = 0x77 // Removes the second-to-top stack item.
|
||||||
OpOver OpCode = 0x78 // Copies the second-to-top stack item to the top.
|
Oover Opcode = 0x78 // Copies the second-to-top stack item to the top.
|
||||||
OpPick OpCode = 0x79 // The item n back in the stack is copied to the top.
|
Opick Opcode = 0x79 // The item n back in the stack is copied to the top.
|
||||||
OpRoll OpCode = 0x7A // The item n back in the stack is moved to the top.
|
Oroll Opcode = 0x7A // The item n back in the stack is moved to the top.
|
||||||
OpRot OpCode = 0x7B // The top three items on the stack are rotated to the left.
|
Orot Opcode = 0x7B // The top three items on the stack are rotated to the left.
|
||||||
OpSwap OpCode = 0x7C // The top two items on the stack are swapped.
|
Oswap Opcode = 0x7C // The top two items on the stack are swapped.
|
||||||
OpTuck OpCode = 0x7D // The item at the top of the stack is copied and inserted before the second-to-top item.
|
Otuck Opcode = 0x7D // The item at the top of the stack is copied and inserted before the second-to-top item.
|
||||||
|
|
||||||
// Splice
|
// Splice
|
||||||
OpCat OpCode = 0x7E // Concatenates two strings.
|
Ocat Opcode = 0x7E // Concatenates two strings.
|
||||||
OpSubStr OpCode = 0x7F // Returns a section of a string.
|
Osubstr Opcode = 0x7F // Returns a section of a string.
|
||||||
OpLeft OpCode = 0x80 // Keeps only characters left of the specified point in a string.
|
Oleft Opcode = 0x80 // Keeps only characters left of the specified point in a string.
|
||||||
OpRight OpCode = 0x81 // Keeps only characters right of the specified point in a string.
|
Oright Opcode = 0x81 // Keeps only characters right of the specified point in a string.
|
||||||
OpSize OpCode = 0x82 // Returns the length of the input string.
|
Osize Opcode = 0x82 // Returns the length of the input string.
|
||||||
|
|
||||||
// Bitwise logic
|
// Bitwise logic
|
||||||
OpInvert OpCode = 0x83 // Flips all of the bits in the input.
|
Oinvert Opcode = 0x83 // Flips all of the bits in the input.
|
||||||
OpAnd OpCode = 0x84 // Boolean and between each bit in the inputs.
|
Oand Opcode = 0x84 // Boolean and between each bit in the inputs.
|
||||||
OpOr OpCode = 0x85 // Boolean or between each bit in the inputs.
|
Oor Opcode = 0x85 // Boolean or between each bit in the inputs.
|
||||||
OpXor OpCode = 0x86 // Boolean exclusive or between each bit in the inputs.
|
Oxor Opcode = 0x86 // Boolean exclusive or between each bit in the inputs.
|
||||||
OpEqual OpCode = 0x87 // Returns 1 if the inputs are exactly equal, 0 otherwise.
|
Oequal Opcode = 0x87 // Returns 1 if the inputs are exactly equal, 0 otherwise.
|
||||||
|
|
||||||
// Arithmetic
|
// Arithmetic
|
||||||
// Note: Arithmetic inputs are limited to signed 32-bit integers, but may overflow their output.
|
// Note: Arithmetic inputs are limited to signed 32-bit integers, but may overflow their output.
|
||||||
OpInc OpCode = 0x8B // 1 is added to the input.
|
Oinc Opcode = 0x8B // 1 is added to the input.
|
||||||
OpDec OpCode = 0x8C // 1 is subtracted from the input.
|
Odec Opcode = 0x8C // 1 is subtracted from the input.
|
||||||
OpSign OpCode = 0x8D
|
Osign Opcode = 0x8D
|
||||||
OpNegate OpCode = 0x8F // The sign of the input is flipped.
|
Onegate Opcode = 0x8F // The sign of the input is flipped.
|
||||||
OpAbs OpCode = 0x90 // The input is made positive.
|
Oabs Opcode = 0x90 // The input is made positive.
|
||||||
OpNot OpCode = 0x91 // If the input is 0 or 1, it is flipped. Otherwise the output will be 0.
|
Onot Opcode = 0x91 // If the input is 0 or 1, it is flipped. Otherwise the output will be 0.
|
||||||
OpNZ OpCode = 0x92 // Returns 0 if the input is 0. 1 otherwise.
|
Onz Opcode = 0x92 // Returns 0 if the input is 0. 1 otherwise.
|
||||||
OpAdd OpCode = 0x93 // a is added to b.
|
Oadd Opcode = 0x93 // a is added to b.
|
||||||
OpSub OpCode = 0x94 // b is subtracted from a.
|
Osub Opcode = 0x94 // b is subtracted from a.
|
||||||
OpMul OpCode = 0x95 // a is multiplied by b.
|
Omul Opcode = 0x95 // a is multiplied by b.
|
||||||
OpDiv OpCode = 0x96 // a is divided by b.
|
Odiv Opcode = 0x96 // a is divided by b.
|
||||||
OpMod OpCode = 0x97 // Returns the remainder after dividing a by b.
|
Omod Opcode = 0x97 // Returns the remainder after dividing a by b.
|
||||||
OpShl OpCode = 0x98 // Shifts a left b bits, preserving sign.
|
Oshl Opcode = 0x98 // Shifts a left b bits, preserving sign.
|
||||||
OpShr OpCode = 0x99 // Shifts a right b bits, preserving sign.
|
Oshr Opcode = 0x99 // Shifts a right b bits, preserving sign.
|
||||||
OpBoolAnd OpCode = 0x9A // If both a and b are not 0, the output is 1. Otherwise 0.
|
Obooland Opcode = 0x9A // If both a and b are not 0, the output is 1. Otherwise 0.
|
||||||
OpBoolOr OpCode = 0x9B // If a or b is not 0, the output is 1. Otherwise 0.
|
Oboolor Opcode = 0x9B // If a or b is not 0, the output is 1. Otherwise 0.
|
||||||
OpNumEqual OpCode = 0x9C // Returns 1 if the numbers are equal, 0 otherwise.
|
Onumequal Opcode = 0x9C // Returns 1 if the numbers are equal, 0 otherwise.
|
||||||
OpNumNotEqual OpCode = 0x9E // Returns 1 if the numbers are not equal, 0 otherwise.
|
Onumnotequal Opcode = 0x9E // Returns 1 if the numbers are not equal, 0 otherwise.
|
||||||
OpLT OpCode = 0x9F // Returns 1 if a is less than b, 0 otherwise.
|
Olt Opcode = 0x9F // Returns 1 if a is less than b, 0 otherwise.
|
||||||
OpGT OpCode = 0xA0 // Returns 1 if a is greater than b, 0 otherwise.
|
Ogt Opcode = 0xA0 // Returns 1 if a is greater than b, 0 otherwise.
|
||||||
OpLTE OpCode = 0xA1 // Returns 1 if a is less than or equal to b, 0 otherwise.
|
Olte Opcode = 0xA1 // Returns 1 if a is less than or equal to b, 0 otherwise.
|
||||||
OpGTE OpCode = 0xA2 // Returns 1 if a is greater than or equal to b, 0 otherwise.
|
Ogte Opcode = 0xA2 // Returns 1 if a is greater than or equal to b, 0 otherwise.
|
||||||
OpMin OpCode = 0xA3 // Returns the smaller of a and b.
|
Omin Opcode = 0xA3 // Returns the smaller of a and b.
|
||||||
OpMax OpCode = 0xA4 // Returns the larger of a and b.
|
Omax Opcode = 0xA4 // Returns the larger of a and b.
|
||||||
OpWithin OpCode = 0xA5 // Returns 1 if x is within the specified range (left-inclusive), 0 otherwise.
|
Owithin Opcode = 0xA5 // Returns 1 if x is within the specified range (left-inclusive), 0 otherwise.
|
||||||
|
|
||||||
// Crypto
|
// Crypto
|
||||||
OpSHA1 OpCode = 0xA7 // The input is hashed using SHA-1.
|
Osha1 Opcode = 0xA7 // The input is hashed using SHA-1.
|
||||||
OpSHA256 OpCode = 0xA8 // The input is hashed using SHA-256.
|
Osha256 Opcode = 0xA8 // The input is hashed using SHA-256.
|
||||||
OpHASH160 OpCode = 0xA9
|
Ohash160 Opcode = 0xA9
|
||||||
OpHASH256 OpCode = 0xAA
|
Ohash256 Opcode = 0xAA
|
||||||
OpCheckSig OpCode = 0xAC
|
Ochecksig Opcode = 0xAC
|
||||||
OpCheckMultiSig OpCode = 0xAE
|
Ocheckmultisig Opcode = 0xAE
|
||||||
|
|
||||||
// Array
|
// array
|
||||||
OpArraySize OpCode = 0xC0
|
Oarraysize Opcode = 0xC0
|
||||||
OpPack OpCode = 0xC1
|
Opack Opcode = 0xC1
|
||||||
OpUnpack OpCode = 0xC2
|
Ounpack Opcode = 0xC2
|
||||||
OpPickItem OpCode = 0xC3
|
Opickitem Opcode = 0xC3
|
||||||
OpSetItem OpCode = 0xC4
|
Osetitem Opcode = 0xC4
|
||||||
OpNewArray OpCode = 0xC5 // Pops size from stack and creates a new array with that size, and pushes the array into the stack
|
Onewarray Opcode = 0xC5 // Pops size from stack and creates a new array with that size, and pushes the array into the stack
|
||||||
OpNewStruct OpCode = 0xC6
|
Onewstruct Opcode = 0xC6
|
||||||
OpAppend OpCode = 0xC8
|
Oappend Opcode = 0xC8
|
||||||
OpReverse OpCode = 0xC9
|
Oreverse Opcode = 0xC9
|
||||||
OpRemove OpCode = 0xCA
|
Oremove Opcode = 0xCA
|
||||||
|
|
||||||
// Exceptions
|
// exceptions
|
||||||
OpThrow OpCode = 0xF0
|
Othrow Opcode = 0xF0
|
||||||
OpThrowIfNot OpCode = 0xF1
|
Othrowifnot Opcode = 0xF1
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,118 +1,118 @@
|
||||||
// Code generated by "stringer -type=OpCode ./pkg/vm"; DO NOT EDIT.
|
// Code generated by "stringer -type=Opcode ./pkg/vm/compiler"; DO NOT EDIT.
|
||||||
|
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import "strconv"
|
import "strconv"
|
||||||
|
|
||||||
const _OpCode_name = "OpPush0OpPushBytes1OpPushBytes75OpPushData1OpPushData2OpPushData4OpPushM1OpPush1OpPush2OpPush3OpPush4OpPush5OpPush6OpPush7OpPush8OpPush9OpPush10OpPush11OpPush12OpPush13OpPush14OpPush15OpPush16OpNOPOpJMPOpJMPIFOpJMPIFNOTOpCallOpRETOpAppCallOpSysCallOpTailCallOpDupFromAltStackOpToAltStackOpFromAltStackOpXDropOpXSwapOpXTuckOpDepthOpDropOpDupOpNipOpOverOpPickOpRollOpRotOpSwapOpTuckOpCatOpSubStrOpLeftOpRightOpSizeOpInvertOpAndOpOrOpXorOpEqualOpIncOpDecOpSignOpNegateOpAbsOpNotOpNZOpAddOpSubOpMulOpDivOpModOpShlOpShrOpBoolAndOpBoolOrOpNumEqualOpNumNotEqualOpLTOpGTOpLTEOpGTEOpMinOpMaxOpWithinOpSHA1OpSHA256OpHASH160OpHASH256OpCheckSigOpCheckMultiSigOpArraySizeOpPackOpUnpackOpPickItemOpSetItemOpNewArrayOpNewStructOpAppendOpReverseOpRemoveOpThrowOpThrowIfNot"
|
const _Opcode_name = "Opush0Opushbytes1Opushbytes75Opushdata1Opushdata2Opushdata4Opushm1Opush1Opush2Opush3Opush4Opush5Opush6Opush7Opush8Opush9Opush10Opush11Opush12Opush13Opush14Opush15Opush16OnopOjmpOjmpifOjmpifnotOcallOretOpcallOsyscallOtailcallOdupfromaltstackOtoaltstackOfromaltstackOxdropOxswapOxtuckOdepthOdropOdupOnipOoverOpickOrollOrotOswapOtuckOcatOsubstrOleftOrightOsizeOinvertOandOorOxorOequalOincOdecOsignOnegateOabsOnotOnzOaddOsubOmulOdivOmodOshlOshrOboolandOboolorOnumequalOnumnotequalOltOgtOlteOgteOminOmaxOwithinOsha1Osha256Ohash160Ohash256OchecksigOcheckmultisigOarraysizeOpackOunpackOpickitemOsetitemOnewarrayOnewstructOappendOreverseOremoveOthrowOthrowifnot"
|
||||||
|
|
||||||
var _OpCode_map = map[OpCode]string{
|
var _Opcode_map = map[Opcode]string{
|
||||||
0: _OpCode_name[0:7],
|
0: _Opcode_name[0:6],
|
||||||
1: _OpCode_name[7:19],
|
1: _Opcode_name[6:17],
|
||||||
75: _OpCode_name[19:32],
|
75: _Opcode_name[17:29],
|
||||||
76: _OpCode_name[32:43],
|
76: _Opcode_name[29:39],
|
||||||
77: _OpCode_name[43:54],
|
77: _Opcode_name[39:49],
|
||||||
78: _OpCode_name[54:65],
|
78: _Opcode_name[49:59],
|
||||||
79: _OpCode_name[65:73],
|
79: _Opcode_name[59:66],
|
||||||
81: _OpCode_name[73:80],
|
81: _Opcode_name[66:72],
|
||||||
82: _OpCode_name[80:87],
|
82: _Opcode_name[72:78],
|
||||||
83: _OpCode_name[87:94],
|
83: _Opcode_name[78:84],
|
||||||
84: _OpCode_name[94:101],
|
84: _Opcode_name[84:90],
|
||||||
85: _OpCode_name[101:108],
|
85: _Opcode_name[90:96],
|
||||||
86: _OpCode_name[108:115],
|
86: _Opcode_name[96:102],
|
||||||
87: _OpCode_name[115:122],
|
87: _Opcode_name[102:108],
|
||||||
88: _OpCode_name[122:129],
|
88: _Opcode_name[108:114],
|
||||||
89: _OpCode_name[129:136],
|
89: _Opcode_name[114:120],
|
||||||
90: _OpCode_name[136:144],
|
90: _Opcode_name[120:127],
|
||||||
91: _OpCode_name[144:152],
|
91: _Opcode_name[127:134],
|
||||||
92: _OpCode_name[152:160],
|
92: _Opcode_name[134:141],
|
||||||
93: _OpCode_name[160:168],
|
93: _Opcode_name[141:148],
|
||||||
94: _OpCode_name[168:176],
|
94: _Opcode_name[148:155],
|
||||||
95: _OpCode_name[176:184],
|
95: _Opcode_name[155:162],
|
||||||
96: _OpCode_name[184:192],
|
96: _Opcode_name[162:169],
|
||||||
97: _OpCode_name[192:197],
|
97: _Opcode_name[169:173],
|
||||||
98: _OpCode_name[197:202],
|
98: _Opcode_name[173:177],
|
||||||
99: _OpCode_name[202:209],
|
99: _Opcode_name[177:183],
|
||||||
100: _OpCode_name[209:219],
|
100: _Opcode_name[183:192],
|
||||||
101: _OpCode_name[219:225],
|
101: _Opcode_name[192:197],
|
||||||
102: _OpCode_name[225:230],
|
102: _Opcode_name[197:201],
|
||||||
103: _OpCode_name[230:239],
|
103: _Opcode_name[201:207],
|
||||||
104: _OpCode_name[239:248],
|
104: _Opcode_name[207:215],
|
||||||
105: _OpCode_name[248:258],
|
105: _Opcode_name[215:224],
|
||||||
106: _OpCode_name[258:275],
|
106: _Opcode_name[224:240],
|
||||||
107: _OpCode_name[275:287],
|
107: _Opcode_name[240:251],
|
||||||
108: _OpCode_name[287:301],
|
108: _Opcode_name[251:264],
|
||||||
109: _OpCode_name[301:308],
|
109: _Opcode_name[264:270],
|
||||||
114: _OpCode_name[308:315],
|
114: _Opcode_name[270:276],
|
||||||
115: _OpCode_name[315:322],
|
115: _Opcode_name[276:282],
|
||||||
116: _OpCode_name[322:329],
|
116: _Opcode_name[282:288],
|
||||||
117: _OpCode_name[329:335],
|
117: _Opcode_name[288:293],
|
||||||
118: _OpCode_name[335:340],
|
118: _Opcode_name[293:297],
|
||||||
119: _OpCode_name[340:345],
|
119: _Opcode_name[297:301],
|
||||||
120: _OpCode_name[345:351],
|
120: _Opcode_name[301:306],
|
||||||
121: _OpCode_name[351:357],
|
121: _Opcode_name[306:311],
|
||||||
122: _OpCode_name[357:363],
|
122: _Opcode_name[311:316],
|
||||||
123: _OpCode_name[363:368],
|
123: _Opcode_name[316:320],
|
||||||
124: _OpCode_name[368:374],
|
124: _Opcode_name[320:325],
|
||||||
125: _OpCode_name[374:380],
|
125: _Opcode_name[325:330],
|
||||||
126: _OpCode_name[380:385],
|
126: _Opcode_name[330:334],
|
||||||
127: _OpCode_name[385:393],
|
127: _Opcode_name[334:341],
|
||||||
128: _OpCode_name[393:399],
|
128: _Opcode_name[341:346],
|
||||||
129: _OpCode_name[399:406],
|
129: _Opcode_name[346:352],
|
||||||
130: _OpCode_name[406:412],
|
130: _Opcode_name[352:357],
|
||||||
131: _OpCode_name[412:420],
|
131: _Opcode_name[357:364],
|
||||||
132: _OpCode_name[420:425],
|
132: _Opcode_name[364:368],
|
||||||
133: _OpCode_name[425:429],
|
133: _Opcode_name[368:371],
|
||||||
134: _OpCode_name[429:434],
|
134: _Opcode_name[371:375],
|
||||||
135: _OpCode_name[434:441],
|
135: _Opcode_name[375:381],
|
||||||
139: _OpCode_name[441:446],
|
139: _Opcode_name[381:385],
|
||||||
140: _OpCode_name[446:451],
|
140: _Opcode_name[385:389],
|
||||||
141: _OpCode_name[451:457],
|
141: _Opcode_name[389:394],
|
||||||
143: _OpCode_name[457:465],
|
143: _Opcode_name[394:401],
|
||||||
144: _OpCode_name[465:470],
|
144: _Opcode_name[401:405],
|
||||||
145: _OpCode_name[470:475],
|
145: _Opcode_name[405:409],
|
||||||
146: _OpCode_name[475:479],
|
146: _Opcode_name[409:412],
|
||||||
147: _OpCode_name[479:484],
|
147: _Opcode_name[412:416],
|
||||||
148: _OpCode_name[484:489],
|
148: _Opcode_name[416:420],
|
||||||
149: _OpCode_name[489:494],
|
149: _Opcode_name[420:424],
|
||||||
150: _OpCode_name[494:499],
|
150: _Opcode_name[424:428],
|
||||||
151: _OpCode_name[499:504],
|
151: _Opcode_name[428:432],
|
||||||
152: _OpCode_name[504:509],
|
152: _Opcode_name[432:436],
|
||||||
153: _OpCode_name[509:514],
|
153: _Opcode_name[436:440],
|
||||||
154: _OpCode_name[514:523],
|
154: _Opcode_name[440:448],
|
||||||
155: _OpCode_name[523:531],
|
155: _Opcode_name[448:455],
|
||||||
156: _OpCode_name[531:541],
|
156: _Opcode_name[455:464],
|
||||||
158: _OpCode_name[541:554],
|
158: _Opcode_name[464:476],
|
||||||
159: _OpCode_name[554:558],
|
159: _Opcode_name[476:479],
|
||||||
160: _OpCode_name[558:562],
|
160: _Opcode_name[479:482],
|
||||||
161: _OpCode_name[562:567],
|
161: _Opcode_name[482:486],
|
||||||
162: _OpCode_name[567:572],
|
162: _Opcode_name[486:490],
|
||||||
163: _OpCode_name[572:577],
|
163: _Opcode_name[490:494],
|
||||||
164: _OpCode_name[577:582],
|
164: _Opcode_name[494:498],
|
||||||
165: _OpCode_name[582:590],
|
165: _Opcode_name[498:505],
|
||||||
167: _OpCode_name[590:596],
|
167: _Opcode_name[505:510],
|
||||||
168: _OpCode_name[596:604],
|
168: _Opcode_name[510:517],
|
||||||
169: _OpCode_name[604:613],
|
169: _Opcode_name[517:525],
|
||||||
170: _OpCode_name[613:622],
|
170: _Opcode_name[525:533],
|
||||||
172: _OpCode_name[622:632],
|
172: _Opcode_name[533:542],
|
||||||
174: _OpCode_name[632:647],
|
174: _Opcode_name[542:556],
|
||||||
192: _OpCode_name[647:658],
|
192: _Opcode_name[556:566],
|
||||||
193: _OpCode_name[658:664],
|
193: _Opcode_name[566:571],
|
||||||
194: _OpCode_name[664:672],
|
194: _Opcode_name[571:578],
|
||||||
195: _OpCode_name[672:682],
|
195: _Opcode_name[578:587],
|
||||||
196: _OpCode_name[682:691],
|
196: _Opcode_name[587:595],
|
||||||
197: _OpCode_name[691:701],
|
197: _Opcode_name[595:604],
|
||||||
198: _OpCode_name[701:712],
|
198: _Opcode_name[604:614],
|
||||||
200: _OpCode_name[712:720],
|
200: _Opcode_name[614:621],
|
||||||
201: _OpCode_name[720:729],
|
201: _Opcode_name[621:629],
|
||||||
202: _OpCode_name[729:737],
|
202: _Opcode_name[629:636],
|
||||||
240: _OpCode_name[737:744],
|
240: _Opcode_name[636:642],
|
||||||
241: _OpCode_name[744:756],
|
241: _Opcode_name[642:653],
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i OpCode) String() string {
|
func (i Opcode) String() string {
|
||||||
if str, ok := _OpCode_map[i]; ok {
|
if str, ok := _Opcode_map[i]; ok {
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
return "OpCode(" + strconv.FormatInt(int64(i), 10) + ")"
|
return "Opcode(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue