github.com/switchupcb/yaegi@v0.10.2/interp/program.go (about) 1 package interp 2 3 import ( 4 "context" 5 "go/ast" 6 "io/ioutil" 7 "reflect" 8 "runtime" 9 "runtime/debug" 10 ) 11 12 // A Program is Go code that has been parsed and compiled. 13 type Program struct { 14 pkgName string 15 root *node 16 init []*node 17 } 18 19 // Compile parses and compiles a Go code represented as a string. 20 func (interp *Interpreter) Compile(src string) (*Program, error) { 21 return interp.compileSrc(src, "", true) 22 } 23 24 // CompilePath parses and compiles a Go code located at the given path. 25 func (interp *Interpreter) CompilePath(path string) (*Program, error) { 26 if !isFile(interp.filesystem, path) { 27 _, err := interp.importSrc(mainID, path, NoTest) 28 return nil, err 29 } 30 31 b, err := ioutil.ReadFile(path) 32 if err != nil { 33 return nil, err 34 } 35 return interp.compileSrc(string(b), path, false) 36 } 37 38 func (interp *Interpreter) compileSrc(src, name string, inc bool) (*Program, error) { 39 if name != "" { 40 interp.name = name 41 } 42 if interp.name == "" { 43 interp.name = DefaultSourceName 44 } 45 46 // Parse source to AST. 47 n, err := interp.parse(src, interp.name, inc) 48 if err != nil { 49 return nil, err 50 } 51 52 return interp.CompileAST(n) 53 } 54 55 // CompileAST builds a Program for the given Go code AST. Files and block 56 // statements can be compiled, as can most expressions. Var declaration nodes 57 // cannot be compiled. 58 func (interp *Interpreter) CompileAST(n ast.Node) (*Program, error) { 59 // Convert AST. 60 pkgName, root, err := interp.ast(n) 61 if err != nil || root == nil { 62 return nil, err 63 } 64 65 if interp.astDot { 66 dotCmd := interp.dotCmd 67 if dotCmd == "" { 68 dotCmd = defaultDotCmd(interp.name, "yaegi-ast-") 69 } 70 root.astDot(dotWriter(dotCmd), interp.name) 71 if interp.noRun { 72 return nil, err 73 } 74 } 75 76 // Perform global types analysis. 77 if err = interp.gtaRetry([]*node{root}, pkgName, pkgName); err != nil { 78 return nil, err 79 } 80 81 // Annotate AST with CFG informations. 82 initNodes, err := interp.cfg(root, nil, pkgName, pkgName) 83 if err != nil { 84 if interp.cfgDot { 85 dotCmd := interp.dotCmd 86 if dotCmd == "" { 87 dotCmd = defaultDotCmd(interp.name, "yaegi-cfg-") 88 } 89 root.cfgDot(dotWriter(dotCmd)) 90 } 91 return nil, err 92 } 93 94 if root.kind != fileStmt { 95 // REPL may skip package statement. 96 setExec(root.start) 97 } 98 interp.mutex.Lock() 99 gs := interp.scopes[pkgName] 100 if interp.universe.sym[pkgName] == nil { 101 // Make the package visible under a path identical to its name. 102 interp.srcPkg[pkgName] = gs.sym 103 interp.universe.sym[pkgName] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT, path: pkgName}} 104 interp.pkgNames[pkgName] = pkgName 105 } 106 interp.mutex.Unlock() 107 108 // Add main to list of functions to run, after all inits. 109 if m := gs.sym[mainID]; pkgName == mainID && m != nil { 110 initNodes = append(initNodes, m.node) 111 } 112 113 if interp.cfgDot { 114 dotCmd := interp.dotCmd 115 if dotCmd == "" { 116 dotCmd = defaultDotCmd(interp.name, "yaegi-cfg-") 117 } 118 root.cfgDot(dotWriter(dotCmd)) 119 } 120 121 return &Program{pkgName, root, initNodes}, nil 122 } 123 124 // Execute executes compiled Go code. 125 func (interp *Interpreter) Execute(p *Program) (res reflect.Value, err error) { 126 defer func() { 127 r := recover() 128 if r != nil { 129 var pc [64]uintptr // 64 frames should be enough. 130 n := runtime.Callers(1, pc[:]) 131 err = Panic{Value: r, Callers: pc[:n], Stack: debug.Stack()} 132 } 133 }() 134 135 // Generate node exec closures. 136 if err = genRun(p.root); err != nil { 137 return res, err 138 } 139 140 // Init interpreter execution memory frame. 141 interp.frame.setrunid(interp.runid()) 142 interp.frame.mutex.Lock() 143 interp.resizeFrame() 144 interp.frame.mutex.Unlock() 145 146 // Execute node closures. 147 interp.run(p.root, nil) 148 149 // Wire and execute global vars. 150 n, err := genGlobalVars([]*node{p.root}, interp.scopes[p.pkgName]) 151 if err != nil { 152 return res, err 153 } 154 interp.run(n, nil) 155 156 for _, n := range p.init { 157 interp.run(n, interp.frame) 158 } 159 v := genValue(p.root) 160 res = v(interp.frame) 161 162 // If result is an interpreter node, wrap it in a runtime callable function. 163 if res.IsValid() { 164 if n, ok := res.Interface().(*node); ok { 165 res = genFunctionWrapper(n)(interp.frame) 166 } 167 } 168 169 return res, err 170 } 171 172 // ExecuteWithContext executes compiled Go code. 173 func (interp *Interpreter) ExecuteWithContext(ctx context.Context, p *Program) (res reflect.Value, err error) { 174 interp.mutex.Lock() 175 interp.done = make(chan struct{}) 176 interp.cancelChan = !interp.opt.fastChan 177 interp.mutex.Unlock() 178 179 done := make(chan struct{}) 180 go func() { 181 defer close(done) 182 res, err = interp.Execute(p) 183 }() 184 185 select { 186 case <-ctx.Done(): 187 interp.stop() 188 return reflect.Value{}, ctx.Err() 189 case <-done: 190 } 191 return res, err 192 }