package comp import ( "fmt" "reflect" ) // Dump represents the state of a data dump. type Dump struct { Values []*DumpValue } // DumpValue is a value of a dump state. type DumpValue struct { Index int Name string Kind int Type string Value any } // 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 { dict := c.symbolsByIndex() dv := make([]*DumpValue, len(c.Data)) for i, d := range c.Data { e := dict[i] dv[i] = &DumpValue{ Index: e.Index, Name: e.name, Kind: int(e.Kind), Type: e.Type.Name, Value: d.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].CanSet() { return fmt.Errorf("value %v cannot be set", dv.Value) } c.Data[dv.Index].Set(reflect.ValueOf(dv.Value)) } return nil }