mirror of
https://github.com/hanzoai/yaegi.git
synced 2026-10-01 00:49:47 +00:00
Per the standing fork rule, a hanzoai fork declares its OWN module path and
is required directly -- never resolved via a `replace` against the upstream
path. Keeping the upstream path meant every import line in every consumer
carried the upstream brand.
module github.com/traefik/yaegi -> module github.com/hanzoai/yaegi
Mechanical and behaviour-neutral: every occurrence is an import path
(package self-imports, cmd/, _test/ fixtures interpreted at test time, and
the guthib.com/<org>/{fromage,cheese,vin} fake vendor trees under
example/pkg, whose directory components were renamed in lockstep with the
import strings that reference them).
GOPATH-mode source-import tests (interp TestFile) resolve the module by its
path under $GOPATH/src, so the required checkout location moves with the
module path -- as README already documents. Verified green at
$GOPATH/src/github.com/hanzoai/yaegi.
LICENSE is untouched: "Copyright 2020 Traefik Labs SAS" is the Apache-2.0
attribution and is a legal obligation, not a brand leak.
Also drops the inherited CONTRIBUTING.md CLA clause, which named an
upstream agreement this fork does not administer.
262 lines
8.2 KiB
Go
262 lines
8.2 KiB
Go
package interp
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"go/constant"
|
|
"log"
|
|
"math/bits"
|
|
"os"
|
|
"path"
|
|
"reflect"
|
|
|
|
gen "github.com/hanzoai/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))
|
|
}
|
|
}
|