From b08387efdb2ac8680f4e1dd2d4350a88de4bb480 Mon Sep 17 00:00:00 2001 From: Vsevolod Brekelov Date: Mon, 2 Dec 2019 13:20:16 +0300 Subject: [PATCH] vm: use BinWriter instead of Buffered version In the future we could reuse emit interface without buf better --- pkg/vm/compiler/codegen.go | 190 ++++++++++++++++++------------------- pkg/vm/compiler/emit.go | 18 ++-- 2 files changed, 104 insertions(+), 104 deletions(-) diff --git a/pkg/vm/compiler/codegen.go b/pkg/vm/compiler/codegen.go index 2d675e330..c850b8dbc 100644 --- a/pkg/vm/compiler/codegen.go +++ b/pkg/vm/compiler/codegen.go @@ -64,17 +64,17 @@ func (c *codegen) emitLoadConst(t types.TypeAndValue) { switch typ.Kind() { case types.Int, types.UntypedInt, types.Uint: val, _ := constant.Int64Val(t.Value) - emitInt(c.prog, val) + emitInt(c.prog.BinWriter, val) case types.String, types.UntypedString: val := constant.StringVal(t.Value) - emitString(c.prog, val) + emitString(c.prog.BinWriter, val) case types.Bool, types.UntypedBool: val := constant.BoolVal(t.Value) - emitBool(c.prog, val) + emitBool(c.prog.BinWriter, val) case types.Byte: val, _ := constant.Int64Val(t.Value) b := byte(val) - emitBytes(c.prog, []byte{b}) + emitBytes(c.prog.BinWriter, []byte{b}) default: c.prog.Err = fmt.Errorf("compiler doesn't know how to convert this basic type: %v", t) return @@ -95,34 +95,34 @@ func (c *codegen) emitLoadLocal(name string) { } func (c *codegen) emitLoadLocalPos(pos int) { - emitOpcode(c.prog, vm.DUPFROMALTSTACK) - emitInt(c.prog, int64(pos)) - emitOpcode(c.prog, vm.PICKITEM) + emitOpcode(c.prog.BinWriter, vm.DUPFROMALTSTACK) + emitInt(c.prog.BinWriter, int64(pos)) + emitOpcode(c.prog.BinWriter, vm.PICKITEM) } func (c *codegen) emitStoreLocal(pos int) { - emitOpcode(c.prog, vm.DUPFROMALTSTACK) + emitOpcode(c.prog.BinWriter, vm.DUPFROMALTSTACK) if pos < 0 { c.prog.Err = fmt.Errorf("invalid position to store local: %d", pos) return } - emitInt(c.prog, int64(pos)) - emitInt(c.prog, 2) - emitOpcode(c.prog, vm.ROLL) - emitOpcode(c.prog, vm.SETITEM) + emitInt(c.prog.BinWriter, int64(pos)) + emitInt(c.prog.BinWriter, 2) + emitOpcode(c.prog.BinWriter, vm.ROLL) + emitOpcode(c.prog.BinWriter, vm.SETITEM) } func (c *codegen) emitLoadField(i int) { - emitInt(c.prog, int64(i)) - emitOpcode(c.prog, vm.PICKITEM) + emitInt(c.prog.BinWriter, int64(i)) + emitOpcode(c.prog.BinWriter, vm.PICKITEM) } func (c *codegen) emitStoreStructField(i int) { - emitInt(c.prog, int64(i)) - emitOpcode(c.prog, vm.ROT) - emitOpcode(c.prog, vm.SETITEM) + emitInt(c.prog.BinWriter, int64(i)) + emitOpcode(c.prog.BinWriter, vm.ROT) + emitOpcode(c.prog.BinWriter, vm.SETITEM) } // convertGlobals traverses the AST and only converts global declarations. @@ -162,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 // to the stack size of the function. - emitInt(c.prog, f.stackSize()+countGlobals(file)) - emitOpcode(c.prog, vm.NEWARRAY) - emitOpcode(c.prog, vm.TOALTSTACK) + emitInt(c.prog.BinWriter, f.stackSize()+countGlobals(file)) + emitOpcode(c.prog.BinWriter, vm.NEWARRAY) + emitOpcode(c.prog.BinWriter, vm.TOALTSTACK) // We need to handle methods, which in Go, is just syntactic sugar. // The method receiver will be passed in as first argument. @@ -202,9 +202,9 @@ 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 !hasReturnStmt(decl) { - emitOpcode(c.prog, vm.FROMALTSTACK) - emitOpcode(c.prog, vm.DROP) - emitOpcode(c.prog, vm.RET) + emitOpcode(c.prog.BinWriter, vm.FROMALTSTACK) + emitOpcode(c.prog.BinWriter, vm.DROP) + emitOpcode(c.prog.BinWriter, vm.RET) } } @@ -295,9 +295,9 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { ast.Walk(c, n.Results[0]) } - emitOpcode(c.prog, vm.FROMALTSTACK) - emitOpcode(c.prog, vm.DROP) // Cleanup the stack. - emitOpcode(c.prog, vm.RET) + emitOpcode(c.prog.BinWriter, vm.FROMALTSTACK) + emitOpcode(c.prog.BinWriter, vm.DROP) // Cleanup the stack. + emitOpcode(c.prog.BinWriter, vm.RET) return nil case *ast.IfStmt: @@ -307,13 +307,13 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { if n.Cond != nil { ast.Walk(c, n.Cond) - emitJmp(c.prog, vm.JMPIFNOT, int16(lElse)) + emitJmp(c.prog.BinWriter, vm.JMPIFNOT, int16(lElse)) } c.setLabel(lIf) ast.Walk(c, n.Body) if n.Else != nil { - emitJmp(c.prog, vm.JMP, int16(lElseEnd)) + emitJmp(c.prog.BinWriter, vm.JMP, int16(lElseEnd)) } c.setLabel(lElse) @@ -358,8 +358,8 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { for i := ln - 1; i >= 0; i-- { c.emitLoadConst(c.typeInfo.Types[n.Elts[i]]) } - emitInt(c.prog, int64(ln)) - emitOpcode(c.prog, vm.PACK) + emitInt(c.prog.BinWriter, int64(ln)) + emitOpcode(c.prog.BinWriter, vm.PACK) return nil } @@ -374,13 +374,13 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { switch n.Op { case token.LAND: 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) return nil case token.LOR: 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) return nil @@ -405,24 +405,24 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { case n.Op == token.ADD: // VM has separate opcodes for number and string concatenation if isStringType(tinfo.Type) { - emitOpcode(c.prog, vm.CAT) + emitOpcode(c.prog.BinWriter, vm.CAT) } else { - emitOpcode(c.prog, vm.ADD) + emitOpcode(c.prog.BinWriter, vm.ADD) } case n.Op == token.EQL: // VM has separate opcodes for number and string equality if isStringType(c.typeInfo.Types[n.X].Type) { - emitOpcode(c.prog, vm.EQUAL) + emitOpcode(c.prog.BinWriter, vm.EQUAL) } else { - emitOpcode(c.prog, vm.NUMEQUAL) + emitOpcode(c.prog.BinWriter, vm.NUMEQUAL) } case n.Op == token.NEQ: // VM has separate opcodes for number and string equality if isStringType(c.typeInfo.Types[n.X].Type) { - emitOpcode(c.prog, vm.EQUAL) - emitOpcode(c.prog, vm.NOT) + emitOpcode(c.prog.BinWriter, vm.EQUAL) + emitOpcode(c.prog.BinWriter, vm.NOT) } else { - emitOpcode(c.prog, vm.NUMNOTEQUAL) + emitOpcode(c.prog.BinWriter, vm.NUMNOTEQUAL) } default: c.convertToken(n.Op) @@ -478,14 +478,14 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { // Do not swap for builtin functions. if !isBuiltin { if numArgs == 2 { - emitOpcode(c.prog, vm.SWAP) + emitOpcode(c.prog.BinWriter, vm.SWAP) } else if numArgs == 3 { - emitInt(c.prog, 2) - emitOpcode(c.prog, vm.XSWAP) + emitInt(c.prog.BinWriter, 2) + emitOpcode(c.prog.BinWriter, vm.XSWAP) } else { for i := 1; i < numArgs; i++ { - emitInt(c.prog, int64(i)) - emitOpcode(c.prog, vm.ROLL) + emitInt(c.prog.BinWriter, int64(i)) + emitOpcode(c.prog.BinWriter, vm.ROLL) } } } @@ -499,7 +499,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { case isSyscall(f): c.convertSyscall(f.selector.Name, f.name) default: - emitCall(c.prog, vm.CALL, int16(f.label)) + emitCall(c.prog.BinWriter, vm.CALL, int16(f.label)) } return nil @@ -529,11 +529,11 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { case token.ADD: // +10 == 10, no need to do anything in this case case token.SUB: - emitOpcode(c.prog, vm.NEGATE) + emitOpcode(c.prog.BinWriter, vm.NEGATE) case token.NOT: - emitOpcode(c.prog, vm.NOT) + emitOpcode(c.prog.BinWriter, vm.NOT) case token.XOR: - emitOpcode(c.prog, vm.INVERT) + emitOpcode(c.prog.BinWriter, vm.INVERT) default: c.prog.Err = fmt.Errorf("invalid unary operator: %s", n.Op) return nil @@ -565,7 +565,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { c.emitLoadField(int(val)) default: ast.Walk(c, n.Index) - emitOpcode(c.prog, vm.PICKITEM) // just pickitem here + emitOpcode(c.prog.BinWriter, vm.PICKITEM) // just pickitem here } return nil @@ -583,14 +583,14 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { ast.Walk(c, n.Cond) // 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). ast.Walk(c, n.Body) ast.Walk(c, n.Post) // Jump back to condition. - emitJmp(c.prog, vm.JMP, int16(fstart)) + emitJmp(c.prog.BinWriter, vm.JMP, int16(fstart)) c.setLabel(fend) return nil @@ -612,11 +612,11 @@ func (c *codegen) convertSyscall(api, name string) { 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 // 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) { @@ -633,32 +633,32 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) { arg := expr.Args[0] typ := c.typeInfo.Types[arg].Type if isStringType(typ) { - emitOpcode(c.prog, vm.SIZE) + emitOpcode(c.prog.BinWriter, vm.SIZE) } else { - emitOpcode(c.prog, vm.ARRAYSIZE) + emitOpcode(c.prog.BinWriter, vm.ARRAYSIZE) } case "append": arg := expr.Args[0] typ := c.typeInfo.Types[arg].Type if isByteArrayType(typ) { - emitOpcode(c.prog, vm.CAT) + emitOpcode(c.prog.BinWriter, vm.CAT) } else { - emitOpcode(c.prog, vm.SWAP) - emitOpcode(c.prog, vm.DUP) - emitOpcode(c.prog, vm.PUSH2) - emitOpcode(c.prog, vm.XSWAP) - emitOpcode(c.prog, vm.APPEND) + emitOpcode(c.prog.BinWriter, vm.SWAP) + emitOpcode(c.prog.BinWriter, vm.DUP) + emitOpcode(c.prog.BinWriter, vm.PUSH2) + emitOpcode(c.prog.BinWriter, vm.XSWAP) + emitOpcode(c.prog.BinWriter, vm.APPEND) } case "SHA256": - emitOpcode(c.prog, vm.SHA256) + emitOpcode(c.prog.BinWriter, vm.SHA256) case "SHA1": - emitOpcode(c.prog, vm.SHA1) + emitOpcode(c.prog.BinWriter, vm.SHA1) case "Hash256": - emitOpcode(c.prog, vm.HASH256) + emitOpcode(c.prog.BinWriter, vm.HASH256) case "Hash160": - emitOpcode(c.prog, vm.HASH160) + emitOpcode(c.prog.BinWriter, vm.HASH160) case "Equals": - emitOpcode(c.prog, vm.EQUAL) + emitOpcode(c.prog.BinWriter, vm.EQUAL) case "FromAddress": // 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 @@ -671,7 +671,7 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) { return } bytes := uint160.Bytes() - emitBytes(c.prog, bytes) + emitBytes(c.prog.BinWriter, bytes) } } @@ -682,7 +682,7 @@ func (c *codegen) convertByteArray(lit *ast.CompositeLit) { val, _ := constant.Int64Val(t.Value) buf[i] = byte(val) } - emitBytes(c.prog, buf) + emitBytes(c.prog.BinWriter, buf) } func (c *codegen) convertStruct(lit *ast.CompositeLit) { @@ -694,10 +694,10 @@ func (c *codegen) convertStruct(lit *ast.CompositeLit) { return } - emitOpcode(c.prog, vm.NOP) - emitInt(c.prog, int64(strct.NumFields())) - emitOpcode(c.prog, vm.NEWSTRUCT) - emitOpcode(c.prog, vm.TOALTSTACK) + emitOpcode(c.prog.BinWriter, vm.NOP) + emitInt(c.prog.BinWriter, int64(strct.NumFields())) + emitOpcode(c.prog.BinWriter, vm.NEWSTRUCT) + emitOpcode(c.prog.BinWriter, vm.TOALTSTACK) // We need to locally store all the fields, even if they are not initialized. // We will initialize all fields to their "zero" value. @@ -730,55 +730,55 @@ func (c *codegen) convertStruct(lit *ast.CompositeLit) { c.emitLoadConst(typeAndVal) c.emitStoreLocal(i) } - emitOpcode(c.prog, vm.FROMALTSTACK) + emitOpcode(c.prog.BinWriter, vm.FROMALTSTACK) } func (c *codegen) convertToken(tok token.Token) { switch tok { case token.ADD_ASSIGN: - emitOpcode(c.prog, vm.ADD) + emitOpcode(c.prog.BinWriter, vm.ADD) case token.SUB_ASSIGN: - emitOpcode(c.prog, vm.SUB) + emitOpcode(c.prog.BinWriter, vm.SUB) case token.MUL_ASSIGN: - emitOpcode(c.prog, vm.MUL) + emitOpcode(c.prog.BinWriter, vm.MUL) case token.QUO_ASSIGN: - emitOpcode(c.prog, vm.DIV) + emitOpcode(c.prog.BinWriter, vm.DIV) case token.ADD: - emitOpcode(c.prog, vm.ADD) + emitOpcode(c.prog.BinWriter, vm.ADD) case token.SUB: - emitOpcode(c.prog, vm.SUB) + emitOpcode(c.prog.BinWriter, vm.SUB) case token.MUL: - emitOpcode(c.prog, vm.MUL) + emitOpcode(c.prog.BinWriter, vm.MUL) case token.QUO: - emitOpcode(c.prog, vm.DIV) + emitOpcode(c.prog.BinWriter, vm.DIV) case token.LSS: - emitOpcode(c.prog, vm.LT) + emitOpcode(c.prog.BinWriter, vm.LT) case token.LEQ: - emitOpcode(c.prog, vm.LTE) + emitOpcode(c.prog.BinWriter, vm.LTE) case token.GTR: - emitOpcode(c.prog, vm.GT) + emitOpcode(c.prog.BinWriter, vm.GT) case token.GEQ: - emitOpcode(c.prog, vm.GTE) + emitOpcode(c.prog.BinWriter, vm.GTE) case token.EQL: - emitOpcode(c.prog, vm.NUMEQUAL) + emitOpcode(c.prog.BinWriter, vm.NUMEQUAL) case token.NEQ: - emitOpcode(c.prog, vm.NUMNOTEQUAL) + emitOpcode(c.prog.BinWriter, vm.NUMNOTEQUAL) case token.DEC: - emitOpcode(c.prog, vm.DEC) + emitOpcode(c.prog.BinWriter, vm.DEC) case token.INC: - emitOpcode(c.prog, vm.INC) + emitOpcode(c.prog.BinWriter, vm.INC) case token.NOT: - emitOpcode(c.prog, vm.NOT) + emitOpcode(c.prog.BinWriter, vm.NOT) case token.AND: - emitOpcode(c.prog, vm.AND) + emitOpcode(c.prog.BinWriter, vm.AND) case token.OR: - emitOpcode(c.prog, vm.OR) + emitOpcode(c.prog.BinWriter, vm.OR) case token.SHL: - emitOpcode(c.prog, vm.SHL) + emitOpcode(c.prog.BinWriter, vm.SHL) case token.SHR: - emitOpcode(c.prog, vm.SHR) + emitOpcode(c.prog.BinWriter, vm.SHR) case token.XOR: - emitOpcode(c.prog, vm.XOR) + emitOpcode(c.prog.BinWriter, vm.XOR) default: c.prog.Err = fmt.Errorf("compiler could not convert token: %s", tok) return diff --git a/pkg/vm/compiler/emit.go b/pkg/vm/compiler/emit.go index 6cf70206c..6e1834e5a 100644 --- a/pkg/vm/compiler/emit.go +++ b/pkg/vm/compiler/emit.go @@ -12,18 +12,18 @@ import ( ) // emit a VM Instruction with data to the given buffer. -func emit(w *io.BufBinWriter, instr vm.Instruction, b []byte) { +func emit(w *io.BinWriter, instr vm.Instruction, b []byte) { emitOpcode(w, instr) w.WriteBytes(b) } // emitOpcode emits a single VM Instruction the given buffer. -func emitOpcode(w *io.BufBinWriter, instr vm.Instruction) { +func emitOpcode(w *io.BinWriter, instr vm.Instruction) { w.WriteLE(byte(instr)) } // emitBool emits a bool type the given buffer. -func emitBool(w *io.BufBinWriter, ok bool) { +func emitBool(w *io.BinWriter, ok bool) { if ok { emitOpcode(w, vm.PUSHT) return @@ -32,7 +32,7 @@ func emitBool(w *io.BufBinWriter, ok bool) { } // emitInt emits a int type to the given buffer. -func emitInt(w *io.BufBinWriter, i int64) { +func emitInt(w *io.BinWriter, i int64) { switch { case i == -1: emitOpcode(w, vm.PUSHM1) @@ -52,12 +52,12 @@ func emitInt(w *io.BufBinWriter, i int64) { } // emitString emits a string to the given buffer. -func emitString(w *io.BufBinWriter, s string) { +func emitString(w *io.BinWriter, s string) { emitBytes(w, []byte(s)) } // emitBytes emits a byte array to the given buffer. -func emitBytes(w *io.BufBinWriter, b []byte) { +func emitBytes(w *io.BinWriter, b []byte) { n := len(b) switch { @@ -84,7 +84,7 @@ func emitBytes(w *io.BufBinWriter, b []byte) { // emitSyscall emits the syscall API to the given buffer. // Syscall API string cannot be 0. -func emitSyscall(w *io.BufBinWriter, api string) { +func emitSyscall(w *io.BinWriter, api string) { if len(api) == 0 { w.Err = errors.New("syscall api cannot be of length 0") return @@ -96,12 +96,12 @@ func emitSyscall(w *io.BufBinWriter, api string) { } // emitCall emits a call Instruction with label to the given buffer. -func emitCall(w *io.BufBinWriter, instr vm.Instruction, label int16) { +func emitCall(w *io.BinWriter, instr vm.Instruction, label int16) { emitJmp(w, instr, label) } // emitJmp emits a jump Instruction along with label to the given buffer. -func emitJmp(w *io.BufBinWriter, instr vm.Instruction, label int16) { +func emitJmp(w *io.BinWriter, instr vm.Instruction, label int16) { if !isInstrJmp(instr) { w.Err = fmt.Errorf("opcode %s is not a jump or call type", instr) return