Merge pull request #467 from nspcc-dev/errcheck_297

This patchset closes #297 and #457.
This commit is contained in:
Roman Khimov 2019-12-03 15:06:11 +03:00 committed by GitHub
commit f48228ef7d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 278 additions and 225 deletions

View file

@ -177,8 +177,7 @@ func dumpDB(ctx *cli.Context) error {
buf := io.NewBufBinWriter() buf := io.NewBufBinWriter()
b.EncodeBinary(buf.BinWriter) b.EncodeBinary(buf.BinWriter)
bytes := buf.Bytes() bytes := buf.Bytes()
writer.WriteLE(uint32(len(bytes))) writer.WriteVarBytes(bytes)
writer.WriteLE(bytes)
if writer.Err != nil { if writer.Err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }

View file

@ -330,9 +330,11 @@ func contractCompile(ctx *cli.Context) error {
Debug: ctx.Bool("debug"), Debug: ctx.Bool("debug"),
} }
if err := compiler.CompileAndSave(src, o); err != nil { result, err := compiler.CompileAndSave(src, o)
if err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
fmt.Println(hex.EncodeToString(result))
return nil return nil
} }

View file

@ -169,7 +169,7 @@ func (p Payload) EncodeBinaryUnsigned(w *io.BinWriter) {
ww := io.NewBufBinWriter() ww := io.NewBufBinWriter()
p.message.EncodeBinary(ww.BinWriter) p.message.EncodeBinary(ww.BinWriter)
w.WriteBytes(ww.Bytes()) w.WriteVarBytes(ww.Bytes())
} }
// EncodeBinary implements io.Serializable interface. // EncodeBinary implements io.Serializable interface.

View file

@ -100,7 +100,7 @@ func (p *changeViewCompact) EncodeBinary(w *io.BinWriter) {
w.WriteLE(p.ValidatorIndex) w.WriteLE(p.ValidatorIndex)
w.WriteLE(p.OriginalViewNumber) w.WriteLE(p.OriginalViewNumber)
w.WriteLE(p.Timestamp) w.WriteLE(p.Timestamp)
w.WriteBytes(p.InvocationScript) w.WriteVarBytes(p.InvocationScript)
} }
// DecodeBinary implements io.Serializable interface. // DecodeBinary implements io.Serializable interface.
@ -116,7 +116,7 @@ func (p *commitCompact) EncodeBinary(w *io.BinWriter) {
w.WriteLE(p.ViewNumber) w.WriteLE(p.ViewNumber)
w.WriteLE(p.ValidatorIndex) w.WriteLE(p.ValidatorIndex)
w.WriteBE(p.Signature) w.WriteBE(p.Signature)
w.WriteBytes(p.InvocationScript) w.WriteVarBytes(p.InvocationScript)
} }
// DecodeBinary implements io.Serializable interface. // DecodeBinary implements io.Serializable interface.
@ -128,7 +128,7 @@ func (p *preparationCompact) DecodeBinary(r *io.BinReader) {
// EncodeBinary implements io.Serializable interface. // EncodeBinary implements io.Serializable interface.
func (p *preparationCompact) EncodeBinary(w *io.BinWriter) { func (p *preparationCompact) EncodeBinary(w *io.BinWriter) {
w.WriteLE(p.ValidatorIndex) w.WriteLE(p.ValidatorIndex)
w.WriteBytes(p.InvocationScript) w.WriteVarBytes(p.InvocationScript)
} }
// AddPayload implements payload.RecoveryMessage interface. // AddPayload implements payload.RecoveryMessage interface.

View file

@ -1508,7 +1508,7 @@ func (bc *Blockchain) verifyBlockWitnesses(block *Block, prevHeader *Header) err
func hashAndIndexToBytes(h util.Uint256, index uint32) []byte { func hashAndIndexToBytes(h util.Uint256, index uint32) []byte {
buf := io.NewBufBinWriter() buf := io.NewBufBinWriter()
buf.WriteLE(h.BytesReverse()) buf.WriteBytes(h.BytesReverse())
buf.WriteLE(index) buf.WriteLE(index)
return buf.Bytes() return buf.Bytes()
} }

View file

@ -69,7 +69,7 @@ func (state *BlockChainState) storeAsBlock(block *Block, sysFee uint32) error {
if err != nil { if err != nil {
return err return err
} }
buf.WriteLE(b) buf.WriteBytes(b)
if buf.Err != nil { if buf.Err != nil {
return buf.Err return buf.Err
} }
@ -79,7 +79,7 @@ func (state *BlockChainState) storeAsBlock(block *Block, sysFee uint32) error {
// storeAsCurrentBlock stores the given block witch prefix SYSCurrentBlock. // storeAsCurrentBlock stores the given block witch prefix SYSCurrentBlock.
func (state *BlockChainState) storeAsCurrentBlock(block *Block) error { func (state *BlockChainState) storeAsCurrentBlock(block *Block) error {
buf := io.NewBufBinWriter() buf := io.NewBufBinWriter()
buf.WriteLE(block.Hash().BytesReverse()) buf.WriteBytes(block.Hash().BytesReverse())
buf.WriteLE(block.Index) buf.WriteLE(block.Index)
return state.store.Put(storage.SYSCurrentBlock.Bytes(), buf.Bytes()) return state.store.Put(storage.SYSCurrentBlock.Bytes(), buf.Bytes())
} }

View file

@ -52,7 +52,7 @@ func (cs *ContractState) DecodeBinary(br *io.BinReader) {
// EncodeBinary implements Serializable interface. // EncodeBinary implements Serializable interface.
func (cs *ContractState) EncodeBinary(bw *io.BinWriter) { func (cs *ContractState) EncodeBinary(bw *io.BinWriter) {
bw.WriteBytes(cs.Script) bw.WriteVarBytes(cs.Script)
bw.WriteArray(cs.ParamList) bw.WriteArray(cs.ParamList)
bw.WriteLE(cs.ReturnType) bw.WriteLE(cs.ReturnType)
bw.WriteLE(cs.Properties) bw.WriteLE(cs.Properties)

View file

@ -53,7 +53,7 @@ func deleteStorageItemInStore(s storage.Store, scripthash util.Uint160, key []by
// EncodeBinary implements Serializable interface. // EncodeBinary implements Serializable interface.
func (si *StorageItem) EncodeBinary(w *io.BinWriter) { func (si *StorageItem) EncodeBinary(w *io.BinWriter) {
w.WriteBytes(si.Value) w.WriteVarBytes(si.Value)
w.WriteLE(si.IsConst) w.WriteLE(si.IsConst)
} }

View file

@ -55,18 +55,18 @@ func (attr *Attribute) EncodeBinary(bw *io.BinWriter) {
bw.WriteLE(&attr.Usage) bw.WriteLE(&attr.Usage)
switch attr.Usage { switch attr.Usage {
case ECDH02, ECDH03: case ECDH02, ECDH03:
bw.WriteLE(attr.Data[1:]) bw.WriteBytes(attr.Data[1:])
case Description, Remark, Remark1, Remark2, Remark3, Remark4, case Description, Remark, Remark1, Remark2, Remark3, Remark4,
Remark5, Remark6, Remark7, Remark8, Remark9, Remark10, Remark11, Remark5, Remark6, Remark7, Remark8, Remark9, Remark10, Remark11,
Remark12, Remark13, Remark14, Remark15: Remark12, Remark13, Remark14, Remark15:
bw.WriteBytes(attr.Data) bw.WriteVarBytes(attr.Data)
case DescriptionURL: case DescriptionURL:
var urllen = uint8(len(attr.Data)) var urllen = uint8(len(attr.Data))
bw.WriteLE(urllen) bw.WriteLE(urllen)
fallthrough fallthrough
case Script, ContractHash, Vote, Hash1, Hash2, Hash3, Hash4, Hash5, Hash6, case Script, ContractHash, Vote, Hash1, Hash2, Hash3, Hash4, Hash5, Hash6,
Hash7, Hash8, Hash9, Hash10, Hash11, Hash12, Hash13, Hash14, Hash15: Hash7, Hash8, Hash9, Hash10, Hash11, Hash12, Hash13, Hash14, Hash15:
bw.WriteLE(attr.Data) bw.WriteBytes(attr.Data)
default: default:
bw.Err = fmt.Errorf("failed encoding TX attribute usage: 0x%2x", attr.Usage) bw.Err = fmt.Errorf("failed encoding TX attribute usage: 0x%2x", attr.Usage)
} }

View file

@ -45,7 +45,7 @@ func (tx *InvocationTX) DecodeBinary(br *io.BinReader) {
// EncodeBinary implements Serializable interface. // EncodeBinary implements Serializable interface.
func (tx *InvocationTX) EncodeBinary(bw *io.BinWriter) { func (tx *InvocationTX) EncodeBinary(bw *io.BinWriter) {
bw.WriteBytes(tx.Script) bw.WriteVarBytes(tx.Script)
if tx.Version >= 1 { if tx.Version >= 1 {
bw.WriteLE(tx.Gas) bw.WriteLE(tx.Gas)
} }

View file

@ -51,7 +51,7 @@ func (tx *PublishTX) DecodeBinary(br *io.BinReader) {
// EncodeBinary implements Serializable interface. // EncodeBinary implements Serializable interface.
func (tx *PublishTX) EncodeBinary(bw *io.BinWriter) { func (tx *PublishTX) EncodeBinary(bw *io.BinWriter) {
bw.WriteBytes(tx.Script) bw.WriteVarBytes(tx.Script)
bw.WriteVarUint(uint64(len(tx.ParamList))) bw.WriteVarUint(uint64(len(tx.ParamList)))
for _, param := range tx.ParamList { for _, param := range tx.ParamList {
bw.WriteLE(uint8(param)) bw.WriteLE(uint8(param))

View file

@ -49,6 +49,6 @@ func (tx *RegisterTX) EncodeBinary(bw *io.BinWriter) {
bw.WriteString(tx.Name) bw.WriteString(tx.Name)
bw.WriteLE(tx.Amount) bw.WriteLE(tx.Amount)
bw.WriteLE(tx.Precision) bw.WriteLE(tx.Precision)
bw.WriteLE(tx.Owner.Bytes()) bw.WriteBytes(tx.Owner.Bytes())
bw.WriteLE(tx.Admin) bw.WriteLE(tx.Admin)
} }

View file

@ -33,7 +33,7 @@ func (s *StateDescriptor) DecodeBinary(r *io.BinReader) {
// EncodeBinary implements Serializable interface. // EncodeBinary implements Serializable interface.
func (s *StateDescriptor) EncodeBinary(w *io.BinWriter) { func (s *StateDescriptor) EncodeBinary(w *io.BinWriter) {
w.WriteLE(s.Type) w.WriteLE(s.Type)
w.WriteBytes(s.Key) w.WriteVarBytes(s.Key)
w.WriteBytes(s.Value) w.WriteVarBytes(s.Value)
w.WriteString(s.Field) w.WriteString(s.Field)
} }

View file

@ -23,8 +23,8 @@ func (w *Witness) DecodeBinary(br *io.BinReader) {
// EncodeBinary implements Serializable interface. // EncodeBinary implements Serializable interface.
func (w *Witness) EncodeBinary(bw *io.BinWriter) { func (w *Witness) EncodeBinary(bw *io.BinWriter) {
bw.WriteBytes(w.InvocationScript) bw.WriteVarBytes(w.InvocationScript)
bw.WriteBytes(w.VerificationScript) bw.WriteVarBytes(w.VerificationScript)
} }
// MarshalJSON implements the json marshaller interface. // MarshalJSON implements the json marshaller interface.

View file

@ -219,7 +219,7 @@ func (p *PublicKey) DecodeBinary(r *io.BinReader) {
// EncodeBinary encodes a PublicKey to the given BinWriter. // EncodeBinary encodes a PublicKey to the given BinWriter.
func (p *PublicKey) EncodeBinary(w *io.BinWriter) { func (p *PublicKey) EncodeBinary(w *io.BinWriter) {
w.WriteLE(p.Bytes()) w.WriteBytes(p.Bytes())
} }
// Signature returns a NEO-specific hash of the key. // Signature returns a NEO-specific hash of the key.

View file

@ -19,6 +19,11 @@ func NewBufBinWriter() *BufBinWriter {
return &BufBinWriter{BinWriter: NewBinWriterFromIO(b), buf: b} return &BufBinWriter{BinWriter: NewBinWriterFromIO(b), buf: b}
} }
// Len returns the number of bytes of the unread portion of the buffer.
func (bw *BufBinWriter) Len() int {
return bw.buf.Len()
}
// Bytes returns resulting buffer and makes future writes return an error. // Bytes returns resulting buffer and makes future writes return an error.
func (bw *BufBinWriter) Bytes() []byte { func (bw *BufBinWriter) Bytes() []byte {
if bw.Err != nil { if bw.Err != nil {

View file

@ -93,13 +93,18 @@ func (w *BinWriter) WriteVarUint(val uint64) {
} }
// WriteBytes writes a variable length byte array into the underlying io.Writer. // WriteBytes writes a variable byte into the underlying io.Writer without prefix.
func (w *BinWriter) WriteBytes(b []byte) { func (w *BinWriter) WriteBytes(b []byte) {
w.WriteLE(b)
}
// WriteVarBytes writes a variable length byte array into the underlying io.Writer.
func (w *BinWriter) WriteVarBytes(b []byte) {
w.WriteVarUint(uint64(len(b))) w.WriteVarUint(uint64(len(b)))
w.WriteLE(b) w.WriteLE(b)
} }
// WriteString writes a variable length string into the underlying io.Writer. // WriteString writes a variable length string into the underlying io.Writer.
func (w *BinWriter) WriteString(s string) { func (w *BinWriter) WriteString(s string) {
w.WriteBytes([]byte(s)) w.WriteVarBytes([]byte(s))
} }

View file

@ -53,6 +53,13 @@ func TestWriteBE(t *testing.T) {
assert.Equal(t, val, readval) assert.Equal(t, val, readval)
} }
func TestBufBinWriter_Len(t *testing.T) {
val := []byte{0xde}
bw := NewBufBinWriter()
bw.WriteLE(val)
require.Equal(t, 1, bw.Len())
}
func TestWriterErrHandling(t *testing.T) { func TestWriterErrHandling(t *testing.T) {
var badio = &badRW{} var badio = &badRW{}
bw := NewBinWriterFromIO(badio) bw := NewBinWriterFromIO(badio)
@ -62,7 +69,7 @@ func TestWriterErrHandling(t *testing.T) {
bw.WriteLE(uint32(0)) bw.WriteLE(uint32(0))
bw.WriteBE(uint32(0)) bw.WriteBE(uint32(0))
bw.WriteVarUint(0) bw.WriteVarUint(0)
bw.WriteBytes([]byte{0x55, 0xaa}) bw.WriteVarBytes([]byte{0x55, 0xaa})
bw.WriteString("neo") bw.WriteString("neo")
assert.NotNil(t, bw.Err) assert.NotNil(t, bw.Err)
} }

View file

@ -31,5 +31,5 @@ func (m *MerkleBlock) EncodeBinary(bw *io.BinWriter) {
bw.WriteVarUint(uint64(m.TxCount)) bw.WriteVarUint(uint64(m.TxCount))
bw.WriteArray(m.Hashes) bw.WriteArray(m.Hashes)
bw.WriteBytes(m.Flags) bw.WriteVarBytes(m.Flags)
} }

View file

@ -73,7 +73,7 @@ func (p *Version) EncodeBinary(br *io.BinWriter) {
br.WriteLE(p.Port) br.WriteLE(p.Port)
br.WriteLE(p.Nonce) br.WriteLE(p.Nonce)
br.WriteBytes(p.UserAgent) br.WriteVarBytes(p.UserAgent)
br.WriteLE(p.StartHeight) br.WriteLE(p.StartHeight)
br.WriteLE(&p.Relay) br.WriteLE(&p.Relay)
} }

View file

@ -1,10 +1,10 @@
package compiler package compiler
import ( import (
"fmt"
"go/ast" "go/ast"
"go/constant" "go/constant"
"go/types" "go/types"
"log"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
) )
@ -19,7 +19,7 @@ var (
) )
// typeAndValueForField returns a zero initialized typeAndValue for the given type.Var. // typeAndValueForField returns a zero initialized typeAndValue for the given type.Var.
func typeAndValueForField(fld *types.Var) types.TypeAndValue { func typeAndValueForField(fld *types.Var) (types.TypeAndValue, error) {
switch t := fld.Type().(type) { switch t := fld.Type().(type) {
case *types.Basic: case *types.Basic:
switch t.Kind() { switch t.Kind() {
@ -27,22 +27,22 @@ func typeAndValueForField(fld *types.Var) types.TypeAndValue {
return types.TypeAndValue{ return types.TypeAndValue{
Type: t, Type: t,
Value: constant.MakeInt64(0), Value: constant.MakeInt64(0),
} }, nil
case types.String: case types.String:
return types.TypeAndValue{ return types.TypeAndValue{
Type: t, Type: t,
Value: constant.MakeString(""), Value: constant.MakeString(""),
} }, nil
case types.Bool, types.UntypedBool: case types.Bool, types.UntypedBool:
return types.TypeAndValue{ return types.TypeAndValue{
Type: t, Type: t,
Value: constant.MakeBool(false), Value: constant.MakeBool(false),
} }, nil
default: default:
log.Fatalf("could not initialize struct field %s to zero, type: %s", fld.Name(), t) return types.TypeAndValue{}, fmt.Errorf("could not initialize struct field %s to zero, type: %s", fld.Name(), t)
} }
} }
return types.TypeAndValue{} return types.TypeAndValue{}, nil
} }
// countGlobals counts the global variables in the program to add // countGlobals counts the global variables in the program to add
@ -69,7 +69,7 @@ func isIdentBool(ident *ast.Ident) bool {
} }
// makeBoolFromIdent creates a bool type from an *ast.Ident. // makeBoolFromIdent creates a bool type from an *ast.Ident.
func makeBoolFromIdent(ident *ast.Ident, tinfo *types.Info) types.TypeAndValue { func makeBoolFromIdent(ident *ast.Ident, tinfo *types.Info) (types.TypeAndValue, error) {
var b bool var b bool
switch ident.Name { switch ident.Name {
case "true": case "true":
@ -77,12 +77,12 @@ func makeBoolFromIdent(ident *ast.Ident, tinfo *types.Info) types.TypeAndValue {
case "false": case "false":
b = false b = false
default: default:
log.Fatalf("givent identifier cannot be converted to a boolean => %s", ident.Name) return types.TypeAndValue{}, fmt.Errorf("givent identifier cannot be converted to a boolean => %s", ident.Name)
} }
return types.TypeAndValue{ return types.TypeAndValue{
Type: tinfo.ObjectOf(ident).Type(), Type: tinfo.ObjectOf(ident).Type(),
Value: constant.MakeBool(b), Value: constant.MakeBool(b),
} }, nil
} }
// resolveEntryPoint returns the function declaration of the entrypoint and the corresponding file. // resolveEntryPoint returns the function declaration of the entrypoint and the corresponding file.

View file

@ -1,18 +1,18 @@
package compiler package compiler
import ( import (
"bytes"
"encoding/binary" "encoding/binary"
"fmt"
"go/ast" "go/ast"
"go/constant" "go/constant"
"go/token" "go/token"
"go/types" "go/types"
"log"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
"github.com/CityOfZion/neo-go/pkg/crypto" "github.com/CityOfZion/neo-go/pkg/crypto"
"github.com/CityOfZion/neo-go/pkg/io"
"github.com/CityOfZion/neo-go/pkg/vm" "github.com/CityOfZion/neo-go/pkg/vm"
) )
@ -24,7 +24,7 @@ type codegen struct {
buildInfo *buildInfo buildInfo *buildInfo
// prog holds the output buffer. // prog holds the output buffer.
prog *bytes.Buffer prog *io.BufBinWriter
// Type information. // Type information.
typeInfo *types.Info typeInfo *types.Info
@ -56,66 +56,73 @@ func (c *codegen) pc() int {
} }
func (c *codegen) emitLoadConst(t types.TypeAndValue) { func (c *codegen) emitLoadConst(t types.TypeAndValue) {
if c.prog.Err != nil {
return
}
switch typ := t.Type.Underlying().(type) { switch typ := t.Type.Underlying().(type) {
case *types.Basic: case *types.Basic:
switch typ.Kind() { switch typ.Kind() {
case types.Int, types.UntypedInt, types.Uint: case types.Int, types.UntypedInt, types.Uint:
val, _ := constant.Int64Val(t.Value) val, _ := constant.Int64Val(t.Value)
emitInt(c.prog, val) emitInt(c.prog.BinWriter, val)
case types.String, types.UntypedString: case types.String, types.UntypedString:
val := constant.StringVal(t.Value) val := constant.StringVal(t.Value)
emitString(c.prog, val) emitString(c.prog.BinWriter, val)
case types.Bool, types.UntypedBool: case types.Bool, types.UntypedBool:
val := constant.BoolVal(t.Value) val := constant.BoolVal(t.Value)
emitBool(c.prog, val) emitBool(c.prog.BinWriter, val)
case types.Byte: case types.Byte:
val, _ := constant.Int64Val(t.Value) val, _ := constant.Int64Val(t.Value)
b := byte(val) b := byte(val)
emitBytes(c.prog, []byte{b}) emitBytes(c.prog.BinWriter, []byte{b})
default: default:
log.Fatalf("compiler doesn't know how to convert this basic type: %v", t) c.prog.Err = fmt.Errorf("compiler doesn't know how to convert this basic type: %v", t)
return
} }
default: default:
log.Fatalf("compiler doesn't know how to convert this constant: %v", t) c.prog.Err = fmt.Errorf("compiler doesn't know how to convert this constant: %v", t)
return
} }
} }
func (c *codegen) emitLoadLocal(name string) { func (c *codegen) emitLoadLocal(name string) {
pos := c.scope.loadLocal(name) pos := c.scope.loadLocal(name)
if pos < 0 { if pos < 0 {
log.Fatalf("cannot load local variable with position: %d", pos) c.prog.Err = fmt.Errorf("cannot load local variable with position: %d", pos)
return
} }
c.emitLoadLocalPos(pos) c.emitLoadLocalPos(pos)
} }
func (c *codegen) emitLoadLocalPos(pos int) { func (c *codegen) emitLoadLocalPos(pos int) {
emitOpcode(c.prog, vm.DUPFROMALTSTACK) emitOpcode(c.prog.BinWriter, vm.DUPFROMALTSTACK)
emitInt(c.prog, int64(pos)) emitInt(c.prog.BinWriter, int64(pos))
emitOpcode(c.prog, vm.PICKITEM) emitOpcode(c.prog.BinWriter, vm.PICKITEM)
} }
func (c *codegen) emitStoreLocal(pos int) { func (c *codegen) emitStoreLocal(pos int) {
emitOpcode(c.prog, vm.DUPFROMALTSTACK) emitOpcode(c.prog.BinWriter, vm.DUPFROMALTSTACK)
if pos < 0 { if pos < 0 {
log.Fatalf("invalid position to store local: %d", pos) c.prog.Err = fmt.Errorf("invalid position to store local: %d", pos)
return
} }
emitInt(c.prog, int64(pos)) emitInt(c.prog.BinWriter, int64(pos))
emitInt(c.prog, 2) emitInt(c.prog.BinWriter, 2)
emitOpcode(c.prog, vm.ROLL) emitOpcode(c.prog.BinWriter, vm.ROLL)
emitOpcode(c.prog, vm.SETITEM) emitOpcode(c.prog.BinWriter, vm.SETITEM)
} }
func (c *codegen) emitLoadField(i int) { func (c *codegen) emitLoadField(i int) {
emitInt(c.prog, int64(i)) emitInt(c.prog.BinWriter, int64(i))
emitOpcode(c.prog, vm.PICKITEM) emitOpcode(c.prog.BinWriter, vm.PICKITEM)
} }
func (c *codegen) emitStoreStructField(i int) { func (c *codegen) emitStoreStructField(i int) {
emitInt(c.prog, int64(i)) emitInt(c.prog.BinWriter, int64(i))
emitOpcode(c.prog, vm.ROT) emitOpcode(c.prog.BinWriter, vm.ROT)
emitOpcode(c.prog, vm.SETITEM) emitOpcode(c.prog.BinWriter, vm.SETITEM)
} }
// convertGlobals traverses the AST and only converts global declarations. // convertGlobals traverses the AST and only converts global declarations.
@ -155,9 +162,9 @@ func (c *codegen) convertFuncDecl(file ast.Node, decl *ast.FuncDecl) {
// All globals copied into the scope of the function need to be added // All globals copied into the scope of the function need to be added
// to the stack size of the function. // to the stack size of the function.
emitInt(c.prog, f.stackSize()+countGlobals(file)) emitInt(c.prog.BinWriter, f.stackSize()+countGlobals(file))
emitOpcode(c.prog, vm.NEWARRAY) emitOpcode(c.prog.BinWriter, vm.NEWARRAY)
emitOpcode(c.prog, vm.TOALTSTACK) emitOpcode(c.prog.BinWriter, vm.TOALTSTACK)
// We need to handle methods, which in Go, is just syntactic sugar. // We need to handle methods, which in Go, is just syntactic sugar.
// The method receiver will be passed in as first argument. // The method receiver will be passed in as first argument.
@ -171,7 +178,8 @@ func (c *codegen) convertFuncDecl(file ast.Node, decl *ast.FuncDecl) {
// Currently only method receives for struct types is supported. // Currently only method receives for struct types is supported.
_, ok := c.typeInfo.Defs[ident].Type().Underlying().(*types.Struct) _, ok := c.typeInfo.Defs[ident].Type().Underlying().(*types.Struct)
if !ok { if !ok {
log.Fatal("method receives for non-struct types is not yet supported") c.prog.Err = fmt.Errorf("method receives for non-struct types is not yet supported")
return
} }
l := c.scope.newLocal(ident.Name) l := c.scope.newLocal(ident.Name)
c.emitStoreLocal(l) c.emitStoreLocal(l)
@ -194,13 +202,16 @@ func (c *codegen) convertFuncDecl(file ast.Node, decl *ast.FuncDecl) {
// If this function returns the void (no return stmt) we will cleanup its junk on the stack. // If this function returns the void (no return stmt) we will cleanup its junk on the stack.
if !hasReturnStmt(decl) { if !hasReturnStmt(decl) {
emitOpcode(c.prog, vm.FROMALTSTACK) emitOpcode(c.prog.BinWriter, vm.FROMALTSTACK)
emitOpcode(c.prog, vm.DROP) emitOpcode(c.prog.BinWriter, vm.DROP)
emitOpcode(c.prog, vm.RET) emitOpcode(c.prog.BinWriter, vm.RET)
} }
} }
func (c *codegen) Visit(node ast.Node) ast.Visitor { func (c *codegen) Visit(node ast.Node) ast.Visitor {
if c.prog.Err != nil {
return nil
}
switch n := node.(type) { switch n := node.(type) {
// General declarations. // General declarations.
@ -248,7 +259,8 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
c.emitStoreStructField(i) // store the field c.emitStoreStructField(i) // store the field
} }
default: default:
log.Fatal("nested selector assigns not supported yet") c.prog.Err = fmt.Errorf("nested selector assigns not supported yet")
return nil
} }
// Assignments to index expressions. // Assignments to index expressions.
@ -262,7 +274,8 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
indexStr := t.Index.(*ast.BasicLit).Value indexStr := t.Index.(*ast.BasicLit).Value
index, err := strconv.Atoi(indexStr) index, err := strconv.Atoi(indexStr)
if err != nil { if err != nil {
log.Fatal("failed to convert slice index to integer") c.prog.Err = fmt.Errorf("failed to convert slice index to integer")
return nil
} }
c.emitStoreStructField(index) c.emitStoreStructField(index)
} }
@ -271,7 +284,8 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
case *ast.ReturnStmt: case *ast.ReturnStmt:
if len(n.Results) > 1 { if len(n.Results) > 1 {
log.Fatal("multiple returns not supported.") c.prog.Err = fmt.Errorf("multiple returns not supported")
return nil
} }
l := c.newLabel() l := c.newLabel()
@ -281,9 +295,9 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
ast.Walk(c, n.Results[0]) ast.Walk(c, n.Results[0])
} }
emitOpcode(c.prog, vm.FROMALTSTACK) emitOpcode(c.prog.BinWriter, vm.FROMALTSTACK)
emitOpcode(c.prog, vm.DROP) // Cleanup the stack. emitOpcode(c.prog.BinWriter, vm.DROP) // Cleanup the stack.
emitOpcode(c.prog, vm.RET) emitOpcode(c.prog.BinWriter, vm.RET)
return nil return nil
case *ast.IfStmt: case *ast.IfStmt:
@ -293,13 +307,13 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
if n.Cond != nil { if n.Cond != nil {
ast.Walk(c, n.Cond) ast.Walk(c, n.Cond)
emitJmp(c.prog, vm.JMPIFNOT, int16(lElse)) emitJmp(c.prog.BinWriter, vm.JMPIFNOT, int16(lElse))
} }
c.setLabel(lIf) c.setLabel(lIf)
ast.Walk(c, n.Body) ast.Walk(c, n.Body)
if n.Else != nil { if n.Else != nil {
emitJmp(c.prog, vm.JMP, int16(lElseEnd)) emitJmp(c.prog.BinWriter, vm.JMP, int16(lElseEnd))
} }
c.setLabel(lElse) c.setLabel(lElse)
@ -315,7 +329,12 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
case *ast.Ident: case *ast.Ident:
if isIdentBool(n) { if isIdentBool(n) {
c.emitLoadConst(makeBoolFromIdent(n, c.typeInfo)) value, err := makeBoolFromIdent(n, c.typeInfo)
if err != nil {
c.prog.Err = err
return nil
}
c.emitLoadConst(value)
} else { } else {
c.emitLoadLocal(n.Name) c.emitLoadLocal(n.Name)
} }
@ -339,8 +358,8 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
for i := ln - 1; i >= 0; i-- { for i := ln - 1; i >= 0; i-- {
c.emitLoadConst(c.typeInfo.Types[n.Elts[i]]) c.emitLoadConst(c.typeInfo.Types[n.Elts[i]])
} }
emitInt(c.prog, int64(ln)) emitInt(c.prog.BinWriter, int64(ln))
emitOpcode(c.prog, vm.PACK) emitOpcode(c.prog.BinWriter, vm.PACK)
return nil return nil
} }
@ -355,13 +374,13 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
switch n.Op { switch n.Op {
case token.LAND: case token.LAND:
ast.Walk(c, n.X) ast.Walk(c, n.X)
emitJmp(c.prog, vm.JMPIFNOT, int16(len(c.l)-1)) emitJmp(c.prog.BinWriter, vm.JMPIFNOT, int16(len(c.l)-1))
ast.Walk(c, n.Y) ast.Walk(c, n.Y)
return nil return nil
case token.LOR: case token.LOR:
ast.Walk(c, n.X) ast.Walk(c, n.X)
emitJmp(c.prog, vm.JMPIF, int16(len(c.l)-3)) emitJmp(c.prog.BinWriter, vm.JMPIF, int16(len(c.l)-3))
ast.Walk(c, n.Y) ast.Walk(c, n.Y)
return nil return nil
@ -386,24 +405,24 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
case n.Op == token.ADD: case n.Op == token.ADD:
// VM has separate opcodes for number and string concatenation // VM has separate opcodes for number and string concatenation
if isStringType(tinfo.Type) { if isStringType(tinfo.Type) {
emitOpcode(c.prog, vm.CAT) emitOpcode(c.prog.BinWriter, vm.CAT)
} else { } else {
emitOpcode(c.prog, vm.ADD) emitOpcode(c.prog.BinWriter, vm.ADD)
} }
case n.Op == token.EQL: case n.Op == token.EQL:
// VM has separate opcodes for number and string equality // VM has separate opcodes for number and string equality
if isStringType(c.typeInfo.Types[n.X].Type) { if isStringType(c.typeInfo.Types[n.X].Type) {
emitOpcode(c.prog, vm.EQUAL) emitOpcode(c.prog.BinWriter, vm.EQUAL)
} else { } else {
emitOpcode(c.prog, vm.NUMEQUAL) emitOpcode(c.prog.BinWriter, vm.NUMEQUAL)
} }
case n.Op == token.NEQ: case n.Op == token.NEQ:
// VM has separate opcodes for number and string equality // VM has separate opcodes for number and string equality
if isStringType(c.typeInfo.Types[n.X].Type) { if isStringType(c.typeInfo.Types[n.X].Type) {
emitOpcode(c.prog, vm.EQUAL) emitOpcode(c.prog.BinWriter, vm.EQUAL)
emitOpcode(c.prog, vm.NOT) emitOpcode(c.prog.BinWriter, vm.NOT)
} else { } else {
emitOpcode(c.prog, vm.NUMNOTEQUAL) emitOpcode(c.prog.BinWriter, vm.NUMNOTEQUAL)
} }
default: default:
c.convertToken(n.Op) c.convertToken(n.Op)
@ -423,7 +442,8 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
case *ast.Ident: case *ast.Ident:
f, ok = c.funcs[fun.Name] f, ok = c.funcs[fun.Name]
if !ok && !isBuiltin { if !ok && !isBuiltin {
log.Fatalf("could not resolve function %s", fun.Name) c.prog.Err = fmt.Errorf("could not resolve function %s", fun.Name)
return nil
} }
case *ast.SelectorExpr: case *ast.SelectorExpr:
// If this is a method call we need to walk the AST to load the struct locally. // If this is a method call we need to walk the AST to load the struct locally.
@ -439,7 +459,8 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
// @FIXME this could cause runtime errors. // @FIXME this could cause runtime errors.
f.selector = fun.X.(*ast.Ident) f.selector = fun.X.(*ast.Ident)
if !ok { if !ok {
log.Fatalf("could not resolve function %s", fun.Sel.Name) c.prog.Err = fmt.Errorf("could not resolve function %s", fun.Sel.Name)
return nil
} }
case *ast.ArrayType: case *ast.ArrayType:
// For now we will assume that there is only 1 argument passed which // For now we will assume that there is only 1 argument passed which
@ -457,14 +478,14 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
// Do not swap for builtin functions. // Do not swap for builtin functions.
if !isBuiltin { if !isBuiltin {
if numArgs == 2 { if numArgs == 2 {
emitOpcode(c.prog, vm.SWAP) emitOpcode(c.prog.BinWriter, vm.SWAP)
} else if numArgs == 3 { } else if numArgs == 3 {
emitInt(c.prog, 2) emitInt(c.prog.BinWriter, 2)
emitOpcode(c.prog, vm.XSWAP) emitOpcode(c.prog.BinWriter, vm.XSWAP)
} else { } else {
for i := 1; i < numArgs; i++ { for i := 1; i < numArgs; i++ {
emitInt(c.prog, int64(i)) emitInt(c.prog.BinWriter, int64(i))
emitOpcode(c.prog, vm.ROLL) emitOpcode(c.prog.BinWriter, vm.ROLL)
} }
} }
} }
@ -478,7 +499,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
case isSyscall(f): case isSyscall(f):
c.convertSyscall(f.selector.Name, f.name) c.convertSyscall(f.selector.Name, f.name)
default: default:
emitCall(c.prog, vm.CALL, int16(f.label)) emitCall(c.prog.BinWriter, vm.CALL, int16(f.label))
} }
return nil return nil
@ -493,7 +514,8 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
c.emitLoadField(i) // load the field c.emitLoadField(i) // load the field
} }
default: default:
log.Fatal("nested selectors not supported yet") c.prog.Err = fmt.Errorf("nested selectors not supported yet")
return nil
} }
return nil return nil
@ -507,13 +529,14 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
case token.ADD: case token.ADD:
// +10 == 10, no need to do anything in this case // +10 == 10, no need to do anything in this case
case token.SUB: case token.SUB:
emitOpcode(c.prog, vm.NEGATE) emitOpcode(c.prog.BinWriter, vm.NEGATE)
case token.NOT: case token.NOT:
emitOpcode(c.prog, vm.NOT) emitOpcode(c.prog.BinWriter, vm.NOT)
case token.XOR: case token.XOR:
emitOpcode(c.prog, vm.INVERT) emitOpcode(c.prog.BinWriter, vm.INVERT)
default: default:
log.Fatalf("invalid unary operator: %s", n.Op) c.prog.Err = fmt.Errorf("invalid unary operator: %s", n.Op)
return nil
} }
return nil return nil
@ -542,7 +565,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
c.emitLoadField(int(val)) c.emitLoadField(int(val))
default: default:
ast.Walk(c, n.Index) ast.Walk(c, n.Index)
emitOpcode(c.prog, vm.PICKITEM) // just pickitem here emitOpcode(c.prog.BinWriter, vm.PICKITEM) // just pickitem here
} }
return nil return nil
@ -560,14 +583,14 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
ast.Walk(c, n.Cond) ast.Walk(c, n.Cond)
// Jump if the condition is false // Jump if the condition is false
emitJmp(c.prog, vm.JMPIFNOT, int16(fend)) emitJmp(c.prog.BinWriter, vm.JMPIFNOT, int16(fend))
// Walk body followed by the iterator (post stmt). // Walk body followed by the iterator (post stmt).
ast.Walk(c, n.Body) ast.Walk(c, n.Body)
ast.Walk(c, n.Post) ast.Walk(c, n.Post)
// Jump back to condition. // Jump back to condition.
emitJmp(c.prog, vm.JMP, int16(fstart)) emitJmp(c.prog.BinWriter, vm.JMP, int16(fstart))
c.setLabel(fend) c.setLabel(fend)
return nil return nil
@ -586,13 +609,14 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
func (c *codegen) convertSyscall(api, name string) { func (c *codegen) convertSyscall(api, name string) {
api, ok := syscalls[api][name] api, ok := syscalls[api][name]
if !ok { if !ok {
log.Fatalf("unknown VM syscall api: %s", name) c.prog.Err = fmt.Errorf("unknown VM syscall api: %s", name)
return
} }
emitSyscall(c.prog, api) emitSyscall(c.prog.BinWriter, api)
// This NOP instruction is basically not needed, but if we do, we have a // This NOP instruction is basically not needed, but if we do, we have a
// one to one matching avm file with neo-python which is very nice for debugging. // one to one matching avm file with neo-python which is very nice for debugging.
emitOpcode(c.prog, vm.NOP) emitOpcode(c.prog.BinWriter, vm.NOP)
} }
func (c *codegen) convertBuiltin(expr *ast.CallExpr) { func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
@ -609,32 +633,32 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
arg := expr.Args[0] arg := expr.Args[0]
typ := c.typeInfo.Types[arg].Type typ := c.typeInfo.Types[arg].Type
if isStringType(typ) { if isStringType(typ) {
emitOpcode(c.prog, vm.SIZE) emitOpcode(c.prog.BinWriter, vm.SIZE)
} else { } else {
emitOpcode(c.prog, vm.ARRAYSIZE) emitOpcode(c.prog.BinWriter, vm.ARRAYSIZE)
} }
case "append": case "append":
arg := expr.Args[0] arg := expr.Args[0]
typ := c.typeInfo.Types[arg].Type typ := c.typeInfo.Types[arg].Type
if isByteArrayType(typ) { if isByteArrayType(typ) {
emitOpcode(c.prog, vm.CAT) emitOpcode(c.prog.BinWriter, vm.CAT)
} else { } else {
emitOpcode(c.prog, vm.SWAP) emitOpcode(c.prog.BinWriter, vm.SWAP)
emitOpcode(c.prog, vm.DUP) emitOpcode(c.prog.BinWriter, vm.DUP)
emitOpcode(c.prog, vm.PUSH2) emitOpcode(c.prog.BinWriter, vm.PUSH2)
emitOpcode(c.prog, vm.XSWAP) emitOpcode(c.prog.BinWriter, vm.XSWAP)
emitOpcode(c.prog, vm.APPEND) emitOpcode(c.prog.BinWriter, vm.APPEND)
} }
case "SHA256": case "SHA256":
emitOpcode(c.prog, vm.SHA256) emitOpcode(c.prog.BinWriter, vm.SHA256)
case "SHA1": case "SHA1":
emitOpcode(c.prog, vm.SHA1) emitOpcode(c.prog.BinWriter, vm.SHA1)
case "Hash256": case "Hash256":
emitOpcode(c.prog, vm.HASH256) emitOpcode(c.prog.BinWriter, vm.HASH256)
case "Hash160": case "Hash160":
emitOpcode(c.prog, vm.HASH160) emitOpcode(c.prog.BinWriter, vm.HASH160)
case "Equals": case "Equals":
emitOpcode(c.prog, vm.EQUAL) emitOpcode(c.prog.BinWriter, vm.EQUAL)
case "FromAddress": case "FromAddress":
// We can be sure that this is a ast.BasicLit just containing a simple // We can be sure that this is a ast.BasicLit just containing a simple
// address string. Note that the string returned from calling Value will // address string. Note that the string returned from calling Value will
@ -643,10 +667,11 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
addressStr = strings.Replace(addressStr, "\"", "", 2) addressStr = strings.Replace(addressStr, "\"", "", 2)
uint160, err := crypto.Uint160DecodeAddress(addressStr) uint160, err := crypto.Uint160DecodeAddress(addressStr)
if err != nil { if err != nil {
log.Fatal(err) c.prog.Err = err
return
} }
bytes := uint160.Bytes() bytes := uint160.Bytes()
emitBytes(c.prog, bytes) emitBytes(c.prog.BinWriter, bytes)
} }
} }
@ -657,7 +682,7 @@ func (c *codegen) convertByteArray(lit *ast.CompositeLit) {
val, _ := constant.Int64Val(t.Value) val, _ := constant.Int64Val(t.Value)
buf[i] = byte(val) buf[i] = byte(val)
} }
emitBytes(c.prog, buf) emitBytes(c.prog.BinWriter, buf)
} }
func (c *codegen) convertStruct(lit *ast.CompositeLit) { func (c *codegen) convertStruct(lit *ast.CompositeLit) {
@ -665,13 +690,14 @@ func (c *codegen) convertStruct(lit *ast.CompositeLit) {
// the positions of its variables. // the positions of its variables.
strct, ok := c.typeInfo.TypeOf(lit).Underlying().(*types.Struct) strct, ok := c.typeInfo.TypeOf(lit).Underlying().(*types.Struct)
if !ok { if !ok {
log.Fatalf("the given literal is not of type struct: %v", lit) c.prog.Err = fmt.Errorf("the given literal is not of type struct: %v", lit)
return
} }
emitOpcode(c.prog, vm.NOP) emitOpcode(c.prog.BinWriter, vm.NOP)
emitInt(c.prog, int64(strct.NumFields())) emitInt(c.prog.BinWriter, int64(strct.NumFields()))
emitOpcode(c.prog, vm.NEWSTRUCT) emitOpcode(c.prog.BinWriter, vm.NEWSTRUCT)
emitOpcode(c.prog, vm.TOALTSTACK) emitOpcode(c.prog.BinWriter, vm.TOALTSTACK)
// We need to locally store all the fields, even if they are not initialized. // We need to locally store all the fields, even if they are not initialized.
// We will initialize all fields to their "zero" value. // We will initialize all fields to their "zero" value.
@ -696,61 +722,66 @@ func (c *codegen) convertStruct(lit *ast.CompositeLit) {
continue continue
} }
typeAndVal := typeAndValueForField(sField) typeAndVal, err := typeAndValueForField(sField)
if err != nil {
c.prog.Err = err
return
}
c.emitLoadConst(typeAndVal) c.emitLoadConst(typeAndVal)
c.emitStoreLocal(i) c.emitStoreLocal(i)
} }
emitOpcode(c.prog, vm.FROMALTSTACK) emitOpcode(c.prog.BinWriter, vm.FROMALTSTACK)
} }
func (c *codegen) convertToken(tok token.Token) { func (c *codegen) convertToken(tok token.Token) {
switch tok { switch tok {
case token.ADD_ASSIGN: case token.ADD_ASSIGN:
emitOpcode(c.prog, vm.ADD) emitOpcode(c.prog.BinWriter, vm.ADD)
case token.SUB_ASSIGN: case token.SUB_ASSIGN:
emitOpcode(c.prog, vm.SUB) emitOpcode(c.prog.BinWriter, vm.SUB)
case token.MUL_ASSIGN: case token.MUL_ASSIGN:
emitOpcode(c.prog, vm.MUL) emitOpcode(c.prog.BinWriter, vm.MUL)
case token.QUO_ASSIGN: case token.QUO_ASSIGN:
emitOpcode(c.prog, vm.DIV) emitOpcode(c.prog.BinWriter, vm.DIV)
case token.ADD: case token.ADD:
emitOpcode(c.prog, vm.ADD) emitOpcode(c.prog.BinWriter, vm.ADD)
case token.SUB: case token.SUB:
emitOpcode(c.prog, vm.SUB) emitOpcode(c.prog.BinWriter, vm.SUB)
case token.MUL: case token.MUL:
emitOpcode(c.prog, vm.MUL) emitOpcode(c.prog.BinWriter, vm.MUL)
case token.QUO: case token.QUO:
emitOpcode(c.prog, vm.DIV) emitOpcode(c.prog.BinWriter, vm.DIV)
case token.LSS: case token.LSS:
emitOpcode(c.prog, vm.LT) emitOpcode(c.prog.BinWriter, vm.LT)
case token.LEQ: case token.LEQ:
emitOpcode(c.prog, vm.LTE) emitOpcode(c.prog.BinWriter, vm.LTE)
case token.GTR: case token.GTR:
emitOpcode(c.prog, vm.GT) emitOpcode(c.prog.BinWriter, vm.GT)
case token.GEQ: case token.GEQ:
emitOpcode(c.prog, vm.GTE) emitOpcode(c.prog.BinWriter, vm.GTE)
case token.EQL: case token.EQL:
emitOpcode(c.prog, vm.NUMEQUAL) emitOpcode(c.prog.BinWriter, vm.NUMEQUAL)
case token.NEQ: case token.NEQ:
emitOpcode(c.prog, vm.NUMNOTEQUAL) emitOpcode(c.prog.BinWriter, vm.NUMNOTEQUAL)
case token.DEC: case token.DEC:
emitOpcode(c.prog, vm.DEC) emitOpcode(c.prog.BinWriter, vm.DEC)
case token.INC: case token.INC:
emitOpcode(c.prog, vm.INC) emitOpcode(c.prog.BinWriter, vm.INC)
case token.NOT: case token.NOT:
emitOpcode(c.prog, vm.NOT) emitOpcode(c.prog.BinWriter, vm.NOT)
case token.AND: case token.AND:
emitOpcode(c.prog, vm.AND) emitOpcode(c.prog.BinWriter, vm.AND)
case token.OR: case token.OR:
emitOpcode(c.prog, vm.OR) emitOpcode(c.prog.BinWriter, vm.OR)
case token.SHL: case token.SHL:
emitOpcode(c.prog, vm.SHL) emitOpcode(c.prog.BinWriter, vm.SHL)
case token.SHR: case token.SHR:
emitOpcode(c.prog, vm.SHR) emitOpcode(c.prog.BinWriter, vm.SHR)
case token.XOR: case token.XOR:
emitOpcode(c.prog, vm.XOR) emitOpcode(c.prog.BinWriter, vm.XOR)
default: default:
log.Fatalf("compiler could not convert token: %s", tok) c.prog.Err = fmt.Errorf("compiler could not convert token: %s", tok)
return
} }
} }
@ -761,11 +792,11 @@ func (c *codegen) newFunc(decl *ast.FuncDecl) *funcScope {
} }
// CodeGen compiles the program to bytecode. // CodeGen compiles the program to bytecode.
func CodeGen(info *buildInfo) (*bytes.Buffer, error) { func CodeGen(info *buildInfo) ([]byte, error) {
pkg := info.program.Package(info.initialPackage) pkg := info.program.Package(info.initialPackage)
c := &codegen{ c := &codegen{
buildInfo: info, buildInfo: info,
prog: new(bytes.Buffer), prog: io.NewBufBinWriter(),
l: []int{}, l: []int{},
funcs: map[string]*funcScope{}, funcs: map[string]*funcScope{},
typeInfo: &pkg.Info, typeInfo: &pkg.Info,
@ -774,7 +805,8 @@ func CodeGen(info *buildInfo) (*bytes.Buffer, error) {
// Resolve the entrypoint of the program. // Resolve the entrypoint of the program.
main, mainFile := resolveEntryPoint(mainIdent, pkg) main, mainFile := resolveEntryPoint(mainIdent, pkg)
if main == nil { if main == nil {
log.Fatal("could not find func main. Did you forget to declare it?") c.prog.Err = fmt.Errorf("could not find func main. Did you forget to declare it? ")
return []byte{}, c.prog.Err
} }
funUsage := analyzeFuncUsage(info.program.AllPackages) funUsage := analyzeFuncUsage(info.program.AllPackages)
@ -815,9 +847,12 @@ func CodeGen(info *buildInfo) (*bytes.Buffer, error) {
} }
} }
c.writeJumps() if c.prog.Err != nil {
return nil, c.prog.Err
return c.prog, nil }
buf := c.prog.Bytes()
c.writeJumps(buf)
return buf, nil
} }
func (c *codegen) resolveFuncDecls(f *ast.File) { func (c *codegen) resolveFuncDecls(f *ast.File) {
@ -831,8 +866,7 @@ func (c *codegen) resolveFuncDecls(f *ast.File) {
} }
} }
func (c *codegen) writeJumps() { func (c *codegen) writeJumps(b []byte) {
b := c.prog.Bytes()
for i, op := range b { for i, op := range b {
j := i + 1 j := i + 1
switch vm.Instruction(op) { switch vm.Instruction(op) {

View file

@ -2,7 +2,6 @@ package compiler
import ( import (
"bytes" "bytes"
"encoding/hex"
"fmt" "fmt"
"go/ast" "go/ast"
"go/build" "go/build"
@ -10,7 +9,6 @@ import (
"go/types" "go/types"
"io" "io"
"io/ioutil" "io/ioutil"
"log"
"os" "os"
"strings" "strings"
@ -60,7 +58,7 @@ func Compile(r io.Reader) ([]byte, error) {
return nil, err return nil, err
} }
return buf.Bytes(), nil return buf, nil
} }
type archive struct { type archive struct {
@ -69,9 +67,9 @@ type archive struct {
} }
// CompileAndSave will compile and save the file to disk. // CompileAndSave will compile and save the file to disk.
func CompileAndSave(src string, o *Options) error { func CompileAndSave(src string, o *Options) ([]byte, error) {
if !strings.HasSuffix(src, ".go") { if !strings.HasSuffix(src, ".go") {
return fmt.Errorf("%s is not a Go file", src) return nil, fmt.Errorf("%s is not a Go file", src)
} }
o.Outfile = strings.TrimSuffix(o.Outfile, fmt.Sprintf(".%s", fileExt)) o.Outfile = strings.TrimSuffix(o.Outfile, fmt.Sprintf(".%s", fileExt))
if len(o.Outfile) == 0 { if len(o.Outfile) == 0 {
@ -82,17 +80,15 @@ func CompileAndSave(src string, o *Options) error {
} }
b, err := ioutil.ReadFile(src) b, err := ioutil.ReadFile(src)
if err != nil { if err != nil {
return err return nil, err
} }
b, err = Compile(bytes.NewReader(b)) b, err = Compile(bytes.NewReader(b))
if err != nil { if err != nil {
return fmt.Errorf("error while trying to compile smart contract file: %v", err) return nil, fmt.Errorf("error while trying to compile smart contract file: %v", err)
} }
log.Println(hex.EncodeToString(b))
out := fmt.Sprintf("%s.%s", o.Outfile, o.Ext) out := fmt.Sprintf("%s.%s", o.Outfile, o.Ext)
return ioutil.WriteFile(out, b, os.ModePerm) return b, ioutil.WriteFile(out, b, os.ModePerm)
} }
func gopath() string { func gopath() string {
@ -102,7 +98,3 @@ func gopath() string {
} }
return gopath return gopath
} }
func init() {
log.SetFlags(0)
}

View file

@ -1,105 +1,114 @@
package compiler package compiler
import ( import (
"bytes"
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt" "fmt"
"io"
"math/big" "math/big"
"github.com/CityOfZion/neo-go/pkg/io"
"github.com/CityOfZion/neo-go/pkg/util" "github.com/CityOfZion/neo-go/pkg/util"
"github.com/CityOfZion/neo-go/pkg/vm" "github.com/CityOfZion/neo-go/pkg/vm"
) )
func emit(w *bytes.Buffer, instr vm.Instruction, b []byte) error { // emit a VM Instruction with data to the given buffer.
if err := w.WriteByte(byte(instr)); err != nil { func emit(w *io.BinWriter, instr vm.Instruction, b []byte) {
return err emitOpcode(w, instr)
} w.WriteBytes(b)
_, err := w.Write(b)
return err
} }
func emitOpcode(w io.ByteWriter, instr vm.Instruction) error { // emitOpcode emits a single VM Instruction the given buffer.
return w.WriteByte(byte(instr)) func emitOpcode(w *io.BinWriter, instr vm.Instruction) {
w.WriteLE(byte(instr))
} }
func emitBool(w io.ByteWriter, ok bool) error { // emitBool emits a bool type the given buffer.
func emitBool(w *io.BinWriter, ok bool) {
if ok { if ok {
return emitOpcode(w, vm.PUSHT) emitOpcode(w, vm.PUSHT)
return
} }
return emitOpcode(w, vm.PUSHF) emitOpcode(w, vm.PUSHF)
} }
func emitInt(w *bytes.Buffer, i int64) error { // emitInt emits a int type to the given buffer.
if i == -1 { func emitInt(w *io.BinWriter, i int64) {
return emitOpcode(w, vm.PUSHM1) switch {
} case i == -1:
if i == 0 { emitOpcode(w, vm.PUSHM1)
return emitOpcode(w, vm.PUSHF) return
} case i == 0:
if i > 0 && i < 16 { emitOpcode(w, vm.PUSHF)
return
case i > 0 && i < 16:
val := vm.Instruction(int(vm.PUSH1) - 1 + int(i)) val := vm.Instruction(int(vm.PUSH1) - 1 + int(i))
return emitOpcode(w, val) emitOpcode(w, val)
return
} }
bInt := big.NewInt(i) bInt := big.NewInt(i)
val := util.ArrayReverse(bInt.Bytes()) val := util.ArrayReverse(bInt.Bytes())
return emitBytes(w, val) emitBytes(w, val)
} }
func emitString(w *bytes.Buffer, s string) error { // emitString emits a string to the given buffer.
return emitBytes(w, []byte(s)) func emitString(w *io.BinWriter, s string) {
emitBytes(w, []byte(s))
} }
func emitBytes(w *bytes.Buffer, b []byte) error { // emitBytes emits a byte array to the given buffer.
var ( func emitBytes(w *io.BinWriter, b []byte) {
err error n := len(b)
n = len(b)
)
switch { switch {
case n <= int(vm.PUSHBYTES75): case n <= int(vm.PUSHBYTES75):
return emit(w, vm.Instruction(n), b) emit(w, vm.Instruction(n), b)
return
case n < 0x100: case n < 0x100:
err = emit(w, vm.PUSHDATA1, []byte{byte(n)}) emit(w, vm.PUSHDATA1, []byte{byte(n)})
case n < 0x10000: case n < 0x10000:
buf := make([]byte, 2) buf := make([]byte, 2)
binary.LittleEndian.PutUint16(buf, uint16(n)) binary.LittleEndian.PutUint16(buf, uint16(n))
err = emit(w, vm.PUSHDATA2, buf) emit(w, vm.PUSHDATA2, buf)
default: default:
buf := make([]byte, 4) buf := make([]byte, 4)
binary.LittleEndian.PutUint32(buf, uint32(n)) binary.LittleEndian.PutUint32(buf, uint32(n))
err = emit(w, vm.PUSHDATA4, buf) emit(w, vm.PUSHDATA4, buf)
if w.Err != nil {
return
}
} }
if err != nil {
return err w.WriteBytes(b)
}
_, err = w.Write(b)
return err
} }
func emitSyscall(w *bytes.Buffer, api string) error { // emitSyscall emits the syscall API to the given buffer.
// Syscall API string cannot be 0.
func emitSyscall(w *io.BinWriter, api string) {
if len(api) == 0 { if len(api) == 0 {
return errors.New("syscall api cannot be of length 0") w.Err = errors.New("syscall api cannot be of length 0")
return
} }
buf := make([]byte, len(api)+1) buf := make([]byte, len(api)+1)
buf[0] = byte(len(api)) buf[0] = byte(len(api))
copy(buf[1:], api) copy(buf[1:], api)
return emit(w, vm.SYSCALL, buf) emit(w, vm.SYSCALL, buf)
} }
func emitCall(w *bytes.Buffer, instr vm.Instruction, label int16) error { // emitCall emits a call Instruction with label to the given buffer.
return emitJmp(w, instr, label) func emitCall(w *io.BinWriter, instr vm.Instruction, label int16) {
emitJmp(w, instr, label)
} }
func emitJmp(w *bytes.Buffer, instr vm.Instruction, label int16) error { // emitJmp emits a jump Instruction along with label to the given buffer.
func emitJmp(w *io.BinWriter, instr vm.Instruction, label int16) {
if !isInstrJmp(instr) { if !isInstrJmp(instr) {
return fmt.Errorf("opcode %s is not a jump or call type", instr) w.Err = fmt.Errorf("opcode %s is not a jump or call type", instr)
return
} }
buf := make([]byte, 2) buf := make([]byte, 2)
binary.LittleEndian.PutUint16(buf, uint16(label)) binary.LittleEndian.PutUint16(buf, uint16(label))
return emit(w, instr, buf) emit(w, instr, buf)
} }
func isInstrJmp(instr vm.Instruction) bool { func isInstrJmp(instr vm.Instruction) bool {

View file

@ -44,13 +44,13 @@ func serializeItemTo(item StackItem, w *io.BinWriter, seen map[StackItem]bool) {
switch t := item.(type) { switch t := item.(type) {
case *ByteArrayItem: case *ByteArrayItem:
w.WriteLE(byte(byteArrayT)) w.WriteLE(byte(byteArrayT))
w.WriteBytes(t.value) w.WriteVarBytes(t.value)
case *BoolItem: case *BoolItem:
w.WriteLE(byte(booleanT)) w.WriteLE(byte(booleanT))
w.WriteLE(t.value) w.WriteLE(t.value)
case *BigIntegerItem: case *BigIntegerItem:
w.WriteLE(byte(integerT)) w.WriteLE(byte(integerT))
w.WriteBytes(t.Bytes()) w.WriteVarBytes(t.Bytes())
case *InteropItem: case *InteropItem:
w.Err = errors.New("not supported") w.Err = errors.New("not supported")
case *ArrayItem, *StructItem: case *ArrayItem, *StructItem: