Compare commits

...

27 Commits

Author SHA1 Message Date
Marc Vertes
d64563edee interp: improve handling values and comparisons in interfaces
Fixes #1347.
2022-05-23 10:30:08 +02:00
Marc Vertes
07039262a0 interp: implements detection of packages with no Go files
This avoids panic during import, and print a proper diagnostic
instead.

Fixes #1394.
2022-05-19 18:34:08 +02:00
Marc Vertes
4ed9ccb5c4 interp: fix retrieving the string value of an interface
Thanks to @bailsman for providing first insight, in addition to
raising the issue.

Fixes #1342.
2022-05-19 18:20:09 +02:00
Marc Vertes
25edcfee7a interp: fix handling of empty interfaces in map index expressions
There should be no need now to wrap empty interfaces in order to
retrieve its value.

Fixes #1344.
2022-05-19 18:08:09 +02:00
Marc Vertes
d183f4205e interp: improve handling of empty interface values (#1393)
At variable, function parameter, slice, map or field element assign,
if the destination type is an empty interface, the value was never
wrapped into a valueInterface (to preserve type mutability in case
of re-assign). Now we wrap it in a valueInterface if the source
type has a non empty set of methods, to allow a future use as a non
empty interface.

There are still corner cases, but it extends notably the support
of interfaces within the interpreter.

Fixes #1355.
2022-05-19 17:53:56 +02:00
Marc Vertes
821e9ee006 interp: recover interpreter internal panics in EvalWithContext 2022-05-19 17:30:09 +02:00
Marc Vertes
00e3f924c1 interp: fix the behaviour of goto, continue and break (#1392)
The logic of goto was false due to mixing break label and goto
label, despite being opposite. In case of break, jump to node (the
exit point) instead of node.start. Also always define label symbols
before their use is parsed.

* Fix continue label, detect invalid labels for break and continue

* fix multiple goto, break, continue to the same label

Fixes #1354.
2022-05-19 11:23:30 +02:00
Marc Vertes
2248851d77 interp: fix creation of binary composite types (#1391)
* interp: fix creation of binary composite types

Use direct assignment instead of reflect.Value Set method to
initialize a binary composite type, as for non binary types.
It ensures that a new reflect.Value is stored in the frame
instead of modifying a possibly existing one, which can defeat
the purpose of initializing variables in a body loop.

While there, remove the need to have and use a mutex on types.

Fixes #1381.

* review: rework a bit the test

Co-authored-by: mpl <mathieu.lonjaret@gmail.com>
2022-05-05 21:31:10 +02:00
Marc Vertes
f74d1ea6d8 interp: detect invalid uses of _ as value
We now detect the use of special identifier _ (blank) during parsing in order to abort compiling early. It allows to not panic later during execution. We must catch all the cases where blank is used as a value, but still preserve the cases where it is assigned, used as a struct field or for import side effects.

Fixes #1386.
2022-05-04 18:51:09 +02:00
Marc Vertes
606b4c3a37 interp: fix import of binary type symbols in current scope (#1380)
Fixes #1360.
2022-05-04 17:27:11 +02:00
Ethan Reesor
4e77fc9436 interp: delete incomplete type on pkg import
When a package is imported, it creates a symbol with a name like "errors/_.go". If a statement such as x := errors.New(...) is executed before import "errors", it creates an incomplete type symbol with a name like "errors". Importing the package after the incomplete type symbol has been created does not fix the compile issue because the incomplete type still exists.

To fix this, this PR deletes the incomplete type symbol, if one exists.

Closes #1388.
2022-04-26 11:04:13 +02:00
Ethan Reesor
ad9db379e7 interp: add a function to get globals (#1387)
Co-authored-by: Marc Vertes <mvertes@free.fr>
2022-04-25 16:11:18 +02:00
Ethan Reesor
7be17d393f interp: expose fset to fix CompileAST issue
The interpreter has its own internal fileset and expects all code to have been parsed using that fileset. If a user creates a fileset, calls `go/parser.Parse*`, then passes the result to `interp.CompileAST`, strange things can happen.

The solutions I can see are:

1. Expose the fileset so the user can use it when parsing source.
2. Add the fileset as an option (input to New) so that the user can tell the interpreter to use a specific fileset.
3. Pass the fileset into `CompileAST`

There are two ways to implement option 3. One is to add a field to nodes and update every reference to `interp.fset` to use `node.fset`. The other is to add a parameter to every function that references `interp.fset` or calls a function that does. Both of these are significant changes and involve an extra pointer for every node or most function calls.

Options 1 and 2 are easy. Option 2 involves adding an option so I went with option 1. I can imagine situations where option 2 could be necessary, but I can open another issue/PR if and when I need that.

Fixes #1383
2022-04-22 11:48:08 +02:00
cclerget
5665c9a410 interp: fix redeclaration scope issue
Fixes #1378
2022-04-09 15:28:07 +02:00
cclerget
1cf9d345aa Ignore private methods for binary types during type assertion
Fixes #1373
2022-04-07 18:16:08 +02:00
Marc Vertes
f07f25f1ba interp: handle struct with multiple recursive fields (#1372)
* interp: handle struct with multiple recursive fields

In case of a recursive struct with several recursive fields of
different type, only the first one was properly fixed when
constructing the corresponding reflect type. We now memorize and
process all fields at the same depth level.

Fixes #1371.

* Update interp/type.go

Co-authored-by: mpl <mathieu.lonjaret@gmail.com>

* fix lint

* fix comment

Co-authored-by: mpl <mathieu.lonjaret@gmail.com>
2022-04-07 14:53:23 +02:00
cclerget
c93b836c77 Prevent variadic arguments from being wrapped as function
Fixes #1375
2022-04-07 14:24:07 +02:00
Marc Vertes
371103f0d1 interp: fix switch expression (#1370)
The control flow graph was incorrect for the initial clause.

Fixes #1368.
2022-04-06 22:07:51 +02:00
Marc Vertes
8bd7afbe62 interp: fix handling of redeclaration in multi-assign expression (#1367)
* interp: fix handling of redeclaration in multi-assign expression

In a statement like `a, b := f()` if `a` was previously declared,
its symbol must be reused, a new symbol must not override its
previous value. This is now fixed.

* In case of redeclaration, reuse the existing only if the redeclared
variable has the same type. Add _test/var16.go to check this use
case.

Fixes #1365.
2022-04-06 20:01:25 +02:00
Marc Vertes
8ea3a493f4 interp: fix interface conversion from binary call (#1366)
Fixes #1364.
2022-04-06 19:51:12 +02:00
Marc Vertes
f2abd346c0 interp: fix passing binary function as parameter
Wrap binary function values in a node if passing it
as a parameter to an interperter defined function.

Fixes #1361.
2022-04-05 17:34:08 +02:00
Marc Vertes
c784713aca interp: make methods passed as value preserve receiver
Fixes #1332.
2022-04-05 16:58:09 +02:00
Marc Vertes
14acf618af interp: improve type switch on binary interfaces
Fixes #1337.
2022-01-04 10:50:08 +01:00
Marc Vertes
fbee2baf9d interp: fix wrapping of returned closure passed to runtime
Fixes #1333.
2021-12-21 17:44:06 +01:00
Marc Vertes
2819b4167b interp: fix derivation of type of slice expression of binary object
Fixes #1328.
2021-12-20 15:46:05 +01:00
Marc Vertes
2af660cb1f interp: improve method resolution on embedded fields
The capability to dereference pointers has been added to
methodByName(), improving method lookup on binary values.

Wrapping to valueInterface is performed in a missing use case at
return of function calls. It was done in the nested call, but not
at assign.

Fixes #1330.
2021-12-20 15:06:11 +01:00
Marc Vertes
8323068414 interp: fix nested calls with variadic interfaces
Fixes #1326
2021-12-08 17:16:05 +01:00
46 changed files with 1330 additions and 185 deletions

24
_test/break0.go Normal file
View File

@@ -0,0 +1,24 @@
package main
func main() {
n := 2
m := 2
foo := true
OuterLoop:
println("Boo")
for i := 0; i < n; i++ {
println("I: ", i)
for j := 0; j < m; j++ {
switch foo {
case true:
println(foo)
break OuterLoop
case false:
println(foo)
}
}
}
}
// Error:
// 15:5: invalid break label OuterLoop

24
_test/break1.go Normal file
View File

@@ -0,0 +1,24 @@
package main
func main() {
n := 2
m := 2
foo := true
OuterLoop:
for i := 0; i < n; i++ {
println("I: ", i)
for j := 0; j < m; j++ {
switch foo {
case true:
println(foo)
break OuterLoop
case false:
println(foo)
}
}
}
}
// Output:
// I: 0
// true

25
_test/break2.go Normal file
View File

@@ -0,0 +1,25 @@
package main
func main() {
n := 2
m := 2
foo := true
OuterLoop:
for i := 0; i < n; i++ {
println("I: ", i)
for j := 0; j < m; j++ {
switch foo {
case true:
println(foo)
break OuterLoop
case false:
println(foo)
continue OuterLoop
}
}
}
}
// Output:
// I: 0
// true

27
_test/break3.go Normal file
View File

@@ -0,0 +1,27 @@
package main
func main() {
n := 2
m := 2
foo := true
goto OuterLoop
println("Boo")
OuterLoop:
for i := 0; i < n; i++ {
println("I: ", i)
for j := 0; j < m; j++ {
switch foo {
case true:
println(foo)
break OuterLoop
case false:
println(foo)
goto OuterLoop
}
}
}
}
// Output:
// I: 0
// true

26
_test/cont2.go Normal file
View File

@@ -0,0 +1,26 @@
package main
func main() {
n := 2
m := 2
foo := true
OuterLoop:
for i := 0; i < n; i++ {
println("I: ", i)
for j := 0; j < m; j++ {
switch foo {
case true:
println(foo)
continue OuterLoop
case false:
println(foo)
}
}
}
}
// Output:
// I: 0
// true
// I: 1
// true

24
_test/cont3.go Normal file
View File

@@ -0,0 +1,24 @@
package main
func main() {
n := 2
m := 2
foo := true
OuterLoop:
println("boo")
for i := 0; i < n; i++ {
println("I: ", i)
for j := 0; j < m; j++ {
switch foo {
case true:
println(foo)
continue OuterLoop
case false:
println(foo)
}
}
}
}
// Error:
// 15:5: invalid continue label OuterLoop

42
_test/issue-1326.go Normal file
View File

@@ -0,0 +1,42 @@
package main
type Option interface {
apply(*T)
}
type T struct {
s string
}
type opt struct {
name string
}
func (o *opt) apply(t *T) {
println(o.name)
}
func BuildOptions() []Option {
return []Option{
&opt{"opt1"},
&opt{"opt2"},
}
}
func NewT(name string, options ...Option) *T {
t := &T{name}
for _, opt := range options {
opt.apply(t)
}
return t
}
func main() {
t := NewT("hello", BuildOptions()...)
println(t.s)
}
// Output:
// opt1
// opt2
// hello

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

@@ -0,0 +1,16 @@
package main
import (
"crypto/sha1"
"encoding/hex"
)
func main() {
script := "hello"
sumRaw := sha1.Sum([]byte(script))
sum := hex.EncodeToString(sumRaw[:])
println(sum)
}
// Output:
// aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d

42
_test/issue-1330.go Normal file
View File

@@ -0,0 +1,42 @@
package main
import (
"fmt"
"io"
"net"
)
type wrappedConn struct {
net.Conn
}
func main() {
_, err := net.Listen("tcp", "127.0.0.1:49153")
if err != nil {
panic(err)
}
dialer := &net.Dialer{
LocalAddr: &net.TCPAddr{
IP: net.ParseIP("127.0.0.1"),
Port: 0,
},
}
conn, err := dialer.Dial("tcp", "127.0.0.1:49153")
if err != nil {
panic(err)
}
defer conn.Close()
t := &wrappedConn{conn}
var w io.Writer = t
if n, err := w.Write([]byte("hello")); err != nil {
fmt.Println(err)
} else {
fmt.Println(n)
}
}
// Output:
// 5

17
_test/issue-1332.go Normal file
View File

@@ -0,0 +1,17 @@
package main
func run(fn func(name string)) { fn("test") }
type T2 struct {
name string
}
func (t *T2) f(s string) { println(s, t.name) }
func main() {
t2 := &T2{"foo"}
run(t2.f)
}
// Output:
// test foo

37
_test/issue-1333.go Normal file
View File

@@ -0,0 +1,37 @@
package main
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
)
func mock(name string) http.HandlerFunc {
return func(rw http.ResponseWriter, req *http.Request) {
fmt.Fprint(rw, "Hello ", name)
}
}
func client(uri string) {
resp, err := http.Get(uri)
if err != nil {
panic(err)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
panic(err)
}
fmt.Println(string(body))
}
func main() {
mux := http.NewServeMux()
server := httptest.NewServer(mux)
defer server.Close()
mux.Handle("/", mock("foo"))
client(server.URL)
}
// Output:
// Hello foo

26
_test/issue-1337.go Normal file
View File

@@ -0,0 +1,26 @@
package main
import (
"io"
"os"
)
func f(i interface{}) {
switch at := i.(type) {
case int, int8:
println("integer", at)
case io.Reader:
println("reader")
}
println("bye")
}
func main() {
var fd *os.File
var r io.Reader = fd
f(r)
}
// Output:
// reader
// bye

12
_test/issue-1342.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import "fmt"
func main() {
var a interface{}
a = "a"
fmt.Println(a, a == "a")
}
// Output:
// a true

13
_test/issue-1344.go Normal file
View File

@@ -0,0 +1,13 @@
package main
import "fmt"
func main() {
var m = map[string]interface{}{"a": "a"}
a, _ := m["a"]
b, ok := a.(string)
fmt.Println("a:", a, ", b:", b, ", ok:", ok)
}
// Output:
// a: a , b: a , ok: true

21
_test/issue-1354.go Normal file
View File

@@ -0,0 +1,21 @@
package main
func main() {
println(test()) // Go prints true, Yaegi false
}
func test() bool {
if true {
goto label
}
goto label
label:
println("Go continues here")
return true
println("Yaegi goes straight to this return (this line is never printed)")
return false
}
// Output:
// Go continues here
// true

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

@@ -0,0 +1,22 @@
package main
import "github.com/traefik/yaegi/_test/p2"
func f(i interface{}) {
_, ok := i.(p2.I)
println("ok:", ok)
}
func main() {
var v *p2.T
var i interface{}
i = v
_, ok := i.(p2.I)
println("ok:", ok)
f(v)
}
// Output:
// ok: true
// ok: true

14
_test/issue-1360.go Normal file
View File

@@ -0,0 +1,14 @@
package main
import (
"fmt"
. "net"
)
func main() {
v := IP{}
fmt.Println(v)
}
// Output:
// <nil>

27
_test/issue-1361.go Normal file
View File

@@ -0,0 +1,27 @@
package main
import (
"fmt"
"math"
)
type obj struct {
num float64
}
type Fun func(o *obj) (r *obj, err error)
func numFun(fn func(f float64) float64) Fun {
return func(o *obj) (*obj, error) {
return &obj{fn(o.num)}, nil
}
}
func main() {
f := numFun(math.Cos)
r, err := f(&obj{})
fmt.Println(r, err)
}
// Output:
// &{1} <nil>

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

@@ -0,0 +1,16 @@
package main
import (
"fmt"
"strconv"
)
func main() {
var value interface{}
var err error
value, err = strconv.ParseFloat("123", 64)
fmt.Println(value, err)
}
// Output:
// 123 <nil>

18
_test/issue-1365.go Normal file
View File

@@ -0,0 +1,18 @@
package main
func genInt() (int, error) { return 3, nil }
func getInt() (value int) {
value, err := genInt()
if err != nil {
panic(err)
}
return
}
func main() {
println(getInt())
}
// Output:
// 3

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

@@ -0,0 +1,16 @@
package main
const dollar byte = 36
func main() {
var c byte = 36
switch true {
case c == dollar:
println("ok")
default:
println("not ok")
}
}
// Output:
// ok

18
_test/issue-1371.go Normal file
View File

@@ -0,0 +1,18 @@
package main
import "fmt"
type node struct {
parent *node
child []*node
key string
}
func main() {
root := &node{key: "root"}
root.child = nil
fmt.Println("root:", root)
}
// Output:
// root: &{<nil> [] root}

17
_test/issue-1373.go Normal file
View File

@@ -0,0 +1,17 @@
package main
import (
"fmt"
"go/ast"
)
func NewBadExpr() ast.Expr {
return &ast.BadExpr{}
}
func main() {
fmt.Printf("%T\n", NewBadExpr().(*ast.BadExpr))
}
// Output:
// *ast.BadExpr

38
_test/issue-1375.go Normal file
View File

@@ -0,0 +1,38 @@
package main
import "fmt"
type Option func(*Struct)
func WithOption(opt string) Option {
return func(s *Struct) {
s.opt = opt
}
}
type Struct struct {
opt string
}
func New(opts ...Option) *Struct {
s := new(Struct)
for _, opt := range opts {
opt(s)
}
return s
}
func (s *Struct) ShowOption() {
fmt.Println(s.opt)
}
func main() {
opts := []Option{
WithOption("test"),
}
s := New(opts...)
s.ShowOption()
}
// Output:
// test

21
_test/issue-1378.go Normal file
View File

@@ -0,0 +1,21 @@
package main
import (
"fmt"
"time"
)
func main() {
t, err := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
if err != nil {
panic(err)
}
fn := func() error {
_, err := t.GobEncode()
return err
}
fmt.Println(fn())
}
// Output:
// <nil>

58
_test/issue-1381.go Normal file
View File

@@ -0,0 +1,58 @@
package main
import (
"bytes"
"fmt"
)
func main() {
var bufPtrOne *bytes.Buffer
var bufPtrTwo *bytes.Buffer
var bufPtrThree *bytes.Buffer
var bufPtrFour *bytes.Buffer
for i := 0; i < 2; i++ {
bufOne := bytes.Buffer{}
bufTwo := &bytes.Buffer{}
var bufThree bytes.Buffer
bufFour := new(bytes.Buffer)
if bufPtrOne == nil {
bufPtrOne = &bufOne
} else if bufPtrOne == &bufOne {
fmt.Println("bufOne was not properly redeclared")
} else {
fmt.Println("bufOne is properly redeclared")
}
if bufPtrTwo == nil {
bufPtrTwo = bufTwo
} else if bufPtrTwo == bufTwo {
fmt.Println("bufTwo was not properly redeclared")
} else {
fmt.Println("bufTwo is properly redeclared")
}
if bufPtrThree == nil {
bufPtrThree = &bufThree
} else if bufPtrThree == &bufThree {
fmt.Println("bufThree was not properly redeclared")
} else {
fmt.Println("bufThree is properly redeclared")
}
if bufPtrFour == nil {
bufPtrFour = bufFour
} else if bufPtrFour == bufFour {
fmt.Println("bufFour was not properly redeclared")
} else {
fmt.Println("bufFour is properly redeclared")
}
}
}
// Output:
// bufOne is properly redeclared
// bufTwo is properly redeclared
// bufThree is properly redeclared
// bufFour is properly redeclared

9
_test/p2/p2.go Normal file
View File

@@ -0,0 +1,9 @@
package p2
type I interface {
isI()
}
type T struct{}
func (t *T) isI() {}

0
_test/p3/empty Normal file
View File

19
_test/var16.go Normal file
View File

@@ -0,0 +1,19 @@
package main
func getArray() ([]int, error) { println("getArray"); return []int{1, 2}, nil }
func getNum() (int, error) { println("getNum"); return 3, nil }
func main() {
if a, err := getNum(); err != nil {
println("#1", a)
} else if a, err := getArray(); err != nil {
println("#2", a)
}
println("#3")
}
// Output:
// getNum
// getArray
// #3

View File

@@ -535,6 +535,7 @@ func {{$name}}(n *node) {
typ := n.typ.concrete().TypeOf()
isInterface := n.typ.TypeOf().Kind() == reflect.Interface
c0, c1 := n.child[0], n.child[1]
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
{{- if or (eq $op.Name "==") (eq $op.Name "!=") }}
@@ -621,9 +622,39 @@ func {{$name}}(n *node) {
}
return
}
// Do not attempt to optimize '==' or '!=' if an operand is an interface.
// This will preserve proper dynamic type checking at runtime. For static types,
// type checks are already performed, so bypass them if possible.
if t0.Kind() == reflect.Interface || t1.Kind() == reflect.Interface {
v0 := genValue(c0)
v1 := genValue(c1)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
i1 := v1(f).Interface()
if i0 {{$op.Name}} i1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
i1 := v1(f).Interface()
dest(f).SetBool(i0 {{$op.Name}} i1)
return tnext
}
}
return
}
{{- end}}
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
switch {
case isString(t0) || isString(t1):
switch {
case isInterface:

View File

@@ -282,6 +282,7 @@ var actions = [...]string{
aDec: "--",
aEqual: "==",
aGreater: ">",
aGreaterEqual: ">=",
aGetFunc: "getFunc",
aGetIndex: "getIndex",
aGetMethod: "getMethod",
@@ -290,6 +291,7 @@ var actions = [...]string{
aLand: "&&",
aLor: "||",
aLower: "<",
aLowerEqual: "<=",
aMethod: "Method",
aMul: "*",
aMulAssign: "*=",

View File

@@ -202,38 +202,42 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
}
}
}
n.findex = -1
n.val = nil
sc = sc.pushBloc()
// Pre-define symbols for labels defined in this block, so we are sure that
// they are already defined when met.
// TODO(marc): labels must be stored outside of symbols to avoid collisions.
for _, c := range n.child {
if c.kind != labeledStmt {
continue
}
label := c.child[0].ident
sym := &symbol{kind: labelSym, node: c, index: -1}
sc.sym[label] = sym
c.sym = sym
}
case breakStmt, continueStmt, gotoStmt:
if len(n.child) > 0 {
// Handle labeled statements.
label := n.child[0].ident
if sym, _, ok := sc.lookup(label); ok {
if sym.kind != labelSym {
err = n.child[0].cfgErrorf("label %s not defined", label)
break
}
sym.from = append(sym.from, n)
n.sym = sym
} else {
n.sym = &symbol{kind: labelSym, from: []*node{n}, index: -1}
sc.sym[label] = n.sym
}
if len(n.child) == 0 {
break
}
case labeledStmt:
// Handle labeled statements.
label := n.child[0].ident
// TODO(marc): labels must be stored outside of symbols to avoid collisions
// Used labels are searched in current and sub scopes, not upper ones.
if sym, ok := sc.lookdown(label); ok {
sym.node = n
if sym, _, ok := sc.lookup(label); ok {
if sym.kind != labelSym {
err = n.child[0].cfgErrorf("label %s not defined", label)
break
}
n.sym = sym
} else {
n.sym = &symbol{kind: labelSym, node: n, index: -1}
n.sym = &symbol{kind: labelSym, index: -1}
sc.sym[label] = n.sym
}
if n.kind == gotoStmt {
n.sym.from = append(n.sym.from, n) // To allow forward goto statements.
}
sc.sym[label] = n.sym
case caseClause:
sc = sc.pushBloc()
@@ -319,6 +323,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
}
// Propagate type to children, to handle implicit types
for _, c := range child {
if isBlank(c) {
err = n.cfgErrorf("cannot use _ as value")
return false
}
switch c.kind {
case binaryExpr, unaryExpr, compositeLitExpr:
// Do not attempt to propagate composite type to operator expressions,
@@ -478,6 +486,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
switch n.kind {
case addressExpr:
if isBlank(n.child[0]) {
err = n.cfgErrorf("cannot use _ as value")
break
}
wireChild(n)
err = check.addressExpr(n)
@@ -513,6 +525,11 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
updateSym := false
var sym *symbol
var level int
if isBlank(src) {
err = n.cfgErrorf("cannot use _ as value")
break
}
if n.kind == defineStmt || (n.kind == assignStmt && dest.ident == "_") {
if atyp != nil {
dest.typ = atyp
@@ -645,6 +662,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
}
case incDecStmt:
err = check.unaryExpr(n)
if err != nil {
break
}
wireChild(n)
n.findex = n.child[0].findex
n.level = n.child[0].level
@@ -763,6 +784,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
}
case indexExpr:
if isBlank(n.child[0]) {
err = n.cfgErrorf("cannot use _ as value")
break
}
wireChild(n)
t := n.child[0].typ
switch t.cat {
@@ -853,30 +878,51 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
n.rval = l.rval
case breakStmt:
if len(n.child) > 0 {
gotoLabel(n.sym)
} else {
if len(n.child) == 0 {
n.tnext = sc.loop
break
}
if !n.hasAnc(n.sym.node) {
err = n.cfgErrorf("invalid break label %s", n.child[0].ident)
break
}
n.tnext = n.sym.node
case continueStmt:
if len(n.child) > 0 {
gotoLabel(n.sym)
} else {
if len(n.child) == 0 {
n.tnext = sc.loopRestart
break
}
if !n.hasAnc(n.sym.node) {
err = n.cfgErrorf("invalid continue label %s", n.child[0].ident)
break
}
n.tnext = n.sym.node.child[1].lastChild().start
case gotoStmt:
gotoLabel(n.sym)
if n.sym.node == nil {
// It can be only due to a forward goto, to be resolved at labeledStmt.
// Invalid goto labels are catched at AST parsing.
break
}
n.tnext = n.sym.node.start
case labeledStmt:
wireChild(n)
if len(n.child) > 1 {
n.start = n.child[1].start
}
gotoLabel(n.sym)
for _, c := range n.sym.from {
c.tnext = n.start // Resolve forward goto.
}
case callExpr:
for _, c := range n.child {
if isBlank(c) {
err = n.cfgErrorf("cannot use _ as value")
return
}
}
wireChild(n)
switch {
case isBuiltinCall(n, sc):
@@ -1392,9 +1438,17 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
sc = sc.pop()
case keyValueExpr:
if isBlank(n.child[1]) {
err = n.cfgErrorf("cannot use _ as value")
break
}
wireChild(n)
case landExpr:
if isBlank(n.child[0]) || isBlank(n.child[1]) {
err = n.cfgErrorf("cannot use _ as value")
break
}
n.start = n.child[0].start
n.child[0].tnext = n.child[1].start
setFNext(n.child[0], n)
@@ -1406,6 +1460,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
}
case lorExpr:
if isBlank(n.child[0]) || isBlank(n.child[1]) {
err = n.cfgErrorf("cannot use _ as value")
break
}
n.start = n.child[0].start
n.child[0].tnext = n
setFNext(n.child[0], n.child[1].start)
@@ -1451,6 +1509,12 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
err = n.cfgErrorf("too many arguments to return")
break
}
for _, c := range n.child {
if isBlank(c) {
err = n.cfgErrorf("cannot use _ as value")
return
}
}
returnSig := sc.def.child[2]
if mustReturnValue(returnSig) {
nret := len(n.child)
@@ -1742,6 +1806,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
}
case starExpr:
if isBlank(n.child[0]) {
err = n.cfgErrorf("cannot use _ as value")
break
}
switch {
case n.anc.kind == defineStmt && len(n.anc.child) == 3 && n.anc.child[1] == n:
// pointer type expression in a var definition
@@ -1832,6 +1900,7 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
setFNext(c, clauses[i+1])
}
}
sbn.start = clauses[0].start
n.start = n.child[0].start
n.child[0].tnext = sbn.start
@@ -1886,6 +1955,10 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string
wireChild(n)
c0, c1 := n.child[0], n.child[1]
if isBlank(c0) || isBlank(c1) {
err = n.cfgErrorf("cannot use _ as value")
break
}
if c1.typ == nil {
if c1.typ, err = nodeType(interp, sc, c1); err != nil {
return
@@ -2060,12 +2133,18 @@ func compDefineX(sc *scope, n *node) error {
}
for i, t := range types {
index := sc.add(t)
sc.sym[n.child[i].ident] = &symbol{index: index, kind: varSym, typ: t}
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) {
// Reuse symbol in case of a variable redeclaration with the same type.
index = sym.index
} else {
index = sc.add(t)
sc.sym[id] = &symbol{index: index, kind: varSym, typ: t}
}
n.child[i].typ = t
n.child[i].findex = index
}
return nil
}
@@ -2419,6 +2498,15 @@ func (n *node) fieldType(m int) *node {
// lastChild returns the last child of a node.
func (n *node) lastChild() *node { return n.child[len(n.child)-1] }
func (n *node) hasAnc(nod *node) bool {
for a := n.anc; a != nil; a = a.anc {
if a == nod {
return true
}
}
return false
}
func isKey(n *node) bool {
return n.anc.kind == fileStmt ||
(n.anc.kind == selectorExpr && n.anc.child[0] != n) ||
@@ -2585,17 +2673,6 @@ func typeSwichAssign(n *node) bool {
return ts.kind == typeSwitch && ts.child[1].action == aAssign
}
func gotoLabel(s *symbol) {
if s.node == nil {
return
}
for _, c := range s.from {
if c.tnext == nil {
c.tnext = s.node.start
}
}
}
func compositeGenerator(n *node, typ *itype, rtyp reflect.Type) (gen bltnGenerator) {
switch typ.cat {
case aliasT, ptrT:
@@ -2728,3 +2805,10 @@ func isBoolAction(n *node) bool {
}
return false
}
func isBlank(n *node) bool {
if n.kind == parenExpr && len(n.child) > 0 {
return isBlank(n.child[0])
}
return n.ident == "_"
}

View File

@@ -10,7 +10,8 @@ import (
)
func TestCompileAST(t *testing.T) {
file, err := parser.ParseFile(token.NewFileSet(), "_.go", `
i := New(Options{})
file, err := parser.ParseFile(i.FileSet(), "_.go", `
package main
import "fmt"
@@ -61,7 +62,6 @@ func TestCompileAST(t *testing.T) {
{desc: "expr", node: dFunc.Body.List[0]},
}
i := New(Options{})
_ = i.Use(stdlib.Symbols)
for _, c := range cases {

View File

@@ -59,6 +59,9 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
for i := 0; i < n.nleft; i++ {
dest, src := n.child[i], n.child[sbase+i]
if isBlank(src) {
err = n.cfgErrorf("cannot use _ as value")
}
val := src.rval
if n.anc.kind == constDecl {
if _, err2 := interp.cfg(n, sc, importPath, pkgName); err2 != nil {
@@ -208,15 +211,23 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
case ".": // import symbols in current scope
for n, v := range pkg {
typ := v.Type()
kind := binSym
if isBinType(v) {
typ = typ.Elem()
kind = typeSym
}
sc.sym[n] = &symbol{kind: binSym, typ: valueTOf(typ, withScope(sc)), rval: v}
sc.sym[n] = &symbol{kind: kind, typ: valueTOf(typ, withScope(sc)), rval: v}
}
default: // import symbols in package namespace
if name == "" {
name = interp.pkgNames[ipath]
}
// If an incomplete type exists, delete it
if sym, exists := sc.sym[name]; exists && sym.kind == typeSym && sym.typ.incomplete {
delete(sc.sym, name)
}
// Imports of a same package are all mapped in the same scope, so we cannot just
// map them by their names, otherwise we could have collisions from same-name
// imports in different source files of the same package. Therefore, we suffix
@@ -266,6 +277,10 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
}
case typeSpec, typeSpecAssign:
if isBlank(n.child[0]) {
err = n.cfgErrorf("cannot use _ as value")
return false
}
typeName := n.child[0].ident
var typ *itype
if typ, err = nodeType(interp, sc, n.child[1]); err != nil {

View File

@@ -19,6 +19,8 @@ import (
"path"
"path/filepath"
"reflect"
"runtime"
"runtime/debug"
"strconv"
"strings"
"sync"
@@ -628,7 +630,14 @@ func (interp *Interpreter) EvalWithContext(ctx context.Context, src string) (ref
done := make(chan struct{})
go func() {
defer close(done)
defer func() {
if r := recover(); r != nil {
var pc [64]uintptr
n := runtime.Callers(1, pc[:])
err = Panic{Value: r, Callers: pc[:n], Stack: debug.Stack()}
}
close(done)
}()
v, err = interp.Eval(src)
}()

View File

@@ -37,6 +37,8 @@ func TestInterpConsistencyBuild(t *testing.T) {
file.Name() == "assign12.go" || // expect error
file.Name() == "assign15.go" || // expect error
file.Name() == "bad0.go" || // expect error
file.Name() == "break0.go" || // expect error
file.Name() == "cont3.go" || // expect error
file.Name() == "const9.go" || // expect error
file.Name() == "export1.go" || // non-main package
file.Name() == "export0.go" || // non-main package
@@ -52,6 +54,7 @@ func TestInterpConsistencyBuild(t *testing.T) {
file.Name() == "io0.go" || // use random number
file.Name() == "issue-1093.go" || // expect error
file.Name() == "issue-1276.go" || // expect error
file.Name() == "issue-1330.go" || // expect error
file.Name() == "op1.go" || // expect error
file.Name() == "op7.go" || // expect error
file.Name() == "op9.go" || // expect error
@@ -197,6 +200,16 @@ func TestInterpErrorConsistency(t *testing.T) {
expectedInterp: "1:1: expected 'package', found println",
expectedExec: "1:1: expected 'package', found println",
},
{
fileName: "break0.go",
expectedInterp: "15:5: invalid break label OuterLoop",
expectedExec: "15:11: invalid break label OuterLoop",
},
{
fileName: "cont3.go",
expectedInterp: "15:5: invalid continue label OuterLoop",
expectedExec: "15:14: invalid continue label OuterLoop",
},
{
fileName: "const9.go",
expectedInterp: "5:2: constant definition loop",

View File

@@ -5,6 +5,8 @@ import (
"bytes"
"context"
"fmt"
"go/build"
"go/parser"
"io"
"log"
"net/http"
@@ -127,6 +129,10 @@ func TestEvalAssign(t *testing.T) {
{src: "i := 1; j := &i; (*j) = 2", res: "2"},
{src: "i64 := testpkg.val; i64 == 11", res: "true"},
{pre: func() { eval(t, i, "k := 1") }, src: `k := "Hello world"`, res: "Hello world"}, // allow reassignment in subsequent evaluations
{src: "_ = _", err: "1:28: cannot use _ as value"},
{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"},
})
}
@@ -177,6 +183,7 @@ func TestEvalBuiltin(t *testing.T) {
{src: `t := map[int]int{}; t[123]--; t`, res: "map[123:-1]"},
{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"},
})
}
@@ -201,6 +208,14 @@ func TestEvalDeclWithExpr(t *testing.T) {
})
}
func TestEvalTypeSpec(t *testing.T) {
i := interp.New(interp.Options{})
runTests(t, i, []testCase{
{src: `type _ struct{}`, err: "1:19: cannot use _ as value"},
{src: `a := struct{a, _ int}{32, 0}`, res: "{32 0}"},
})
}
func TestEvalFunc(t *testing.T) {
i := interp.New(interp.Options{})
runTests(t, i, []testCase{
@@ -209,6 +224,8 @@ func TestEvalFunc(t *testing.T) {
{src: `(func () int {f := func() (a, b int) {a, b = 3, 4; return}; x, y := f(); return x+y})()`, res: "7"},
{src: `(func () int {f := func() (a int, b, c int) {a, b, c = 3, 4, 5; return}; x, y, z := f(); return x+y+z})()`, res: "12"},
{src: `(func () int {f := func() (a, b, c int) {a, b, c = 3, 4, 5; return}; x, y, z := f(); return x+y+z})()`, res: "12"},
{src: `func f() int { return _ }`, err: "1:29: cannot use _ as value"},
{src: `(func (x int) {})(_)`, err: "1:28: cannot use _ as value"},
})
}
@@ -431,6 +448,16 @@ func TestEvalComparison(t *testing.T) {
`,
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"},
{src: `v := interface{}(2); v > 1`, err: "1:49: invalid operation: operator > not defined on interface{}"},
{src: `v := interface{}(int64(2)); v == 2`, res: "false"},
{src: `v := interface{}(int64(2)); v != 2`, res: "true"},
{src: `v := interface{}(2.3); v == 2.3`, res: "true"},
{src: `v := interface{}(float32(2.3)); v != 2.3`, res: "true"},
{src: `v := interface{}("hello"); v == "hello"`, res: "true"},
{src: `v := interface{}("hello"); v < "hellp"`, err: "1:55: invalid operation: operator < not defined on interface{}"},
})
}
@@ -476,6 +503,8 @@ func TestEvalCompositeStruct(t *testing.T) {
{src: `a := struct{A,B,C int}{A:1,A:2,C:3}`, err: "1:55: duplicate field name A in struct literal"},
{src: `a := struct{A,B,C int}{A:1,B:2.2,C:3}`, err: "1:57: 11/5 truncated to int"},
{src: `a := struct{A,B,C int}{A:1,2,C:3}`, err: "1:55: mixture of field:value and value elements in struct literal"},
{src: `a := struct{A,B,C int}{1,2,_}`, err: "1:33: cannot use _ as value"},
{src: `a := struct{A,B,C int}{B: _}`, err: "1:51: cannot use _ as value"},
})
}
@@ -500,6 +529,8 @@ func TestEvalSliceExpression(t *testing.T) {
{src: `a := []int{0,1,2,3}[1:3:]`, err: "1:51: 3rd index required in 3-index slice"},
{src: `a := []int{0,1,2}[3:1]`, err: "invalid index values, must be low <= high <= max"},
{pre: func() { eval(t, i, `type Str = string; var r Str = "truc"`) }, src: `r[1]`, res: "114"},
{src: `_[12]`, err: "1:28: cannot use _ as value"},
{src: `b := []int{0,1,2}[_:4]`, err: "1:33: cannot use _ as value"},
})
}
@@ -510,6 +541,7 @@ func TestEvalConversion(t *testing.T) {
{src: `i := 1.1; a := uint64(i)`, res: "1"},
{src: `b := string(49)`, res: "1"},
{src: `c := uint64(1.1)`, err: "1:40: cannot convert expression of type untyped float to type uint64"},
{src: `int(_)`, err: "1:28: cannot use _ as value"},
})
}
@@ -519,6 +551,9 @@ func TestEvalUnary(t *testing.T) {
{src: "a := -1", res: "-1"},
{src: "b := +1", res: "1", skip: "BUG"},
{src: "c := !false", res: "true"},
{src: "_ = 2; _++", err: "1:35: cannot use _ as value"},
{src: "_ = false; !_ == true", err: "1:39: cannot use _ as value"},
{src: "!((((_))))", err: "1:28: cannot use _ as value"},
})
}
@@ -1625,6 +1660,16 @@ func TestStdio(t *testing.T) {
}
}
func TestNoGoFiles(t *testing.T) {
i := interp.New(interp.Options{GoPath: build.Default.GOPATH})
_, err := i.Eval(`import "github.com/traefik/yaegi/_test/p3"`)
if strings.Contains(err.Error(), "no Go files in") {
return
}
t.Fatalf("failed to detect no Go files: %v", err)
}
func TestIssue1142(t *testing.T) {
i := interp.New(interp.Options{})
runTests(t, i, []testCase{
@@ -1749,3 +1794,59 @@ func TestRestrictedEnv(t *testing.T) {
t.Fatal("expected \"\", got " + s)
}
}
func TestIssue1388(t *testing.T) {
i := interp.New(interp.Options{Env: []string{"foo=bar"}})
err := i.Use(stdlib.Symbols)
if err != nil {
t.Fatal(err)
}
_, err = i.Eval(`x := errors.New("")`)
if err == nil {
t.Fatal("Expected an error")
}
_, err = i.Eval(`import "errors"`)
if err != nil {
t.Fatal(err)
}
_, err = i.Eval(`x := errors.New("")`)
if err != nil {
t.Fatal(err)
}
}
func TestIssue1383(t *testing.T) {
const src = `
package main
func main() {
fmt.Println("Hello")
}
`
interp := interp.New(interp.Options{})
err := interp.Use(stdlib.Symbols)
if err != nil {
t.Fatal(err)
}
_, err = interp.Eval(`import "fmt"`)
if err != nil {
t.Fatal(err)
}
ast, err := parser.ParseFile(interp.FileSet(), "_.go", src, parser.DeclarationErrors)
if err != nil {
t.Fatal(err)
}
prog, err := interp.CompileAST(ast)
if err != nil {
t.Fatal(err)
}
_, err = interp.Execute(prog)
if err != nil {
t.Fatal(err)
}
}

View File

@@ -1,9 +1,12 @@
package interp
import (
"go/constant"
"log"
"reflect"
"testing"
"github.com/traefik/yaegi/stdlib"
)
func init() { log.SetFlags(log.Lshortfile) }
@@ -19,13 +22,13 @@ func TestIsNatural(t *testing.T) {
n: &node{
typ: &itype{
rtype: func() reflect.Type {
var x uint = 3
return reflect.TypeOf(x)
var a uint = 3
return reflect.TypeOf(a)
}(),
},
rval: func() reflect.Value {
var x uint = 3
return reflect.ValueOf(x)
var a uint = 3
return reflect.ValueOf(a)
}(),
},
expected: true,
@@ -35,13 +38,13 @@ func TestIsNatural(t *testing.T) {
n: &node{
typ: &itype{
rtype: func() reflect.Type {
x := 3
return reflect.TypeOf(x)
a := 3
return reflect.TypeOf(a)
}(),
},
rval: func() reflect.Value {
x := 3
return reflect.ValueOf(x)
a := 3
return reflect.ValueOf(a)
}(),
},
expected: true,
@@ -51,13 +54,13 @@ func TestIsNatural(t *testing.T) {
n: &node{
typ: &itype{
rtype: func() reflect.Type {
var x int = 3
return reflect.TypeOf(x)
var a int = 3
return reflect.TypeOf(a)
}(),
},
rval: func() reflect.Value {
var x int = 3
return reflect.ValueOf(x)
var a int = 3
return reflect.ValueOf(a)
}(),
},
expected: true,
@@ -67,13 +70,13 @@ func TestIsNatural(t *testing.T) {
n: &node{
typ: &itype{
rtype: func() reflect.Type {
var x float64 = 3.0
return reflect.TypeOf(x)
var a float64 = 3.0
return reflect.TypeOf(a)
}(),
},
rval: func() reflect.Value {
var x float64 = 3.0
return reflect.ValueOf(x)
var a float64 = 3.0
return reflect.ValueOf(a)
}(),
},
expected: true,
@@ -83,13 +86,13 @@ func TestIsNatural(t *testing.T) {
n: &node{
typ: &itype{
rtype: func() reflect.Type {
var x float64 = 3.14
return reflect.TypeOf(x)
var a float64 = 3.14
return reflect.TypeOf(a)
}(),
},
rval: func() reflect.Value {
var x float64 = 3.14
return reflect.ValueOf(x)
var a float64 = 3.14
return reflect.ValueOf(a)
}(),
},
expected: false,
@@ -99,13 +102,13 @@ func TestIsNatural(t *testing.T) {
n: &node{
typ: &itype{
rtype: func() reflect.Type {
var x int = -3
return reflect.TypeOf(x)
var a int = -3
return reflect.TypeOf(a)
}(),
},
rval: func() reflect.Value {
var x int = -3
return reflect.ValueOf(x)
var a int = -3
return reflect.ValueOf(a)
}(),
},
expected: false,
@@ -188,3 +191,42 @@ func TestIsNatural(t *testing.T) {
}
}
}
func TestGlobals(t *testing.T) {
i := New(Options{})
if err := i.Use(stdlib.Symbols); err != nil {
t.Fatal(err)
}
if _, err := i.Eval("var a = 1"); err != nil {
t.Fatal(err)
}
if _, err := i.Eval("b := 2"); err != nil {
t.Fatal(err)
}
if _, err := i.Eval("const c = 3"); err != nil {
t.Fatal(err)
}
g := i.Globals()
a := g["a"]
if !a.IsValid() {
t.Fatal("a not found")
}
if a := a.Interface(); a != 1 {
t.Fatalf("wrong a: want (%[1]T) %[1]v, have (%[2]T) %[2]v", 1, a)
}
b := g["b"]
if !b.IsValid() {
t.Fatal("b not found")
}
if b := b.Interface(); b != 2 {
t.Fatalf("wrong b: want (%[1]T) %[1]v, have (%[2]T) %[2]v", 2, b)
}
c := g["c"]
if !c.IsValid() {
t.Fatal("c not found")
}
if cc, ok := c.Interface().(constant.Value); ok && constant.MakeInt64(3) != cc {
t.Fatalf("wrong c: want (%[1]T) %[1]v, have (%[2]T) %[2]v", constant.MakeInt64(3), cc)
}
}

View File

@@ -2624,6 +2624,7 @@ func equal(n *node) {
typ := n.typ.concrete().TypeOf()
isInterface := n.typ.TypeOf().Kind() == reflect.Interface
c0, c1 := n.child[0], n.child[1]
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
if c0.typ.cat == aliasT || c1.typ.cat == aliasT {
switch {
@@ -2709,7 +2710,37 @@ func equal(n *node) {
return
}
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
// Do not attempt to optimize '==' or '!=' if an operand is an interface.
// This will preserve proper dynamic type checking at runtime. For static types,
// type checks are already performed, so bypass them if possible.
if t0.Kind() == reflect.Interface || t1.Kind() == reflect.Interface {
v0 := genValue(c0)
v1 := genValue(c1)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
i1 := v1(f).Interface()
if i0 == i1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
i1 := v1(f).Interface()
dest(f).SetBool(i0 == i1)
return tnext
}
}
return
}
switch {
case isString(t0) || isString(t1):
switch {
case isInterface:
@@ -3193,8 +3224,9 @@ func greater(n *node) {
typ := n.typ.concrete().TypeOf()
isInterface := n.typ.TypeOf().Kind() == reflect.Interface
c0, c1 := n.child[0], n.child[1]
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
switch {
case isString(t0) || isString(t1):
switch {
case isInterface:
@@ -3520,8 +3552,9 @@ func greaterEqual(n *node) {
typ := n.typ.concrete().TypeOf()
isInterface := n.typ.TypeOf().Kind() == reflect.Interface
c0, c1 := n.child[0], n.child[1]
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
switch {
case isString(t0) || isString(t1):
switch {
case isInterface:
@@ -3847,8 +3880,9 @@ func lower(n *node) {
typ := n.typ.concrete().TypeOf()
isInterface := n.typ.TypeOf().Kind() == reflect.Interface
c0, c1 := n.child[0], n.child[1]
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
switch {
case isString(t0) || isString(t1):
switch {
case isInterface:
@@ -4174,8 +4208,9 @@ func lowerEqual(n *node) {
typ := n.typ.concrete().TypeOf()
isInterface := n.typ.TypeOf().Kind() == reflect.Interface
c0, c1 := n.child[0], n.child[1]
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
switch {
case isString(t0) || isString(t1):
switch {
case isInterface:
@@ -4501,6 +4536,7 @@ func notEqual(n *node) {
typ := n.typ.concrete().TypeOf()
isInterface := n.typ.TypeOf().Kind() == reflect.Interface
c0, c1 := n.child[0], n.child[1]
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
if c0.typ.cat == aliasT || c1.typ.cat == aliasT {
switch {
@@ -4586,7 +4622,37 @@ func notEqual(n *node) {
return
}
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
// Do not attempt to optimize '==' or '!=' if an operand is an interface.
// This will preserve proper dynamic type checking at runtime. For static types,
// type checks are already performed, so bypass them if possible.
if t0.Kind() == reflect.Interface || t1.Kind() == reflect.Interface {
v0 := genValue(c0)
v1 := genValue(c1)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
i1 := v1(f).Interface()
if i0 != i1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
i1 := v1(f).Interface()
dest(f).SetBool(i0 != i1)
return tnext
}
}
return
}
switch {
case isString(t0) || isString(t1):
switch {
case isInterface:

View File

@@ -3,6 +3,7 @@ package interp
import (
"context"
"go/ast"
"go/token"
"os"
"reflect"
"runtime"
@@ -16,6 +17,12 @@ type Program struct {
init []*node
}
// FileSet is the fileset that must be used for parsing Go that will be passed
// to interp.CompileAST().
func (interp *Interpreter) FileSet() *token.FileSet {
return interp.fset
}
// Compile parses and compiles a Go code represented as a string.
func (interp *Interpreter) Compile(src string) (*Program, error) {
return interp.compileSrc(src, "", true)
@@ -55,6 +62,9 @@ func (interp *Interpreter) compileSrc(src, name string, inc bool) (*Program, err
// CompileAST builds a Program for the given Go code AST. Files and block
// statements can be compiled, as can most expressions. Var declaration nodes
// cannot be compiled.
//
// WARNING: The node must have been parsed using interp.FileSet(). Results are
// unpredictable otherwise.
func (interp *Interpreter) CompileAST(n ast.Node) (*Program, error) {
// Convert AST.
pkgName, root, err := interp.ast(n)

View File

@@ -9,7 +9,6 @@ import (
"reflect"
"regexp"
"strings"
"sync"
)
// bltn type defines functions which run at CFG execution.
@@ -1086,7 +1085,7 @@ func genInterfaceWrapper(n *node, typ reflect.Type) func(*frame) reflect.Value {
for i, m := range methods {
if m == nil {
// First direct method lookup on field.
if r := methodByName(v, names[i]); r.IsValid() {
if r := methodByName(v, names[i], indexes[i]); r.IsValid() {
w.Field(i + 1).Set(r)
continue
}
@@ -1111,15 +1110,50 @@ func genInterfaceWrapper(n *node, typ reflect.Type) func(*frame) reflect.Value {
}
}
// methodByName return the method corresponding to name on value, or nil if not found.
// methodByName returns the method corresponding to name on value, or nil if not found.
// The search is extended on valueInterface wrapper if present.
func methodByName(value reflect.Value, name string) reflect.Value {
// If valid, the returned value is a method function with the receiver already set
// (no need to pass it at call).
func methodByName(value reflect.Value, name string, index []int) (v reflect.Value) {
if vi, ok := value.Interface().(valueInterface); ok {
if v := getConcreteValue(vi.value).MethodByName(name); v.IsValid() {
return v
if v = getConcreteValue(vi.value).MethodByName(name); v.IsValid() {
return
}
}
return value.MethodByName(name)
if v = value.MethodByName(name); v.IsValid() {
return
}
for value.Kind() == reflect.Ptr {
value = value.Elem()
if checkFieldIndex(value.Type(), index) {
value = value.FieldByIndex(index)
}
if v = value.MethodByName(name); v.IsValid() {
return
}
}
return
}
func checkFieldIndex(typ reflect.Type, index []int) bool {
if len(index) == 0 {
return false
}
t := typ
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
if t.Kind() != reflect.Struct {
return false
}
i := index[0]
if i >= t.NumField() {
return false
}
if len(index) > 1 {
return checkFieldIndex(t.Field(i).Type, index[1:])
}
return true
}
func call(n *node) {
@@ -1151,6 +1185,7 @@ func call(n *node) {
child := n.child[1:]
tnext := getExec(n.tnext)
fnext := getExec(n.fnext)
hasVariadicArgs := n.action == aCallSlice // callSlice implies variadic call with ellipsis.
// Compute input argument value functions.
for i, c := range child {
@@ -1166,7 +1201,7 @@ func call(n *node) {
numOut := c.child[0].typ.rtype.NumOut()
for j := 0; j < numOut; j++ {
ind := c.findex + j
if !isInterfaceSrc(arg) || isEmptyInterface(arg) {
if hasVariadicArgs || !isInterfaceSrc(arg) || isEmptyInterface(arg) {
values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
continue
}
@@ -1179,7 +1214,7 @@ func call(n *node) {
cc0 := c.child[0]
for j := range cc0.typ.ret {
ind := c.findex + j
if !isInterfaceSrc(arg) || isEmptyInterface(arg) {
if hasVariadicArgs || !isInterfaceSrc(arg) || isEmptyInterface(arg) {
values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
continue
}
@@ -1193,13 +1228,14 @@ func call(n *node) {
convertLiteralValue(c, argType)
}
switch {
case isEmptyInterface(arg):
case hasVariadicArgs:
values = append(values, genValue(c))
case isInterfaceSrc(arg) && n.action != aCallSlice:
// callSlice implies variadic call with ellipsis, do not wrap in valueInterface.
case isInterfaceSrc(arg) && (!isEmptyInterface(arg) || len(c.typ.method) > 0):
values = append(values, genValueInterface(c))
case isInterfaceBin(arg):
values = append(values, genInterfaceWrapper(c, arg.rtype))
case isFuncSrc(arg):
values = append(values, genValueNode(c))
default:
values = append(values, genValue(c))
}
@@ -1369,12 +1405,22 @@ func call(n *node) {
}
default:
val := v(f)
// The !val.IsZero is to work around a recursive struct zero interface
// issue. Once there is a better way to handle this case, the dest
// can just be set.
if !val.IsZero() || dest[i].Kind() == reflect.Interface {
dest[i].Set(val)
if val.IsZero() && dest[i].Kind() != reflect.Interface {
// Work around a recursive struct zero interface issue.
// Once there is a better way to handle this case, the dest can just be set.
continue
}
if nod, ok := val.Interface().(*node); ok && nod.recv != nil {
// An interpreted method is passed as value in a function call.
// It must be wrapped now, otherwise the receiver will be missing
// at the method call (#1332).
// TODO (marc): wrapping interpreted functions should be always done
// everywhere at runtime to simplify the whole code,
// but it requires deeper refactoring.
dest[i] = genFunctionWrapper(nod)(f)
continue
}
dest[i].Set(val)
}
}
}
@@ -1610,11 +1656,16 @@ func callBin(n *node) {
}
out := callFn(value(f), in)
for i := 0; i < len(out); i++ {
if out[i].Kind() == reflect.Func {
getFrame(f, n.level).data[n.findex+i] = out[i]
} else {
getFrame(f, n.level).data[n.findex+i].Set(out[i])
r := out[i]
if r.Kind() == reflect.Func {
getFrame(f, n.level).data[n.findex+i] = r
continue
}
dest := getFrame(f, n.level).data[n.findex+i]
if _, ok := dest.Interface().(valueInterface); ok {
r = reflect.ValueOf(valueInterface{value: r})
}
dest.Set(r)
}
return tnext
}
@@ -1715,9 +1766,6 @@ func getIndexArray(n *node) {
}
}
// valueInterfaceType is the reflection type of valueInterface.
var valueInterfaceType = reflect.TypeOf((*valueInterface)(nil)).Elem()
// getIndexMap retrieves map value from index.
func getIndexMap(n *node) {
dest := genValue(n)
@@ -1782,7 +1830,6 @@ func getIndexMap2(n *node) {
value0 := genValue(n.child[0]) // map
value2 := genValue(n.anc.child[1]) // status
next := getExec(n.tnext)
typ := n.anc.child[0].typ
doValue := n.anc.child[0].ident != "_"
doStatus := n.anc.child[1].ident != "_"
@@ -1799,21 +1846,6 @@ func getIndexMap2(n *node) {
value2(f).SetBool(v.IsValid())
return next
}
case isInterfaceSrc(typ):
n.exec = func(f *frame) bltn {
v := value0(f).MapIndex(mi)
if v.IsValid() {
if e := v.Elem(); e.Type().AssignableTo(valueInterfaceType) {
dest(f).Set(e)
} else {
dest(f).Set(reflect.ValueOf(valueInterface{n, e}))
}
}
if doStatus {
value2(f).SetBool(v.IsValid())
}
return next
}
default:
n.exec = func(f *frame) bltn {
v := value0(f).MapIndex(mi)
@@ -1835,21 +1867,6 @@ func getIndexMap2(n *node) {
value2(f).SetBool(v.IsValid())
return next
}
case isInterfaceSrc(typ):
n.exec = func(f *frame) bltn {
v := value0(f).MapIndex(value1(f))
if v.IsValid() {
if e := v.Elem(); e.Type().AssignableTo(valueInterfaceType) {
dest(f).Set(e)
} else {
dest(f).Set(reflect.ValueOf(valueInterface{n, e}))
}
}
if doStatus {
value2(f).SetBool(v.IsValid())
}
return next
}
default:
n.exec = func(f *frame) bltn {
v := value0(f).MapIndex(value1(f))
@@ -1926,7 +1943,7 @@ func getMethodByName(n *node) {
}
return next
}
m, li := val.node.typ.lookupMethod(name)
m, li := typ.lookupMethod(name)
if m == nil {
panic(n.cfgErrorf("method not found: %s", name))
}
@@ -2335,9 +2352,13 @@ func _return(n *node) {
}
values[i] = genValueInterface(c)
case valueT:
if t.rtype.Kind() == reflect.Interface {
switch t.rtype.Kind() {
case reflect.Interface:
values[i] = genInterfaceWrapper(c, t.rtype)
break
continue
case reflect.Func:
values[i] = genFunctionWrapper(c)
continue
}
fallthrough
default:
@@ -2570,6 +2591,9 @@ func doCompositeBinStruct(n *node, hasType bool) {
}
}
frameIndex := n.findex
l := n.level
n.exec = func(f *frame) bltn {
s := reflect.New(typ).Elem()
for i, v := range values {
@@ -2580,7 +2604,7 @@ func doCompositeBinStruct(n *node, hasType bool) {
case d.Kind() == reflect.Ptr:
d.Set(s.Addr())
default:
d.Set(s)
getFrame(f, l).data[frameIndex] = s
}
return next
}
@@ -2605,8 +2629,6 @@ func doComposite(n *node, hasType bool, keyed bool) {
if typ.cat == ptrT || typ.cat == aliasT {
typ = typ.val
}
var mu sync.Mutex
typ.mu = &mu
child := n.child
if hasType {
child = n.child[1:]
@@ -2634,7 +2656,7 @@ func doComposite(n *node, hasType bool, keyed bool) {
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):
case isInterfaceSrc(ft) && (!isEmptyInterface(ft) || len(val.typ.method) > 0):
values[fieldIndex] = genValueInterface(val)
case isInterface(ft):
values[fieldIndex] = genInterfaceWrapper(val, rft)
@@ -2645,11 +2667,10 @@ func doComposite(n *node, hasType bool, keyed bool) {
frameIndex := n.findex
l := n.level
rt := typ.TypeOf()
n.exec = func(f *frame) bltn {
typ.mu.Lock()
// No need to call zero() as doComposite is only called for a structT.
a := reflect.New(typ.TypeOf()).Elem()
typ.mu.Unlock()
a := reflect.New(rt).Elem()
for i, v := range values {
a.Field(i).Set(v(f))
}
@@ -2912,13 +2933,21 @@ func _case(n *node) {
return tnext
}
rtyp := typ.TypeOf()
if rtyp != nil && rtyp.String() == t.String() && implementsInterface(v, typ) {
destValue(f).Set(v.Elem())
if rtyp == nil {
return fnext
}
elem := v.Elem()
if rtyp.String() == t.String() && implementsInterface(v, typ) {
destValue(f).Set(elem)
return tnext
}
ival := v.Interface()
if ival != nil && rtyp != nil && rtyp.String() == reflect.TypeOf(ival).String() {
destValue(f).Set(v.Elem())
if ival != nil && rtyp.String() == reflect.TypeOf(ival).String() {
destValue(f).Set(elem)
return tnext
}
if typ.cat == valueT && rtyp.Kind() == reflect.Interface && elem.IsValid() && elem.Type().Implements(rtyp) {
destValue(f).Set(elem)
return tnext
}
return fnext
@@ -2936,12 +2965,37 @@ func _case(n *node) {
}
return fnext
}
default:
// TODO(mpl): probably needs to be fixed for empty interfaces, like above.
// match against multiple types: assign var to interface value
n.exec = func(f *frame) bltn {
val := srcValue(f)
if v := srcValue(f).Interface().(valueInterface).node; v != nil {
if t := val.Type(); t.Kind() == reflect.Interface {
for _, typ := range types {
if typ.cat == nilT && val.IsNil() {
return tnext
}
rtyp := typ.TypeOf()
if rtyp == nil {
continue
}
elem := val.Elem()
if rtyp.String() == t.String() && implementsInterface(val, typ) {
destValue(f).Set(elem)
return tnext
}
ival := val.Interface()
if ival != nil && rtyp.String() == reflect.TypeOf(ival).String() {
destValue(f).Set(elem)
return tnext
}
if typ.cat == valueT && rtyp.Kind() == reflect.Interface && elem.IsValid() && elem.Type().Implements(rtyp) {
destValue(f).Set(elem)
return tnext
}
}
return fnext
}
if v := val.Interface().(valueInterface).node; v != nil {
for _, typ := range types {
if v.typ.id() == typ.id() {
destValue(f).Set(val)
@@ -3043,9 +3097,7 @@ func _append(n *node) {
values := make([]func(*frame) reflect.Value, l)
for i, arg := range args {
switch elem := n.typ.elem(); {
case isEmptyInterface(elem):
values[i] = genValue(arg)
case isInterfaceSrc(elem):
case isInterfaceSrc(elem) && (!isEmptyInterface(elem) || len(arg.typ.method) > 0):
values[i] = genValueInterface(arg)
case isInterfaceBin(elem):
values[i] = genInterfaceWrapper(arg, elem.rtype)
@@ -3067,9 +3119,7 @@ func _append(n *node) {
default:
var value0 func(*frame) reflect.Value
switch elem := n.typ.elem(); {
case isEmptyInterface(elem):
value0 = genValue(n.child[2])
case isInterfaceSrc(elem):
case isInterfaceSrc(elem) && (!isEmptyInterface(elem) || len(n.child[2].typ.method) > 0):
value0 = genValueInterface(n.child[2])
case isInterfaceBin(elem):
value0 = genInterfaceWrapper(n.child[2], elem.rtype)

View File

@@ -47,7 +47,7 @@ type symbol struct {
kind sKind
typ *itype // Type of value
node *node // Node value if index is negative
from []*node // list of nodes jumping to node if kind is label, or nil
from []*node // list of goto nodes jumping to this label node, or nil
recv *receiver // receiver node value, if sym refers to a method
index int // index of value in frame or -1
rval reflect.Value // default value (used for constants)
@@ -144,20 +144,6 @@ func (s *scope) lookup(ident string) (*symbol, int, bool) {
return nil, 0, false
}
// lookdown searches for a symbol in the current scope and included ones, recursively.
// It returns the first found symbol and true, or nil and false.
func (s *scope) lookdown(ident string) (*symbol, bool) {
if sym, ok := s.sym[ident]; ok {
return sym, true
}
for _, c := range s.child {
if sym, ok := c.lookdown(ident); ok {
return sym, true
}
}
return nil, false
}
func (s *scope) rangeChanType(n *node) *itype {
if sym, _, found := s.lookup(n.child[1].ident); found {
if t := sym.typ; len(n.child) == 3 && t != nil && (t.cat == chanT || t.cat == chanRecvT) {
@@ -241,3 +227,26 @@ func (interp *Interpreter) initScopePkg(pkgID, pkgName string) *scope {
interp.mutex.Unlock()
return sc
}
// Globals returns a map of global variables and constants in the main package.
func (interp *Interpreter) Globals() map[string]reflect.Value {
syms := map[string]reflect.Value{}
interp.mutex.RLock()
defer interp.mutex.RUnlock()
v, ok := interp.srcPkg["main"]
if !ok {
return syms
}
for n, s := range v {
switch s.kind {
case constSym:
syms[n] = s.rval
case varSym:
syms[n] = interp.frame.data[s.index]
}
}
return syms
}

View File

@@ -132,6 +132,10 @@ func (interp *Interpreter) importSrc(rPath, importPath string, skipTest bool) (s
// the global symbols in the package scope.
interp.mutex.Lock()
gs := interp.scopes[importPath]
if gs == nil {
// A nil scope means that no even an empty package is created from source.
return "", fmt.Errorf("no Go files in %s", dir)
}
interp.srcPkg[importPath] = gs.sym
interp.pkgNames[importPath] = pkgName

View File

@@ -7,7 +7,6 @@ import (
"reflect"
"strconv"
"strings"
"sync"
"github.com/traefik/yaegi/internal/unsafe2"
)
@@ -110,7 +109,6 @@ type structField struct {
// itype defines the internal representation of types in the interpreter.
type itype struct {
mu *sync.Mutex
cat tcat // Type category
field []structField // Array of struct fields if structT or interfaceT
key *itype // Type of key element if MapT or nil
@@ -854,6 +852,14 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype,
if err != nil {
return nil, err
}
if t.cat == valueT {
switch t.rtype.Kind() {
case reflect.Array, reflect.Ptr:
t = valueTOf(reflect.SliceOf(t.rtype.Elem()), withScope(sc))
}
break
}
if t.cat == ptrT {
t = t.val
}
@@ -1819,6 +1825,7 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
}
}
}
fieldFix := []int{} // Slice of field indices to fix for recursivity.
t.rtype = reflect.StructOf(fields)
if ctx.isComplete() {
for _, s := range ctx.defined {
@@ -1826,6 +1833,9 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
f := s.rtype.Field(i)
if strings.HasSuffix(f.Type.String(), "unsafe2.dummy") {
unsafe2.SetFieldType(s.rtype, i, ctx.rect.fixDummy(s.rtype.Field(i).Type))
if name == s.path+"/"+s.name {
fieldFix = append(fieldFix, i)
}
}
}
}
@@ -1834,13 +1844,16 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
// The rtype has now been built, we can go back and rebuild
// all the recursive types that relied on this type.
// However, as we are keyed by type name, if two or more (recursive) fields at
// the same depth level are of the same type, they "mask" each other, and only one
// of them is in ctx.refs, which means this pass below does not fully do the job.
// Which is why we have the pass above that is done one last time, for all fields,
// one the recursion has been fully resolved.
// the same depth level are of the same type, or a "variation" of the same type
// (slice of, map of, etc), they "mask" each other, and only one
// of them is in ctx.refs. That is why the code around here is a bit convoluted,
// and we need both the loop above, around all the struct fields, and the loop
// below, around the ctx.refs.
for _, f := range ctx.refs[name] {
ftyp := f.typ.field[f.idx].typ.refType(&refTypeContext{defined: ctx.defined, rebuilding: true})
unsafe2.SetFieldType(f.typ.rtype, f.idx, ftyp)
for _, index := range fieldFix {
ftyp := f.typ.field[index].typ.refType(&refTypeContext{defined: ctx.defined, rebuilding: true})
unsafe2.SetFieldType(f.typ.rtype, index, ftyp)
}
}
default:
if z, _ := t.zero(); z.IsValid() {

View File

@@ -3,6 +3,7 @@ package interp
import (
"errors"
"go/constant"
"go/token"
"math"
"reflect"
)
@@ -124,6 +125,8 @@ func (check typecheck) starExpr(n *node) error {
}
var unaryOpPredicates = opPredicates{
aInc: isNumber,
aDec: isNumber,
aPos: isNumber,
aNeg: isNumber,
aBitNot: isInt,
@@ -133,6 +136,9 @@ var unaryOpPredicates = opPredicates{
// unaryExpr type checks a unary expression.
func (check typecheck) unaryExpr(n *node) error {
c0 := n.child[0]
if isBlank(c0) {
return n.cfgErrorf("cannot use _ as value")
}
t0 := c0.typ.TypeOf()
if n.action == aRecv {
@@ -196,7 +202,7 @@ func (check typecheck) comparison(n *node) error {
if typ.isNil() {
typ = c1.typ
}
return n.cfgErrorf("invalid operation: operator %v not defined on %s", n.action, typ.id(), ".")
return n.cfgErrorf("invalid operation: operator %v not defined on %s", n.action, typ.id())
}
return nil
}
@@ -221,6 +227,10 @@ var binaryOpPredicates = opPredicates{
func (check typecheck) binaryExpr(n *node) error {
c0, c1 := n.child[0], n.child[1]
if isBlank(c0) || isBlank(c1) {
return n.cfgErrorf("cannot use _ as value")
}
a := n.action
if isAssignAction(a) {
a--
@@ -476,6 +486,12 @@ func (check typecheck) structBinLitExpr(child []*node, typ reflect.Type) error {
// sliceExpr type checks a slice expression.
func (check typecheck) sliceExpr(n *node) error {
for _, c := range n.child {
if isBlank(c) {
return n.cfgErrorf("cannot use _ as value")
}
}
c, child := n.child[0], n.child[1:]
t := c.typ.TypeOf()
@@ -591,6 +607,12 @@ func (check typecheck) typeAssertionExpr(n *node, typ *itype) error {
continue
}
if tm == nil {
// Lookup for non-exported methods is impossible
// for bin types, ignore them as they can't be used
// directly by the interpreted programs.
if !token.IsExported(name) && isBin(typ) {
continue
}
return n.cfgErrorf("impossible type assertion: %s does not implement %s (missing %v method)", typ.id(), n.typ.id(), name)
}
if tm.recv != nil && tm.recv.TypeOf().Kind() == reflect.Ptr && typ.TypeOf().Kind() != reflect.Ptr {

View File

@@ -219,7 +219,7 @@ func genValue(n *node) func(*frame) reflect.Value {
func genDestValue(typ *itype, n *node) func(*frame) reflect.Value {
convertLiteralValue(n, typ.TypeOf())
switch {
case isInterfaceSrc(typ) && !isEmptyInterface(typ):
case isInterfaceSrc(typ) && (!isEmptyInterface(typ) || len(n.typ.method) > 0):
return genValueInterface(n)
case isFuncSrc(typ) && (n.typ.cat == valueT || n.typ.cat == nilT):
return genValueNode(n)
@@ -408,7 +408,7 @@ func genValueInterfaceValue(n *node) func(*frame) reflect.Value {
return func(f *frame) reflect.Value {
v := value(f)
if v.Interface().(valueInterface).node == nil {
if vi, ok := v.Interface().(valueInterface); ok && vi.node == nil {
// Uninitialized interface value, set it to a correct zero value.
v.Set(zeroInterfaceValue())
v = value(f)
@@ -421,7 +421,11 @@ func genValueNode(n *node) func(*frame) reflect.Value {
value := genValue(n)
return func(f *frame) reflect.Value {
return reflect.ValueOf(&node{rval: value(f)})
v := value(f)
if _, ok := v.Interface().(*node); ok {
return v
}
return reflect.ValueOf(&node{rval: v})
}
}
@@ -591,5 +595,6 @@ func genComplex(n *node) func(*frame) complex128 {
func genValueString(n *node) func(*frame) (reflect.Value, string) {
value := genValue(n)
return func(f *frame) (reflect.Value, string) { v := value(f); return v, v.String() }
}