github.com/google/skylark@v0.0.0-20181101142754-a5f7082aabed/internal/compile/serial.go (about) 1 package compile 2 3 // This files defines functions to read and write a compile.Program to a file. 4 // Currently we use gob encoding because it is convenient. 5 // 6 // It is the client's responsibility to manage version skew between the 7 // compiler used to produce a file and the interpreter that consumes it. 8 // The version number is provided as a constant. Incompatible protocol 9 // changes should also increment the version number. 10 11 import ( 12 "encoding/gob" 13 "fmt" 14 "io" 15 16 "github.com/google/skylark/syntax" 17 ) 18 19 const magic = "!sky" 20 21 type gobProgram struct { 22 Version int 23 Filename string 24 Loads []gobIdent 25 Names []string 26 Constants []interface{} 27 Functions []gobFunction 28 Globals []gobIdent 29 Toplevel gobFunction 30 } 31 32 type gobFunction struct { 33 Id gobIdent // hack: name and pos 34 Code []byte 35 Pclinetab []uint16 36 Locals []gobIdent 37 Freevars []gobIdent 38 MaxStack int 39 NumParams int 40 HasVarargs, HasKwargs bool 41 } 42 43 type gobIdent struct { 44 Name string 45 Line, Col int32 // the filename is gobProgram.Filename 46 } 47 48 // Write writes a compiled Skylark program to out. 49 func (prog *Program) Write(out io.Writer) error { 50 out.Write([]byte(magic)) 51 52 gobIdents := func(idents []Ident) []gobIdent { 53 res := make([]gobIdent, len(idents)) 54 for i, id := range idents { 55 res[i].Name = id.Name 56 res[i].Line = id.Pos.Line 57 res[i].Col = id.Pos.Col 58 } 59 return res 60 } 61 62 gobFunc := func(fn *Funcode) gobFunction { 63 return gobFunction{ 64 Id: gobIdent{ 65 Name: fn.Name, 66 Line: fn.Pos.Line, 67 Col: fn.Pos.Col, 68 }, 69 Code: fn.Code, 70 Pclinetab: fn.pclinetab, 71 Locals: gobIdents(fn.Locals), 72 Freevars: gobIdents(fn.Freevars), 73 MaxStack: fn.MaxStack, 74 NumParams: fn.NumParams, 75 HasVarargs: fn.HasVarargs, 76 HasKwargs: fn.HasKwargs, 77 } 78 } 79 80 gp := &gobProgram{ 81 Version: Version, 82 Filename: prog.Toplevel.Pos.Filename(), 83 Loads: gobIdents(prog.Loads), 84 Names: prog.Names, 85 Constants: prog.Constants, 86 Functions: make([]gobFunction, len(prog.Functions)), 87 Globals: gobIdents(prog.Globals), 88 Toplevel: gobFunc(prog.Toplevel), 89 } 90 for i, f := range prog.Functions { 91 gp.Functions[i] = gobFunc(f) 92 } 93 94 return gob.NewEncoder(out).Encode(gp) 95 } 96 97 // ReadProgram reads a compiled Skylark program from in. 98 func ReadProgram(in io.Reader) (*Program, error) { 99 magicBuf := []byte(magic) 100 n, err := in.Read(magicBuf) 101 if err != nil { 102 return nil, err 103 } 104 if n != len(magic) { 105 return nil, fmt.Errorf("not a compiled module: no magic number") 106 } 107 if string(magicBuf) != magic { 108 return nil, fmt.Errorf("not a compiled module: got magic number %q, want %q", 109 magicBuf, magic) 110 } 111 112 dec := gob.NewDecoder(in) 113 var gp gobProgram 114 if err := dec.Decode(&gp); err != nil { 115 return nil, fmt.Errorf("decoding program: %v", err) 116 } 117 118 if gp.Version != Version { 119 return nil, fmt.Errorf("version mismatch: read %d, want %d", 120 gp.Version, Version) 121 } 122 123 file := gp.Filename // copy, to avoid keeping gp live 124 125 ungobIdents := func(idents []gobIdent) []Ident { 126 res := make([]Ident, len(idents)) 127 for i, id := range idents { 128 res[i].Name = id.Name 129 res[i].Pos = syntax.MakePosition(&file, id.Line, id.Col) 130 } 131 return res 132 } 133 134 prog := &Program{ 135 Loads: ungobIdents(gp.Loads), 136 Names: gp.Names, 137 Constants: gp.Constants, 138 Globals: ungobIdents(gp.Globals), 139 Functions: make([]*Funcode, len(gp.Functions)), 140 } 141 142 ungobFunc := func(gf *gobFunction) *Funcode { 143 pos := syntax.MakePosition(&file, gf.Id.Line, gf.Id.Col) 144 return &Funcode{ 145 Prog: prog, 146 Pos: pos, 147 Name: gf.Id.Name, 148 Code: gf.Code, 149 pclinetab: gf.Pclinetab, 150 Locals: ungobIdents(gf.Locals), 151 Freevars: ungobIdents(gf.Freevars), 152 MaxStack: gf.MaxStack, 153 NumParams: gf.NumParams, 154 HasVarargs: gf.HasVarargs, 155 HasKwargs: gf.HasKwargs, 156 } 157 } 158 159 for i := range gp.Functions { 160 prog.Functions[i] = ungobFunc(&gp.Functions[i]) 161 } 162 prog.Toplevel = ungobFunc(&gp.Toplevel) 163 return prog, nil 164 }