diff --git a/VERSION b/VERSION index bb22182d4..4ef2eb086 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.38.1 +0.39.0 diff --git a/pkg/vm/README.md b/pkg/vm/README.md index fc7a12230..76b440f33 100644 --- a/pkg/vm/README.md +++ b/pkg/vm/README.md @@ -52,27 +52,29 @@ More information about standalone installation coming soon. NEO-GO-VM > help COMMAND USAGE -step step (n) instruction in the program (> step 10) -ops show the opcodes of the current loaded program -ip show the current instruction -estack show evaluation stack details astack show alt stack details -istack show invocation stack details -run execute the current loaded script -cont continue execution of the current loaded script -help show available commands -exit exit the VM prompt break place a breakpoint (> break 1) -load load a script into the VM (> load /path/to/script.avm) +cont continue execution of the current loaded script +estack show evaluation stack details +exit exit the VM prompt +help show available commands +ip show the current instruction +istack show invocation stack details +loadavm load an avm script into the VM (> load /path/to/script.avm) +loadgo compile and load a .go file into the VM (> load /path/to/file.go) +loadhex load a hex string into the VM (> loadhex 006166 ) +ops show the opcodes of the current loaded program +run execute the current loaded script +step step (n) instruction in the program (> step 10) ``` ### Loading in your script -To load a script into the VM: +To load an avm script into the VM: ``` -NEO-GO-VM > load ../contract.avm -READY +NEO-GO-VM > loadavm ../contract.avm +READY: loaded 36 instructions ``` Run the script: @@ -87,6 +89,28 @@ NEO-GO-VM > run ] ``` +You can also directly compile and load `.go` files: + +``` +NEO-GO-VM > loadgo ../contract.go +READY: loaded 36 instructions +``` + +To make it even more complete, you can directly load hex strings into the VM: + +``` +NEO-GO-VM > loadhex 54c56b006c766b00527ac46c766b00c391640b006203005a616c756662030000616c7566 +READY: loaded 36 instructions +NEO-GO-VM > run +[ + { + "value": 10, + "type": "BigInteger" + } +] + +``` + ### Debugging The `neo-go-vm` provides a debugger to inspect your program in-depth. @@ -95,6 +119,7 @@ Step 4 instructions. ``` NEO-GO-VM > step 4 at breakpoint 4 (Opush4) +NEO-GO-VM 4 > ``` Using just `step` will execute 1 instruction at a time. @@ -102,6 +127,7 @@ Using just `step` will execute 1 instruction at a time. ``` NEO-GO-VM > step instruction pointer at 5 (Odup) +NEO-GO-VM 5 > ``` To place breakpoints: @@ -109,14 +135,15 @@ To place breakpoints: ``` NEO-GO-VM > break 10 breakpoint added at instruction 10 -NEO-GO-VM > resume +NEO-GO-VM > cont at breakpoint 10 (Osetitem) +NEO-GO-VM 10 > cont ``` -Inspecting the stack: +Inspecting the evaluation stack: ``` -NEO-GO-VM > stack +NEO-GO-VM > estack [ { "value": [ @@ -137,4 +164,7 @@ NEO-GO-VM > stack ] ``` -And a lot more features coming next weeks.. +There are more stacks that you can inspect. +- `astack` alt stack +- `istack` invocation stack + diff --git a/pkg/vm/cli/cli.go b/pkg/vm/cli/cli.go index 0063d5eaf..782adf5ed 100644 --- a/pkg/vm/cli/cli.go +++ b/pkg/vm/cli/cli.go @@ -2,40 +2,45 @@ package cli import ( "bufio" + "bytes" + "encoding/hex" "fmt" + "io/ioutil" "os" + "sort" "strconv" "strings" "text/tabwriter" "github.com/CityOfZion/neo-go/pkg/vm" + "github.com/CityOfZion/neo-go/pkg/vm/compiler" ) // command describes a VM command. type command struct { // number of minimun arguments the command needs. args int - // description of the command. usage string - // whether the VM needs to be "ready" to execute this command. ready bool } var commands = map[string]command{ - "help": {0, "show available commands", false}, - "exit": {0, "exit the VM prompt", false}, - "ip": {0, "show the current instruction", true}, - "break": {1, "place a breakpoint (> break 1)", true}, - "estack": {0, "show evaluation stack details", false}, - "astack": {0, "show alt stack details", false}, - "istack": {0, "show invocation stack details", false}, - "load": {1, "load a script into the VM (> load /path/to/script.avm)", false}, - "run": {0, "execute the current loaded script", true}, - "cont": {0, "continue execution of the current loaded script", true}, - "step": {0, "step (n) instruction in the program (> step 10)", true}, - "ops": {0, "show the opcodes of the current loaded program", true}, + "help": {0, "show available commands", false}, + "exit": {0, "exit the VM prompt", false}, + "ip": {0, "show the current instruction", true}, + "break": {1, "place a breakpoint (> break 1)", true}, + "estack": {0, "show evaluation stack details", false}, + "astack": {0, "show alt stack details", false}, + "istack": {0, "show invocation stack details", false}, + "loadavm": {1, "load an avm script into the VM (> load /path/to/script.avm)", false}, + "loadhex": {1, "load a hex string into the VM (> loadhex 006166 )", false}, + "loadgo": {1, "compile and load a .go file into the VM (> load /path/to/file.go)", false}, + "run": {0, "execute the current loaded script", true}, + "cont": {0, "continue execution of the current loaded script", true}, + "step": {0, "step (n) instruction in the program (> step 10)", true}, + "ops": {0, "show the opcodes of the current loaded program", true}, } // VMCLI object for interacting with the VM. @@ -67,14 +72,7 @@ func (c *VMCLI) handleCommand(cmd string, args ...string) { switch cmd { case "help": - fmt.Println() - w := tabwriter.NewWriter(os.Stdout, 0, 0, 4, ' ', 0) - fmt.Fprintln(w, "COMMAND\tUSAGE") - for name, details := range commands { - fmt.Fprintf(w, "%s\t%s\n", name, details.usage) - } - w.Flush() - fmt.Println() + printHelp() case "exit": fmt.Println("Bye!") @@ -97,13 +95,36 @@ func (c *VMCLI) handleCommand(cmd string, args ...string) { case "estack", "istack", "astack": fmt.Println(c.vm.Stack(cmd)) - case "load": + case "loadavm": if err := c.vm.LoadFile(args[0]); err != nil { fmt.Println(err) } else { fmt.Printf("READY: loaded %d instructions\n", c.vm.Context().LenInstr()) } + case "loadhex": + b, err := hex.DecodeString(args[0]) + if err != nil { + fmt.Println(err) + return + } + c.vm.Load(b) + fmt.Printf("READY: loaded %d instructions\n", c.vm.Context().LenInstr()) + + case "loadgo": + fb, err := ioutil.ReadFile(args[0]) + if err != nil { + fmt.Println(err) + return + } + b, err := compiler.Compile(bytes.NewReader(fb), &compiler.Options{}) + if err != nil { + fmt.Println(err) + return + } + c.vm.Load(b) + fmt.Printf("READY: loaded %d instructions\n", c.vm.Context().LenInstr()) + case "run", "cont": c.vm.Run() @@ -132,7 +153,11 @@ func (c *VMCLI) Run() error { printLogo() reader := bufio.NewReader(os.Stdin) for { - fmt.Print("NEO-GO-VM > ") + if c.vm.Ready() && c.vm.Context().IP()-1 >= 0 { + fmt.Printf("NEO-GO-VM %d > ", c.vm.Context().IP()-1) + } else { + fmt.Print("NEO-GO-VM > ") + } input, _ := reader.ReadString('\n') input = strings.Trim(input, "\n") if len(input) != 0 { @@ -147,6 +172,25 @@ func (c *VMCLI) Run() error { } } +func printHelp() { + names := make([]string, len(commands)) + i := 0 + for name, _ := range commands { + names[i] = name + i++ + } + sort.Strings(names) + + fmt.Println() + w := tabwriter.NewWriter(os.Stdout, 0, 0, 4, ' ', 0) + fmt.Fprintln(w, "COMMAND\tUSAGE") + for _, name := range names { + fmt.Fprintf(w, "%s\t%s\n", name, commands[name].usage) + } + w.Flush() + fmt.Println() +} + func printLogo() { logo := ` _ ____________ __________ _ ____ ___ diff --git a/pkg/vm/compiler/README.md b/pkg/vm/compiler/README.md index 6cd0456c3..fac47a5a7 100644 --- a/pkg/vm/compiler/README.md +++ b/pkg/vm/compiler/README.md @@ -15,19 +15,19 @@ The neo-go compiler compiles Go programs to bytecode that the NEO virtual machin - basic if statements - binary expressions - return statements +- for loops - imports ### Go builtins - len +- append ### VM API (interop layer) - storage - runtime ## Not yet implemented -- for loops - range -- builtin (append) - some parts of the interop layer (VM API) ## Not supported diff --git a/pkg/vm/compiler/analysis.go b/pkg/vm/compiler/analysis.go index f9ee20179..85a267e61 100644 --- a/pkg/vm/compiler/analysis.go +++ b/pkg/vm/compiler/analysis.go @@ -192,3 +192,7 @@ func isNoRetSyscall(name string) bool { } return false } + +func isStringType(t types.Type) bool { + return t.String() == "string" +} diff --git a/pkg/vm/compiler/codegen.go b/pkg/vm/compiler/codegen.go index bc4285b88..49b0e91c2 100644 --- a/pkg/vm/compiler/codegen.go +++ b/pkg/vm/compiler/codegen.go @@ -380,15 +380,16 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { case *ast.CallExpr: var ( - f *funcScope - ok bool - numArgs = len(n.Args) + f *funcScope + ok bool + numArgs = len(n.Args) + isBuiltin = isBuiltin(n.Fun) ) switch fun := n.Fun.(type) { case *ast.Ident: f, ok = c.funcs[fun.Name] - if !ok && !isBuiltin(n.Fun) { + if !ok && !isBuiltin { log.Fatalf("could not resolve function %s", fun.Name) } case *ast.SelectorExpr: @@ -410,12 +411,15 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { for _, arg := range n.Args { ast.Walk(c, arg) } - if numArgs == 2 { - emitOpcode(c.prog, vm.Oswap) - } - if numArgs == 3 { - emitInt(c.prog, 2) - emitOpcode(c.prog, vm.Oxswap) + // Do not swap for builtin functions. + if !isBuiltin { + if numArgs == 2 { + emitOpcode(c.prog, vm.Oswap) + } + if numArgs == 3 { + emitInt(c.prog, 2) + emitOpcode(c.prog, vm.Oxswap) + } } // c# compiler adds a NOP (0x61) before every function call. Dont think its relevant @@ -424,7 +428,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { emitOpcode(c.prog, vm.Onop) // Check builtin first to avoid nil pointer on funcScope! - if isBuiltin(n.Fun) { + if isBuiltin { // Use the ident to check, builtins are not in func scopes. // We can be sure builtins are of type *ast.Ident. c.convertBuiltin(n.Fun.(*ast.Ident).Name, n) @@ -456,7 +460,9 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { return nil case *ast.UnaryExpr: - // TODO(@anthdm) + ast.Walk(c, n.X) + c.convertToken(n.Op) + return nil case *ast.IncDecStmt: ast.Walk(c, n.X) @@ -528,9 +534,15 @@ func (c *codegen) convertSyscall(name string) { func (c *codegen) convertBuiltin(name string, expr *ast.CallExpr) { switch name { case "len": - emitOpcode(c.prog, vm.Oarraysize) + arg := expr.Args[0] + typ := c.typeInfo.Types[arg].Type + if isStringType(typ) { + emitOpcode(c.prog, vm.Osize) + } else { + emitOpcode(c.prog, vm.Oarraysize) + } case "append": - log.Fatal("builtin (append) not yet implemented.") + emitOpcode(c.prog, vm.Oappend) } } @@ -621,6 +633,8 @@ func (c *codegen) convertToken(tok token.Token) { emitOpcode(c.prog, vm.Odec) case token.INC: emitOpcode(c.prog, vm.Oinc) + case token.NOT: + emitOpcode(c.prog, vm.Onot) default: log.Fatalf("compiler could not convert token: %s", tok) } @@ -702,7 +716,7 @@ func (c *codegen) writeJumps() { switch vm.Opcode(op) { case vm.Ojmp, vm.Ojmpifnot, vm.Ojmpif, vm.Ocall: index := int16(binary.LittleEndian.Uint16(b[j : j+2])) - if int(index) > len(c.l) { + if int(index) > len(c.l) || int(index) < 0 { continue } offset := uint16(c.l[index] - i) diff --git a/pkg/vm/compiler/tests/bool_test.go b/pkg/vm/compiler/tests/bool_test.go deleted file mode 100644 index 5a59c9d16..000000000 --- a/pkg/vm/compiler/tests/bool_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package compiler - -var boolTestCases = []testCase{ - { - "bool assign", - ` - package foo - func Main() bool { - x := true - return x - } - `, - "52c56b516c766b00527ac46203006c766b00c3616c7566", - }, - { - "bool compare", - ` - package foo - func Main() int { - x := true - if x { - return 10 - } - return 0 - } - `, - "54c56b516c766b00527ac46c766b00c3640b006203005a616c756662030000616c7566", - }, - { - "bool compare verbose", - ` - package foo - func Main() int { - x := true - if x == true { - return 10 - } - return 0 - } - `, - "54c56b516c766b00527ac46c766b00c3519c640b006203005a616c756662030000616c7566", - }, - // { - // "bool invert (unary expr)", - // ` - // package foo - // func Main() int { - // x := true - // if !x { - // return 10 - // } - // return 0 - // } - // `, - // "54c56b516c766b00527ac46c766b00c3630b006203005a616c756662030000616c7566", - // }, -} diff --git a/pkg/vm/compiler/tests/string_test.go b/pkg/vm/compiler/tests/string_test.go deleted file mode 100644 index 284b87c9f..000000000 --- a/pkg/vm/compiler/tests/string_test.go +++ /dev/null @@ -1,15 +0,0 @@ -package compiler - -var stringTestCases = []testCase{ - { - "simple string", - ` - package testcase - func Main() string { - x := "NEO" - return x - } - `, - "52c56b034e454f6c766b00527ac46203006c766b00c3616c7566", - }, -} diff --git a/pkg/vm/context.go b/pkg/vm/context.go index 3c56904b9..ed4fef036 100644 --- a/pkg/vm/context.go +++ b/pkg/vm/context.go @@ -28,10 +28,13 @@ func NewContext(b []byte) *Context { // Next return the next instruction to execute. func (c *Context) Next() Opcode { c.ip++ + if c.ip >= len(c.prog) { + return Oret + } return Opcode(c.prog[c.ip]) } -// IP returns the absosulute instruction without taking 0 into account. +// IP returns the absolute instruction without taking 0 into account. // If that program starts the ip = 0 but IP() will return 1, cause its // the first instruction. func (c *Context) IP() int { diff --git a/pkg/vm/stack.go b/pkg/vm/stack.go index 7b6d05559..80d139324 100644 --- a/pkg/vm/stack.go +++ b/pkg/vm/stack.go @@ -72,6 +72,9 @@ func (e *Element) BigInt() *big.Int { // Bool attempts to get the underlying value of the element as a boolean. // Will panic if the assertion failed which will be catched by the VM. func (e *Element) Bool() bool { + if v, ok := e.value.Value().(*big.Int); ok { + return v.Int64() == 1 + } return e.value.Value().(bool) } diff --git a/pkg/vm/test/array_test.go b/pkg/vm/test/array_test.go deleted file mode 100644 index 0c36e39cf..000000000 --- a/pkg/vm/test/array_test.go +++ /dev/null @@ -1,117 +0,0 @@ -package vm_test - -import ( - "math/big" - - "github.com/CityOfZion/neo-go/pkg/vm" -) - -var arrayTestCases = []testCase{ - { - "assign int array", - ` - package foo - func Main() []int { - x := []int{1, 2, 3} - return x - } - `, - []vm.StackItem{ - vm.NewBigIntegerItem(1), - vm.NewBigIntegerItem(2), - vm.NewBigIntegerItem(3), - }, - }, - { - "assign string array", - ` - package foo - func Main() []string { - x := []string{"foo", "bar", "foobar"} - return x - } - `, - []vm.StackItem{ - vm.NewByteArrayItem([]byte("foo")), - vm.NewByteArrayItem([]byte("bar")), - vm.NewByteArrayItem([]byte("foobar")), - }, - }, - { - "array item assign", - ` - package foo - func Main() int { - x := []int{0, 1, 2} - y := x[0] - return y - } - `, - big.NewInt(0), - }, - { - "array item return", - ` - package foo - func Main() int { - x := []int{0, 1, 2} - return x[1] - } - `, - big.NewInt(1), - }, - { - "array item in bin expr", - ` - package foo - func Main() int { - x := []int{0, 1, 2} - return x[1] + 10 - } - `, - big.NewInt(11), - }, - { - "array item ident", - ` - package foo - func Main() int { - x := 1 - y := []int{0, 1, 2} - return y[x] - } - `, - big.NewInt(1), - }, - { - "array item index with binExpr", - ` - package foo - func Main() int { - x := 1 - y := []int{0, 1, 2} - return y[x + 1] - } - `, - big.NewInt(2), - }, - { - "array item struct", - ` - package foo - - type Bar struct { - arr []int - } - - func Main() int { - b := Bar{ - arr: []int{0, 1, 2}, - } - x := b.arr[2] - return x + 2 - } - `, - big.NewInt(4), - }, -} diff --git a/pkg/vm/test/for_test.go b/pkg/vm/test/for_test.go deleted file mode 100644 index ac0b16234..000000000 --- a/pkg/vm/test/for_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package vm_test - -import ( - "math/big" - "testing" -) - -func TestClassicForLoop(t *testing.T) { - src := ` - package foo - func Main() int { - x := 0 - for i := 0; i < 10; i++ { - x = i - } - return x - } - ` - eval(t, src, big.NewInt(9)) -} - -// TODO: This could be a nasty bug. Output of the VM is 65695. -// Only happens above 100000, could be binary read issue. -//func TestForLoopBigIter(t *testing.T) { -// src := ` -// package foo -// func Main() int { -// x := 0 -// for i := 0; i < 100000; i++ { -// x = i -// } -// return x -// } -// ` -// eval(t, src, big.NewInt(99999)) -//} diff --git a/pkg/vm/test/assign_test.go b/pkg/vm/tests/assign_test.go similarity index 100% rename from pkg/vm/test/assign_test.go rename to pkg/vm/tests/assign_test.go diff --git a/pkg/vm/test/binary_expr_test.go b/pkg/vm/tests/binary_expr_test.go similarity index 100% rename from pkg/vm/test/binary_expr_test.go rename to pkg/vm/tests/binary_expr_test.go diff --git a/pkg/vm/tests/for_test.go b/pkg/vm/tests/for_test.go new file mode 100644 index 000000000..1e17b3d5a --- /dev/null +++ b/pkg/vm/tests/for_test.go @@ -0,0 +1,306 @@ +package vm_test + +import ( + "math/big" + "testing" + + "github.com/CityOfZion/neo-go/pkg/vm" +) + +func TestArrayFieldInStruct(t *testing.T) { + src := ` + package foo + + type Bar struct { + arr []int + } + + func Main() int { + b := Bar{ + arr: []int{0, 1, 2}, + } + x := b.arr[2] + return x + 2 + } + ` + eval(t, src, big.NewInt(4)) +} + +func TestArrayItemGetIndexBinaryExpr(t *testing.T) { + src := ` + package foo + func Main() int { + x := 1 + y := []int{0, 1, 2} + return y[x + 1] + } + ` + eval(t, src, big.NewInt(2)) +} + +func TestArrayItemGetIndexIdent(t *testing.T) { + src := ` + package foo + func Main() int { + x := 1 + y := []int{0, 1, 2} + return y[x] + } + ` + eval(t, src, big.NewInt(1)) +} + +func TestArrayItemBinExpr(t *testing.T) { + src := ` + package foo + func Main() int { + x := []int{0, 1, 2} + return x[1] + 10 + } + ` + eval(t, src, big.NewInt(11)) +} + +func TestArrayItemReturn(t *testing.T) { + src := ` + package foo + func Main() int { + arr := []int{0, 1, 2} + return arr[1] + } + ` + eval(t, src, big.NewInt(1)) +} + +func TestArrayItemAssign(t *testing.T) { + src := ` + package foo + func Main() int { + arr := []int{1, 2, 3} + y := arr[0] + return y + } + ` + eval(t, src, big.NewInt(1)) +} + +func TestStringArray(t *testing.T) { + src := ` + package foo + func Main() []string { + x := []string{"foo", "bar", "foobar"} + return x + } + ` + eval(t, src, []vm.StackItem{ + vm.NewByteArrayItem([]byte("foo")), + vm.NewByteArrayItem([]byte("bar")), + vm.NewByteArrayItem([]byte("foobar")), + }) +} + +func TestIntArray(t *testing.T) { + src := ` + package foo + func Main() []int { + arr := []int{1, 2, 3} + return arr + } + ` + eval(t, src, []vm.StackItem{ + vm.NewBigIntegerItem(1), + vm.NewBigIntegerItem(2), + vm.NewBigIntegerItem(3), + }) +} + +func TestArrayLen(t *testing.T) { + src := ` + package foo + func Main() int { + arr := []int{0, 1, 2} + return len(arr) + } + ` + eval(t, src, big.NewInt(3)) +} + +func TestStringLen(t *testing.T) { + src := ` + package foo + func Main() int { + str := "this is medium sized string" + return len(str) + } + ` + eval(t, src, big.NewInt(27)) +} + +func TestSimpleString(t *testing.T) { + src := ` + package foo + func Main() string { + x := "NEO" + return x + } + ` + eval(t, src, vm.NewByteArrayItem([]byte("NEO")).Value()) +} + +func TestBoolAssign(t *testing.T) { + src := ` + package foo + func Main() bool { + x := true + return x + } + ` + eval(t, src, big.NewInt(1)) +} + +func TestBoolCompare(t *testing.T) { + src := ` + package foo + func Main() int { + x := true + if x { + return 10 + } + return 0 + } + ` + eval(t, src, big.NewInt(10)) +} + +func TestBoolCompareVerbose(t *testing.T) { + src := ` + package foo + func Main() int { + x := true + if x == true { + return 10 + } + return 0 + } + ` + eval(t, src, big.NewInt(10)) +} + +func TestUnaryExpr(t *testing.T) { + src := ` + package foo + func Main() bool { + x := false + return !x + } + ` + eval(t, src, true) +} + +func TestIfUnaryInvertPass(t *testing.T) { + src := ` + package foo + func Main() int { + x := false + if !x { + return 10 + } + return 0 + } + ` + eval(t, src, big.NewInt(10)) +} + +func TestIfUnaryInvert(t *testing.T) { + src := ` + package foo + func Main() int { + x := true + if !x { + return 10 + } + return 0 + } + ` + eval(t, src, big.NewInt(0)) +} + +func TestAppendString(t *testing.T) { + src := ` + package foo + func Main() string { + arr := []string{"a", "b", "c"} + arr = append(arr, "d") + return arr[3] + } + ` + eval(t, src, vm.NewByteArrayItem([]byte("d")).Value()) +} + +func TestAppendInt(t *testing.T) { + src := ` + package foo + func Main() int { + arr := []int{0, 1, 2} + arr = append(arr, 3) + return arr[3] + } + ` + eval(t, src, big.NewInt(3)) +} + +func TestClassicForLoop(t *testing.T) { + src := ` + package foo + func Main() int { + x := 0 + for i := 0; i < 10; i++ { + x = i + } + return x + } + ` + eval(t, src, big.NewInt(9)) +} + +func TestInc(t *testing.T) { + src := ` + package foo + func Main() int { + x := 0 + x++ + return x + } + ` + + eval(t, src, big.NewInt(1)) +} + +func TestDec(t *testing.T) { + src := ` + package foo + func Main() int { + x := 2 + x-- + return x + } + ` + + eval(t, src, big.NewInt(1)) +} + +// TODO: This could be a nasty bug. Output of the VM is 65695. +// Only happens above 100000, could be binary read issue. +//func TestForLoopBigIter(t *testing.T) { +// src := ` +// package foo +// func Main() int { +// x := 0 +// for i := 0; i < 100000; i++ { +// x = i +// } +// return x +// } +// ` +// eval(t, src, big.NewInt(99999)) +//} diff --git a/pkg/vm/test/numeric_test.go b/pkg/vm/tests/numeric_test.go similarity index 100% rename from pkg/vm/test/numeric_test.go rename to pkg/vm/tests/numeric_test.go diff --git a/pkg/vm/test/struct_test.go b/pkg/vm/tests/struct_test.go similarity index 100% rename from pkg/vm/test/struct_test.go rename to pkg/vm/tests/struct_test.go diff --git a/pkg/vm/test/vm_test.go b/pkg/vm/tests/vm_test.go similarity index 95% rename from pkg/vm/test/vm_test.go rename to pkg/vm/tests/vm_test.go index d0a8e9b9b..f533578a6 100644 --- a/pkg/vm/test/vm_test.go +++ b/pkg/vm/tests/vm_test.go @@ -33,7 +33,6 @@ func TestVMAndCompilerCases(t *testing.T) { testCases := []testCase{} testCases = append(testCases, numericTestCases...) testCases = append(testCases, assignTestCases...) - testCases = append(testCases, arrayTestCases...) testCases = append(testCases, binaryExprTestCases...) testCases = append(testCases, structTestCases...) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index fbb3149b9..fa197a323 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -186,10 +186,6 @@ func (v *VM) Run() { func (v *VM) Step() { ctx := v.Context() op := ctx.Next() - if ctx.ip >= len(ctx.prog) { - op = Oret - } - v.execute(ctx, op) // re-peek the context as it could been changed during execution. @@ -460,8 +456,8 @@ func (v *VM) execute(ctx *Context, op Opcode) { v.estack.PushVal(x.Abs(x)) case Onot: - x := v.estack.Pop().BigInt() - v.estack.PushVal(x.Not(x)) + x := v.estack.Pop().Bool() + v.estack.PushVal(!x) case Onz: panic("todo NZ") @@ -486,6 +482,7 @@ func (v *VM) execute(ctx *Context, op Opcode) { case *ArrayItem, *StructItem: arr := t.Value().([]StackItem) arr = append(arr, itemElem.value) + v.estack.PushVal(arr) default: panic("APPEND: not of underlying type Array") } @@ -558,6 +555,14 @@ func (v *VM) execute(ctx *Context, op Opcode) { } v.estack.PushVal(len(arr)) + case Osize: + elem := v.estack.Pop() + arr, ok := elem.value.Value().([]uint8) + if !ok { + panic("SIZE: item not of type []uint8") + } + v.estack.PushVal(len(arr)) + case Ojmp, Ojmpif, Ojmpifnot: var ( rOffset = int16(ctx.readUint16())