Implement verifier as writer

This commit is contained in:
lash 2023-04-04 08:10:25 +01:00
parent 684de95198
commit d95d27f8fe
Signed by untrusted user who does not match committer: lash
GPG Key ID: 21D2E7BB88C2A746
2 changed files with 111 additions and 52 deletions

View File

@ -1,121 +1,165 @@
package vm package vm
import ( import (
"bytes"
"fmt" "fmt"
"io"
"log"
) )
// ToString verifies all instructions in bytecode and returns an assmebly code instruction for it.
func ToString(b []byte) (string, error) { 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 s string
var rs string
var rn int
running := true running := true
for running { for running {
op, bb, err := opSplit(b) op, bb, err := opSplit(b)
b = bb b = bb
if err != nil { if err != nil {
return "", err return rn, err
} }
opString := OpcodeString[op] s = OpcodeString[op]
if opString == "" { if s == "" {
return "", fmt.Errorf("unknown opcode: %v", op) return rn, fmt.Errorf("unknown opcode: %v", op)
} }
s += opString
switch op { switch op {
case CATCH: case CATCH:
r, n, m, bb, err := ParseCatch(b) r, n, m, bb, err := ParseCatch(b)
b = bb b = bb
if err != nil { if err == nil {
return "", err if w != nil {
}
vv := 0 vv := 0
if m { if m {
vv = 1 vv = 1
} }
s = fmt.Sprintf("%s %s %v %v # invertmatch=%v", s, r, n, vv, m) if w != nil {
rs = fmt.Sprintf("%s %s %v %v # invertmatch=%v\n", s, r, n, vv, m)
}
}
}
case CROAK: case CROAK:
n, m, bb, err := ParseCroak(b) n, m, bb, err := ParseCroak(b)
b = bb b = bb
if err != nil { if err == nil {
return "", err if w != nil {
}
vv := 0 vv := 0
if m { if m {
vv = 1 vv = 1
} }
s = fmt.Sprintf("%s %v %v # invertmatch=%v", s, n, vv, m) rs = fmt.Sprintf("%s %v %v # invertmatch=%v\n", s, n, vv, m)
}
}
case LOAD: case LOAD:
r, n, bb, err := ParseLoad(b) r, n, bb, err := ParseLoad(b)
b = bb b = bb
if err != nil { if err == nil {
return "", err if w != nil {
rs = fmt.Sprintf("%s %s %v\n", s, r, n)
}
} }
s = fmt.Sprintf("%s %s %v", s, r, n)
case RELOAD: case RELOAD:
r, bb, err := ParseReload(b) r, bb, err := ParseReload(b)
b = bb b = bb
if err != nil { if err == nil {
return "", err if w != nil {
rs = fmt.Sprintf("%s %s\n", s, r)
}
} }
s = fmt.Sprintf("%s %s", s, r)
case MAP: case MAP:
r, bb, err := ParseMap(b) r, bb, err := ParseMap(b)
b = bb b = bb
if err != nil { if err == nil {
return "", err if w != nil {
rs = fmt.Sprintf("%s %s\n", s, r)
}
} }
s = fmt.Sprintf("%s %s", s, r)
case MOVE: case MOVE:
r, bb, err := ParseMove(b) r, bb, err := ParseMove(b)
b = bb b = bb
if err != nil { if err == nil {
return "", err if w != nil {
rs = fmt.Sprintf("%s %s\n", s, r)
}
} }
s = fmt.Sprintf("%s %s", s, r)
case INCMP: case INCMP:
r, v, bb, err := ParseInCmp(b) r, v, bb, err := ParseInCmp(b)
b = bb b = bb
if err != nil { if err == nil {
return "", err if w != nil {
rs = fmt.Sprintf("%s %s %s\n", s, r, v)
}
} }
s = fmt.Sprintf("%s %s %s", s, r, v)
case HALT: case HALT:
b, err = ParseHalt(b) b, err = ParseHalt(b)
if err != nil { rs = "HALT\n"
return "", err
}
case MSIZE: case MSIZE:
r, v, bb, err := ParseMSize(b) r, v, bb, err := ParseMSize(b)
b = bb b = bb
if err != nil { if err == nil {
return "", err if w != nil {
rs = fmt.Sprintf("%s %v %v\n", s, r, v)
}
} }
s = fmt.Sprintf("%s %v %v", s, r, v)
case MOUT: case MOUT:
r, v, bb, err := ParseMOut(b) r, v, bb, err := ParseMOut(b)
b = bb b = bb
if err != nil { if err == nil {
return "", err if w != nil {
rs = fmt.Sprintf("%s %s \"%s\"\n", s, r, v)
}
} }
s = fmt.Sprintf("%s %s \"%s\"", s, r, v)
case MNEXT: case MNEXT:
r, v, bb, err := ParseMNext(b) r, v, bb, err := ParseMNext(b)
b = bb b = bb
if err != nil { if err == nil {
return "", err if w != nil {
rs = fmt.Sprintf("%s %s \"%s\"\n", s, r, v)
}
} }
s = fmt.Sprintf("%s %s \"%s\"", s, r, v)
case MPREV: case MPREV:
r, v, bb, err := ParseMPrev(b) r, v, bb, err := ParseMPrev(b)
b = bb b = bb
if err == nil {
if w != nil {
rs = fmt.Sprintf("%s %s \"%s\"\n", s, r, v)
}
}
}
if err != nil { if err != nil {
return "", err return rn, err
} }
s = fmt.Sprintf("%s %s \"%s\"", s, r, v) if w != nil {
n, err := io.WriteString(w, rs)
if err != nil {
return rn, err
} }
s += "\n" rn += n
log.Printf("wrote %v bytes from instruction %v", n, s)
}
//rs += "\n"
if len(b) == 0 { if len(b) == 0 {
running = false running = false
} }
} }
return s, nil return rn, nil
} }

View File

@ -152,3 +152,18 @@ HALT
t.Fatalf("expected:\n\t%v\ngot:\n\t%v", expect, r) 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)
}
}