From d95d27f8fe5fe87954278c604b0451a9553d7896 Mon Sep 17 00:00:00 2001 From: lash Date: Tue, 4 Apr 2023 08:10:25 +0100 Subject: [PATCH] Implement verifier as writer --- go/vm/debug.go | 148 ++++++++++++++++++++++++++++---------------- go/vm/debug_test.go | 15 +++++ 2 files changed, 111 insertions(+), 52 deletions(-) diff --git a/go/vm/debug.go b/go/vm/debug.go index c74d137..7c25369 100644 --- a/go/vm/debug.go +++ b/go/vm/debug.go @@ -1,121 +1,165 @@ package vm import ( + "bytes" "fmt" + "io" + "log" ) - +// ToString verifies all instructions in bytecode and returns an assmebly code instruction for it. func ToString(b []byte) (string, error) { + buf := bytes.NewBuffer(nil) + n, err := ParseAll(b, buf) + if err != nil { + return "", err + } + log.Printf("Total %v bytes written to string buffer", n) + return buf.String(), nil +} + +// ParseAll parses and verifies all instructions from bytecode. +// +// If writer is not nil, the parsed instruction as assembly code line string is written to it. +// +// Bytecode is consumed (and written) one instruction at a time. +// +// It fails on any parse error encountered before the bytecode EOF is reached. +func ParseAll(b []byte, w io.Writer) (int, error) { var s string + var rs string + var rn int running := true for running { op, bb, err := opSplit(b) b = bb if err != nil { - return "", err + return rn, err } - opString := OpcodeString[op] - if opString == "" { - return "", fmt.Errorf("unknown opcode: %v", op) + s = OpcodeString[op] + if s == "" { + return rn, fmt.Errorf("unknown opcode: %v", op) } - s += opString switch op { case CATCH: r, n, m, bb, err := ParseCatch(b) b = bb - if err != nil { - return "", err + if err == nil { + if w != nil { + vv := 0 + if m { + vv = 1 + } + if w != nil { + rs = fmt.Sprintf("%s %s %v %v # invertmatch=%v\n", s, r, n, vv, m) + } + } } - vv := 0 - if m { - vv = 1 - } - s = fmt.Sprintf("%s %s %v %v # invertmatch=%v", s, r, n, vv, m) case CROAK: n, m, bb, err := ParseCroak(b) b = bb - if err != nil { - return "", err + if err == nil { + if w != nil { + vv := 0 + if m { + vv = 1 + } + rs = fmt.Sprintf("%s %v %v # invertmatch=%v\n", s, n, vv, m) + } } - vv := 0 - if m { - vv = 1 - } - s = fmt.Sprintf("%s %v %v # invertmatch=%v", s, n, vv, m) case LOAD: r, n, bb, err := ParseLoad(b) b = bb - if err != nil { - return "", err + if err == nil { + if w != nil { + rs = fmt.Sprintf("%s %s %v\n", s, r, n) + } } - s = fmt.Sprintf("%s %s %v", s, r, n) case RELOAD: r, bb, err := ParseReload(b) b = bb - if err != nil { - return "", err + if err == nil { + if w != nil { + rs = fmt.Sprintf("%s %s\n", s, r) + } } - s = fmt.Sprintf("%s %s", s, r) case MAP: r, bb, err := ParseMap(b) b = bb - if err != nil { - return "", err + if err == nil { + if w != nil { + rs = fmt.Sprintf("%s %s\n", s, r) + } } - s = fmt.Sprintf("%s %s", s, r) case MOVE: r, bb, err := ParseMove(b) b = bb - if err != nil { - return "", err + if err == nil { + if w != nil { + rs = fmt.Sprintf("%s %s\n", s, r) + } } - s = fmt.Sprintf("%s %s", s, r) case INCMP: r, v, bb, err := ParseInCmp(b) b = bb - if err != nil { - return "", err + if err == nil { + if w != nil { + rs = fmt.Sprintf("%s %s %s\n", s, r, v) + } } - s = fmt.Sprintf("%s %s %s", s, r, v) case HALT: b, err = ParseHalt(b) - if err != nil { - return "", err - } + rs = "HALT\n" case MSIZE: r, v, bb, err := ParseMSize(b) b = bb - if err != nil { - return "", err + if err == nil { + if w != nil { + rs = fmt.Sprintf("%s %v %v\n", s, r, v) + } } - s = fmt.Sprintf("%s %v %v", s, r, v) case MOUT: r, v, bb, err := ParseMOut(b) b = bb - if err != nil { - return "", err + if err == nil { + if w != nil { + rs = fmt.Sprintf("%s %s \"%s\"\n", s, r, v) + } } - s = fmt.Sprintf("%s %s \"%s\"", s, r, v) case MNEXT: r, v, bb, err := ParseMNext(b) b = bb - if err != nil { - return "", err + if err == nil { + if w != nil { + rs = fmt.Sprintf("%s %s \"%s\"\n", s, r, v) + } } - s = fmt.Sprintf("%s %s \"%s\"", s, r, v) case MPREV: r, v, bb, err := ParseMPrev(b) b = bb - if err != nil { - return "", err + if err == nil { + if w != nil { + rs = fmt.Sprintf("%s %s \"%s\"\n", s, r, v) + } } - s = fmt.Sprintf("%s %s \"%s\"", s, r, v) } - s += "\n" + if err != nil { + return rn, err + } + if w != nil { + n, err := io.WriteString(w, rs) + if err != nil { + return rn, err + } + rn += n + log.Printf("wrote %v bytes from instruction %v", n, s) + } + + //rs += "\n" if len(b) == 0 { running = false } } - return s, nil + return rn, nil } diff --git a/go/vm/debug_test.go b/go/vm/debug_test.go index 522f99f..9c75ea3 100644 --- a/go/vm/debug_test.go +++ b/go/vm/debug_test.go @@ -152,3 +152,18 @@ HALT t.Fatalf("expected:\n\t%v\ngot:\n\t%v", expect, r) } } + +func TestVerifyMultiple(t *testing.T) { + b := NewLine(nil, INCMP, []string{"1", "foo"}, nil, nil) + b = NewLine(b, INCMP, []string{"2", "bar"}, nil, nil) + b = NewLine(b, CATCH, []string{"aiee"}, []byte{0x02, 0x9a}, []uint8{0}) + b = NewLine(b, LOAD, []string{"inky"}, []byte{0x2a}, nil) + b = NewLine(b, HALT, nil, nil, nil) + n, err := ParseAll(b, nil) + if err != nil { + t.Fatal(err) + } + if n != 0 { + t.Fatalf("expected write count to be 0, was %v (how is that possible)", n) + } +}