diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index 27cfeca14..8110df9ce 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -900,23 +900,47 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { c.currentSwitch = label ast.Walk(c, n.X) - emit.Syscall(c.prog.BinWriter, "System.Iterator.Create") - c.pushStackLabel(label, 1) + // Implementation is a bit different for slices and maps: + // 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) + if isMap { + emit.Opcode(c.prog.BinWriter, opcode.KEYS) + emit.Opcode(c.prog.BinWriter, opcode.DUP) + } + emit.Opcode(c.prog.BinWriter, opcode.SIZE) + emit.Opcode(c.prog.BinWriter, opcode.PUSH0) + + stackSize := 3 // slice, len(slice), index + if isMap { + stackSize++ // map, keys, len(keys), index in keys + } + c.pushStackLabel(label, stackSize) c.setLabel(start) - emit.Opcode(c.prog.BinWriter, opcode.DUP) - emit.Syscall(c.prog.BinWriter, "System.Enumerator.Next") - emit.Jmp(c.prog.BinWriter, opcode.JMPIFNOTL, end) + emit.Opcode(c.prog.BinWriter, opcode.OVER) + emit.Opcode(c.prog.BinWriter, opcode.OVER) + emit.Jmp(c.prog.BinWriter, opcode.JMPLEL, end) if n.Key != nil { - emit.Opcode(c.prog.BinWriter, opcode.DUP) - emit.Syscall(c.prog.BinWriter, "System.Iterator.Key") + if isMap { + c.rangeLoadKey() + } else { + emit.Opcode(c.prog.BinWriter, opcode.DUP) + } c.emitStoreVar(n.Key.(*ast.Ident).Name) } if n.Value != nil { - emit.Opcode(c.prog.BinWriter, opcode.DUP) - emit.Syscall(c.prog.BinWriter, "System.Enumerator.Value") + c.rangeLoadKey() + 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) + } c.emitStoreVar(n.Value.(*ast.Ident).Name) } @@ -924,6 +948,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { c.setLabel(post) + emit.Opcode(c.prog.BinWriter, opcode.INC) emit.Jmp(c.prog.BinWriter, opcode.JMPL, start) c.setLabel(end) @@ -945,6 +970,13 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { return c } +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) +} + func isFallthroughStmt(c ast.Node) bool { s, ok := c.(*ast.BranchStmt) return ok && s.Tok == token.FALLTHROUGH