package interp import ( "flag" "fmt" "go/constant" "log" "math/bits" "os" "path" "reflect" gen "github.com/traefik/yaegi/stdlib/generic" ) // Symbols returns a map of interpreter exported symbol values for the given // import path. If the argument is the empty string, all known symbols are // returned. func (interp *Interpreter) Symbols(importPath string) Exports { m := map[string]map[string]reflect.Value{} interp.mutex.RLock() defer interp.mutex.RUnlock() for k, v := range interp.srcPkg { if importPath != "" && k != importPath { continue } syms := map[string]reflect.Value{} for n, s := range v { if !canExport(n) { // Skip private non-exported symbols. continue } switch s.kind { case constSym: syms[n] = s.rval case funcSym: syms[n] = genFunctionWrapper(s.node)(interp.frame) case varSym: syms[n] = interp.frame.data[s.index] case typeSym: syms[n] = reflect.New(s.typ.TypeOf()) } } if len(syms) > 0 { m[k] = syms } if importPath != "" { return m } } if importPath != "" && len(m) > 0 { return m } for k, v := range interp.binPkg { if importPath != "" && k != importPath { continue } m[k] = v if importPath != "" { return m } } return m } // getWrapper returns the wrapper type of the corresponding interface, trying // first the composed ones, or nil if not found. func getWrapper(n *node, t reflect.Type) reflect.Type { p, ok := n.interp.binPkg[t.PkgPath()] if !ok { return nil } w := p["_"+t.Name()] lm := n.typ.methods() // mapTypes may contain composed interfaces wrappers to test against, from // most complex to simplest (guaranteed by construction of mapTypes). Find the // first for which the interpreter type has all the methods. for _, rt := range n.interp.mapTypes[w] { match := true for i := 1; i < rt.NumField(); i++ { // The interpreter type must have all required wrapper methods. if _, ok := lm[rt.Field(i).Name[1:]]; !ok { match = false break } } if match { return rt } } // Otherwise return the direct "non-composed" interface. return w.Type().Elem() } // Use loads binary runtime symbols in the interpreter context so // they can be used in interpreted code. func (interp *Interpreter) Use(values Exports) error { for k, v := range values { importPath := path.Dir(k) packageName := path.Base(k) if k == "." && v["MapTypes"].IsValid() { // Use mapping for special interface wrappers. for kk, vv := range v["MapTypes"].Interface().(map[reflect.Value][]reflect.Type) { interp.mapTypes[kk] = vv } continue } if importPath == "." { return fmt.Errorf("export path %[1]q is missing a package name; did you mean '%[1]s/%[1]s'?", k) } if importPath == selfPrefix { interp.hooks.Parse(v) continue } if interp.binPkg[importPath] == nil { interp.binPkg[importPath] = make(map[string]reflect.Value) interp.pkgNames[importPath] = packageName } for s, sym := range v { interp.binPkg[importPath][s] = sym } if k == selfPath { interp.binPkg[importPath]["Self"] = reflect.ValueOf(interp) } } // Checks if input values correspond to stdlib packages by looking for one // well known stdlib package path. if _, ok := values["fmt/fmt"]; ok { fixStdlib(interp) // Load stdlib generic source. for _, s := range gen.Sources { if _, err := interp.Compile(s); err != nil { return err } } } return nil } // fixStdlib redefines interpreter stdlib symbols to use the standard input, // output and errror assigned to the interpreter. The changes are limited to // the interpreter only. // Note that it is possible to escape the virtualized stdio by // read/write directly to file descriptors 0, 1, 2. func fixStdlib(interp *Interpreter) { p := interp.binPkg["fmt"] if p == nil { return } stdin, stdout, stderr := interp.stdin, interp.stdout, interp.stderr p["Print"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fprint(stdout, a...) }) p["Printf"] = reflect.ValueOf(func(f string, a ...interface{}) (n int, err error) { return fmt.Fprintf(stdout, f, a...) }) p["Println"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fprintln(stdout, a...) }) p["Scan"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fscan(stdin, a...) }) p["Scanf"] = reflect.ValueOf(func(f string, a ...interface{}) (n int, err error) { return fmt.Fscanf(stdin, f, a...) }) p["Scanln"] = reflect.ValueOf(func(a ...interface{}) (n int, err error) { return fmt.Fscanln(stdin, a...) }) // Update mapTypes to virtualized symbols as well. interp.mapTypes[p["Print"]] = interp.mapTypes[reflect.ValueOf(fmt.Print)] interp.mapTypes[p["Printf"]] = interp.mapTypes[reflect.ValueOf(fmt.Printf)] interp.mapTypes[p["Println"]] = interp.mapTypes[reflect.ValueOf(fmt.Println)] interp.mapTypes[p["Scan"]] = interp.mapTypes[reflect.ValueOf(fmt.Scan)] interp.mapTypes[p["Scanf"]] = interp.mapTypes[reflect.ValueOf(fmt.Scanf)] interp.mapTypes[p["Scanln"]] = interp.mapTypes[reflect.ValueOf(fmt.Scanln)] if p = interp.binPkg["flag"]; p != nil { c := flag.NewFlagSet(os.Args[0], flag.PanicOnError) c.SetOutput(stderr) p["CommandLine"] = reflect.ValueOf(&c).Elem() } if p = interp.binPkg["log"]; p != nil { l := log.New(stderr, "", log.LstdFlags) // Restrict Fatal symbols to panic instead of exit. p["Fatal"] = reflect.ValueOf(l.Panic) p["Fatalf"] = reflect.ValueOf(l.Panicf) p["Fatalln"] = reflect.ValueOf(l.Panicln) p["Flags"] = reflect.ValueOf(l.Flags) p["Output"] = reflect.ValueOf(l.Output) p["Panic"] = reflect.ValueOf(l.Panic) p["Panicf"] = reflect.ValueOf(l.Panicf) p["Panicln"] = reflect.ValueOf(l.Panicln) p["Prefix"] = reflect.ValueOf(l.Prefix) p["Print"] = reflect.ValueOf(l.Print) p["Printf"] = reflect.ValueOf(l.Printf) p["Println"] = reflect.ValueOf(l.Println) p["SetFlags"] = reflect.ValueOf(l.SetFlags) p["SetOutput"] = reflect.ValueOf(l.SetOutput) p["SetPrefix"] = reflect.ValueOf(l.SetPrefix) p["Writer"] = reflect.ValueOf(l.Writer) // Update mapTypes to virtualized symbols as well. interp.mapTypes[p["Print"]] = interp.mapTypes[reflect.ValueOf(log.Print)] interp.mapTypes[p["Printf"]] = interp.mapTypes[reflect.ValueOf(log.Printf)] interp.mapTypes[p["Println"]] = interp.mapTypes[reflect.ValueOf(log.Println)] interp.mapTypes[p["Panic"]] = interp.mapTypes[reflect.ValueOf(log.Panic)] interp.mapTypes[p["Panicf"]] = interp.mapTypes[reflect.ValueOf(log.Panicf)] interp.mapTypes[p["Panicln"]] = interp.mapTypes[reflect.ValueOf(log.Panicln)] } if p = interp.binPkg["os"]; p != nil { p["Args"] = reflect.ValueOf(&interp.args).Elem() if interp.specialStdio { // Inherit streams from interpreter even if they do not have a file descriptor. p["Stdin"] = reflect.ValueOf(&stdin).Elem() p["Stdout"] = reflect.ValueOf(&stdout).Elem() p["Stderr"] = reflect.ValueOf(&stderr).Elem() } else { // Inherits streams from interpreter only if they have a file descriptor and preserve original type. if s, ok := stdin.(*os.File); ok { p["Stdin"] = reflect.ValueOf(&s).Elem() } if s, ok := stdout.(*os.File); ok { p["Stdout"] = reflect.ValueOf(&s).Elem() } if s, ok := stderr.(*os.File); ok { p["Stderr"] = reflect.ValueOf(&s).Elem() } } if !interp.unrestricted { // In restricted mode, scripts can only access to a passed virtualized env, and can not write the real one. getenv := func(key string) string { return interp.env[key] } p["Clearenv"] = reflect.ValueOf(func() { interp.env = map[string]string{} }) p["ExpandEnv"] = reflect.ValueOf(func(s string) string { return os.Expand(s, getenv) }) p["Getenv"] = reflect.ValueOf(getenv) p["LookupEnv"] = reflect.ValueOf(func(key string) (s string, ok bool) { s, ok = interp.env[key]; return }) p["Setenv"] = reflect.ValueOf(func(key, value string) error { interp.env[key] = value; return nil }) p["Unsetenv"] = reflect.ValueOf(func(key string) error { delete(interp.env, key); return nil }) p["Environ"] = reflect.ValueOf(func() (a []string) { for k, v := range interp.env { a = append(a, k+"="+v) } return }) } } if p = interp.binPkg["math/bits"]; p != nil { // Do not trust extracted value maybe from another arch. p["UintSize"] = reflect.ValueOf(constant.MakeInt64(bits.UintSize)) } }