Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79747f3d6f | ||
|
|
63825e7201 | ||
|
|
03ccda1a69 | ||
|
|
e02621577f | ||
|
|
ab869c8d20 | ||
|
|
b2aa636ea0 | ||
|
|
ae725fb3d9 |
@@ -5,8 +5,8 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Defined an interface of stringBuilder that compatible with
|
||||
// strings.Builder(go 1.10) and bytes.Buffer(< go 1.10)
|
||||
// Define an interface of stringBuilder that is compatible with
|
||||
// strings.Builder(go 1.10) and bytes.Buffer(< go 1.10).
|
||||
type stringBuilder interface {
|
||||
WriteRune(r rune) (n int, err error)
|
||||
WriteString(s string) (int, error)
|
||||
|
||||
21
_test/assign17.go
Normal file
21
_test/assign17.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
s := make([]map[string]string, 0)
|
||||
m := make(map[string]string)
|
||||
m["m1"] = "m1"
|
||||
m["m2"] = "m2"
|
||||
s = append(s, m)
|
||||
tmpStr := "start"
|
||||
println(tmpStr)
|
||||
for _, v := range s {
|
||||
tmpStr, ok := v["m1"]
|
||||
println(tmpStr, ok)
|
||||
}
|
||||
println(tmpStr)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// start
|
||||
// m1 true
|
||||
// start
|
||||
21
_test/assign18.go
Normal file
21
_test/assign18.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
s := make([]map[string]string, 0)
|
||||
m := make(map[string]string)
|
||||
m["m1"] = "m1"
|
||||
m["m2"] = "m2"
|
||||
s = append(s, m)
|
||||
tmpStr := "start"
|
||||
println(tmpStr)
|
||||
for _, v := range s {
|
||||
tmpStr, _ := v["m1"]
|
||||
println(tmpStr)
|
||||
}
|
||||
println(tmpStr)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// start
|
||||
// m1
|
||||
// start
|
||||
12
_test/gen10.go
Normal file
12
_test/gen10.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
func genFunc() (f func()) {
|
||||
return f
|
||||
}
|
||||
|
||||
func main() {
|
||||
println(genFunc() == nil)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
44
_test/issue-1425.go
Normal file
44
_test/issue-1425.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type WrappedReader struct {
|
||||
reader io.Reader
|
||||
}
|
||||
|
||||
func (wr WrappedReader) Read(p []byte) (n int, err error) {
|
||||
return wr.reader.Read(p)
|
||||
}
|
||||
|
||||
// Of course, this implementation is completely stupid because it does not write
|
||||
// to the intended writer, as any honest WriteTo implementation should. its
|
||||
// implemtion is just to make obvious the divergence of behaviour with yaegi.
|
||||
func (wr WrappedReader) WriteTo(w io.Writer) (n int64, err error) {
|
||||
// Ignore w, send to Stdout to prove whether this WriteTo is used.
|
||||
data, err := io.ReadAll(wr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
nn, err := os.Stdout.Write(data)
|
||||
return int64(nn), err
|
||||
}
|
||||
|
||||
func main() {
|
||||
f := strings.NewReader("hello world")
|
||||
wr := WrappedReader{reader: f}
|
||||
|
||||
// behind the scenes, io.Copy is supposed to use wr.WriteTo if the implementation exists.
|
||||
// With Go, it works as expected, i.e. the output is sent to os.Stdout.
|
||||
// With Yaegi, it doesn't, i.e. the output is sent to io.Discard.
|
||||
if _, err := io.Copy(io.Discard, wr); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello world
|
||||
25
_test/issue-1439.go
Normal file
25
_test/issue-1439.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
type Transformer interface {
|
||||
Reset()
|
||||
}
|
||||
|
||||
type Encoder struct {
|
||||
Transformer
|
||||
}
|
||||
|
||||
type nop struct{}
|
||||
|
||||
func (nop) Reset() { println("Reset") }
|
||||
|
||||
func f(e Transformer) {
|
||||
e.Reset()
|
||||
}
|
||||
|
||||
func main() {
|
||||
e := Encoder{Transformer: nop{}}
|
||||
f(e)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Reset
|
||||
20
_test/issue-1447.go
Normal file
20
_test/issue-1447.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type I interface {
|
||||
Name() string
|
||||
}
|
||||
|
||||
type S struct {
|
||||
iMap map[string]I
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := S{}
|
||||
s.iMap = map[string]I{}
|
||||
fmt.Println(s)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {map[]}
|
||||
3
_test/p4/p4.go
Normal file
3
_test/p4/p4.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package p4
|
||||
|
||||
var Value1 = "value1"
|
||||
10
_test/p5.go
Normal file
10
_test/p5.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
import "github.com/traefik/yaegi/_test/p5"
|
||||
|
||||
func main() {
|
||||
println(*p5.Value1)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// value1
|
||||
8
_test/p5/p5.go
Normal file
8
_test/p5/p5.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package p5
|
||||
|
||||
import "github.com/traefik/yaegi/_test/p4"
|
||||
|
||||
var (
|
||||
Value1 = &val1
|
||||
val1 = p4.Value1
|
||||
)
|
||||
17
_test/switch39.go
Normal file
17
_test/switch39.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
func f(params ...interface{}) {
|
||||
switch p0 := params[0].(type) {
|
||||
case string:
|
||||
println("string:", p0)
|
||||
default:
|
||||
println("not a string")
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
f("Hello")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// string: Hello
|
||||
17
_test/switch40.go
Normal file
17
_test/switch40.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
func f(params ...interface{}) {
|
||||
switch params[0].(type) {
|
||||
case string:
|
||||
println("a string")
|
||||
default:
|
||||
println("not a string")
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
f("Hello")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// a string
|
||||
@@ -11,7 +11,7 @@ Output files are written in the current directory, and prefixed with the go vers
|
||||
|
||||
Usage:
|
||||
|
||||
extract package...
|
||||
extract package...
|
||||
|
||||
The same program is used for all target operating systems and architectures.
|
||||
The GOOS and GOARCH environment variables set the desired target.
|
||||
|
||||
@@ -221,6 +221,14 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
sc.sym[label] = sym
|
||||
c.sym = sym
|
||||
}
|
||||
// If block is the body of a function, get declared variables in current scope.
|
||||
// This is done in order to add the func signature symbols into sc.sym,
|
||||
// as we will need them in post-processing.
|
||||
if n.anc != nil && n.anc.kind == funcDecl {
|
||||
for k, v := range sc.anc.sym {
|
||||
sc.sym[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
case breakStmt, continueStmt, gotoStmt:
|
||||
if len(n.child) == 0 {
|
||||
@@ -2051,7 +2059,14 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
|
||||
}
|
||||
sbn.start = clauses[0].start
|
||||
n.start = n.child[0].start
|
||||
n.child[0].tnext = sbn.start
|
||||
if n.kind == typeSwitch {
|
||||
// Handle the typeSwitch init (the type assert expression).
|
||||
init := n.child[1].lastChild().child[0]
|
||||
init.tnext = sbn.start
|
||||
n.child[0].tnext = init.start
|
||||
} else {
|
||||
n.child[0].tnext = sbn.start
|
||||
}
|
||||
|
||||
case switchIfStmt: // like an if-else chain
|
||||
sc = sc.pop()
|
||||
@@ -2281,11 +2296,36 @@ func compDefineX(sc *scope, n *node) error {
|
||||
return n.cfgErrorf("unsupported assign expression")
|
||||
}
|
||||
|
||||
// Handle redeclarations: find out new symbols vs existing ones.
|
||||
symIsNew := map[string]bool{}
|
||||
hasNewSymbol := false
|
||||
for i := range types {
|
||||
id := n.child[i].ident
|
||||
if id == "_" || id == "" {
|
||||
continue
|
||||
}
|
||||
if _, found := symIsNew[id]; found {
|
||||
return n.cfgErrorf("%s repeated on left side of :=", id)
|
||||
}
|
||||
// A new symbol doesn't exist in current scope. Upper scopes are not
|
||||
// taken into accout here, as a new symbol can shadow an existing one.
|
||||
if _, found := sc.sym[id]; found {
|
||||
symIsNew[id] = false
|
||||
} else {
|
||||
symIsNew[id] = true
|
||||
hasNewSymbol = true
|
||||
}
|
||||
}
|
||||
|
||||
for i, t := range types {
|
||||
var index int
|
||||
id := n.child[i].ident
|
||||
if sym, level, ok := sc.lookup(id); ok && level == n.child[i].level && sym.kind == varSym && sym.typ.id() == t.id() {
|
||||
// Reuse symbol in case of a variable redeclaration with the same type.
|
||||
// A variable can be redeclared if at least one other not blank variable is created.
|
||||
// The redeclared variable must be of same type (it is reassigned, not created).
|
||||
// Careful to not reuse a variable which has been shadowed (it must not be a newSym).
|
||||
sym, level, ok := sc.lookup(id)
|
||||
canRedeclare := hasNewSymbol && len(symIsNew) > 1 && !symIsNew[id] && ok
|
||||
if canRedeclare && level == n.child[i].level && sym.kind == varSym && sym.typ.id() == t.id() {
|
||||
index = sym.index
|
||||
} else {
|
||||
index = sc.add(t)
|
||||
@@ -2420,15 +2460,22 @@ func genGlobalVarDecl(nodes []*node, sc *scope) (*node, error) {
|
||||
|
||||
func getVarDependencies(nod *node, sc *scope) (deps []*node) {
|
||||
nod.Walk(func(n *node) bool {
|
||||
if n.kind == identExpr {
|
||||
if sym, _, ok := sc.lookup(n.ident); ok {
|
||||
if sym.kind != varSym || !sym.global || sym.node == nod {
|
||||
return false
|
||||
}
|
||||
deps = append(deps, sym.node)
|
||||
}
|
||||
if n.kind != identExpr {
|
||||
return true
|
||||
}
|
||||
return true
|
||||
// Process ident nodes, and avoid false dependencies.
|
||||
if n.anc.kind == selectorExpr && childPos(n) == 1 {
|
||||
return false
|
||||
}
|
||||
sym, _, ok := sc.lookup(n.ident)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if sym.kind != varSym || !sym.global || sym.node == nod {
|
||||
return false
|
||||
}
|
||||
deps = append(deps, sym.node)
|
||||
return false
|
||||
}, nil)
|
||||
return deps
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ Package interp provides a complete Go interpreter.
|
||||
For the Go language itself, refer to the official Go specification
|
||||
https://golang.org/ref/spec.
|
||||
|
||||
Importing packages
|
||||
# Importing packages
|
||||
|
||||
Packages can be imported in source or binary form, using the standard
|
||||
Go import statement. In source form, packages are searched first in the
|
||||
@@ -16,7 +16,7 @@ Binary form packages are compiled and linked with the interpreter
|
||||
executable, and exposed to scripts with the Use method. The extract
|
||||
subcommand of yaegi can be used to generate package wrappers.
|
||||
|
||||
Custom build tags
|
||||
# Custom build tags
|
||||
|
||||
Custom build tags allow to control which files in imported source
|
||||
packages are interpreted, in the same way as the "-tags" option of the
|
||||
|
||||
221
interp/interp.go
221
interp/interp.go
@@ -4,16 +4,12 @@ import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"go/constant"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log"
|
||||
"math/bits"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
@@ -547,62 +543,6 @@ func (interp *Interpreter) EvalTest(path string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
func isFile(filesystem fs.FS, path string) bool {
|
||||
fi, err := fs.Stat(filesystem, path)
|
||||
return err == nil && fi.Mode().IsRegular()
|
||||
@@ -664,167 +604,6 @@ func (interp *Interpreter) stop() {
|
||||
|
||||
func (interp *Interpreter) runid() uint64 { return atomic.LoadUint64(&interp.id) }
|
||||
|
||||
// getWrapper returns the wrapper type of the corresponding interface, or nil if not found.
|
||||
func (interp *Interpreter) getWrapper(t reflect.Type) reflect.Type {
|
||||
if p, ok := interp.binPkg[t.PkgPath()]; ok {
|
||||
return p["_"+t.Name()].Type().Elem()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
// ignoreScannerError returns true if the error from Go scanner can be safely ignored
|
||||
// to let the caller grab one more line before retrying to parse its input.
|
||||
func ignoreScannerError(e *scanner.Error, s string) bool {
|
||||
|
||||
@@ -133,6 +133,8 @@ func TestEvalAssign(t *testing.T) {
|
||||
{src: "j := true || _", err: "1:33: cannot use _ as value"},
|
||||
{src: "j := true && _", err: "1:33: cannot use _ as value"},
|
||||
{src: "j := interface{}(int(1)); j.(_)", err: "1:54: cannot use _ as value"},
|
||||
{src: "ff := func() (a, b, c int) {return 1, 2, 3}; x, y, x := ff()", err: "1:73: x repeated on left side of :="},
|
||||
{src: "xx := 1; xx, _ := 2, 3", err: "1:37: no new variables on left side of :="},
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ func Hi(h Helloer) {
|
||||
//
|
||||
// Only the Wrap type definition needs to be exported to the interpreter (not
|
||||
// the interfaces and methods definitions).
|
||||
//
|
||||
type Wrap struct {
|
||||
DoHello func() // related to the Hello() method.
|
||||
// Other interface method wrappers...
|
||||
|
||||
@@ -553,7 +553,7 @@ func convert(n *node) {
|
||||
if c.isNil() { // convert nil to type
|
||||
// TODO(mpl): Try to completely remove, as maybe frameType already does the job for interfaces.
|
||||
if isInterfaceSrc(n.child[0].typ) && !isEmptyInterface(n.child[0].typ) {
|
||||
typ = reflect.TypeOf((*valueInterface)(nil)).Elem()
|
||||
typ = valueInterfaceType
|
||||
}
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).Set(reflect.New(typ).Elem())
|
||||
@@ -713,7 +713,7 @@ func assign(n *node) {
|
||||
case isFuncSrc(typ):
|
||||
t = reflect.TypeOf((*node)(nil))
|
||||
case isInterfaceSrc(typ):
|
||||
t = reflect.TypeOf((*valueInterface)(nil)).Elem()
|
||||
t = valueInterfaceType
|
||||
default:
|
||||
t = typ.TypeOf()
|
||||
}
|
||||
@@ -1007,7 +1007,7 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
typ := def.typ.arg[i]
|
||||
switch {
|
||||
case isEmptyInterface(typ):
|
||||
case isEmptyInterface(typ) || typ.TypeOf() == valueInterfaceType:
|
||||
d[i].Set(arg)
|
||||
case isInterfaceSrc(typ):
|
||||
d[i].Set(reflect.ValueOf(valueInterface{value: arg.Elem()}))
|
||||
@@ -1050,19 +1050,24 @@ func genInterfaceWrapper(n *node, typ reflect.Type) func(*frame) reflect.Value {
|
||||
return value
|
||||
}
|
||||
}
|
||||
mn := typ.NumMethod()
|
||||
|
||||
// Retrieve methods from the interface wrapper, which is a struct where all fields
|
||||
// except the first define the methods to implement.
|
||||
// As the field name was generated with a prefixed first character (in order to avoid
|
||||
// collisions with method names), this first character is ignored in comparisons.
|
||||
wrap := getWrapper(n, typ)
|
||||
mn := wrap.NumField() - 1
|
||||
names := make([]string, mn)
|
||||
methods := make([]*node, mn)
|
||||
indexes := make([][]int, mn)
|
||||
for i := 0; i < mn; i++ {
|
||||
names[i] = typ.Method(i).Name
|
||||
names[i] = wrap.Field(i + 1).Name[1:]
|
||||
methods[i], indexes[i] = n.typ.lookupMethod(names[i])
|
||||
if methods[i] == nil && n.typ.cat != nilT {
|
||||
// interpreted method not found, look for binary method, possibly embedded
|
||||
_, indexes[i], _, _ = n.typ.lookupBinMethod(names[i])
|
||||
}
|
||||
}
|
||||
wrap := n.interp.getWrapper(typ)
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
v := value(f)
|
||||
@@ -1555,12 +1560,9 @@ func callBin(n *node) {
|
||||
case isInterfaceSrc(c.typ):
|
||||
values = append(values, genValueInterfaceValue(c))
|
||||
case c.typ.cat == arrayT || c.typ.cat == variadicT:
|
||||
switch {
|
||||
case isEmptyInterface(c.typ.val):
|
||||
if isEmptyInterface(c.typ.val) {
|
||||
values = append(values, genValueArray(c))
|
||||
case isInterfaceSrc(c.typ.val):
|
||||
values = append(values, genValueInterfaceArray(c))
|
||||
default:
|
||||
} else {
|
||||
values = append(values, genInterfaceWrapper(c, defType))
|
||||
}
|
||||
case isPtrSrc(c.typ):
|
||||
@@ -1950,10 +1952,12 @@ func getMethodByName(n *node) {
|
||||
}
|
||||
val = v
|
||||
}
|
||||
|
||||
if met := val.value.MethodByName(name); met.IsValid() {
|
||||
getFrame(f, l).data[i] = met
|
||||
return next
|
||||
}
|
||||
|
||||
typ := val.node.typ
|
||||
if typ.node == nil && typ.cat == valueT {
|
||||
// happens with a var of empty interface type, that has value of concrete type
|
||||
@@ -1963,10 +1967,32 @@ func getMethodByName(n *node) {
|
||||
}
|
||||
return next
|
||||
}
|
||||
|
||||
m, li := typ.lookupMethod(name)
|
||||
|
||||
// Try harder to find a matching embedded valueInterface.
|
||||
// TODO (marc): make sure it works for arbitrary depth and breadth.
|
||||
if m == nil && isStruct(val.node.typ) {
|
||||
v := val.value
|
||||
for v.Type().Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
nf := v.NumField()
|
||||
for i := 0; i < nf; i++ {
|
||||
var ok bool
|
||||
if val, ok = v.Field(i).Interface().(valueInterface); !ok {
|
||||
continue
|
||||
}
|
||||
if m, li = val.node.typ.lookupMethod(name); m != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if m == nil {
|
||||
panic(n.cfgErrorf("method not found: %s", name))
|
||||
}
|
||||
|
||||
fr := f.clone(!fork)
|
||||
nod := *m
|
||||
nod.val = &nod
|
||||
@@ -2674,8 +2700,6 @@ func doComposite(n *node, hasType bool, keyed bool) {
|
||||
values[fieldIndex] = func(*frame) reflect.Value { return reflect.New(rft).Elem() }
|
||||
case isFuncSrc(val.typ):
|
||||
values[fieldIndex] = genValueAsFunctionWrapper(val)
|
||||
case isArray(val.typ) && val.typ.val != nil && isInterfaceSrc(val.typ.val) && !isEmptyInterface(val.typ.val):
|
||||
values[fieldIndex] = genValueInterfaceArray(val)
|
||||
case isInterfaceSrc(ft) && (!isEmptyInterface(ft) || len(val.typ.method) > 0):
|
||||
values[fieldIndex] = genValueInterface(val)
|
||||
case isInterface(ft):
|
||||
@@ -3556,7 +3580,7 @@ func convertLiteralValue(n *node, t reflect.Type) {
|
||||
case n.typ.cat == nilT:
|
||||
// Create a zero value of target type.
|
||||
n.rval = reflect.New(t).Elem()
|
||||
case !(n.kind == basicLit || n.rval.IsValid()) || t == nil || t.Kind() == reflect.Interface || t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Interface:
|
||||
case !(n.kind == basicLit || n.rval.IsValid()) || t == nil || t.Kind() == reflect.Interface || t == valueInterfaceType || t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Interface:
|
||||
// Skip non-constant values, undefined target type or interface target type.
|
||||
case n.rval.IsValid():
|
||||
// Convert constant value to target type.
|
||||
@@ -3917,7 +3941,7 @@ func isNotNil(n *node) {
|
||||
dest := genValue(n)
|
||||
|
||||
if n.fnext == nil {
|
||||
if isInterfaceSrc(c0.typ) {
|
||||
if isInterfaceSrc(c0.typ) && c0.typ.TypeOf() != valueInterfaceType {
|
||||
if isInterface {
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).Set(reflect.ValueOf(!value(f).IsNil()).Convert(typ))
|
||||
@@ -3962,7 +3986,7 @@ func isNotNil(n *node) {
|
||||
|
||||
fnext := getExec(n.fnext)
|
||||
|
||||
if isInterfaceSrc(c0.typ) {
|
||||
if isInterfaceSrc(c0.typ) && c0.typ.TypeOf() != valueInterfaceType {
|
||||
n.exec = func(f *frame) bltn {
|
||||
if value(f).IsNil() {
|
||||
dest(f).SetBool(false)
|
||||
|
||||
@@ -72,7 +72,6 @@ type symbol struct {
|
||||
//
|
||||
// In symbols, the index value corresponds to the index in scope.types, and at
|
||||
// execution to the index in frame, created exactly from the types layout.
|
||||
//
|
||||
type scope struct {
|
||||
anc *scope // ancestor upper scope
|
||||
child []*scope // included scopes
|
||||
|
||||
@@ -1817,8 +1817,9 @@ func exportName(s string) string {
|
||||
|
||||
var (
|
||||
// TODO(mpl): generators.
|
||||
interf = reflect.TypeOf((*interface{})(nil)).Elem()
|
||||
constVal = reflect.TypeOf((*constant.Value)(nil)).Elem()
|
||||
emptyInterfaceType = reflect.TypeOf((*interface{})(nil)).Elem()
|
||||
valueInterfaceType = reflect.TypeOf((*valueInterface)(nil)).Elem()
|
||||
constVal = reflect.TypeOf((*constant.Value)(nil)).Elem()
|
||||
)
|
||||
|
||||
type fieldRebuild struct {
|
||||
@@ -1971,7 +1972,12 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
|
||||
}
|
||||
t.rtype = reflect.FuncOf(in, out, variadic)
|
||||
case interfaceT:
|
||||
t.rtype = interf
|
||||
if len(t.field) == 0 {
|
||||
// empty interface, do not wrap it
|
||||
t.rtype = emptyInterfaceType
|
||||
break
|
||||
}
|
||||
t.rtype = valueInterfaceType
|
||||
case mapT:
|
||||
t.rtype = reflect.MapOf(t.key.refType(ctx), t.val.refType(ctx))
|
||||
case ptrT:
|
||||
@@ -2056,10 +2062,10 @@ func (t *itype) frameType() (r reflect.Type) {
|
||||
case interfaceT:
|
||||
if len(t.field) == 0 {
|
||||
// empty interface, do not wrap it
|
||||
r = reflect.TypeOf((*interface{})(nil)).Elem()
|
||||
r = emptyInterfaceType
|
||||
break
|
||||
}
|
||||
r = reflect.TypeOf((*valueInterface)(nil)).Elem()
|
||||
r = valueInterfaceType
|
||||
case mapT:
|
||||
r = reflect.MapOf(t.key.frameType(), t.val.frameType())
|
||||
case ptrT:
|
||||
@@ -2072,6 +2078,14 @@ func (t *itype) frameType() (r reflect.Type) {
|
||||
|
||||
func (t *itype) implements(it *itype) bool {
|
||||
if isBin(t) {
|
||||
// Note: in case of a valueInterfaceType, we
|
||||
// miss required data which will be available
|
||||
// later, so we optimistically return true to progress,
|
||||
// and additional checks will be hopefully performed at
|
||||
// runtime.
|
||||
if rt := it.TypeOf(); rt == valueInterfaceType {
|
||||
return true
|
||||
}
|
||||
return t.TypeOf().Implements(it.TypeOf())
|
||||
}
|
||||
return t.methods().contains(it.methods())
|
||||
@@ -2127,11 +2141,15 @@ func (t *itype) defaultType(v reflect.Value, sc *scope) *itype {
|
||||
func (t *itype) isNil() bool { return t.cat == nilT }
|
||||
|
||||
func (t *itype) hasNil() bool {
|
||||
switch t.TypeOf().Kind() {
|
||||
switch rt := t.TypeOf(); rt.Kind() {
|
||||
case reflect.UnsafePointer:
|
||||
return true
|
||||
case reflect.Slice, reflect.Ptr, reflect.Func, reflect.Interface, reflect.Map, reflect.Chan:
|
||||
return true
|
||||
case reflect.Struct:
|
||||
if rt == valueInterfaceType {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -2213,7 +2231,7 @@ func isEmptyInterface(t *itype) bool {
|
||||
}
|
||||
|
||||
func isGeneric(t *itype) bool {
|
||||
return t.cat == funcT && t.node != nil && len(t.node.child[0].child) > 0
|
||||
return t.cat == funcT && t.node != nil && len(t.node.child) > 0 && len(t.node.child[0].child) > 0
|
||||
}
|
||||
|
||||
func isFuncSrc(t *itype) bool {
|
||||
@@ -2246,7 +2264,7 @@ func isInterfaceBin(t *itype) bool {
|
||||
}
|
||||
|
||||
func isInterface(t *itype) bool {
|
||||
return isInterfaceSrc(t) || t.TypeOf() != nil && t.TypeOf().Kind() == reflect.Interface
|
||||
return isInterfaceSrc(t) || t.TypeOf() == valueInterfaceType || t.TypeOf() != nil && t.TypeOf().Kind() == reflect.Interface
|
||||
}
|
||||
|
||||
func isBin(t *itype) bool {
|
||||
|
||||
@@ -590,7 +590,7 @@ func (check typecheck) typeAssertionExpr(n *node, typ *itype) error {
|
||||
// https://github.com/golang/go/issues/39717 lands. It is currently impractical to
|
||||
// type check Named types as they cannot be asserted.
|
||||
|
||||
if n.typ.TypeOf().Kind() != reflect.Interface {
|
||||
if rt := n.typ.TypeOf(); rt.Kind() != reflect.Interface && rt != valueInterfaceType {
|
||||
return n.cfgErrorf("invalid type assertion: non-interface type %s on left", n.typ.id())
|
||||
}
|
||||
ims := n.typ.methods()
|
||||
|
||||
252
interp/use.go
Normal file
252
interp/use.go
Normal file
@@ -0,0 +1,252 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"log"
|
||||
"math/bits"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// 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)
|
||||
}
|
||||
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))
|
||||
}
|
||||
}
|
||||
@@ -176,7 +176,7 @@ func genValue(n *node) func(*frame) reflect.Value {
|
||||
convertConstantValue(n)
|
||||
v := n.rval
|
||||
if !v.IsValid() {
|
||||
v = reflect.New(interf).Elem()
|
||||
v = reflect.New(emptyInterfaceType).Elem()
|
||||
}
|
||||
return func(f *frame) reflect.Value { return v }
|
||||
case funcDecl:
|
||||
@@ -287,19 +287,6 @@ func genValueRangeArray(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
}
|
||||
|
||||
func genValueInterfaceArray(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
return func(f *frame) reflect.Value {
|
||||
vi := value(f).Interface().([]valueInterface)
|
||||
v := reflect.MakeSlice(reflect.TypeOf([]interface{}{}), len(vi), len(vi))
|
||||
for i, vv := range vi {
|
||||
v.Index(i).Set(vv.value)
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
func genValueInterface(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
|
||||
@@ -356,7 +343,7 @@ func getConcreteValue(val reflect.Value) reflect.Value {
|
||||
|
||||
func zeroInterfaceValue() reflect.Value {
|
||||
n := &node{kind: basicLit, typ: &itype{cat: nilT, untyped: true, str: "nil"}}
|
||||
v := reflect.New(interf).Elem()
|
||||
v := reflect.New(emptyInterfaceType).Elem()
|
||||
return reflect.ValueOf(valueInterface{n, v})
|
||||
}
|
||||
|
||||
|
||||
88
stdlib/wrapper-composed.go
Normal file
88
stdlib/wrapper-composed.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Wrappers for composed interfaces which trigger a special behavior in stdlib.
|
||||
// Note: it may become useless to pre-compile composed interface wrappers
|
||||
// once golang/go#15924 is resolved.
|
||||
|
||||
// In net/http, a ResponseWriter may also implement a Hijacker.
|
||||
|
||||
type _netHTTPResponseWriterHijacker struct {
|
||||
IValue interface{}
|
||||
WHeader func() http.Header
|
||||
WWrite func(a0 []byte) (int, error)
|
||||
WWriteHeader func(statusCode int)
|
||||
|
||||
WHijack func() (net.Conn, *bufio.ReadWriter, error)
|
||||
}
|
||||
|
||||
func (w _netHTTPResponseWriterHijacker) Header() http.Header {
|
||||
return w.WHeader()
|
||||
}
|
||||
|
||||
func (w _netHTTPResponseWriterHijacker) Write(a0 []byte) (int, error) {
|
||||
return w.WWrite(a0)
|
||||
}
|
||||
|
||||
func (w _netHTTPResponseWriterHijacker) WriteHeader(statusCode int) {
|
||||
w.WWriteHeader(statusCode)
|
||||
}
|
||||
|
||||
func (w _netHTTPResponseWriterHijacker) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
return w.WHijack()
|
||||
}
|
||||
|
||||
// In io, a Reader may implement WriteTo, used by io.Copy().
|
||||
|
||||
type _ioReaderWriteTo struct {
|
||||
IValue interface{}
|
||||
WRead func(p []byte) (n int, err error)
|
||||
|
||||
WWriteTo func(w io.Writer) (n int64, err error)
|
||||
}
|
||||
|
||||
func (w _ioReaderWriteTo) Read(p []byte) (n int, err error) {
|
||||
return w.WRead(p)
|
||||
}
|
||||
|
||||
func (w _ioReaderWriteTo) WriteTo(wr io.Writer) (n int64, err error) {
|
||||
return w.WWriteTo(wr)
|
||||
}
|
||||
|
||||
// In io, a Writer may implement ReadFrom, used by io.Copy().
|
||||
|
||||
type _ioWriterReadFrom struct {
|
||||
IValue interface{}
|
||||
WWrite func(p []byte) (n int, err error)
|
||||
|
||||
WReadFrom func(r io.Reader) (n int64, err error)
|
||||
}
|
||||
|
||||
func (w _ioWriterReadFrom) Write(p []byte) (n int, err error) {
|
||||
return w.WWrite(p)
|
||||
}
|
||||
|
||||
func (w _ioWriterReadFrom) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
return w.WReadFrom(r)
|
||||
}
|
||||
|
||||
// Each MapType value (each slice) must be sorted by complexity, i.e. by number
|
||||
// of interface methods.
|
||||
func init() {
|
||||
MapTypes[reflect.ValueOf((*_net_http_ResponseWriter)(nil))] = []reflect.Type{
|
||||
reflect.ValueOf((*_netHTTPResponseWriterHijacker)(nil)).Type().Elem(),
|
||||
}
|
||||
MapTypes[reflect.ValueOf((*_io_Reader)(nil))] = []reflect.Type{
|
||||
reflect.ValueOf((*_ioReaderWriteTo)(nil)).Type().Elem(),
|
||||
}
|
||||
MapTypes[reflect.ValueOf((*_io_Writer)(nil))] = []reflect.Type{
|
||||
reflect.ValueOf((*_ioWriterReadFrom)(nil)).Type().Elem(),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user