From 649ae829220d6ddd8758d24c3bc2ea7d43e788d6 Mon Sep 17 00:00:00 2001 From: Antonio Navarro Perez Date: Wed, 20 Mar 2024 11:15:48 +0100 Subject: feat: Add simple Dump creation and recovery. Memory Dump functionality that can restore the previous VM state. It dumps *global* variables, the only ones defining the program state. The dump depends on the program itself, and on the index system, which right now is defined by the variable order. Signed-off-by: Antonio Navarro Perez --- parser/compiler.go | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 3 deletions(-) (limited to 'parser/compiler.go') diff --git a/parser/compiler.go b/parser/compiler.go index 4594ab9..568ed4a 100644 --- a/parser/compiler.go +++ b/parser/compiler.go @@ -4,6 +4,7 @@ import ( "fmt" "log" "os" + "reflect" "strconv" "github.com/mvertes/parscan/lang" @@ -328,18 +329,104 @@ type entry struct { *symbol } +func (e entry) String() string { + if e.symbol != nil { + return fmt.Sprintf("name: %s,local: %t, i: %d, k: %d, t: %s, v: %v", + e.name, + e.symbol.local, + e.symbol.index, + e.symbol.kind, + e.symbol.Type, + e.symbol.value, + ) + } + + return e.name +} + func (c *Compiler) PrintData() { + dict := c.symbolsByIndex() + + fmt.Fprintln(os.Stderr, "# Data:") + for i, d := range c.Data { + fmt.Fprintf(os.Stderr, "%4d %T %v %v\n", i, d.Data.Interface(), d.Data, dict[i]) + } +} + +func (c *Compiler) symbolsByIndex() map[int]entry { dict := map[int]entry{} for name, sym := range c.symbols { - if !sym.used || sym.local || sym.kind == symLabel { + if sym.index == unsetAddr { continue } dict[sym.index] = entry{name, sym} } - fmt.Fprintln(os.Stderr, "# Data:") + + return dict +} + +type Dump struct { + Values []*DumpValue +} + +type DumpValue struct { + Index int + Name string + Kind int + Type string + Value any +} + +// Dump gets the execution state of global variables. +func (c *Compiler) Dump() *Dump { + var dv []*DumpValue + dict := c.symbolsByIndex() for i, d := range c.Data { - fmt.Fprintf(os.Stderr, "%4d %T %v %v\n", i, d.Data.Interface(), d.Data, dict[i]) + e := dict[i] + dv = append(dv, &DumpValue{ + Index: e.index, + Name: e.name, + Kind: int(e.kind), + Type: e.Type.Name, + Value: d.Data.Interface(), + }) } + + return &Dump{Values: dv} +} + +// ApplyDump sets previously saved dump, restoring the state of global variables. +func (c *Compiler) ApplyDump(d *Dump) error { + dict := c.symbolsByIndex() + for _, dv := range d.Values { + // do all the checks to be sure we are applying the correct values + e, ok := dict[dv.Index] + if !ok { + return fmt.Errorf("entry not found on index %d", dv.Index) + } + + if dv.Name != e.name || + dv.Type != e.Type.Name || + dv.Kind != int(e.kind) { + return fmt.Errorf("entry with index %d does not match with provided entry. "+ + "dumpValue: %s, %s, %d. memoryValue: %s, %s, %d", + dv.Index, + dv.Name, dv.Type, dv.Kind, + e.name, e.Type, e.kind) + } + + if dv.Index >= len(c.Data) { + return fmt.Errorf("index (%d) bigger than memory (%d)", dv.Index, len(c.Data)) + } + + if !c.Data[dv.Index].Data.CanSet() { + return fmt.Errorf("value %v cannot be set", dv.Value) + } + + c.Data[dv.Index].Data.Set(reflect.ValueOf(dv.Value)) + } + + return nil } func (c *Compiler) typeSym(t *vm.Type) *symbol { -- cgit v1.2.3 From 7a9ac73037f207e3895332e5ba2b30d465c9c339 Mon Sep 17 00:00:00 2001 From: Antonio Navarro Perez Date: Thu, 21 Mar 2024 15:46:33 +0100 Subject: Add extra GoDoc to explain why Dump is on the Compiler. Signed-off-by: Antonio Navarro Perez --- parser/compiler.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'parser/compiler.go') diff --git a/parser/compiler.go b/parser/compiler.go index 568ed4a..fb79720 100644 --- a/parser/compiler.go +++ b/parser/compiler.go @@ -377,7 +377,13 @@ type DumpValue struct { Value any } -// Dump gets the execution state of global variables. +// Dump creates a snapshot of the execution state of global variables. +// This method is specifically implemented in the Compiler to minimize the coupling between +// the dump format and other components. By situating the dump logic in the Compiler, +// it relies solely on the program being executed and the indexing algorithm used for ordering variables +// (currently, this is an integer that corresponds to the order of variables in the program). +// This design choice allows the Virtual Machine (VM) to evolve its memory management strategies +// without compromising backward compatibility with dumps generated by previous versions. func (c *Compiler) Dump() *Dump { var dv []*DumpValue dict := c.symbolsByIndex() -- cgit v1.2.3