emit: accept multiple opcodes in Opcode()

This commit is contained in:
Evgenii Stratonikov 2020-10-02 11:30:15 +03:00
parent 0e82d4cbd1
commit b2a3a0851e
15 changed files with 145 additions and 162 deletions

View file

@ -102,7 +102,7 @@ func handleCandidate(ctx *cli.Context, method string) error {
gas := flags.Fixed8FromContext(ctx, "gas")
w := io.NewBufBinWriter()
emit.AppCallWithOperationAndArgs(w.BinWriter, client.NeoContractHash, method, acc.PrivateKey().PublicKey().Bytes())
emit.Opcode(w.BinWriter, opcode.ASSERT)
emit.Opcodes(w.BinWriter, opcode.ASSERT)
tx, err := c.CreateTxFromScript(w.Bytes(), acc, -1, int64(gas), transaction.Signer{
Account: acc.Contract.ScriptHash(),
Scopes: transaction.CalledByEntry,
@ -160,7 +160,7 @@ func handleVote(ctx *cli.Context) error {
gas := flags.Fixed8FromContext(ctx, "gas")
w := io.NewBufBinWriter()
emit.AppCallWithOperationAndArgs(w.BinWriter, client.NeoContractHash, "vote", addr.BytesBE(), pubArg)
emit.Opcode(w.BinWriter, opcode.ASSERT)
emit.Opcodes(w.BinWriter, opcode.ASSERT)
tx, err := c.CreateTxFromScript(w.Bytes(), acc, -1, int64(gas), transaction.Signer{
Account: acc.Contract.ScriptHash(),

View file

@ -176,13 +176,12 @@ func (c *codegen) emitLoadConst(t types.TypeAndValue) {
func (c *codegen) emitLoadField(i int) {
emit.Int(c.prog.BinWriter, int64(i))
emit.Opcode(c.prog.BinWriter, opcode.PICKITEM)
emit.Opcodes(c.prog.BinWriter, opcode.PICKITEM)
}
func (c *codegen) emitStoreStructField(i int) {
emit.Int(c.prog.BinWriter, int64(i))
emit.Opcode(c.prog.BinWriter, opcode.ROT)
emit.Opcode(c.prog.BinWriter, opcode.SETITEM)
emit.Opcodes(c.prog.BinWriter, opcode.ROT, opcode.SETITEM)
}
// getVarIndex returns variable type and position in corresponding slot,
@ -226,7 +225,7 @@ func (c *codegen) emitLoadVar(pkg string, name string) {
func (c *codegen) emitLoadByIndex(t varType, i int) {
base, _ := getBaseOpcode(t)
if i < 7 {
emit.Opcode(c.prog.BinWriter, base+opcode.Opcode(i))
emit.Opcodes(c.prog.BinWriter, base+opcode.Opcode(i))
} else {
emit.Instruction(c.prog.BinWriter, base+7, []byte{byte(i)})
}
@ -235,7 +234,7 @@ func (c *codegen) emitLoadByIndex(t varType, i int) {
// emitStoreVar stores top value from the evaluation stack in the specified variable.
func (c *codegen) emitStoreVar(pkg string, name string) {
if name == "_" {
emit.Opcode(c.prog.BinWriter, opcode.DROP)
emit.Opcodes(c.prog.BinWriter, opcode.DROP)
return
}
t, i := c.getVarIndex(pkg, name)
@ -246,7 +245,7 @@ func (c *codegen) emitStoreVar(pkg string, name string) {
func (c *codegen) emitStoreByIndex(t varType, i int) {
_, base := getBaseOpcode(t)
if i < 7 {
emit.Opcode(c.prog.BinWriter, base+opcode.Opcode(i))
emit.Opcodes(c.prog.BinWriter, base+opcode.Opcode(i))
} else {
emit.Instruction(c.prog.BinWriter, base+7, []byte{byte(i)})
}
@ -264,20 +263,20 @@ func (c *codegen) emitDefault(t types.Type) {
case info&types.IsBoolean != 0:
emit.Bool(c.prog.BinWriter, false)
default:
emit.Opcode(c.prog.BinWriter, opcode.PUSHNULL)
emit.Opcodes(c.prog.BinWriter, opcode.PUSHNULL)
}
case *types.Struct:
num := t.NumFields()
emit.Int(c.prog.BinWriter, int64(num))
emit.Opcode(c.prog.BinWriter, opcode.NEWSTRUCT)
emit.Opcodes(c.prog.BinWriter, opcode.NEWSTRUCT)
for i := 0; i < num; i++ {
emit.Opcode(c.prog.BinWriter, opcode.DUP)
emit.Opcodes(c.prog.BinWriter, opcode.DUP)
emit.Int(c.prog.BinWriter, int64(i))
c.emitDefault(t.Field(i).Type())
emit.Opcode(c.prog.BinWriter, opcode.SETITEM)
emit.Opcodes(c.prog.BinWriter, opcode.SETITEM)
}
default:
emit.Opcode(c.prog.BinWriter, opcode.PUSHNULL)
emit.Opcodes(c.prog.BinWriter, opcode.PUSHNULL)
}
}
@ -389,7 +388,7 @@ func (c *codegen) convertFuncDecl(file ast.Node, decl *ast.FuncDecl, pkg *types.
// This can be the case with void and named-return functions.
if !isInit && !lastStmtIsReturn(decl) {
c.saveSequencePoint(decl.Body)
emit.Opcode(c.prog.BinWriter, opcode.RET)
emit.Opcodes(c.prog.BinWriter, opcode.RET)
}
f.rng.End = uint16(c.prog.Len() - 1)
@ -510,8 +509,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
}
ast.Walk(c, t.X)
ast.Walk(c, t.Index)
emit.Opcode(c.prog.BinWriter, opcode.ROT)
emit.Opcode(c.prog.BinWriter, opcode.SETITEM)
emit.Opcodes(c.prog.BinWriter, opcode.ROT, opcode.SETITEM)
}
}
return nil
@ -527,19 +525,16 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
if n.Low != nil {
ast.Walk(c, n.Low)
} else {
emit.Opcode(c.prog.BinWriter, opcode.PUSH0)
emit.Opcodes(c.prog.BinWriter, opcode.PUSH0)
}
if n.High != nil {
ast.Walk(c, n.High)
} else {
emit.Opcode(c.prog.BinWriter, opcode.OVER)
emit.Opcode(c.prog.BinWriter, opcode.SIZE)
emit.Opcodes(c.prog.BinWriter, opcode.OVER, opcode.SIZE)
}
emit.Opcode(c.prog.BinWriter, opcode.OVER)
emit.Opcode(c.prog.BinWriter, opcode.SUB)
emit.Opcode(c.prog.BinWriter, opcode.SUBSTR)
emit.Opcodes(c.prog.BinWriter, opcode.OVER, opcode.SUB, opcode.SUBSTR)
return nil
@ -574,7 +569,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
c.processDefers()
c.saveSequencePoint(n)
emit.Opcode(c.prog.BinWriter, opcode.RET)
emit.Opcodes(c.prog.BinWriter, opcode.RET)
return nil
case *ast.IfStmt:
@ -630,9 +625,9 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
if l := len(cc.List); l != 0 { // if not `default`
for j := range cc.List {
emit.Opcode(c.prog.BinWriter, opcode.DUP)
emit.Opcodes(c.prog.BinWriter, opcode.DUP)
ast.Walk(c, cc.List[j])
emit.Opcode(c.prog.BinWriter, eqOpcode)
emit.Opcodes(c.prog.BinWriter, eqOpcode)
if j == l-1 {
emit.Jmp(c.prog.BinWriter, opcode.JMPIFNOTL, lEnd)
} else {
@ -691,7 +686,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
if tv := c.typeAndValueOf(n); tv.Value != nil {
c.emitLoadConst(tv)
} else if n.Name == "nil" {
emit.Opcode(c.prog.BinWriter, opcode.PUSHNULL)
emit.Opcodes(c.prog.BinWriter, opcode.PUSHNULL)
} else {
c.emitLoadVar("", n.Name)
}
@ -714,7 +709,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
ast.Walk(c, n.Elts[i])
}
emit.Int(c.prog.BinWriter, int64(ln))
emit.Opcode(c.prog.BinWriter, opcode.PACK)
emit.Opcodes(c.prog.BinWriter, opcode.PACK)
}
return nil
@ -786,12 +781,9 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
if ok && !isInteropPath(typ.String()) {
// To clone struct fields we create a new array and append struct to it.
// This way even non-pointer struct fields will be copied.
emit.Opcode(c.prog.BinWriter, opcode.NEWARRAY0)
emit.Opcode(c.prog.BinWriter, opcode.DUP)
emit.Opcode(c.prog.BinWriter, opcode.ROT)
emit.Opcode(c.prog.BinWriter, opcode.APPEND)
emit.Opcode(c.prog.BinWriter, opcode.PUSH0)
emit.Opcode(c.prog.BinWriter, opcode.PICKITEM)
emit.Opcodes(c.prog.BinWriter, opcode.NEWARRAY0,
opcode.DUP, opcode.ROT, opcode.APPEND,
opcode.PUSH0, opcode.PICKITEM)
}
}
// Do not swap for builtin functions.
@ -802,7 +794,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
varSize := len(n.Args) - typ.Params().Len() + 1
c.emitReverse(varSize)
emit.Int(c.prog.BinWriter, int64(varSize))
emit.Opcode(c.prog.BinWriter, opcode.PACK)
emit.Opcodes(c.prog.BinWriter, opcode.PACK)
numArgs -= varSize - 1
}
c.emitReverse(numArgs)
@ -822,11 +814,11 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
c.emitConvert(stackitem.ByteArrayT)
} else if isFunc {
c.emitLoadVar("", name)
emit.Opcode(c.prog.BinWriter, opcode.CALLA)
emit.Opcodes(c.prog.BinWriter, opcode.CALLA)
}
case isLiteral:
ast.Walk(c, n.Fun)
emit.Opcode(c.prog.BinWriter, opcode.CALLA)
emit.Opcodes(c.prog.BinWriter, opcode.CALLA)
case isSyscall(f):
c.convertSyscall(n, f.pkg.Name(), f.name)
default:
@ -843,7 +835,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
sz = f.Results().Len()
}
for i := 0; i < sz; i++ {
emit.Opcode(c.prog.BinWriter, opcode.DROP)
emit.Opcodes(c.prog.BinWriter, opcode.DROP)
}
}
@ -909,11 +901,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:
emit.Opcode(c.prog.BinWriter, opcode.NEGATE)
emit.Opcodes(c.prog.BinWriter, opcode.NEGATE)
case token.NOT:
emit.Opcode(c.prog.BinWriter, opcode.NOT)
emit.Opcodes(c.prog.BinWriter, opcode.NOT)
case token.XOR:
emit.Opcode(c.prog.BinWriter, opcode.INVERT)
emit.Opcodes(c.prog.BinWriter, opcode.INVERT)
default:
c.prog.Err = fmt.Errorf("invalid unary operator: %s", n.Op)
return nil
@ -937,7 +929,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
// This will load local whatever X is.
ast.Walk(c, n.X)
ast.Walk(c, n.Index)
emit.Opcode(c.prog.BinWriter, opcode.PICKITEM) // just pickitem here
emit.Opcodes(c.prog.BinWriter, opcode.PICKITEM) // just pickitem here
return nil
@ -1049,13 +1041,11 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
// For slices we iterate index from 0 to len-1, storing array, len and index on stack.
// For maps we iterate index from 0 to len-1, storing map, keyarray, size and index on stack.
_, isMap := c.typeOf(n.X).Underlying().(*types.Map)
emit.Opcode(c.prog.BinWriter, opcode.DUP)
emit.Opcodes(c.prog.BinWriter, opcode.DUP)
if isMap {
emit.Opcode(c.prog.BinWriter, opcode.KEYS)
emit.Opcode(c.prog.BinWriter, opcode.DUP)
emit.Opcodes(c.prog.BinWriter, opcode.KEYS, opcode.DUP)
}
emit.Opcode(c.prog.BinWriter, opcode.SIZE)
emit.Opcode(c.prog.BinWriter, opcode.PUSH0)
emit.Opcodes(c.prog.BinWriter, opcode.SIZE, opcode.PUSH0)
stackSize := 3 // slice, len(slice), index
if isMap {
@ -1064,8 +1054,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
c.pushStackLabel(label, stackSize)
c.setLabel(start)
emit.Opcode(c.prog.BinWriter, opcode.OVER)
emit.Opcode(c.prog.BinWriter, opcode.OVER)
emit.Opcodes(c.prog.BinWriter, opcode.OVER, opcode.OVER)
emit.Jmp(c.prog.BinWriter, opcode.JMPLEL, end)
var keyLoaded bool
@ -1074,11 +1063,11 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
if isMap {
c.rangeLoadKey()
if needValue {
emit.Opcode(c.prog.BinWriter, opcode.DUP)
emit.Opcodes(c.prog.BinWriter, opcode.DUP)
keyLoaded = true
}
} else {
emit.Opcode(c.prog.BinWriter, opcode.DUP)
emit.Opcodes(c.prog.BinWriter, opcode.DUP)
}
c.emitStoreVar("", n.Key.(*ast.Ident).Name)
}
@ -1089,9 +1078,10 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
if isMap {
// we have loaded only key from key array, now load value
emit.Int(c.prog.BinWriter, 4)
emit.Opcode(c.prog.BinWriter, opcode.PICK) // load map itself (+1 because key was pushed)
emit.Opcode(c.prog.BinWriter, opcode.SWAP) // key should be on top
emit.Opcode(c.prog.BinWriter, opcode.PICKITEM)
emit.Opcodes(c.prog.BinWriter,
opcode.PICK, // load map itself (+1 because key was pushed)
opcode.SWAP, // key should be on top
opcode.PICKITEM)
}
c.emitStoreVar("", n.Value.(*ast.Ident).Name)
}
@ -1100,7 +1090,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
c.setLabel(post)
emit.Opcode(c.prog.BinWriter, opcode.INC)
emit.Opcodes(c.prog.BinWriter, opcode.INC)
emit.Jmp(c.prog.BinWriter, opcode.JMPL, start)
c.setLabel(end)
@ -1164,16 +1154,17 @@ func (c *codegen) processDefers() {
c.setLabel(before)
emit.Int(c.prog.BinWriter, 0)
c.emitStoreByIndex(varLocal, c.scope.finallyProcessedIndex)
emit.Opcode(c.prog.BinWriter, opcode.ENDFINALLY)
emit.Opcodes(c.prog.BinWriter, opcode.ENDFINALLY)
c.setLabel(after)
}
}
func (c *codegen) rangeLoadKey() {
emit.Int(c.prog.BinWriter, 2)
emit.Opcode(c.prog.BinWriter, opcode.PICK) // load keys
emit.Opcode(c.prog.BinWriter, opcode.OVER) // load index in key array
emit.Opcode(c.prog.BinWriter, opcode.PICKITEM)
emit.Opcodes(c.prog.BinWriter,
opcode.PICK, // load keys
opcode.OVER, // load index in key array
opcode.PICKITEM)
}
func isFallthroughStmt(c ast.Node) bool {
@ -1230,11 +1221,11 @@ func (c *codegen) emitBinaryExpr(n *ast.BinaryExpr, needJump bool, cond bool, jm
return
} else if arg := c.getCompareWithNilArg(n); arg != nil {
ast.Walk(c, arg)
emit.Opcode(c.prog.BinWriter, opcode.ISNULL)
emit.Opcodes(c.prog.BinWriter, opcode.ISNULL)
if needJump {
c.emitJumpOnCondition(cond == (n.Op == token.EQL), jmpLabel)
} else if n.Op == token.NEQ {
emit.Opcode(c.prog.BinWriter, opcode.NOT)
emit.Opcodes(c.prog.BinWriter, opcode.NOT)
}
return
}
@ -1299,14 +1290,13 @@ func (c *codegen) dropStackLabel() {
func (c *codegen) dropItems(n int) {
if n < 4 {
for i := 0; i < n; i++ {
emit.Opcode(c.prog.BinWriter, opcode.DROP)
emit.Opcodes(c.prog.BinWriter, opcode.DROP)
}
return
}
emit.Int(c.prog.BinWriter, int64(n))
emit.Opcode(c.prog.BinWriter, opcode.PACK)
emit.Opcode(c.prog.BinWriter, opcode.DROP)
emit.Opcodes(c.prog.BinWriter, opcode.PACK, opcode.DROP)
}
// emitReverse reverses top num items of the stack.
@ -1314,14 +1304,14 @@ func (c *codegen) emitReverse(num int) {
switch num {
case 0, 1:
case 2:
emit.Opcode(c.prog.BinWriter, opcode.SWAP)
emit.Opcodes(c.prog.BinWriter, opcode.SWAP)
case 3:
emit.Opcode(c.prog.BinWriter, opcode.REVERSE3)
emit.Opcodes(c.prog.BinWriter, opcode.REVERSE3)
case 4:
emit.Opcode(c.prog.BinWriter, opcode.REVERSE4)
emit.Opcodes(c.prog.BinWriter, opcode.REVERSE4)
default:
emit.Int(c.prog.BinWriter, int64(num))
emit.Opcode(c.prog.BinWriter, opcode.REVERSEN)
emit.Opcodes(c.prog.BinWriter, opcode.REVERSEN)
}
}
@ -1400,7 +1390,7 @@ func (c *codegen) convertSyscall(expr *ast.CallExpr, api, name string) {
// 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.
emit.Opcode(c.prog.BinWriter, opcode.NOP)
emit.Opcodes(c.prog.BinWriter, opcode.NOP)
}
// emitSliceHelper emits 3 items on stack: slice, its first index, and its size.
@ -1416,8 +1406,7 @@ func (c *codegen) emitSliceHelper(e ast.Expr) {
if src.High != nil {
ast.Walk(c, src.High)
} else {
emit.Opcode(c.prog.BinWriter, opcode.DUP)
emit.Opcode(c.prog.BinWriter, opcode.SIZE)
emit.Opcodes(c.prog.BinWriter, opcode.DUP, opcode.SIZE)
}
if src.Low != nil {
ast.Walk(c, src.Low)
@ -1427,17 +1416,13 @@ func (c *codegen) emitSliceHelper(e ast.Expr) {
}
default:
ast.Walk(c, src)
emit.Opcode(c.prog.BinWriter, opcode.DUP)
emit.Opcode(c.prog.BinWriter, opcode.SIZE)
emit.Opcodes(c.prog.BinWriter, opcode.DUP, opcode.SIZE)
emit.Int(c.prog.BinWriter, 0)
}
if !hasLowIndex {
emit.Opcode(c.prog.BinWriter, opcode.SWAP)
emit.Opcodes(c.prog.BinWriter, opcode.SWAP)
} else {
emit.Opcode(c.prog.BinWriter, opcode.DUP)
emit.Opcode(c.prog.BinWriter, opcode.ROT)
emit.Opcode(c.prog.BinWriter, opcode.SWAP)
emit.Opcode(c.prog.BinWriter, opcode.SUB)
emit.Opcodes(c.prog.BinWriter, opcode.DUP, opcode.ROT, opcode.SWAP, opcode.SUB)
}
}
@ -1456,22 +1441,21 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
c.emitSliceHelper(expr.Args[0])
c.emitSliceHelper(expr.Args[1])
emit.Int(c.prog.BinWriter, 3)
emit.Opcode(c.prog.BinWriter, opcode.ROLL)
emit.Opcode(c.prog.BinWriter, opcode.MIN)
emit.Opcodes(c.prog.BinWriter, opcode.ROLL, opcode.MIN)
if !c.scope.voidCalls[expr] {
// insert top item to the bottom of MEMCPY args, so that it is left on stack
emit.Opcode(c.prog.BinWriter, opcode.DUP)
emit.Opcodes(c.prog.BinWriter, opcode.DUP)
emit.Int(c.prog.BinWriter, 6)
emit.Opcode(c.prog.BinWriter, opcode.REVERSEN)
emit.Opcodes(c.prog.BinWriter, opcode.REVERSEN)
emit.Int(c.prog.BinWriter, 5)
emit.Opcode(c.prog.BinWriter, opcode.REVERSEN)
emit.Opcodes(c.prog.BinWriter, opcode.REVERSEN)
}
emit.Opcode(c.prog.BinWriter, opcode.MEMCPY)
emit.Opcodes(c.prog.BinWriter, opcode.MEMCPY)
case "make":
typ := c.typeOf(expr.Args[0])
switch {
case isMap(typ):
emit.Opcode(c.prog.BinWriter, opcode.NEWMAP)
emit.Opcodes(c.prog.BinWriter, opcode.NEWMAP)
default:
if len(expr.Args) == 3 {
c.prog.Err = fmt.Errorf("`make()` with a capacity argument is not supported")
@ -1479,57 +1463,47 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
}
ast.Walk(c, expr.Args[1])
if isByteSlice(typ) {
emit.Opcode(c.prog.BinWriter, opcode.NEWBUFFER)
emit.Opcodes(c.prog.BinWriter, opcode.NEWBUFFER)
} else {
neoT := toNeoType(typ.(*types.Slice).Elem())
emit.Instruction(c.prog.BinWriter, opcode.NEWARRAYT, []byte{byte(neoT)})
}
}
case "len":
emit.Opcode(c.prog.BinWriter, opcode.DUP)
emit.Opcode(c.prog.BinWriter, opcode.ISNULL)
emit.Opcodes(c.prog.BinWriter, opcode.DUP, opcode.ISNULL)
emit.Instruction(c.prog.BinWriter, opcode.JMPIF, []byte{2 + 1 + 2})
emit.Opcode(c.prog.BinWriter, opcode.SIZE)
emit.Opcodes(c.prog.BinWriter, opcode.SIZE)
emit.Instruction(c.prog.BinWriter, opcode.JMP, []byte{2 + 1 + 1})
emit.Opcode(c.prog.BinWriter, opcode.DROP)
emit.Opcode(c.prog.BinWriter, opcode.PUSH0)
emit.Opcodes(c.prog.BinWriter, opcode.DROP, opcode.PUSH0)
case "append":
arg := expr.Args[0]
typ := c.typeInfo.Types[arg].Type
c.emitReverse(len(expr.Args))
emit.Opcode(c.prog.BinWriter, opcode.DUP)
emit.Opcode(c.prog.BinWriter, opcode.ISNULL)
emit.Opcodes(c.prog.BinWriter, opcode.DUP, opcode.ISNULL)
emit.Instruction(c.prog.BinWriter, opcode.JMPIFNOT, []byte{2 + 3})
if isByteSlice(typ) {
emit.Opcode(c.prog.BinWriter, opcode.DROP)
emit.Opcode(c.prog.BinWriter, opcode.PUSH0)
emit.Opcode(c.prog.BinWriter, opcode.NEWBUFFER)
emit.Opcodes(c.prog.BinWriter, opcode.DROP, opcode.PUSH0, opcode.NEWBUFFER)
} else {
emit.Opcode(c.prog.BinWriter, opcode.DROP)
emit.Opcode(c.prog.BinWriter, opcode.NEWARRAY0)
emit.Opcode(c.prog.BinWriter, opcode.NOP)
emit.Opcodes(c.prog.BinWriter, opcode.DROP, opcode.NEWARRAY0, opcode.NOP)
}
// Jump target.
for range expr.Args[1:] {
if isByteSlice(typ) {
emit.Opcode(c.prog.BinWriter, opcode.SWAP)
emit.Opcode(c.prog.BinWriter, opcode.CAT)
emit.Opcodes(c.prog.BinWriter, opcode.SWAP, opcode.CAT)
} else {
emit.Opcode(c.prog.BinWriter, opcode.DUP)
emit.Opcode(c.prog.BinWriter, opcode.ROT)
emit.Opcode(c.prog.BinWriter, opcode.APPEND)
emit.Opcodes(c.prog.BinWriter, opcode.DUP, opcode.ROT, opcode.APPEND)
}
}
case "panic":
emit.Opcode(c.prog.BinWriter, opcode.THROW)
emit.Opcodes(c.prog.BinWriter, opcode.THROW)
case "recover":
if !c.scope.voidCalls[expr] {
c.emitLoadByIndex(varGlobal, c.exceptionIndex)
}
emit.Opcode(c.prog.BinWriter, opcode.PUSHNULL)
emit.Opcodes(c.prog.BinWriter, opcode.PUSHNULL)
c.emitStoreByIndex(varGlobal, c.exceptionIndex)
case "delete":
emit.Opcode(c.prog.BinWriter, opcode.REMOVE)
emit.Opcodes(c.prog.BinWriter, opcode.REMOVE)
case "ToInteger", "ToByteArray", "ToBool":
typ := stackitem.IntegerT
switch name {
@ -1544,9 +1518,9 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
c.prog.Err = errors.New("`Remove` supports only non-byte slices")
return
}
emit.Opcode(c.prog.BinWriter, opcode.REMOVE)
emit.Opcodes(c.prog.BinWriter, opcode.REMOVE)
case "Equals":
emit.Opcode(c.prog.BinWriter, opcode.EQUAL)
emit.Opcodes(c.prog.BinWriter, opcode.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
@ -1607,21 +1581,21 @@ func (c *codegen) convertByteArray(lit *ast.CompositeLit) {
emit.Bytes(c.prog.BinWriter, buf)
c.emitConvert(stackitem.BufferT)
for _, i := range varIndices {
emit.Opcode(c.prog.BinWriter, opcode.DUP)
emit.Opcodes(c.prog.BinWriter, opcode.DUP)
emit.Int(c.prog.BinWriter, int64(i))
ast.Walk(c, lit.Elts[i])
emit.Opcode(c.prog.BinWriter, opcode.SETITEM)
emit.Opcodes(c.prog.BinWriter, opcode.SETITEM)
}
}
func (c *codegen) convertMap(lit *ast.CompositeLit) {
emit.Opcode(c.prog.BinWriter, opcode.NEWMAP)
emit.Opcodes(c.prog.BinWriter, opcode.NEWMAP)
for i := range lit.Elts {
elem := lit.Elts[i].(*ast.KeyValueExpr)
emit.Opcode(c.prog.BinWriter, opcode.DUP)
emit.Opcodes(c.prog.BinWriter, opcode.DUP)
ast.Walk(c, elem.Key)
ast.Walk(c, elem.Value)
emit.Opcode(c.prog.BinWriter, opcode.SETITEM)
emit.Opcodes(c.prog.BinWriter, opcode.SETITEM)
}
}
@ -1646,12 +1620,12 @@ func (c *codegen) convertStruct(lit *ast.CompositeLit, ptr bool) {
return
}
emit.Opcode(c.prog.BinWriter, opcode.NOP)
emit.Opcodes(c.prog.BinWriter, opcode.NOP)
emit.Int(c.prog.BinWriter, int64(strct.NumFields()))
if ptr {
emit.Opcode(c.prog.BinWriter, opcode.NEWARRAY)
emit.Opcodes(c.prog.BinWriter, opcode.NEWARRAY)
} else {
emit.Opcode(c.prog.BinWriter, opcode.NEWSTRUCT)
emit.Opcodes(c.prog.BinWriter, opcode.NEWSTRUCT)
}
keyedLit := len(lit.Elts) > 0
@ -1665,7 +1639,7 @@ func (c *codegen) convertStruct(lit *ast.CompositeLit, ptr bool) {
sField := strct.Field(i)
var initialized bool
emit.Opcode(c.prog.BinWriter, opcode.DUP)
emit.Opcodes(c.prog.BinWriter, opcode.DUP)
emit.Int(c.prog.BinWriter, int64(i))
if !keyedLit {
@ -1689,7 +1663,7 @@ func (c *codegen) convertStruct(lit *ast.CompositeLit, ptr bool) {
if !initialized {
c.emitDefault(sField.Type())
}
emit.Opcode(c.prog.BinWriter, opcode.SETITEM)
emit.Opcodes(c.prog.BinWriter, opcode.SETITEM)
}
}
@ -1699,7 +1673,7 @@ func (c *codegen) emitToken(tok token.Token, typ types.Type) {
c.prog.Err = err
return
}
emit.Opcode(c.prog.BinWriter, op)
emit.Opcodes(c.prog.BinWriter, op)
}
func convertToken(tok token.Token, typ types.Type) (opcode.Opcode, error) {
@ -1805,7 +1779,7 @@ func (c *codegen) compile(info *buildInfo, pkg *loader.PackageInfo) error {
n, hasInit := c.traverseGlobals()
if n > 0 || hasInit {
emit.Opcode(c.prog.BinWriter, opcode.RET)
emit.Opcodes(c.prog.BinWriter, opcode.RET)
c.initEndOffset = c.prog.Len()
}

View file

@ -237,12 +237,12 @@ func TestVerifyTx(t *testing.T) {
}
emit.AppCallWithOperationAndArgs(w.BinWriter, sc, "transfer",
neoOwner, a.Contract.ScriptHash(), amount)
emit.Opcode(w.BinWriter, opcode.ASSERT)
emit.Opcodes(w.BinWriter, opcode.ASSERT)
}
}
emit.AppCallWithOperationAndArgs(w.BinWriter, gasHash, "transfer",
neoOwner, testchain.CommitteeScriptHash(), int64(1_000_000_000))
emit.Opcode(w.BinWriter, opcode.ASSERT)
emit.Opcodes(w.BinWriter, opcode.ASSERT)
require.NoError(t, w.Err)
txMove := bc.newTestTx(neoOwner, w.Bytes())
@ -782,7 +782,7 @@ func TestSubscriptions(t *testing.T) {
script = io.NewBufBinWriter()
emit.Bytes(script.BinWriter, []byte("nay!"))
emit.Syscall(script.BinWriter, interopnames.SystemRuntimeNotify)
emit.Opcode(script.BinWriter, opcode.THROW)
emit.Opcodes(script.BinWriter, opcode.THROW)
require.NoError(t, script.Err)
txBad := transaction.New(netmode.UnitTestNet, script.Bytes(), 0)
txBad.Signers = []transaction.Signer{{Account: neoOwner}}

View file

@ -361,7 +361,7 @@ func TestCreateBasicChain(t *testing.T) {
func newNEP5Transfer(sc, from, to util.Uint160, amount int64) *transaction.Transaction {
w := io.NewBufBinWriter()
emit.AppCallWithOperationAndArgs(w.BinWriter, sc, "transfer", from, to, amount)
emit.Opcode(w.BinWriter, opcode.ASSERT)
emit.Opcodes(w.BinWriter, opcode.ASSERT)
script := w.Bytes()
return transaction.New(testchain.Network(), script, 10000000)

View file

@ -83,10 +83,10 @@ func (cs *Contracts) GetPersistScript() []byte {
continue
}
emit.Int(w.BinWriter, 0)
emit.Opcode(w.BinWriter, opcode.NEWARRAY)
emit.Opcodes(w.BinWriter, opcode.NEWARRAY)
emit.String(w.BinWriter, "onPersist")
emit.AppCall(w.BinWriter, md.Hash)
emit.Opcode(w.BinWriter, opcode.DROP)
emit.Opcodes(w.BinWriter, opcode.DROP)
}
cs.persistScript = w.Bytes()
return cs.persistScript
@ -106,10 +106,10 @@ func (cs *Contracts) GetPostPersistScript() []byte {
continue
}
emit.Int(w.BinWriter, 0)
emit.Opcode(w.BinWriter, opcode.NEWARRAY)
emit.Opcodes(w.BinWriter, opcode.NEWARRAY)
emit.String(w.BinWriter, "postPersist")
emit.AppCall(w.BinWriter, md.Hash)
emit.Opcode(w.BinWriter, opcode.DROP)
emit.Opcodes(w.BinWriter, opcode.DROP)
}
cs.postPersistScript = w.Bytes()
return cs.postPersistScript

View file

@ -56,7 +56,7 @@ func init() {
w.Reset()
emit.Int(w.BinWriter, 0)
emit.Opcode(w.BinWriter, opcode.NEWARRAY)
emit.Opcodes(w.BinWriter, opcode.NEWARRAY)
emit.String(w.BinWriter, "finish")
emit.Bytes(w.BinWriter, h.BytesBE())
emit.Syscall(w.BinWriter, interopnames.SystemContractCall)

View file

@ -23,10 +23,10 @@ func (bc *Blockchain) setNodesByRole(t *testing.T, ok bool, r native.Role, nodes
emit.Bytes(w.BinWriter, pub.Bytes())
}
emit.Int(w.BinWriter, int64(len(nodes)))
emit.Opcode(w.BinWriter, opcode.PACK)
emit.Opcodes(w.BinWriter, opcode.PACK)
emit.Int(w.BinWriter, int64(r))
emit.Int(w.BinWriter, 2)
emit.Opcode(w.BinWriter, opcode.PACK)
emit.Opcodes(w.BinWriter, opcode.PACK)
emit.String(w.BinWriter, "designateAsRole")
emit.AppCall(w.BinWriter, bc.contracts.Designate.Hash)
require.NoError(t, w.Err)

View file

@ -27,26 +27,26 @@ import (
func getOracleContractState(h util.Uint160) *state.Contract {
w := io.NewBufBinWriter()
emit.Int(w.BinWriter, 5)
emit.Opcode(w.BinWriter, opcode.PACK)
emit.Opcodes(w.BinWriter, opcode.PACK)
emit.String(w.BinWriter, "request")
emit.Bytes(w.BinWriter, h.BytesBE())
emit.Syscall(w.BinWriter, interopnames.SystemContractCall)
emit.Opcode(w.BinWriter, opcode.RET)
emit.Opcodes(w.BinWriter, opcode.RET)
// `handle` method aborts if len(userData) == 2
offset := w.Len()
emit.Opcode(w.BinWriter, opcode.OVER)
emit.Opcode(w.BinWriter, opcode.SIZE)
emit.Opcodes(w.BinWriter, opcode.OVER)
emit.Opcodes(w.BinWriter, opcode.SIZE)
emit.Int(w.BinWriter, 2)
emit.Instruction(w.BinWriter, opcode.JMPNE, []byte{3})
emit.Opcode(w.BinWriter, opcode.ABORT)
emit.Opcodes(w.BinWriter, opcode.ABORT)
emit.Int(w.BinWriter, 4) // url, userData, code, result
emit.Opcode(w.BinWriter, opcode.PACK)
emit.Opcodes(w.BinWriter, opcode.PACK)
emit.Syscall(w.BinWriter, interopnames.SystemBinarySerialize)
emit.String(w.BinWriter, "lastOracleResponse")
emit.Syscall(w.BinWriter, interopnames.SystemStorageGetContext)
emit.Syscall(w.BinWriter, interopnames.SystemStoragePut)
emit.Opcode(w.BinWriter, opcode.RET)
emit.Opcodes(w.BinWriter, opcode.RET)
m := manifest.NewManifest(h)
m.Features = smartcontract.HasStorage

View file

@ -314,7 +314,7 @@ func (p *PublicKey) GetVerificationScript() []byte {
return buf.Bytes()
}
emit.Bytes(buf.BinWriter, b)
emit.Opcode(buf.BinWriter, opcode.PUSHNULL)
emit.Opcodes(buf.BinWriter, opcode.PUSHNULL)
emit.Syscall(buf.BinWriter, interopnames.NeoCryptoVerifyWithECDsaSecp256r1)
return buf.Bytes()

View file

@ -132,7 +132,7 @@ func (c *Client) CreateNEP5MultiTransferTx(acc *wallet.Account, gas int64, recip
for i := range recipients {
emit.AppCallWithOperationAndArgs(w.BinWriter, recipients[i].Token, "transfer", from,
recipients[i].Address, recipients[i].Amount)
emit.Opcode(w.BinWriter, opcode.ASSERT)
emit.Opcodes(w.BinWriter, opcode.ASSERT)
}
return c.CreateTxFromScript(w.Bytes(), acc, -1, gas, transaction.Signer{
Account: acc.Contract.ScriptHash(),

View file

@ -107,7 +107,7 @@ func expandArrayIntoScript(script *io.BinWriter, slice []Param) error {
return err
}
emit.Int(script, int64(len(val)))
emit.Opcode(script, opcode.PACK)
emit.Opcodes(script, opcode.PACK)
default:
return fmt.Errorf("parameter type %v is not supported", fp.Type)
}
@ -139,7 +139,7 @@ func CreateFunctionInvocationScript(contract util.Uint160, params Params) ([]byt
return nil, err
}
emit.Int(script.BinWriter, int64(len(slice)))
emit.Opcode(script.BinWriter, opcode.PACK)
emit.Opcodes(script.BinWriter, opcode.PACK)
}
}

View file

@ -31,7 +31,7 @@ func CreateMultiSigRedeemScript(m int, publicKeys keys.PublicKeys) ([]byte, erro
emit.Bytes(buf.BinWriter, pubKey.Bytes())
}
emit.Int(buf.BinWriter, int64(len(publicKeys)))
emit.Opcode(buf.BinWriter, opcode.PUSHNULL)
emit.Opcodes(buf.BinWriter, opcode.PUSHNULL)
emit.Syscall(buf.BinWriter, interopnames.NeoCryptoCheckMultisigWithECDsaSecp256r1)
return buf.Bytes(), nil

View file

@ -21,18 +21,20 @@ func Instruction(w *io.BinWriter, op opcode.Opcode, b []byte) {
w.WriteBytes(b)
}
// Opcode emits a single VM Instruction without arguments to the given buffer.
func Opcode(w *io.BinWriter, op opcode.Opcode) {
// Opcodes emits a single VM Instruction without arguments to the given buffer.
func Opcodes(w *io.BinWriter, ops ...opcode.Opcode) {
for _, op := range ops {
w.WriteB(byte(op))
}
}
// Bool emits a bool type the given buffer.
func Bool(w *io.BinWriter, ok bool) {
if ok {
Opcode(w, opcode.PUSHT)
Opcodes(w, opcode.PUSHT)
return
}
Opcode(w, opcode.PUSHF)
Opcodes(w, opcode.PUSHF)
Instruction(w, opcode.CONVERT, []byte{byte(stackitem.BooleanT)})
}
@ -51,15 +53,15 @@ func padRight(s int, buf []byte) []byte {
func Int(w *io.BinWriter, i int64) {
switch {
case i == -1:
Opcode(w, opcode.PUSHM1)
Opcodes(w, opcode.PUSHM1)
case i >= 0 && i < 16:
val := opcode.Opcode(int(opcode.PUSH1) - 1 + int(i))
Opcode(w, val)
Opcodes(w, val)
default:
buf := bigint.ToPreallocatedBytes(big.NewInt(i), make([]byte, 0, 32))
// l != 0 becase of switch
padSize := byte(8 - bits.LeadingZeros8(byte(len(buf)-1)))
Opcode(w, opcode.PUSHINT8+opcode.Opcode(padSize))
Opcodes(w, opcode.PUSHINT8+opcode.Opcode(padSize))
w.WriteBytes(padRight(1<<padSize, buf))
}
}
@ -83,11 +85,11 @@ func Array(w *io.BinWriter, es ...interface{}) {
w.Err = errors.New("unsupported type")
return
}
Opcode(w, opcode.PUSHNULL)
Opcodes(w, opcode.PUSHNULL)
}
}
Int(w, int64(len(es)))
Opcode(w, opcode.PACK)
Opcodes(w, opcode.PACK)
}
// String emits a string to the given buffer.

View file

@ -181,6 +181,13 @@ func TestEmitBool(t *testing.T) {
assert.Equal(t, opcode.Opcode(result[1]), opcode.PUSH0)
}
func TestEmitOpcode(t *testing.T) {
w := io.NewBufBinWriter()
Opcodes(w.BinWriter, opcode.PUSH1, opcode.NEWMAP)
result := w.Bytes()
assert.Equal(t, result, []byte{byte(opcode.PUSH1), byte(opcode.NEWMAP)})
}
func TestEmitString(t *testing.T) {
buf := io.NewBufBinWriter()
str := "City Of Zion"

View file

@ -39,7 +39,7 @@ func TestInteropHook(t *testing.T) {
buf := io.NewBufBinWriter()
emit.Syscall(buf.BinWriter, "foo")
emit.Opcode(buf.BinWriter, opcode.RET)
emit.Opcodes(buf.BinWriter, opcode.RET)
v.Load(buf.Bytes())
runVM(t, v)
assert.Equal(t, 1, v.estack.Len())
@ -773,11 +773,11 @@ func TestSerializeMapCompat(t *testing.T) {
// Create a map, push key and value, add KV to map, serialize.
buf := io.NewBufBinWriter()
emit.Opcode(buf.BinWriter, opcode.NEWMAP)
emit.Opcode(buf.BinWriter, opcode.DUP)
emit.Opcodes(buf.BinWriter, opcode.NEWMAP)
emit.Opcodes(buf.BinWriter, opcode.DUP)
emit.Bytes(buf.BinWriter, []byte("key"))
emit.Bytes(buf.BinWriter, []byte("value"))
emit.Opcode(buf.BinWriter, opcode.SETITEM)
emit.Opcodes(buf.BinWriter, opcode.SETITEM)
emit.Syscall(buf.BinWriter, interopnames.SystemBinarySerialize)
require.NoError(t, buf.Err)
@ -1654,12 +1654,12 @@ func TestSIGN(t *testing.T) {
func TestSimpleCall(t *testing.T) {
buf := io.NewBufBinWriter()
w := buf.BinWriter
emit.Opcode(w, opcode.PUSH2)
emit.Opcodes(w, opcode.PUSH2)
emit.Instruction(w, opcode.CALL, []byte{03})
emit.Opcode(w, opcode.RET)
emit.Opcode(w, opcode.PUSH10)
emit.Opcode(w, opcode.ADD)
emit.Opcode(w, opcode.RET)
emit.Opcodes(w, opcode.RET)
emit.Opcodes(w, opcode.PUSH10)
emit.Opcodes(w, opcode.ADD)
emit.Opcodes(w, opcode.RET)
result := 12
vm := load(buf.Bytes())