From 6325ef4afda403c1b4db5f6d6ee3f13e8813078a Mon Sep 17 00:00:00 2001 From: lash Date: Thu, 6 Apr 2023 09:54:51 +0100 Subject: [PATCH] Catch missing input on empty bytecode buffer --- go/state/flag.go | 1 + go/vm/runner.go | 37 ++++++++++++++++++++++++++++++++++ go/vm/runner_test.go | 48 +++++++++++++++++++++++++++----------------- 3 files changed, 68 insertions(+), 18 deletions(-) diff --git a/go/state/flag.go b/go/state/flag.go index e235381..611ed6d 100644 --- a/go/state/flag.go +++ b/go/state/flag.go @@ -2,4 +2,5 @@ package state const ( FLAG_INMATCH = 1 + FLAG_TERMINATE = 2 ) diff --git a/go/vm/runner.go b/go/vm/runner.go index f769b79..8bab0dc 100644 --- a/go/vm/runner.go +++ b/go/vm/runner.go @@ -57,6 +57,12 @@ func Run(b []byte, st *state.State, rs resource.Resource, ctx context.Context) ( if err != nil { return b, err } + if len(b) == 0 { + b, err = RunDeadCheck(b, st, rs, ctx) + if err != nil { + return b, err + } + } if len(b) == 0 { return []byte{}, nil } @@ -64,6 +70,36 @@ func Run(b []byte, st *state.State, rs resource.Resource, ctx context.Context) ( return b, nil } +// RunDeadCheck determines whether a state of empty bytecode should result in termination. +// +// If there is remaining bytecode, this method is a noop. +// +// If input has not been matched, a default invalid input page should be generated aswell as a possiblity of return to last screen (or exit). +// +// If the termination flag has been set but not yet handled, execution is allowed to terminate. +func RunDeadCheck(b []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) { + if len(b) > 0 { + return b, nil + } + log.Printf("no code remaining, let's check if we terminate") + r, err := matchFlag(st, state.FLAG_TERMINATE, false) + if err != nil { + panic(err) + } + if r { + return b, nil + } + location := st.Where() + if location == "" { + return b, fmt.Errorf("dead runner with no current location") + } + b = NewLine(nil, MOVE, []string{"_catch"}, nil, nil) + b = NewLine(b, HALT, nil, nil, nil) + b = NewLine(b, MOVE, []string{location}, nil, nil) + log.Printf("code is now %x", b) + return b, nil +} + // RunMap executes the MAP opcode func RunMap(b []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) { sym, b, err := ParseMap(b) @@ -73,6 +109,7 @@ func RunMap(b []byte, st *state.State, rs resource.Resource, ctx context.Context // RunMap executes the CATCH opcode func RunCatch(b []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) { + log.Printf("zzz %x", b) sym, sig, mode, b, err := ParseCatch(b) if err != nil { return b, err diff --git a/go/vm/runner_test.go b/go/vm/runner_test.go index 3603717..73c6649 100644 --- a/go/vm/runner_test.go +++ b/go/vm/runner_test.go @@ -104,6 +104,7 @@ func TestRunLoadRender(t *testing.T) { var err error b := NewLine(nil, LOAD, []string{"one"}, []byte{0x0a}, nil) + b = NewLine(b, HALT, nil, nil, nil) b, err = Run(b, &st, &rs, context.TODO()) if err != nil { t.Error(err) @@ -127,11 +128,13 @@ func TestRunLoadRender(t *testing.T) { } b = NewLine(nil, LOAD, []string{"two"}, []byte{0x0a}, nil) + b = NewLine(b, HALT, nil, nil, nil) b, err = Run(b, &st, &rs, context.TODO()) if err != nil { t.Error(err) } b = NewLine(nil, MAP, []string{"one"}, nil, nil) + b = NewLine(b, HALT, nil, nil, nil) _, err = Run(b, &st, &rs, context.TODO()) if err != nil { t.Error(err) @@ -155,6 +158,7 @@ func TestRunMultiple(t *testing.T) { rs := TestResource{} b := NewLine(nil, LOAD, []string{"one"}, []byte{0x00}, nil) b = NewLine(b, LOAD, []string{"two"}, []byte{42}, nil) + b = NewLine(b, HALT, nil, nil, nil) b, err := Run(b, &st, &rs, context.TODO()) if err != nil { t.Error(err) @@ -167,33 +171,35 @@ func TestRunMultiple(t *testing.T) { func TestRunReload(t *testing.T) { st := state.NewState(5) rs := TestResource{} - b := NewLine(nil, LOAD, []string{"dyn"}, nil, []uint8{0}) + b := NewLine(nil, MOVE, []string{"root"}, nil, nil) + b = NewLine(b, LOAD, []string{"dyn"}, nil, []uint8{0}) b = NewLine(b, MAP, []string{"dyn"}, nil, nil) + b = NewLine(b, HALT, nil, nil, nil) _, err := Run(b, &st, &rs, context.TODO()) if err != nil { - t.Error(err) + t.Fatal(err) } r, err := st.Val("dyn") if err != nil { - t.Error(err) + t.Fatal(err) } if r != "three" { - t.Errorf("expected result 'three', got %v", r) + t.Fatalf("expected result 'three', got %v", r) } dynVal = "baz" - b = []byte{} - b = NewLine(b, RELOAD, []string{"dyn"}, nil, nil) + b = NewLine(nil, RELOAD, []string{"dyn"}, nil, nil) + b = NewLine(b, HALT, nil, nil, nil) _, err = Run(b, &st, &rs, context.TODO()) if err != nil { - t.Error(err) + t.Fatal(err) } r, err = st.Val("dyn") if err != nil { - t.Error(err) + t.Fatal(err) } log.Printf("dun now %s", r) if r != "baz" { - t.Errorf("expected result 'baz', got %v", r) + t.Fatalf("expected result 'baz', got %v", r) } } @@ -225,7 +231,8 @@ func TestRunArg(t *testing.T) { input := []byte("bar") _ = st.SetInput(input) - bi := NewLine([]byte{}, INCMP, []string{"bar", "baz"}, nil, nil) + bi := NewLine(nil, INCMP, []string{"bar", "baz"}, nil, nil) + bi = NewLine(bi, HALT, nil, nil, nil) b, err := Run(bi, &st, &rs, context.TODO()) if err != nil { t.Error(err) @@ -252,6 +259,7 @@ func TestRunInputHandler(t *testing.T) { bi = NewLine(bi, LOAD, []string{"two"}, []byte{0x03}, nil) bi = NewLine(bi, MAP, []string{"one"}, nil, nil) bi = NewLine(bi, MAP, []string{"two"}, nil, nil) + bi = NewLine(bi, HALT, nil, nil, nil) var err error _, err = Run(bi, &st, &rs, context.TODO()) @@ -271,21 +279,22 @@ func TestRunArgInvalid(t *testing.T) { _ = st.SetInput([]byte("foo")) var err error - - b := NewLine([]byte{}, INCMP, []string{"bar", "baz"}, nil, nil) - b = NewLine(b, CATCH, []string{"_catch"}, []byte{state.FLAG_INMATCH}, []uint8{1}) + + st.Down("root") + b := NewLine(nil, INCMP, []string{"bar", "baz"}, nil, nil) + //b = NewLine(b, CATCH, []string{"_catch"}, []byte{state.FLAG_INMATCH}, []uint8{1}) b, err = Run(b, &st, &rs, context.TODO()) if err != nil { - t.Error(err) + t.Fatal(err) } - l := len(b) - if l != 0 { - t.Errorf("expected empty remainder, got length %v: %v", l, b) + expect := NewLine(nil, MOVE, []string{"root"}, nil, nil) + if !bytes.Equal(b, expect) { + t.Fatalf("expected:\n\t%x\ngot:\b\t%x\n", expect, b) } r := st.Where() if r != "_catch" { - t.Errorf("expected where-state _catch, got %v", r) + t.Fatalf("expected where-state _catch, got %v", r) } } @@ -298,6 +307,7 @@ func TestRunMenu(t *testing.T) { b := NewLine(nil, MOVE, []string{"foo"}, nil, nil) b = NewLine(b, MOUT, []string{"0", "one"}, nil, nil) b = NewLine(b, MOUT, []string{"1", "two"}, nil, nil) + b = NewLine(b, HALT, nil, nil, nil) b, err = Run(b, &st, &rs, context.TODO()) if err != nil { @@ -331,6 +341,7 @@ func TestRunMenuBrowse(t *testing.T) { b = NewLine(b, MPREV, []string{"22", "two"}, nil, nil) b = NewLine(b, MOUT, []string{"0", "one"}, nil, nil) b = NewLine(b, MOUT, []string{"1", "two"}, nil, nil) + b = NewLine(b, HALT, nil, nil, nil) b, err = Run(b, &st, &rs, context.TODO()) if err != nil { @@ -350,3 +361,4 @@ func TestRunMenuBrowse(t *testing.T) { t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expect, r) } } +