Compare commits

..

8 Commits

Author SHA1 Message Date
Marc Vertes
f76db27c77 interp: fix resolution of methods on aliased types
The type.val field was always pointing to the final underlying type
for aliased types, defeating a possible match if a method was
attached to a type in between. Now the complete chain of aliases
is always preserved.

We have added an underlying() itype method which returns the underlying
type of a defined type (aliasT), even in the presence of multiple
indirections.

We have added a definedType function which checks if type t1 is
defined from type t2 or t2 defined from t1, required when checking
assignability of aliasT types.

Fixes #1411.

PS: this is the 2nd attempt, as the first version #1412 wasn't passing
_test/issue-1408.go as well. This PR does pass and supersedes #1412.
2022-06-14 16:42:09 +02:00
mpl
996b1e33c8 interp: catch mismatched types for other comparisons
The check for mismatched types was already added recently for ==  and != comparisons.
This PR now adds it for other comparisons ( < , <=, > , >=).
2022-06-14 10:52:08 +02:00
Marc Vertes
236a0effaf interp: improve the behaviour of interface{} function parameters
We finally address a long standing limitation of the interpreter:
the capacity to generate the correct interface wrapper for an
anonymous interface{} function parameter of a binary function.

It allows for example fmt.Printf to invoke the String method
of an object defined within the interpreter, or json.Marshal
to invoke a textMarshaler method if it exists and if there is
no Marshaler method already defined for the passed interpreter
object.

To achieve that, we add a new mapType part of the "Used" symbols
to describe what not empty interfaces are expected and in which
priority order. This information can not be guessed and is found
in the related package documentation, then captured in stdlib/maptypes.go.

Then, at compile time and/or during execution, a lookup on mapTypes
is performed to allow the correct wrapper to be generated.

This change adds a new MapType type to the stdlib package.

Fixes #435.
2022-06-14 10:18:08 +02:00
Marc Vertes
eaeb445e17 interp: create interpreter interface value with new
In that case, the interface must be wrapped in an valueInterface
at creation.

With that fix, it is now possible to import
github.com/google/go-querystring/query. Not tested beyond that.

Fixes #1123.
2022-06-13 11:36:09 +02:00
Marc Vertes
6933ba2b4e interp: improve type checking for defined types
Fixes #1408.
2022-06-13 11:24:09 +02:00
Marc Vertes
a61a7d5bcd interp: avoid panic when defining a label in incremental parsing mode
In REPL mode, a panic (stack overflow) could be triggered by:

	$ yaegi
	> a:
	runtime: goroutine stack exceeds 1000000000-byte limit
	runtime: sp=0x14020760330 stack=[0x14020760000, 0x14040760000]
	fatal error: stack overflow
	[...]

This issue occurs in incremental parsing mode only, and not when the parser
is in file mode. We avoid it by being more defensive when generating
values.

Fixes #982.
2022-06-13 11:10:09 +02:00
Marc Vertes
259f64cfd4 interp: fix redeclaration of an interface variable
Fixes #1404.
2022-06-13 10:56:09 +02:00
Marc Vertes
6c74ab7bec interp: allow conversions of untyped complex
For untyped numerical types, conversions to different numerical
types can be allowed if there is no overflow (not checked here).

Fixes #1402.
2022-06-13 10:42:08 +02:00
13 changed files with 331 additions and 51 deletions

22
_test/issue-1404.go Normal file
View File

@@ -0,0 +1,22 @@
package main
type I interface {
inI()
}
type T struct {
name string
}
func (t *T) inI() {}
func main() {
var i I = &T{name: "foo"}
if i, ok := i.(*T); ok {
println(i.name)
}
}
// Output:
// foo

16
_test/issue-1408.go Normal file
View File

@@ -0,0 +1,16 @@
package main
type (
Number = int32
Number2 = Number
)
func f(n Number2) { println(n) }
func main() {
var n Number = 5
f(n)
}
// Output:
// 5

16
_test/issue-1411.go Normal file
View File

@@ -0,0 +1,16 @@
package main
type Number int32
func (n Number) IsValid() bool { return true }
type Number1 = Number
type Number2 = Number1
func main() {
a := Number2(5)
println(a.IsValid())
}
// Output: true

25
_test/issue-435.go Normal file
View File

@@ -0,0 +1,25 @@
package main
import (
"fmt"
"strconv"
)
type Foo int
func (f Foo) String() string {
return "foo-" + strconv.Itoa(int(f))
}
func print1(arg interface{}) {
fmt.Println(arg)
}
func main() {
var arg Foo = 3
var f = print1
f(arg)
}
// Output:
// foo-3

View File

@@ -1324,7 +1324,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
// retry with the filename, in case ident is a package name.
sym, level, found = sc.lookup(filepath.Join(n.ident, baseName))
if !found {
err = n.cfgErrorf("undefined: %s %d", n.ident, n.index)
err = n.cfgErrorf("undefined: %s", n.ident)
break
}
}
@@ -2135,7 +2135,7 @@ func compDefineX(sc *scope, n *node) error {
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.equals(t) {
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.
index = sym.index
} else {
@@ -2700,11 +2700,7 @@ func compositeGenerator(n *node, typ *itype, rtyp reflect.Type) (gen bltnGenerat
}
case valueT:
if rtyp == nil {
rtyp = n.typ.rtype
}
// TODO(mpl): I do not understand where this side-effect is coming from, and why it happens. quickfix for now.
if rtyp == nil {
rtyp = n.typ.val.rtype
rtyp = n.typ.TypeOf()
}
switch k := rtyp.Kind(); k {
case reflect.Struct:

View File

@@ -204,11 +204,12 @@ type Interpreter struct {
name string // name of the input source file (or main)
opt // user settable options
cancelChan bool // enables cancellable chan operations
fset *token.FileSet // fileset to locate node in source code
binPkg Exports // binary packages used in interpreter, indexed by path
rdir map[string]bool // for src import cycle detection
opt // user settable options
cancelChan bool // enables cancellable chan operations
fset *token.FileSet // fileset to locate node in source code
binPkg Exports // binary packages used in interpreter, indexed by path
rdir map[string]bool // for src import cycle detection
mapTypes map[reflect.Value][]reflect.Type // special interfaces mapping for wrappers
mutex sync.RWMutex
frame *frame // program data storage during execution
@@ -333,6 +334,7 @@ func New(options Options) *Interpreter {
universe: initUniverse(),
scopes: map[string]*scope{},
binPkg: Exports{"": map[string]reflect.Value{"_error": reflect.ValueOf((*_error)(nil))}},
mapTypes: map[reflect.Value][]reflect.Type{},
srcPkg: imports{},
pkgNames: map[string]string{},
rdir: map[string]bool{},
@@ -675,6 +677,14 @@ func (interp *Interpreter) Use(values Exports) error {
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)
}
@@ -726,6 +736,14 @@ func fixStdlib(interp *Interpreter) {
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)
@@ -752,6 +770,14 @@ func fixStdlib(interp *Interpreter) {
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 {

View File

@@ -176,7 +176,7 @@ func TestEvalBuiltin(t *testing.T) {
{src: `m := complex(3, 2); real(m)`, res: "3"},
{src: `m := complex(3, 2); imag(m)`, res: "2"},
{src: `m := complex("test", 2)`, err: "1:33: invalid types string and int"},
{src: `imag("test")`, err: "1:33: cannot convert \"test\" to complex128"},
{src: `imag("test")`, err: "1:33: cannot convert untyped string to untyped complex"},
{src: `imag(a)`, err: "1:33: invalid argument type []int for imag"},
{src: `real(a)`, err: "1:33: invalid argument type []int for real"},
{src: `t := map[int]int{}; t[123]++; t`, res: "map[123:1]"},
@@ -184,6 +184,9 @@ func TestEvalBuiltin(t *testing.T) {
{src: `t := map[int]int{}; t[123] += 1; t`, res: "map[123:1]"},
{src: `t := map[int]int{}; t[123] -= 1; t`, res: "map[123:-1]"},
{src: `println("hello", _)`, err: "1:28: cannot use _ as value"},
{src: `f := func() complex64 { return complex(0, 0) }()`, res: "(0+0i)"},
{src: `f := func() float32 { return real(complex(2, 1)) }()`, res: "2"},
{src: `f := func() int8 { return imag(complex(2, 1)) }()`, res: "1"},
})
}
@@ -437,7 +440,7 @@ func TestEvalComparison(t *testing.T) {
{src: `a, b, c := 1, 1, false; if a == b { c = true }; c`, res: "true"},
{src: `a, b, c := 1, 2, false; if a != b { c = true }; c`, res: "true"},
{
desc: "mismatched types",
desc: "mismatched types equality",
src: `
type Foo string
type Bar string
@@ -448,6 +451,18 @@ func TestEvalComparison(t *testing.T) {
`,
err: "7:13: invalid operation: mismatched types main.Foo and main.Bar",
},
{
desc: "mismatched types less than",
src: `
type Foo string
type Bar string
var a = Foo("test")
var b = Bar("test")
var c = a < b
`,
err: "7:13: invalid operation: mismatched types main.Foo and main.Bar",
},
{src: `1 > _`, err: "1:28: cannot use _ as value"},
{src: `(_) > 1`, err: "1:28: cannot use _ as value"},
{src: `v := interface{}(2); v == 2`, res: "true"},
@@ -706,6 +721,29 @@ func TestEvalBinCall(t *testing.T) {
})
}
func TestEvalReflect(t *testing.T) {
i := interp.New(interp.Options{})
if err := i.Use(stdlib.Symbols); err != nil {
t.Fatal(err)
}
if _, err := i.Eval(`
import (
"net/url"
"reflect"
)
type Encoder interface {
EncodeValues(key string, v *url.Values) error
}
`); err != nil {
t.Fatal(err)
}
runTests(t, i, []testCase{
{src: "reflect.TypeOf(new(Encoder)).Elem()", res: "interp.valueInterface"},
})
}
func TestEvalMissingSymbol(t *testing.T) {
defer func() {
r := recover()
@@ -1372,7 +1410,7 @@ func testConcurrentComposite(t *testing.T, filePath string) {
}
}
func TestEvalScanner(t *testing.T) {
func TestEvalREPL(t *testing.T) {
if testing.Short() {
return
}
@@ -1460,6 +1498,13 @@ func TestEvalScanner(t *testing.T) {
},
errorLine: -1,
},
{
desc: "define a label",
src: []string{
`a:`,
},
errorLine: -1,
},
}
runREPL := func(t *testing.T, test testCase) {

View File

@@ -1479,6 +1479,20 @@ func callBin(n *node) {
}
}
// getMapType returns a reflect type suitable for interface wrapper for functions
// with some special processing in case of interface{} argument, i.e. fmt.Printf.
var getMapType func(*itype) reflect.Type
if lr, ok := n.interp.mapTypes[c0.rval]; ok {
getMapType = func(typ *itype) reflect.Type {
for _, rt := range lr {
if typ.implements(&itype{cat: valueT, rtype: rt}) {
return rt
}
}
return nil
}
}
// Determine if we should use `Call` or `CallSlice` on the function Value.
callFn := func(v reflect.Value, in []reflect.Value) []reflect.Value { return v.Call(in) }
if n.action == aCallSlice {
@@ -1486,13 +1500,6 @@ func callBin(n *node) {
}
for i, c := range child {
var defType reflect.Type
if variadic >= 0 && i+rcvrOffset >= variadic {
defType = funcType.In(variadic)
} else {
defType = funcType.In(rcvrOffset + i)
}
switch {
case isBinCall(c, c.scope):
// Handle nested function calls: pass returned values as arguments
@@ -1527,6 +1534,19 @@ func callBin(n *node) {
break
}
// defType is the target type for a potential interface wrapper.
var defType reflect.Type
if variadic >= 0 && i+rcvrOffset >= variadic {
defType = funcType.In(variadic)
} else {
defType = funcType.In(rcvrOffset + i)
}
if getMapType != nil {
if rt := getMapType(c.typ); rt != nil {
defType = rt
}
}
switch {
case isFuncSrc(c.typ):
values = append(values, genFunctionWrapper(c))
@@ -1565,7 +1585,7 @@ func callBin(n *node) {
val := make([]reflect.Value, l+1)
val[0] = value(f)
for i, v := range values {
val[i+1] = v(f)
val[i+1] = getBinValue(getMapType, v, f)
}
f.deferred = append([][]reflect.Value{val}, f.deferred...)
return tnext
@@ -1575,7 +1595,7 @@ func callBin(n *node) {
n.exec = func(f *frame) bltn {
in := make([]reflect.Value, l)
for i, v := range values {
in[i] = v(f)
in[i] = getBinValue(getMapType, v, f)
}
go callFn(value(f), in)
return tnext
@@ -1587,7 +1607,7 @@ func callBin(n *node) {
n.exec = func(f *frame) bltn {
in := make([]reflect.Value, l)
for i, v := range values {
in[i] = v(f)
in[i] = getBinValue(getMapType, v, f)
}
res := callFn(value(f), in)
b := res[0].Bool()
@@ -1619,7 +1639,7 @@ func callBin(n *node) {
n.exec = func(f *frame) bltn {
in := make([]reflect.Value, l)
for i, v := range values {
in[i] = v(f)
in[i] = getBinValue(getMapType, v, f)
}
out := callFn(value(f), in)
for i, v := range rvalues {
@@ -1636,7 +1656,7 @@ func callBin(n *node) {
n.exec = func(f *frame) bltn {
in := make([]reflect.Value, l)
for i, v := range values {
in[i] = v(f)
in[i] = getBinValue(getMapType, v, f)
}
out := callFn(value(f), in)
for i, v := range out {
@@ -1652,7 +1672,7 @@ func callBin(n *node) {
n.exec = func(f *frame) bltn {
in := make([]reflect.Value, l)
for i, v := range values {
in[i] = v(f)
in[i] = getBinValue(getMapType, v, f)
}
out := callFn(value(f), in)
for i := 0; i < len(out); i++ {
@@ -3303,11 +3323,20 @@ func _len(n *node) {
func _new(n *node) {
next := getExec(n.tnext)
typ := n.child[1].typ.TypeOf()
t1 := n.child[1].typ
typ := t1.TypeOf()
dest := genValueOutput(n, reflect.PtrTo(typ))
if isInterfaceSrc(t1) && (!isEmptyInterface(t1) || len(t1.method) > 0) {
typ = zeroInterfaceValue().Type()
}
n.exec = func(f *frame) bltn {
dest(f).Set(reflect.New(typ))
v := reflect.New(typ)
if vi, ok := v.Interface().(*valueInterface); ok {
vi.node = n
}
dest(f).Set(v)
return next
}
}

View File

@@ -238,9 +238,6 @@ func namedOf(val *itype, path, name string, opts ...itypeOption) *itype {
if path != "" {
str = path + "." + name
}
for val.cat == aliasT {
val = val.val
}
t := &itype{cat: aliasT, val: val, path: path, name: name, str: str}
for _, opt := range opts {
opt(t)
@@ -585,12 +582,12 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
}
if !t.incomplete {
switch k := t.TypeOf().Kind(); {
case t.untyped && isNumber(t.TypeOf()):
t = untypedFloat()
case k == reflect.Complex64:
t = sc.getType("float32")
case k == reflect.Complex128:
t = sc.getType("float64")
case t.untyped && isNumber(t.TypeOf()):
t = valueTOf(floatType, withUntyped(true), withScope(sc))
default:
err = n.cfgErrorf("invalid complex type %s", k)
}
@@ -1125,6 +1122,24 @@ func (t *itype) concrete() *itype {
return t
}
func (t *itype) underlying() *itype {
if t.cat == aliasT {
return t.val.underlying()
}
return t
}
// typeDefined returns true if type t1 is defined from type t2 or t2 from t1.
func typeDefined(t1, t2 *itype) bool {
if t1.cat == aliasT && t1.val == t2 {
return true
}
if t2.cat == aliasT && t2.val == t1 {
return true
}
return false
}
// isVariadic returns true if the function type is variadic.
// If the type is not a function or is not variadic, it will
// return false.
@@ -1198,8 +1213,7 @@ func (t *itype) assignableTo(o *itype) bool {
if t.equals(o) {
return true
}
if t.cat == aliasT && o.cat == aliasT {
// If alias types are not identical, it is not assignable.
if t.cat == aliasT && o.cat == aliasT && (t.underlying().id() != o.underlying().id() || !typeDefined(t, o)) {
return false
}
if t.isNil() && o.hasNil() || o.isNil() && t.hasNil() {
@@ -1219,6 +1233,11 @@ func (t *itype) assignableTo(o *itype) bool {
return true
}
if t.untyped && isNumber(t.TypeOf()) && isNumber(o.TypeOf()) {
// Assignability depends on constant numeric value (overflow check), to be tested elsewhere.
return true
}
n := t.node
if n == nil || !n.rval.IsValid() {
return false

View File

@@ -184,23 +184,29 @@ func (check typecheck) shift(n *node) error {
// comparison type checks a comparison binary expression.
func (check typecheck) comparison(n *node) error {
c0, c1 := n.child[0], n.child[1]
t0, t1 := n.child[0].typ, n.child[1].typ
if !c0.typ.assignableTo(c1.typ) && !c1.typ.assignableTo(c0.typ) {
return n.cfgErrorf("invalid operation: mismatched types %s and %s", c0.typ.id(), c1.typ.id())
if !t0.assignableTo(t1) && !t1.assignableTo(t0) {
return n.cfgErrorf("invalid operation: mismatched types %s and %s", t0.id(), t1.id())
}
ok := false
if !isInterface(t0) && !isInterface(t1) && !t0.isNil() && !t1.isNil() && t0.untyped == t1.untyped && t0.id() != t1.id() {
// Non interface types must be really equals.
return n.cfgErrorf("invalid operation: mismatched types %s and %s", t0.id(), t1.id())
}
switch n.action {
case aEqual, aNotEqual:
ok = c0.typ.comparable() && c1.typ.comparable() || c0.typ.isNil() && c1.typ.hasNil() || c1.typ.isNil() && c0.typ.hasNil()
ok = t0.comparable() && t1.comparable() || t0.isNil() && t1.hasNil() || t1.isNil() && t0.hasNil()
case aLower, aLowerEqual, aGreater, aGreaterEqual:
ok = c0.typ.ordered() && c1.typ.ordered()
ok = t0.ordered() && t1.ordered()
}
if !ok {
typ := c0.typ
typ := t0
if typ.isNil() {
typ = c1.typ
typ = t1
}
return n.cfgErrorf("invalid operation: operator %v not defined on %s", n.action, typ.id())
}
@@ -815,7 +821,7 @@ func (check typecheck) builtin(name string, n *node, child []*node, ellipsis boo
case !typ0.untyped && typ1.untyped:
err = check.convertUntyped(p1.nod, typ0)
case typ0.untyped && typ1.untyped:
fltType := check.scope.getType("float64")
fltType := untypedFloat()
err = check.convertUntyped(p0.nod, fltType)
if err != nil {
break
@@ -838,7 +844,7 @@ func (check typecheck) builtin(name string, n *node, child []*node, ellipsis boo
p := params[0]
typ := p.Type()
if typ.untyped {
if err := check.convertUntyped(p.nod, check.scope.getType("complex128")); err != nil {
if err := check.convertUntyped(p.nod, untypedComplex()); err != nil {
return err
}
}

View File

@@ -34,10 +34,10 @@ func valueGenerator(n *node, i int) func(*frame) reflect.Value {
// because a cancellation prior to any evaluation result may leave
// the frame's data empty.
func valueOf(data []reflect.Value, i int) reflect.Value {
if i < len(data) {
return data[i]
if i < 0 || i >= len(data) {
return reflect.Value{}
}
return reflect.Value{}
return data[i]
}
func genValueBinMethodOnInterface(n *node, defaultGen func(*frame) reflect.Value) func(*frame) reflect.Value {
@@ -195,7 +195,7 @@ func genValue(n *node) func(*frame) reflect.Value {
}
if n.sym != nil {
i := n.sym.index
if i < 0 {
if i < 0 && n != n.sym.node {
return genValue(n.sym.node)
}
if n.sym.global {
@@ -392,6 +392,21 @@ func genValueOutput(n *node, t reflect.Type) func(*frame) reflect.Value {
return value
}
func getBinValue(getMapType func(*itype) reflect.Type, value func(*frame) reflect.Value, f *frame) reflect.Value {
v := value(f)
if getMapType == nil {
return v
}
val, ok := v.Interface().(valueInterface)
if !ok || val.node == nil {
return v
}
if rt := getMapType(val.node.typ); rt != nil {
return genInterfaceWrapper(val.node, rt)(f)
}
return v
}
func valueInterfaceValue(v reflect.Value) reflect.Value {
for {
vv, ok := v.Interface().(valueInterface)

58
stdlib/maptypes.go Normal file
View File

@@ -0,0 +1,58 @@
package stdlib
import (
"encoding"
"encoding/json"
"encoding/xml"
"fmt"
"log"
"reflect"
)
func init() {
mt := []reflect.Type{
reflect.TypeOf((*fmt.Formatter)(nil)).Elem(),
reflect.TypeOf((*fmt.Stringer)(nil)).Elem(),
}
MapTypes[reflect.ValueOf(fmt.Errorf)] = mt
MapTypes[reflect.ValueOf(fmt.Fprint)] = mt
MapTypes[reflect.ValueOf(fmt.Fprintf)] = mt
MapTypes[reflect.ValueOf(fmt.Fprintln)] = mt
MapTypes[reflect.ValueOf(fmt.Print)] = mt
MapTypes[reflect.ValueOf(fmt.Printf)] = mt
MapTypes[reflect.ValueOf(fmt.Println)] = mt
MapTypes[reflect.ValueOf(fmt.Sprint)] = mt
MapTypes[reflect.ValueOf(fmt.Sprintf)] = mt
MapTypes[reflect.ValueOf(fmt.Sprintln)] = mt
MapTypes[reflect.ValueOf(log.Fatal)] = mt
MapTypes[reflect.ValueOf(log.Fatalf)] = mt
MapTypes[reflect.ValueOf(log.Fatalln)] = mt
MapTypes[reflect.ValueOf(log.Panic)] = mt
MapTypes[reflect.ValueOf(log.Panicf)] = mt
MapTypes[reflect.ValueOf(log.Panicln)] = mt
mt = []reflect.Type{reflect.TypeOf((*fmt.Scanner)(nil)).Elem()}
MapTypes[reflect.ValueOf(fmt.Scan)] = mt
MapTypes[reflect.ValueOf(fmt.Scanf)] = mt
MapTypes[reflect.ValueOf(fmt.Scanln)] = mt
MapTypes[reflect.ValueOf(json.Marshal)] = []reflect.Type{
reflect.TypeOf((*json.Marshaler)(nil)).Elem(),
reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem(),
}
MapTypes[reflect.ValueOf(json.Unmarshal)] = []reflect.Type{
reflect.TypeOf((*json.Unmarshaler)(nil)).Elem(),
reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem(),
}
MapTypes[reflect.ValueOf(xml.Marshal)] = []reflect.Type{
reflect.TypeOf((*xml.Marshaler)(nil)).Elem(),
reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem(),
}
MapTypes[reflect.ValueOf(xml.Unmarshal)] = []reflect.Type{
reflect.TypeOf((*xml.Unmarshaler)(nil)).Elem(),
reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem(),
}
}

View File

@@ -9,10 +9,17 @@ import "reflect"
// Symbols variable stores the map of stdlib symbols per package.
var Symbols = map[string]map[string]reflect.Value{}
// MapTypes variable contains a map of functions which have an interface{} as parameter but
// do something special if the parameter implements a given interface.
var MapTypes = map[reflect.Value][]reflect.Type{}
func init() {
Symbols["github.com/traefik/yaegi/stdlib"] = map[string]reflect.Value{
Symbols["github.com/traefik/yaegi/stdlib/stdlib"] = map[string]reflect.Value{
"Symbols": reflect.ValueOf(Symbols),
}
Symbols["."] = map[string]reflect.Value{
"MapTypes": reflect.ValueOf(MapTypes),
}
}
// Provide access to go standard library (http://golang.org/pkg/)