Compare commits

...

26 Commits

Author SHA1 Message Date
Marc Vertes
7037424edf fix: correctly store boolean result for branching operations 2020-02-14 16:26:04 +01:00
Marc Vertes
1b971b539c fix: correctly handle arbitrary type of literal array index 2020-02-12 15:06:04 +01:00
Marc Vertes
681f2f9c40 fix: correctly handle constant init for further type declarations 2020-02-12 12:32:03 +01:00
Marc Vertes
05960316f8 fix: correct type inference in composite literal init 2020-02-11 10:10:04 +01:00
Marc Vertes
902af477b8 fix: correct behavior for rune and byte types 2020-02-09 05:18:04 +01:00
Marc Vertes
812e55b95e fix: handle conversion of nil to an interface type 2020-02-09 05:04:04 +01:00
Marc Vertes
6c339ce562 fix: handle method declaration with forward declared type 2020-02-07 15:44:04 +01:00
Marc Vertes
23dfef0ac8 fix: define a correct zero value for an not initialized interface{} 2020-02-04 18:04:05 +01:00
Marc Vertes
4fd6a2dc56 fix: handle recursive type definition involving a map object 2020-02-04 16:36:05 +01:00
Marc Vertes
92a63dbe09 fix: handle out of order type declaration for global var declaration 2020-02-03 17:36:04 +01:00
Marc Vertes
e434892b9a fix: import different source packages with same base name 2020-02-03 17:22:04 +01:00
Marc Vertes
712891dd77 fix: allow reuse of _ symbol in the same scope 2020-02-03 17:08:05 +01:00
Marc Vertes
137b16580c fix: handle binary methods where receiver is implicit 2020-02-03 16:54:04 +01:00
Sven Dowideit
b057ada531 Add an example of yaegi in a shebang line 2020-01-15 16:54:06 +01:00
Camal Cakar
6d90c5a641 Case sensitivity fix 2020-01-15 16:40:06 +01:00
Marc Vertes
5381ee65d1 fix: continue statement was not applied correctly 2020-01-10 17:50:05 +01:00
Marc Vertes
f1cde2be0f fix: apply automatic type conversion to constant expressions (#484) 2020-01-09 18:01:44 +01:00
Marc Vertes
bb04af2d4d doc: document import of source and binary packages (#477) 2020-01-08 18:51:30 +01:00
Marc Vertes
9a8a88dcb9 fix: use branch operation in || and && operators, fix storage for ! (#476) 2020-01-07 17:27:22 +01:00
Marc Vertes
f3f54a5302 doc: add explanation about CLA in CONTRIBUTING.md (#483) 2020-01-07 16:30:05 +01:00
Marc Vertes
878fcc835c fix: add support for ^ and + as unary operators 2020-01-07 15:34:05 +01:00
Marc Vertes
a1f2d3bf1d fix: isNil was not forwarding result when used in a branch expression 2019-12-19 18:32:04 +01:00
Marc Vertes
3cd37645eb fix: correct isValueUntyped() to handle typed constants 2019-12-19 15:38:05 +01:00
Marc Vertes
e1ac83f7d8 fix: correct type extraction for returned value 2019-12-17 10:18:06 +01:00
Marc Vertes
4f93be7f19 fix: emulate struct by interface{} only for recursive struct types 2019-12-16 19:00:07 +01:00
Marc Vertes
7a0c09f5eb fix: detect untyped values when importing from binary packages 2019-12-13 11:18:04 +01:00
58 changed files with 1080 additions and 206 deletions

View File

@@ -13,7 +13,12 @@ discussions.
Once the proposal is approved, a Pull Request can be opened. If you want
to provide early visibility to reviewers, create a [Draft Pull Request].
We will also require you to sign the [Containous Contributor License Agreement]
after you submit your first pull request to this project. The link to sign the
agreement will be presented to you in the web interface of the pull request.
[Issues]: https://github.com/containous/yaegi/issues
[Pull Requests]: https://github.com/containous/yaegi/issues
[Feature Request]: https://github.com/containous/yaegi/issues/new?template=feature_request.md
[Draft Pull Request]: https://github.blog/2019-02-14-introducing-draft-pull-requests/
[Containous Contributor License Agreement]: https://cla-assistant.io/containous/yaegi

View File

@@ -129,6 +129,24 @@ $ yaegi cmd/yaegi/yaegi.go
>
```
Or for Go scripting in the shebang line:
```console
$ cat /tmp/test
#!/usr/bin/env yaegi
package main
import "fmt"
func main() {
fmt.Println("test")
}
$ ls -la /tmp/test
-rwxr-xr-x 1 dow184 dow184 93 Jan 6 13:38 /tmp/test
$ /tmp/test
test
```
## Documentation
Documentation about Yaegi commands and libraries can be found at usual [godoc.org][docs].

23
_test/a40.go Normal file
View File

@@ -0,0 +1,23 @@
package main
import "fmt"
type rule uint8
const (
r0 rule = iota
r1
r2
)
var a = [...]int{
r0: 1,
r1: 12,
}
func main() {
fmt.Println(a)
}
// Output:
// [1 12]

10
_test/a41.go Normal file
View File

@@ -0,0 +1,10 @@
package main
var a = [...]bool{true, true}
func main() {
println(a[0] && true)
}
// Output:
// true

10
_test/and3.go Normal file
View File

@@ -0,0 +1,10 @@
package main
var a = true && true
func main() {
println(a)
}
// Output:
// true

7
_test/b1/foo/foo.go Normal file
View File

@@ -0,0 +1,7 @@
package foo
import bar "github.com/containous/yaegi/_test/b2/foo"
var Desc = "in b1/foo"
var Desc2 = Desc + bar.Desc

3
_test/b2/foo/foo.go Normal file
View File

@@ -0,0 +1,3 @@
package foo
var Desc = "in b2/foo"

13
_test/bin2.go Normal file
View File

@@ -0,0 +1,13 @@
package main
import (
"fmt"
"math"
)
func main() {
fmt.Println(math.Abs(-5))
}
// Output:
// 5

12
_test/chan8.go Normal file
View File

@@ -0,0 +1,12 @@
package main
func main() {
messages := make(chan bool)
go func() { messages <- true }()
println(<-messages && true)
}
// Output:
// true

16
_test/composite5.go Normal file
View File

@@ -0,0 +1,16 @@
package main
import "fmt"
type T struct {
m uint16
}
var t = T{1<<2 | 1<<3}
func main() {
fmt.Println(t)
}
// Output:
// {12}

20
_test/composite6.go Normal file
View File

@@ -0,0 +1,20 @@
package main
import (
"fmt"
"github.com/containous/yaegi/_test/ct1"
)
type T struct {
m uint16
}
var t = T{1 << ct1.R}
func main() {
fmt.Println(t)
}
// Output:
// {2}

22
_test/const6.go Normal file
View File

@@ -0,0 +1,22 @@
package main
const (
maxNonStarters = 30
maxBufferSize = maxNonStarters + 2
)
type reorderBuffer struct {
rune [maxBufferSize]Properties
}
type Properties struct {
pos uint8
size uint8
}
func main() {
println(len(reorderBuffer{}.rune))
}
// Output:
// 32

19
_test/const7.go Normal file
View File

@@ -0,0 +1,19 @@
package main
import "fmt"
const (
a = iota
b
c
d
)
type T [c]int
func main() {
fmt.Println(T{})
}
// Output:
// [0 0]

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

@@ -0,0 +1,9 @@
package ct1
type Class uint
const (
L Class = iota
R
AL
)

18
_test/for8.go Normal file
View File

@@ -0,0 +1,18 @@
package main
func main() {
for i := 0; i < 4; i++ {
for {
break
}
if i == 1 {
continue
}
println(i)
}
}
// Output:
// 0
// 2
// 3

12
_test/fun11.go Normal file
View File

@@ -0,0 +1,12 @@
package main
var f F
type F func(int)
func main() {
println("ok")
}
// Output:
// ok

10
_test/import8.go Normal file
View File

@@ -0,0 +1,10 @@
package main
import "github.com/containous/yaegi/_test/b1/foo"
func main() {
println(foo.Desc)
}
// Output:
// in b1/foo

12
_test/interface19.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import "fmt"
var I interface{}
func main() {
fmt.Printf("%T %v\n", I, I)
}
// Output:
// <nil> <nil>

16
_test/map19.go Normal file
View File

@@ -0,0 +1,16 @@
package main
import "fmt"
type cmap struct {
servers map[int64]*server
}
type server struct {
cm *cmap
}
func main() {
m := cmap{}
fmt.Println(m)
}

10
_test/map20.go Normal file
View File

@@ -0,0 +1,10 @@
package main
var a = map[int]bool{1: true, 2: true}
func main() {
println(a[1] && true)
}
// Output:
// true

16
_test/method29.go Normal file
View File

@@ -0,0 +1,16 @@
package main
import (
"context"
"net"
)
var lookupHost = net.DefaultResolver.LookupHost
func main() {
res, err := lookupHost(context.Background(), "localhost")
println(len(res) > 0, err == nil)
}
// Output:
// true true

20
_test/method30.go Normal file
View File

@@ -0,0 +1,20 @@
package main
type T struct {
Name string
}
func (t *T) foo(a string) string {
return t.Name + a
}
var g = &T{"global"}
var f = g.foo
func main() {
println(f("-x"))
}
// Output:
// global-x

29
_test/method31.go Normal file
View File

@@ -0,0 +1,29 @@
package main
import "fmt"
var db dbWrapper
type dbWrapper struct {
DB *cmap
}
func (d *dbWrapper) get() *cmap {
return d.DB
}
type cmap struct {
name string
}
func (c *cmap) f() {
fmt.Println("in f, c", c)
}
func main() {
db.DB = &cmap{name: "test"}
db.get().f()
}
// Output:
// in f, c &{test}

15
_test/nil0.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import "fmt"
func f() (host, port string, err error) {
return "", "", nil
}
func main() {
h, p, err := f()
fmt.Println(h, p, err)
}
// Output:
// <nil>

12
_test/nil1.go Normal file
View File

@@ -0,0 +1,12 @@
package main
func main() {
var a error = nil
if a == nil || a.Error() == "nil" {
println("a is nil")
}
}
// Output:
// a is nil

9
_test/or0.go Normal file
View File

@@ -0,0 +1,9 @@
package main
func main() {
c := false
println(c || !c)
}
// Output:
// true

9
_test/or1.go Normal file
View File

@@ -0,0 +1,9 @@
package main
func main() {
c := false
println(!c || c)
}
// Output:
// true

10
_test/or2.go Normal file
View File

@@ -0,0 +1,10 @@
package main
var a = false || true
func main() {
println(a)
}
// Output:
// true

26
_test/primes.go Normal file
View File

@@ -0,0 +1,26 @@
package main
func Primes(n int) int {
var xs []int
for i := 2; len(xs) < n; i++ {
ok := true
for _, x := range xs {
if i%x == 0 {
ok = false
break
}
}
if !ok {
continue
}
xs = append(xs, i)
}
return xs[n-1]
}
func main() {
println(Primes(3))
}
// Output:
// 5

10
_test/ptr8.go Normal file
View File

@@ -0,0 +1,10 @@
package main
var a = func() *bool { b := true; return &b }()
func main() {
println(*a && true)
}
// Output:
// true

11
_test/str4.go Normal file
View File

@@ -0,0 +1,11 @@
package main
import "unicode/utf8"
func main() {
r, _ := utf8.DecodeRuneInString("Hello")
println(r < utf8.RuneSelf)
}
// Output:
// true

19
_test/struct29.go Normal file
View File

@@ -0,0 +1,19 @@
package main
type T1 struct {
A []T2
B []T2
}
type T2 struct {
name string
}
var t = T1{}
func main() {
println("ok")
}
// Output:
// ok

19
_test/struct30.go Normal file
View File

@@ -0,0 +1,19 @@
package main
type T1 struct {
A []T2
M map[uint64]T2
}
type T2 struct {
name string
}
var t = T1{}
func main() {
println("ok")
}
// Output:
// ok

14
_test/struct31.go Normal file
View File

@@ -0,0 +1,14 @@
package main
type T struct {
bool
}
var t = T{true}
func main() {
println(t.bool && true)
}
// Output:
// true

15
_test/time11.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import (
"fmt"
"time"
)
const df = time.Minute * 30
func main() {
fmt.Printf("df: %v %T\n", df, df)
}
// Output:
// df: 30m0s time.Duration

9
_test/type15.go Normal file
View File

@@ -0,0 +1,9 @@
package main
func main() {
err := error(nil)
println(err == nil)
}
// Output:
// true

11
_test/type16.go Normal file
View File

@@ -0,0 +1,11 @@
package main
import "fmt"
func main() {
a := uint8(15) ^ byte(0)
fmt.Printf("%T %v\n", a, a)
}
// Output:
// uint8 15

11
_test/type17.go Normal file
View File

@@ -0,0 +1,11 @@
package main
import "fmt"
func main() {
a := int32(15) ^ rune(0)
fmt.Printf("%T %v\n", a, a)
}
// Output:
// int32 15

11
_test/var10.go Normal file
View File

@@ -0,0 +1,11 @@
package main
var _ = true
var _ = "hello"
func main() {
println("hello")
}
// Output:
// hello

10
_test/var11.go Normal file
View File

@@ -0,0 +1,10 @@
package main
var a, _, _, b = 1, true, "foo", 2
func main() {
println(a, b)
}
// Output:
// 1 2

View File

@@ -8,7 +8,7 @@ If invoked with no arguments, it processes the standard input
in a Read-Eval-Print-Loop. A prompt is displayed if standard input
is a terminal.
Given a file, it operates on that file. if the first line starts with
Given a file, it operates on that file. If the first line starts with
"#!/usr/bin/env yaegi", and the file has exec permission, then the file
can be invoked directly from the shell.

View File

@@ -352,6 +352,30 @@ func {{$name}}(n *node) {
}
}
{{end}}
{{range $name, $op := .Unary}}
func {{$name}}Const(n *node) {
t := n.typ.rtype
v := n.child[0].rval
n.rval = reflect.New(t).Elem()
{{- if $op.Bool}}
n.rval.SetBool({{$op.Name}} v.Bool())
{{- else}}
switch t.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n.rval.SetInt({{$op.Name}} v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
n.rval.SetUint({{$op.Name}} v.Uint())
{{- if $op.Float}}
case reflect.Float32, reflect.Float64:
n.rval.SetFloat({{$op.Name}} v.Float())
case reflect.Complex64, reflect.Complex128:
n.rval.SetComplex({{$op.Name}} v.Complex())
{{- end}}
}
{{- end}}
}
{{end}}
{{range $name, $op := .Comparison}}
func {{$name}}(n *node) {
tnext := getExec(n.tnext)
@@ -793,6 +817,7 @@ type Op struct {
Float bool // true if operator applies to float
Complex bool // true if operator applies to complex
Shift bool // true if operator is a shift operation
Bool bool // true if operator applies to bool
}
func main() {
@@ -805,17 +830,17 @@ func main() {
b := &bytes.Buffer{}
data := map[string]interface{}{
"Arithmetic": map[string]Op{
"add": {"+", true, true, true, false},
"sub": {"-", false, true, true, false},
"mul": {"*", false, true, true, false},
"quo": {"/", false, true, true, false},
"rem": {"%", false, false, false, false},
"shl": {"<<", false, false, false, true},
"shr": {">>", false, false, false, true},
"and": {"&", false, false, false, false},
"or": {"|", false, false, false, false},
"xor": {"^", false, false, false, false},
"andNot": {"&^", false, false, false, false},
"add": {"+", true, true, true, false, false},
"sub": {"-", false, true, true, false, false},
"mul": {"*", false, true, true, false, false},
"quo": {"/", false, true, true, false, false},
"rem": {"%", false, false, false, false, false},
"shl": {"<<", false, false, false, true, false},
"shr": {">>", false, false, false, true, false},
"and": {"&", false, false, false, false, false},
"or": {"|", false, false, false, false, false},
"xor": {"^", false, false, false, false, false},
"andNot": {"&^", false, false, false, false, false},
},
"IncDec": map[string]Op{
"inc": {Name: "+"},
@@ -829,6 +854,12 @@ func main() {
"lowerEqual": {Name: "<=", Complex: false},
"notEqual": {Name: "!=", Complex: true},
},
"Unary": map[string]Op{
"not": {Name: "!", Float: false, Bool: true},
"neg": {Name: "-", Float: true, Bool: false},
"pos": {Name: "+", Float: true, Bool: false},
"bitNot": {Name: "^", Float: false, Bool: false},
},
}
if err = parse.Execute(b, data); err != nil {
log.Fatal(err)

View File

@@ -75,8 +75,6 @@ const (
parenExpr
rangeStmt
returnStmt
rvalueExpr
rtypeExpr
selectStmt
selectorExpr
selectorImport
@@ -154,8 +152,6 @@ var kinds = [...]string{
parenExpr: "parenExpr",
rangeStmt: "rangeStmt",
returnStmt: "returnStmt",
rvalueExpr: "rvalueExpr",
rtypeExpr: "rtypeExpr",
selectStmt: "selectStmt",
selectorExpr: "selectorExpr",
selectorImport: "selectorImport",
@@ -199,6 +195,7 @@ const (
aAndAssign
aAndNot
aAndNotAssign
aBitNot
aCall
aCase
aCompositeLit
@@ -217,11 +214,12 @@ const (
aMethod
aMul
aMulAssign
aNegate
aNeg
aNot
aNotEqual
aOr
aOrAssign
aPos
aQuo
aQuoAssign
aRange
@@ -255,6 +253,7 @@ var actions = [...]string{
aAndAssign: "&=",
aAndNot: "&^",
aAndNotAssign: "&^=",
aBitNot: "^",
aCall: "call",
aCase: "case",
aCompositeLit: "compositeLit",
@@ -271,11 +270,12 @@ var actions = [...]string{
aMethod: "Method",
aMul: "*",
aMulAssign: "*=",
aNegate: "-",
aNeg: "-",
aNot: "!",
aNotEqual: "!=",
aOr: "|",
aOrAssign: "|=",
aPos: "+",
aQuo: "/",
aQuoAssign: "/=",
aRange: "range",
@@ -771,6 +771,8 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
var kind = unaryExpr
var act action
switch a.Op {
case token.ADD:
act = aPos
case token.AND:
kind = addressExpr
act = aAddr
@@ -779,7 +781,9 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
case token.NOT:
act = aNot
case token.SUB:
act = aNegate
act = aNeg
case token.XOR:
act = aBitNot
}
st.push(addChild(&root, anc, pos, kind, act), nod)

View File

@@ -24,6 +24,10 @@ var constOp = map[action]func(*node){
aShr: shrConst,
aAndNot: andNotConst,
aXor: xorConst,
aNot: notConst,
aBitNot: bitNotConst,
aNeg: negConst,
aPos: posConst,
}
var constBltn = map[string]func(*node){
@@ -38,9 +42,8 @@ var identifier = regexp.MustCompile(`([\pL_][\pL_\d]*)$`)
// and pre-compute frame sizes and indexes for all un-named (temporary) and named
// variables. A list of nodes of init functions is returned.
// Following this pass, the CFG is ready to run
func (interp *Interpreter) cfg(root *node) ([]*node, error) {
sc, pkgName := interp.initScopePkg(root)
var loop, loopRestart *node
func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
sc := interp.initScopePkg(pkgID)
var initNodes []*node
var iotaValue int
var err error
@@ -228,16 +231,22 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
}
// Propagate type to children, to handle implicit types
for _, c := range n.child {
c.typ = n.typ
switch c.kind {
case binaryExpr, unaryExpr:
// Do not attempt to propagate composite type to operator expressions,
// it breaks constant folding.
default:
c.typ = n.typ
}
}
case forStmt0, forRangeStmt:
loop, loopRestart = n, n.child[0]
sc = sc.pushBloc()
sc.loop, sc.loopRestart = n, n.child[0]
case forStmt1, forStmt2, forStmt3, forStmt3a, forStmt4:
loop, loopRestart = n, n.lastChild()
sc = sc.pushBloc()
sc.loop, sc.loopRestart = n, n.lastChild()
case funcLit:
n.typ = nil // to force nodeType to recompute the type
@@ -309,7 +318,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
c[i], c[l] = c[l], c[i]
}
sc = sc.pushBloc()
loop = n
sc.loop = n
case importSpec:
var name, ipath string
@@ -407,7 +416,11 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
if src.typ, err = nodeType(interp, sc, src); err != nil {
return
}
dest.typ = src.typ
if src.typ.isBinMethod {
dest.typ = &itype{cat: valueT, rtype: src.typ.methodCallType()}
} else {
dest.typ = src.typ
}
}
if dest.typ.sizedef {
dest.typ.size = arrayTypeLen(src)
@@ -416,7 +429,8 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
if sc.global {
// Do not overload existing symbols (defined in GTA) in global scope
sym, _, _ = sc.lookup(dest.ident)
} else {
}
if sym == nil {
sym = &symbol{index: sc.add(dest.typ), kind: varSym, typ: dest.typ}
sc.sym[dest.ident] = sym
}
@@ -496,9 +510,10 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
if isMapEntry(dest) {
dest.gen = nop // skip getIndexMap
}
}
if n.anc.kind == constDecl {
iotaValue++
if n.anc.kind == constDecl {
sc.sym[dest.ident].kind = constSym
iotaValue++
}
}
case incDecStmt:
@@ -602,8 +617,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
constOp[n.action](n)
}
switch {
//case n.typ != nil && n.typ.cat == BoolT && isAncBranch(n):
// n.findex = -1
case n.rval.IsValid():
n.gen = nop
n.findex = -1
@@ -696,14 +709,14 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
if len(n.child) > 0 {
gotoLabel(n.sym)
} else {
n.tnext = loop
n.tnext = sc.loop
}
case continueStmt:
if len(n.child) > 0 {
gotoLabel(n.sym)
} else {
n.tnext = loopRestart
n.tnext = sc.loopRestart
}
case gotoStmt:
@@ -738,7 +751,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
if isInt(n.child[0].typ.TypeOf()) && n.child[1].kind == basicLit && isFloat(n.child[1].typ.TypeOf()) {
err = n.cfgErrorf("truncated to integer")
}
if isInterface(n.child[0].typ) {
if isInterface(n.child[0].typ) && !n.child[1].isNil() {
// Convert to interface: just check that all required methods are defined by concrete type.
c0, c1 := n.child[0], n.child[1]
if !c1.typ.implements(c0.typ) {
@@ -827,7 +840,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
body := n.child[0]
n.start = body.start
body.tnext = n.start
loop, loopRestart = nil, nil
sc = sc.pop()
case forStmt1: // for cond {}
@@ -839,7 +851,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
cond.tnext = body.start
cond.fnext = n
body.tnext = cond.start
loop, loopRestart = nil, nil
sc = sc.pop()
case forStmt2: // for init; cond; {}
@@ -852,7 +863,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
cond.tnext = body.start
cond.fnext = n
body.tnext = cond.start
loop, loopRestart = nil, nil
sc = sc.pop()
case forStmt3: // for ; cond; post {}
@@ -865,7 +875,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
cond.fnext = n
body.tnext = post.start
post.tnext = cond.start
loop, loopRestart = nil, nil
sc = sc.pop()
case forStmt3a: // for int; ; post {}
@@ -874,7 +883,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
init.tnext = body.start
body.tnext = post.start
post.tnext = body.start
loop, loopRestart = nil, nil
sc = sc.pop()
case forStmt4: // for init; cond; post {}
@@ -888,11 +896,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
cond.fnext = n
body.tnext = post.start
post.tnext = cond.start
loop, loopRestart = nil, nil
sc = sc.pop()
case forRangeStmt:
loop, loopRestart = nil, nil
n.start = n.child[0].start
n.child[0].fnext = n
sc = sc.pop()
@@ -903,10 +909,10 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
sc = sc.pop()
funcName := n.child[1].ident
if !isMethod(n) {
interp.scopes[pkgName].sym[funcName].index = -1 // to force value to n.val
interp.scopes[pkgName].sym[funcName].typ = n.typ
interp.scopes[pkgName].sym[funcName].kind = funcSym
interp.scopes[pkgName].sym[funcName].node = n
interp.scopes[pkgID].sym[funcName].index = -1 // to force value to n.val
interp.scopes[pkgID].sym[funcName].typ = n.typ
interp.scopes[pkgID].sym[funcName].kind = funcSym
interp.scopes[pkgID].sym[funcName].node = n
}
case funcLit:
@@ -920,7 +926,8 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
case identExpr:
if isKey(n) || isNewDefine(n, sc) {
break
} else if sym, level, ok := sc.lookup(n.ident); ok {
}
if sym, level, ok := sc.lookup(n.ident); ok {
// Found symbol, populate node info
n.typ, n.findex, n.level = sym.typ, sym.index, level
if n.findex < 0 {
@@ -937,11 +944,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
case n.ident == "nil":
n.kind = basicLit
case sym.kind == binSym:
if sym.rval.IsValid() {
n.kind = rvalueExpr
} else {
n.kind = rtypeExpr
}
n.typ = sym.typ
n.rval = sym.rval
case sym.kind == bltnSym:
@@ -1021,6 +1023,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.child[1].tnext = n
n.typ = n.child[0].typ
n.findex = sc.add(n.typ)
if n.start.action == aNop {
n.start.gen = branch
}
case lorExpr:
n.start = n.child[0].start
@@ -1029,6 +1034,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.child[1].tnext = n
n.typ = n.child[0].typ
n.findex = sc.add(n.typ)
if n.start.action == aNop {
n.start.gen = branch
}
case parenExpr:
wireChild(n)
@@ -1068,7 +1076,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
// nil: Set node value to zero of return type
f := sc.def
var typ *itype
if typ, err = nodeType(interp, sc, f.child[2].child[1].child[i].lastChild()); err != nil {
if typ, err = nodeType(interp, sc, f.child[2].child[1].fieldType(i)); err != nil {
return
}
if typ.cat == funcT {
@@ -1097,7 +1105,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.val = method.Index
n.gen = getIndexBinMethod
n.recv = &receiver{node: n.child[0]}
n.typ = &itype{cat: valueT, rtype: method.Type}
n.typ = &itype{cat: valueT, rtype: method.Type, isBinMethod: true}
case n.typ.rtype.Kind() == reflect.Ptr:
if field, ok := n.typ.rtype.Elem().FieldByName(n.child[1].ident); ok {
n.typ = &itype{cat: valueT, rtype: field.Type}
@@ -1151,11 +1159,9 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
pkg := n.child[0].sym.typ.path
if s, ok := interp.binPkg[pkg][name]; ok {
if isBinType(s) {
n.kind = rtypeExpr
n.typ = &itype{cat: valueT, rtype: s.Type().Elem()}
} else {
n.kind = rvalueExpr
n.typ = &itype{cat: valueT, rtype: s.Type()}
n.typ = &itype{cat: valueT, rtype: s.Type(), untyped: isValueUntyped(s)}
n.rval = s
}
n.gen = nop
@@ -1171,6 +1177,7 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.gen = nop
n.typ = sym.typ
n.sym = sym
n.rval = sym.rval
} else {
err = n.cfgErrorf("undefined selector: %s.%s", pkg, name)
}
@@ -1312,7 +1319,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.start = sbn.start
}
sc = sc.pop()
loop = nil
case switchIfStmt: // like an if-else chain
sbn := n.lastChild() // switch block node
@@ -1349,7 +1355,6 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
n.start = sbn.start
}
sc = sc.pop()
loop = nil
case typeAssertExpr:
if len(n.child) > 1 {
@@ -1377,16 +1382,60 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
case unaryExpr:
wireChild(n)
n.typ = n.child[0].typ
if n.action == aRecv {
switch n.action {
case aRecv:
// Channel receive operation: set type to the channel data type
if n.typ.cat == valueT {
n.typ = &itype{cat: valueT, rtype: n.typ.rtype.Elem()}
} else {
n.typ = n.typ.val
}
case aBitNot:
if !isInt(n.typ.TypeOf()) {
err = n.cfgErrorf("illegal operand type for '^' operator")
return
}
case aNot:
if !isBool(n.typ) {
err = n.cfgErrorf("illegal operand type for '!' operator")
return
}
case aNeg, aPos:
if !isNumber(n.typ.TypeOf()) {
err = n.cfgErrorf("illegal operand type for '%v' operator", n.action)
return
}
}
// TODO: Optimisation: avoid allocation if boolean branch op (i.e. '!' in an 'if' expr)
n.findex = sc.add(n.typ)
if n.child[0].rval.IsValid() && constOp[n.action] != nil {
if n.typ == nil {
if n.typ, err = nodeType(interp, sc, n); err != nil {
return
}
}
n.typ.TypeOf() // init reflect type
constOp[n.action](n)
}
switch {
case n.rval.IsValid():
n.gen = nop
n.findex = -1
case n.anc.kind == assignStmt && n.anc.action == aAssign:
dest := n.anc.child[childPos(n)-n.anc.nright]
n.typ = dest.typ
n.findex = dest.findex
case n.anc.kind == returnStmt:
pos := childPos(n)
n.typ = sc.def.typ.ret[pos]
n.findex = pos
default:
if n.typ == nil {
if n.typ, err = nodeType(interp, sc, n); err != nil {
return
}
}
n.findex = sc.add(n.typ)
}
case valueSpec:
n.gen = reset
@@ -1397,8 +1446,14 @@ func (interp *Interpreter) cfg(root *node) ([]*node, error) {
}
}
for _, c := range n.child[:l] {
index := sc.add(n.typ)
sc.sym[c.ident] = &symbol{index: index, kind: varSym, typ: n.typ}
var index int
if sc.global {
// Global object allocation is already performed in GTA.
index = sc.sym[c.ident].index
} else {
index = sc.add(n.typ)
sc.sym[c.ident] = &symbol{index: index, kind: varSym, typ: n.typ}
}
c.typ = n.typ
c.findex = index
}
@@ -1528,7 +1583,7 @@ func isBinType(v reflect.Value) bool { return v.IsValid() && v.Kind() == reflect
// isType returns true if node refers to a type definition, false otherwise
func (n *node) isType(sc *scope) bool {
switch n.kind {
case arrayType, chanType, funcType, interfaceType, mapType, structType, rtypeExpr:
case arrayType, chanType, funcType, interfaceType, mapType, structType:
return true
case parenExpr, starExpr:
if len(n.child) == 1 {
@@ -1643,6 +1698,32 @@ func (n *node) isNatural() bool {
return false
}
// isNil returns true if node is a literal nil value, false otherwise
func (n *node) isNil() bool { return n.kind == basicLit && !n.rval.IsValid() }
// fieldType returns the nth parameter field node (type) of a fieldList node
func (n *node) fieldType(m int) *node {
k := 0
l := len(n.child)
for i := 0; i < l; i++ {
cl := len(n.child[i].child)
if cl < 2 {
if k == m {
return n.child[i].lastChild()
}
k++
continue
}
for j := 0; j < cl-1; j++ {
if k == m {
return n.child[i].lastChild()
}
k++
}
}
return nil
}
// lastChild returns the last child of a node
func (n *node) lastChild() *node { return n.child[len(n.child)-1] }
@@ -1718,13 +1799,6 @@ func getExec(n *node) bltn {
return n.exec
}
func fileNode(n *node) *node {
if n == nil || n.kind == fileStmt {
return n
}
return fileNode(n.anc)
}
// setExec recursively sets the node exec builtin function by walking the CFG
// from the entry point (first node to exec).
func setExec(n *node) {
@@ -1825,3 +1899,13 @@ func arrayTypeLen(n *node) int {
}
return max + 1
}
// isValueUntyped returns true if value is untyped
func isValueUntyped(v reflect.Value) bool {
// Consider only constant values.
if v.CanSet() {
return false
}
t := v.Type()
return t.String() == t.Kind().String()
}

View File

@@ -4,6 +4,18 @@ 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
Packages can be imported in source or binary form, using the standard
Go import statement. In source form, packages are searched first in the
vendor directory, the preferred way to store source dependencies. If not
found in vendor, sources modules will be searched in GOPATH. Go modules
are not supported yet by yaegi.
Binary form packages are compiled and linked with the interpreter
executable, and exposed to scripts with the Use method. The goexports
command can be used to generate package wrappers.
Custom build tags
Custom build tags allow to control which files in imported source

View File

@@ -38,7 +38,7 @@ func (n *node) astDot(out io.Writer, name string) {
func (n *node) cfgDot(out io.Writer) {
fmt.Fprintf(out, "digraph cfg {\n")
n.Walk(nil, func(n *node) {
if n.kind == basicLit || n.kind == identExpr || n.tnext == nil {
if n.kind == basicLit || n.tnext == nil {
return
}
var label string

View File

@@ -8,8 +8,8 @@ import (
// variables and functions symbols at package level, prior to CFG.
// All function bodies are skipped. GTA is necessary to handle out of
// order declarations and multiple source files packages.
func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
sc, _ := interp.initScopePkg(root)
func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error) {
sc := interp.initScopePkg(pkgID)
var err error
var iotaValue int
var revisit []*node
@@ -21,6 +21,9 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
switch n.kind {
case constDecl:
iotaValue = 0
// Early parse of constDecl subtree, to compute all constant
// values which may be necessary in further declarations.
_, err = interp.cfg(n, pkgID)
case blockStmt:
if n != root {
@@ -30,6 +33,7 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
case defineStmt:
var atyp *itype
if n.nleft+n.nright < len(n.child) {
// Type is declared explicitly in the assign expression.
if atyp, err = nodeType(interp, sc, n.child[n.nleft]); err != nil {
return false
}
@@ -51,7 +55,7 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
val = src.rval
}
if typ.incomplete {
// Come back when type is known
// Come back when type is known.
revisit = append(revisit, n)
return false
}
@@ -59,7 +63,12 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
err = n.cfgErrorf("use of untyped nil")
return false
}
sc.sym[dest.ident] = &symbol{kind: varSym, global: true, index: sc.add(typ), typ: typ, rval: val}
if typ.isBinMethod {
typ = &itype{cat: valueT, rtype: typ.methodCallType(), isBinMethod: true}
}
if sc.sym[dest.ident] == nil {
sc.sym[dest.ident] = &symbol{kind: varSym, global: true, index: sc.add(typ), typ: typ, rval: val}
}
if n.anc.kind == constDecl {
sc.sym[dest.ident].kind = constSym
iotaValue++
@@ -76,6 +85,11 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
if n.typ, err = nodeType(interp, sc, n.child[l]); err != nil {
return false
}
if n.typ.incomplete {
// Come back when type is known.
revisit = append(revisit, n)
return false
}
}
for _, c := range n.child[:l] {
sc.sym[c.ident] = &symbol{index: sc.add(n.typ), kind: varSym, global: true, typ: n.typ}
@@ -116,6 +130,9 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
// Add a function symbol in the package name space
sc.sym[n.child[1].ident] = &symbol{kind: funcSym, typ: n.typ, node: n, index: -1}
}
if n.typ.incomplete {
revisit = append(revisit, n)
}
return false
case importSpec:
@@ -142,7 +159,7 @@ func (interp *Interpreter) gta(root *node, rpath string) ([]*node, error) {
default: // import symbols in package namespace
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT, path: ipath}}
}
} else if err = interp.importSrc(rpath, ipath, name); err == nil {
} else if err = interp.importSrc(rpath, ipath); err == nil {
sc.types = interp.universe.types
switch name {
case "_": // no import of symbols

View File

@@ -216,7 +216,7 @@ func initUniverse() *scope {
sc := &scope{global: true, sym: map[string]*symbol{
// predefined Go types
"bool": {kind: typeSym, typ: &itype{cat: boolT, name: "bool"}},
"byte": {kind: typeSym, typ: &itype{cat: byteT, name: "byte"}},
"byte": {kind: typeSym, typ: &itype{cat: uint8T, name: "uint8"}},
"complex64": {kind: typeSym, typ: &itype{cat: complex64T, name: "complex64"}},
"complex128": {kind: typeSym, typ: &itype{cat: complex128T, name: "complex128"}},
"error": {kind: typeSym, typ: &itype{cat: errorT, name: "error"}},
@@ -228,7 +228,7 @@ func initUniverse() *scope {
"int32": {kind: typeSym, typ: &itype{cat: int32T, name: "int32"}},
"int64": {kind: typeSym, typ: &itype{cat: int64T, name: "int64"}},
"interface{}": {kind: typeSym, typ: &itype{cat: interfaceT}},
"rune": {kind: typeSym, typ: &itype{cat: runeT, name: "rune"}},
"rune": {kind: typeSym, typ: &itype{cat: int32T, name: "int32"}},
"string": {kind: typeSym, typ: &itype{cat: stringT, name: "string"}},
"uint": {kind: typeSym, typ: &itype{cat: uintT, name: "uint"}},
"uint8": {kind: typeSym, typ: &itype{cat: uint8T, name: "uint8"}},
@@ -283,7 +283,7 @@ func (interp *Interpreter) resizeFrame() {
func (interp *Interpreter) main() *node {
interp.mutex.RLock()
defer interp.mutex.RUnlock()
if m, ok := interp.scopes[mainID]; ok && m.sym[mainID] != nil {
if m, ok := interp.scopes[interp.Name]; ok && m.sym[mainID] != nil {
return m.sym[mainID].node
}
return nil
@@ -308,18 +308,18 @@ func (interp *Interpreter) Eval(src string) (reflect.Value, error) {
}
// Global type analysis
revisit, err := interp.gta(root, pkgName)
revisit, err := interp.gta(root, pkgName, interp.Name)
if err != nil {
return res, err
}
for _, n := range revisit {
if _, err = interp.gta(n, pkgName); err != nil {
if _, err = interp.gta(n, pkgName, interp.Name); err != nil {
return res, err
}
}
// Annotate AST with CFG infos
initNodes, err := interp.cfg(root)
initNodes, err := interp.cfg(root, interp.Name)
if err != nil {
return res, err
}
@@ -336,7 +336,7 @@ func (interp *Interpreter) Eval(src string) (reflect.Value, error) {
interp.mutex.Lock()
if interp.universe.sym[pkgName] == nil {
// Make the package visible under a path identical to its name
interp.srcPkg[pkgName] = interp.scopes[pkgName].sym
interp.srcPkg[pkgName] = interp.scopes[interp.Name].sym
interp.universe.sym[pkgName] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT, path: pkgName}}
}
interp.mutex.Unlock()

View File

@@ -47,11 +47,17 @@ func TestEvalArithmetic(t *testing.T) {
{desc: "shl_IN", src: "1 << -1", err: "1:28: illegal operand types for '<<' operator"},
{desc: "shl_IF", src: "1 << 1.0", res: "2"},
{desc: "shl_IF1", src: "1 << 1.1", err: "1:28: illegal operand types for '<<' operator"},
{desc: "shl_IF", src: "1.0 << 1", res: "2"},
{desc: "shl_IF2", src: "1.0 << 1", res: "2"},
{desc: "shr_II", src: "1 >> 8", res: "0"},
{desc: "shr_IN", src: "1 >> -1", err: "1:28: illegal operand types for '>>' operator"},
{desc: "shr_IF", src: "1 >> 1.0", res: "0"},
{desc: "shr_IF1", src: "1 >> 1.1", err: "1:28: illegal operand types for '>>' operator"},
{desc: "neg_I", src: "-2", res: "-2"},
{desc: "pos_I", src: "+2", res: "2"},
{desc: "bitnot_I", src: "^2", res: "-3"},
{desc: "bitnot_F", src: "^0.2", err: "1:28: illegal operand type for '^' operator"},
{desc: "not_B", src: "!false", res: "true"},
{desc: "not_I", src: "!0", err: "1:28: illegal operand type for '!' operator"},
})
}
@@ -72,11 +78,11 @@ func TestEvalBuiltin(t *testing.T) {
i := interp.New(interp.Options{})
runTests(t, i, []testCase{
{src: `a := []int{}; a = append(a, 1); a`, res: "[1]"},
{src: `a := []int{1}; a = append(a, 2, 3); a`, res: "[1 2 3]"},
{src: `a := []int{1}; b := []int{2, 3}; a = append(a, b...); a`, res: "[1 2 3]"},
{src: `b := []int{1}; b = append(a, 2, 3); b`, res: "[1 2 3]"},
{src: `c := []int{1}; d := []int{2, 3}; c = append(c, d...); c`, res: "[1 2 3]"},
{src: `string(append([]byte("hello "), "world"...))`, res: "hello world"},
{src: `a := "world"; string(append([]byte("hello "), a...))`, res: "hello world"},
{src: `a := []byte("Hello"); copy(a, "world"); string(a)`, res: "world"},
{src: `e := "world"; string(append([]byte("hello "), e...))`, res: "hello world"},
{src: `f := []byte("Hello"); copy(f, "world"); string(f)`, res: "world"},
})
}

View File

@@ -1864,6 +1864,57 @@ func inc(n *node) {
}
}
func bitNotConst(n *node) {
t := n.typ.rtype
v := n.child[0].rval
n.rval = reflect.New(t).Elem()
switch t.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n.rval.SetInt(^v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
n.rval.SetUint(^v.Uint())
}
}
func negConst(n *node) {
t := n.typ.rtype
v := n.child[0].rval
n.rval = reflect.New(t).Elem()
switch t.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n.rval.SetInt(-v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
n.rval.SetUint(-v.Uint())
case reflect.Float32, reflect.Float64:
n.rval.SetFloat(-v.Float())
case reflect.Complex64, reflect.Complex128:
n.rval.SetComplex(-v.Complex())
}
}
func notConst(n *node) {
t := n.typ.rtype
v := n.child[0].rval
n.rval = reflect.New(t).Elem()
n.rval.SetBool(!v.Bool())
}
func posConst(n *node) {
t := n.typ.rtype
v := n.child[0].rval
n.rval = reflect.New(t).Elem()
switch t.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n.rval.SetInt(+v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
n.rval.SetUint(+v.Uint())
case reflect.Float32, reflect.Float64:
n.rval.SetFloat(+v.Float())
case reflect.Complex64, reflect.Complex128:
n.rval.SetComplex(+v.Complex())
}
}
func equal(n *node) {
tnext := getExec(n.tnext)
dest := genValue(n)

View File

@@ -24,6 +24,7 @@ var builtin = [...]bltnGenerator{
aAndAssign: andAssign,
aAndNot: andNot,
aAndNotAssign: andNotAssign,
aBitNot: bitNot,
aCall: call,
aCase: _case,
aCompositeLit: arrayLit,
@@ -40,11 +41,12 @@ var builtin = [...]bltnGenerator{
aLowerEqual: lowerEqual,
aMul: mul,
aMulAssign: mulAssign,
aNegate: negate,
aNeg: neg,
aNot: not,
aNotEqual: notEqual,
aOr: or,
aOrAssign: orAssign,
aPos: pos,
aQuo: quo,
aQuoAssign: quoAssign,
aRange: _range,
@@ -214,7 +216,7 @@ func convert(n *node) {
typ := n.child[0].typ.TypeOf()
next := getExec(n.tnext)
if c.kind == basicLit && !c.rval.IsValid() { // convert nil to type
if c.isNil() { // convert nil to type
n.exec = func(f *frame) bltn {
dest(f).Set(reflect.New(typ).Elem())
return next
@@ -239,7 +241,7 @@ func isRecursiveStruct(t *itype, rtype reflect.Type) bool {
if t.cat == structT && rtype.Kind() == reflect.Interface {
return true
}
if t.cat == ptrT {
if t.cat == ptrT && t.rtype != nil {
return isRecursiveStruct(t.val, t.rtype.Elem())
}
return false
@@ -291,12 +293,17 @@ func assign(n *node) {
}
if n.nleft == 1 {
if s, d, i := svalue[0], dvalue[0], ivalue[0]; i != nil {
switch s, d, i := svalue[0], dvalue[0], ivalue[0]; {
case n.child[0].ident == "_":
n.exec = func(f *frame) bltn {
return next
}
case i != nil:
n.exec = func(f *frame) bltn {
d(f).SetMapIndex(i(f), s(f))
return next
}
} else {
default:
n.exec = func(f *frame) bltn {
d(f).Set(s(f))
return next
@@ -314,7 +321,6 @@ func assign(n *node) {
default:
t = typ.TypeOf()
}
//types[i] = n.child[sbase+i].typ.TypeOf()
types[i] = t
}
@@ -324,10 +330,16 @@ func assign(n *node) {
n.exec = func(f *frame) bltn {
t := make([]reflect.Value, len(svalue))
for i, s := range svalue {
if n.child[i].ident == "_" {
continue
}
t[i] = reflect.New(types[i]).Elem()
t[i].Set(s(f))
}
for i, d := range dvalue {
if n.child[i].ident == "_" {
continue
}
if j := ivalue[i]; j != nil {
d(f).SetMapIndex(j(f), t[i]) // Assign a map entry
} else {
@@ -340,6 +352,7 @@ func assign(n *node) {
}
func not(n *node) {
dest := genValue(n)
value := genValue(n.child[0])
tnext := getExec(n.tnext)
@@ -347,14 +360,15 @@ func not(n *node) {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
if !value(f).Bool() {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
i := n.findex
n.exec = func(f *frame) bltn {
f.data[i].SetBool(!value(f).Bool())
dest(f).SetBool(!value(f).Bool())
return tnext
}
}
@@ -374,17 +388,18 @@ func addr(n *node) {
func deref(n *node) {
value := genValue(n.child[0])
tnext := getExec(n.tnext)
i := n.findex
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
if value(f).Elem().Bool() {
f.data[i] = value(f).Elem()
if f.data[i].Bool() {
return tnext
}
return fnext
}
} else {
i := n.findex
n.exec = func(f *frame) bltn {
f.data[i] = value(f).Elem()
return tnext
@@ -658,7 +673,7 @@ func call(n *node) {
values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
}
default:
if c.kind == basicLit {
if c.kind == basicLit || c.rval.IsValid() {
var argType reflect.Type
if variadic >= 0 && i >= variadic {
argType = n.child[0].typ.arg[variadic].val.TypeOf()
@@ -823,7 +838,9 @@ func callBin(n *node) {
// method signature obtained from reflect.Type include receiver as 1st arg, except for interface types
rcvrOffset := 0
if recv := n.child[0].recv; recv != nil && recv.node.typ.TypeOf().Kind() != reflect.Interface {
rcvrOffset = 1
if funcType.NumIn() > len(child) {
rcvrOffset = 1
}
}
for i, c := range child {
@@ -843,7 +860,7 @@ func callBin(n *node) {
values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
}
default:
if c.kind == basicLit {
if c.kind == basicLit || c.rval.IsValid() {
// Convert literal value (untyped) to function argument type (if not an interface{})
var argType reflect.Type
if variadic >= 0 && i >= variadic {
@@ -862,7 +879,6 @@ func callBin(n *node) {
case interfaceT:
values = append(values, genValueInterfaceValue(c))
default:
//values = append(values, genValue(c))
values = append(values, genInterfaceWrapper(c, defType))
}
}
@@ -962,21 +978,21 @@ func getIndexBinPtrMethod(n *node) {
func getIndexArray(n *node) {
tnext := getExec(n.tnext)
value0 := genValueArray(n.child[0]) // array
i := n.findex
if n.child[1].rval.IsValid() { // constant array index
ai := int(vInt(n.child[1].rval))
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
if value0(f).Index(ai).Bool() {
f.data[i] = value0(f).Index(ai)
if f.data[i].Bool() {
return tnext
}
return fnext
}
} else {
i := n.findex
n.exec = func(f *frame) bltn {
// Can not use .Set due to constraint of being able to assign an array element
f.data[i] = value0(f).Index(ai)
return tnext
}
@@ -988,16 +1004,15 @@ func getIndexArray(n *node) {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
_, vi := value1(f)
if value0(f).Index(int(vi)).Bool() {
f.data[i] = value0(f).Index(int(vi))
if f.data[i].Bool() {
return tnext
}
return fnext
}
} else {
i := n.findex
n.exec = func(f *frame) bltn {
_, vi := value1(f)
// Can not use .Set due to constraint of being able to assign an array element
f.data[i] = value0(f).Index(int(vi))
return tnext
}
@@ -1019,8 +1034,10 @@ func getIndexMap(n *node) {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
if v := value0(f).MapIndex(mi); v.IsValid() && v.Bool() {
dest(f).SetBool(true)
return tnext
}
dest(f).Set(z)
return fnext
}
} else {
@@ -1040,8 +1057,10 @@ func getIndexMap(n *node) {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
if v := value0(f).MapIndex(value1(f)); v.IsValid() && v.Bool() {
dest(f).SetBool(true)
return tnext
}
dest(f).Set(z)
return fnext
}
} else {
@@ -1139,17 +1158,28 @@ func getIndexSeq(n *node) {
value := genValue(n.child[0])
index := n.val.([]int)
tnext := getExec(n.tnext)
i := n.findex
// Note:
// Here we have to store the result using
// f.data[i] = value(...)
// instead of normal
// dest(f).Set(value(...)
// because the value returned by FieldByIndex() must be preserved
// for possible future Set operations on the struct field (avoid a
// dereference from Set, resulting in setting a copy of the
// original field).
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
if value(f).FieldByIndex(index).Bool() {
f.data[i] = value(f).FieldByIndex(index)
if f.data[i].Bool() {
return tnext
}
return fnext
}
} else {
i := n.findex
n.exec = func(f *frame) bltn {
f.data[i] = value(f).FieldByIndex(index)
return tnext
@@ -1167,17 +1197,18 @@ func getPtrIndexSeq(n *node) {
} else {
value = genValue(n.child[0])
}
i := n.findex
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
if value(f).Elem().FieldByIndex(index).Bool() {
f.data[i] = value(f).Elem().FieldByIndex(index)
if f.data[i].Bool() {
return tnext
}
return fnext
}
} else {
i := n.findex
n.exec = func(f *frame) bltn {
f.data[i] = value(f).Elem().FieldByIndex(index)
return tnext
@@ -1189,17 +1220,38 @@ func getIndexSeqField(n *node) {
value := genValue(n.child[0])
index := n.val.([]int)
i := n.findex
next := getExec(n.tnext)
tnext := getExec(n.tnext)
if n.child[0].typ.TypeOf().Kind() == reflect.Ptr {
n.exec = func(f *frame) bltn {
f.data[i] = value(f).Elem().FieldByIndex(index)
return next
if n.fnext != nil {
fnext := getExec(n.fnext)
if n.child[0].typ.TypeOf().Kind() == reflect.Ptr {
n.exec = func(f *frame) bltn {
f.data[i] = value(f).Elem().FieldByIndex(index)
if f.data[i].Bool() {
return tnext
}
return fnext
}
} else {
n.exec = func(f *frame) bltn {
f.data[i] = value(f).FieldByIndex(index)
if f.data[i].Bool() {
return tnext
}
return fnext
}
}
} else {
n.exec = func(f *frame) bltn {
f.data[i] = value(f).FieldByIndex(index)
return next
if n.child[0].typ.TypeOf().Kind() == reflect.Ptr {
n.exec = func(f *frame) bltn {
f.data[i] = value(f).Elem().FieldByIndex(index)
return tnext
}
} else {
n.exec = func(f *frame) bltn {
f.data[i] = value(f).FieldByIndex(index)
return tnext
}
}
}
}
@@ -1246,7 +1298,7 @@ func getIndexSeqMethod(n *node) {
}
}
func negate(n *node) {
func neg(n *node) {
dest := genValue(n)
value := genValue(n.child[0])
next := getExec(n.tnext)
@@ -1270,6 +1322,37 @@ func negate(n *node) {
}
}
func pos(n *node) {
dest := genValue(n)
value := genValue(n.child[0])
next := getExec(n.tnext)
n.exec = func(f *frame) bltn {
dest(f).Set(value(f))
return next
}
}
func bitNot(n *node) {
dest := genValue(n)
value := genValue(n.child[0])
next := getExec(n.tnext)
typ := n.typ.TypeOf()
switch typ.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n.exec = func(f *frame) bltn {
dest(f).SetInt(^value(f).Int())
return next
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
n.exec = func(f *frame) bltn {
dest(f).SetUint(^value(f).Uint())
return next
}
}
}
func land(n *node) {
value0 := genValue(n.child[0])
value1 := genValue(n.child[1])
@@ -1414,7 +1497,7 @@ func arrayLit(n *node) {
if c.kind == keyValueExpr {
convertLiteralValue(c.child[1], rtype)
values[i] = genValue(c.child[1])
index[i] = int(c.child[0].rval.Int())
index[i] = int(vInt(c.child[0].rval))
} else {
convertLiteralValue(c, rtype)
values[i] = genValue(c)
@@ -1946,7 +2029,7 @@ func _close(n *node) {
}
func _complex(n *node) {
i := n.findex
dest := genValue(n)
c1, c2 := n.child[1], n.child[2]
convertLiteralValue(c1, floatType)
convertLiteralValue(c2, floatType)
@@ -1956,38 +2039,38 @@ func _complex(n *node) {
if typ := n.typ.TypeOf(); isComplex(typ) {
n.exec = func(f *frame) bltn {
f.data[i].SetComplex(complex(value0(f).Float(), value1(f).Float()))
dest(f).SetComplex(complex(value0(f).Float(), value1(f).Float()))
return next
}
} else {
// Not a complex type: ignore imaginary part
n.exec = func(f *frame) bltn {
f.data[i].Set(value0(f).Convert(typ))
dest(f).Set(value0(f).Convert(typ))
return next
}
}
}
func _imag(n *node) {
i := n.findex
dest := genValue(n)
convertLiteralValue(n.child[1], complexType)
value := genValue(n.child[1])
next := getExec(n.tnext)
n.exec = func(f *frame) bltn {
f.data[i].SetFloat(imag(value(f).Complex()))
dest(f).SetFloat(imag(value(f).Complex()))
return next
}
}
func _real(n *node) {
i := n.findex
dest := genValue(n)
convertLiteralValue(n.child[1], complexType)
value := genValue(n.child[1])
next := getExec(n.tnext)
n.exec = func(f *frame) bltn {
f.data[i].SetFloat(real(value(f).Complex()))
dest(f).SetFloat(real(value(f).Complex()))
return next
}
}
@@ -2005,12 +2088,12 @@ func _delete(n *node) {
}
func _len(n *node) {
i := n.findex
dest := genValue(n)
value := genValue(n.child[1])
next := getExec(n.tnext)
n.exec = func(f *frame) bltn {
f.data[i].SetInt(int64(value(f).Len()))
dest(f).SetInt(int64(value(f).Len()))
return next
}
}
@@ -2123,16 +2206,18 @@ func reset(n *node) {
func recv(n *node) {
value := genValue(n.child[0])
tnext := getExec(n.tnext)
i := n.findex
if n.interp.cancelChan {
// Cancellable channel read
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
ch := value(f)
// Fast: channel read doesn't block
if x, ok := ch.TryRecv(); ok {
if x.Bool() {
var ok bool
ch := value(f)
if f.data[i], ok = ch.TryRecv(); ok {
if f.data[i].Bool() {
return tnext
}
return fnext
@@ -2148,7 +2233,6 @@ func recv(n *node) {
return fnext
}
} else {
i := n.findex
n.exec = func(f *frame) bltn {
// Fast: channel read doesn't block
var ok bool
@@ -2170,7 +2254,7 @@ func recv(n *node) {
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
if v, _ := value(f).Recv(); v.Bool() {
if f.data[i], _ = value(f).Recv(); f.data[i].Bool() {
return tnext
}
return fnext
@@ -2222,13 +2306,16 @@ func recv2(n *node) {
}
func convertLiteralValue(n *node, t reflect.Type) {
if n.kind != basicLit || t == nil || t.Kind() == reflect.Interface {
// Skip non-constant values, undefined target type or interface target type.
if !(n.kind == basicLit || n.rval.IsValid()) || t == nil || t.Kind() == reflect.Interface {
return
}
if n.rval.IsValid() {
// Convert constant value to target type.
n.rval = n.rval.Convert(t)
} else {
n.rval = reflect.New(t).Elem() // convert to type nil value
// Create a zero value of target type.
n.rval = reflect.New(t).Elem()
}
}
@@ -2420,19 +2507,21 @@ func isNil(n *node) {
value = genValue(n.child[0])
}
tnext := getExec(n.tnext)
dest := genValue(n)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
if value(f).IsNil() {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
i := n.findex
n.exec = func(f *frame) bltn {
f.data[i].SetBool(value(f).IsNil())
dest(f).SetBool(value(f).IsNil())
return tnext
}
}
@@ -2446,19 +2535,21 @@ func isNotNil(n *node) {
value = genValue(n.child[0])
}
tnext := getExec(n.tnext)
dest := genValue(n)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
if value(f).IsNil() {
dest(f).SetBool(false)
return fnext
}
dest(f).SetBool(true)
return tnext
}
} else {
i := n.findex
n.exec = func(f *frame) bltn {
f.data[i].SetBool(!value(f).IsNil())
dest(f).SetBool(!value(f).IsNil())
return tnext
}
}

View File

@@ -74,12 +74,15 @@ type symbol struct {
// execution to the index in frame, created exactly from the types layout.
//
type scope struct {
anc *scope // Ancestor upper scope
def *node // function definition node this scope belongs to, or nil
types []reflect.Type // Frame layout, may be shared by same level scopes
level int // Frame level: number of frame indirections to access var during execution
sym map[string]*symbol // Map of symbols defined in this current scope
global bool // true if scope refers to global space (single frame for universe and package level scopes)
anc *scope // Ancestor upper scope
def *node // function definition node this scope belongs to, or nil
loop *node // loop exit node for break statement
loopRestart *node // loop restart node for continue statement
pkgID string // unique id of package in which scope is defined
types []reflect.Type // Frame layout, may be shared by same level scopes
level int // Frame level: number of frame indirections to access var during execution
sym map[string]*symbol // Map of symbols defined in this current scope
global bool // true if scope refers to global space (single frame for universe and package level scopes)
}
// push creates a new scope and chain it to the current one
@@ -95,6 +98,8 @@ func (s *scope) push(indirect bool) *scope {
sc.global = s.global
sc.level = s.level
}
// inherit loop state and pkgID from ancestor
sc.loop, sc.loopRestart, sc.pkgID = s.loop, s.loopRestart, s.pkgID
return &sc
}
@@ -156,19 +161,15 @@ func (s *scope) add(typ *itype) (index int) {
return
}
func (interp *Interpreter) initScopePkg(n *node) (*scope, string) {
func (interp *Interpreter) initScopePkg(pkgID string) *scope {
sc := interp.universe
pkgName := mainID
if p := fileNode(n); p != nil {
pkgName = p.child[0].ident
}
interp.mutex.Lock()
if _, ok := interp.scopes[pkgName]; !ok {
interp.scopes[pkgName] = sc.pushBloc()
if _, ok := interp.scopes[pkgID]; !ok {
interp.scopes[pkgID] = sc.pushBloc()
}
sc = interp.scopes[pkgName]
sc = interp.scopes[pkgID]
sc.pkgID = pkgID
interp.mutex.Unlock()
return sc, pkgName
return sc
}

View File

@@ -8,7 +8,7 @@ import (
"strings"
)
func (interp *Interpreter) importSrc(rPath, path, alias string) error {
func (interp *Interpreter) importSrc(rPath, path string) error {
var dir string
var err error
@@ -29,6 +29,7 @@ func (interp *Interpreter) importSrc(rPath, path, alias string) error {
} else if dir, rPath, err = pkgDir(interp.context.GOPATH, rPath, path); err != nil {
return err
}
if interp.rdir[path] {
return fmt.Errorf("import cycle not allowed\n\timports %s", path)
}
@@ -66,6 +67,9 @@ func (interp *Interpreter) importSrc(rPath, path, alias string) error {
if root == nil {
continue
}
if interp.astDot {
root.astDot(dotX(), name)
}
if pkgName == "" {
pkgName = pname
} else if pkgName != pname {
@@ -75,7 +79,7 @@ func (interp *Interpreter) importSrc(rPath, path, alias string) error {
subRPath := effectivePkg(rPath, path)
var list []*node
list, err = interp.gta(root, subRPath)
list, err = interp.gta(root, subRPath, path)
if err != nil {
return err
}
@@ -85,7 +89,7 @@ func (interp *Interpreter) importSrc(rPath, path, alias string) error {
// revisit incomplete nodes where GTA could not complete
for pkg, nodes := range revisit {
for _, n := range nodes {
if _, err = interp.gta(n, pkg); err != nil {
if _, err = interp.gta(n, pkg, path); err != nil {
return err
}
}
@@ -94,7 +98,7 @@ func (interp *Interpreter) importSrc(rPath, path, alias string) error {
// Generate control flow graphs
for _, root := range rootNodes {
var nodes []*node
if nodes, err = interp.cfg(root); err != nil {
if nodes, err = interp.cfg(root, path); err != nil {
return err
}
initNodes = append(initNodes, nodes...)
@@ -103,13 +107,7 @@ func (interp *Interpreter) importSrc(rPath, path, alias string) error {
// Register source package in the interpreter. The package contains only
// the global symbols in the package scope.
interp.mutex.Lock()
interp.srcPkg[path] = interp.scopes[pkgName].sym
// Rename imported pkgName to alias if they are different
if pkgName != alias {
interp.scopes[alias] = interp.scopes[pkgName]
delete(interp.scopes, pkgName)
}
interp.srcPkg[path] = interp.scopes[path].sym
interp.frame.mutex.Lock()
interp.resizeFrame()
@@ -189,7 +187,7 @@ func effectivePkg(root, path string) string {
for i := 0; i < len(splitPath); i++ {
part := splitPath[len(splitPath)-1-i]
if part == splitRoot[len(splitRoot)-1-rootIndex] {
if part == splitRoot[len(splitRoot)-1-rootIndex] && i != 0 {
prevRootIndex = rootIndex
rootIndex++
} else if prevRootIndex == rootIndex {

View File

@@ -17,7 +17,6 @@ const (
binPkgT
boolT
builtinT
byteT
chanT
complex64T
complex128T
@@ -33,7 +32,6 @@ const (
int64T
mapT
ptrT
runeT
srcPkgT
stringT
structT
@@ -54,7 +52,6 @@ var cats = [...]string{
arrayT: "arrayT",
binT: "binT",
binPkgT: "binPkgT",
byteT: "byteT",
boolT: "boolT",
builtinT: "builtinT",
chanT: "chanT",
@@ -72,7 +69,6 @@ var cats = [...]string{
int64T: "int64T",
mapT: "mapT",
ptrT: "ptrT",
runeT: "runeT",
srcPkgT: "srcPkgT",
stringT: "stringT",
structT: "structT",
@@ -103,22 +99,23 @@ type structField struct {
// itype defines the internal representation of types in the interpreter
type itype struct {
cat tcat // Type category
field []structField // Array of struct fields if structT or interfaceT
key *itype // Type of key element if MapT or nil
val *itype // Type of value element if chanT, mapT, ptrT, aliasT, arrayT or variadicT
arg []*itype // Argument types if funcT or nil
ret []*itype // Return types if funcT or nil
method []*node // Associated methods or nil
name string // name of type within its package for a defined type
path string // for a defined type, the package import path
size int // Size of array if ArrayT
rtype reflect.Type // Reflection type if ValueT, or nil
incomplete bool // true if type must be parsed again (out of order declarations)
untyped bool // true for a literal value (string or number)
sizedef bool // true if array size is computed from type definition
node *node // root AST node of type definition
scope *scope // type declaration scope (in case of re-parse incomplete type)
cat tcat // Type category
field []structField // Array of struct fields if structT or interfaceT
key *itype // Type of key element if MapT or nil
val *itype // Type of value element if chanT, mapT, ptrT, aliasT, arrayT or variadicT
arg []*itype // Argument types if funcT or nil
ret []*itype // Return types if funcT or nil
method []*node // Associated methods or nil
name string // name of type within its package for a defined type
path string // for a defined type, the package import path
size int // Size of array if ArrayT
rtype reflect.Type // Reflection type if ValueT, or nil
incomplete bool // true if type must be parsed again (out of order declarations)
untyped bool // true for a literal value (string or number)
sizedef bool // true if array size is computed from type definition
isBinMethod bool // true if the type refers to a bin method function
node *node // root AST node of type definition
scope *scope // type declaration scope (in case of re-parse incomplete type)
}
// nodeType returns a type definition for the corresponding AST subtree
@@ -175,7 +172,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
}
} else {
// Evaluate constant array size expression
if _, err = interp.cfg(n.child[0]); err != nil {
if _, err = interp.cfg(n.child[0], sc.pkgID); err != nil {
return nil, err
}
t.incomplete = true
@@ -199,8 +196,8 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
t.cat = boolT
t.name = "bool"
case byte:
t.cat = byteT
t.name = "byte"
t.cat = uint8T
t.name = "uint8"
t.untyped = true
case complex64:
t.cat = complex64T
@@ -226,8 +223,8 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
t.name = "uint"
t.untyped = true
case rune:
t.cat = runeT
t.name = "rune"
t.cat = int32T
t.name = "int32"
t.untyped = true
case string:
t.cat = stringT
@@ -434,6 +431,9 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
}
}
case landExpr, lorExpr:
t.cat = boolT
case mapType:
t.cat = mapT
if t.key, err = nodeType(interp, sc, n.child[0]); err != nil {
@@ -482,7 +482,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
if m, _ := lt.lookupMethod(name); m != nil {
t, err = nodeType(interp, sc, m.child[2])
} else if bm, _, _, ok := lt.lookupBinMethod(name); ok {
t = &itype{cat: valueT, rtype: bm.Type}
t = &itype{cat: valueT, rtype: bm.Type, isBinMethod: true}
} else if ti := lt.lookupField(name); len(ti) > 0 {
t = lt.fieldSeq(ti)
} else if bs, _, ok := lt.lookupBinField(name); ok {
@@ -593,7 +593,6 @@ var zeroValues [maxT]reflect.Value
func init() {
zeroValues[boolT] = reflect.ValueOf(false)
zeroValues[byteT] = reflect.ValueOf(byte(0))
zeroValues[complex64T] = reflect.ValueOf(complex64(0))
zeroValues[complex128T] = reflect.ValueOf(complex128(0))
zeroValues[errorT] = reflect.ValueOf(new(error)).Elem()
@@ -604,7 +603,6 @@ func init() {
zeroValues[int16T] = reflect.ValueOf(int16(0))
zeroValues[int32T] = reflect.ValueOf(int32(0))
zeroValues[int64T] = reflect.ValueOf(int64(0))
zeroValues[runeT] = reflect.ValueOf(rune(0))
zeroValues[stringT] = reflect.ValueOf("")
zeroValues[uintT] = reflect.ValueOf(uint(0))
zeroValues[uint8T] = reflect.ValueOf(uint8(0))
@@ -809,6 +807,22 @@ func (t *itype) lookupBinField(name string) (s reflect.StructField, index []int,
return s, index, ok
}
// methodCallType returns a method function type without the receiver defined.
// The input type must be a method function type with the receiver as the first input argument.
func (t *itype) methodCallType() reflect.Type {
it := []reflect.Type{}
ni := t.rtype.NumIn()
for i := 1; i < ni; i++ {
it = append(it, t.rtype.In(i))
}
ot := []reflect.Type{}
no := t.rtype.NumOut()
for i := 0; i < no; i++ {
ot = append(ot, t.rtype.Out(i))
}
return reflect.FuncOf(it, ot, t.rtype.IsVariadic())
}
// getMethod returns a pointer to the method definition
func (t *itype) getMethod(name string) *node {
for _, m := range t.method {
@@ -885,7 +899,9 @@ func (t *itype) refType(defined map[string]bool) reflect.Type {
panic(err)
}
}
if t.val != nil && defined[t.val.name] {
if t.val != nil && defined[t.val.name] && !t.val.incomplete && t.val.rtype == nil {
// Replace reference to self (direct or indirect) by an interface{} to handle
// recursive types with reflect.
t.val.rtype = interf
}
switch t.cat {
@@ -914,7 +930,7 @@ func (t *itype) refType(defined map[string]bool) reflect.Type {
case interfaceT:
t.rtype = interf
case mapT:
t.rtype = reflect.MapOf(t.key.TypeOf(), t.val.TypeOf())
t.rtype = reflect.MapOf(t.key.refType(defined), t.val.refType(defined))
case ptrT:
t.rtype = reflect.PtrTo(t.val.refType(defined))
case structT:

View File

@@ -90,9 +90,6 @@ func genValue(n *node) func(*frame) reflect.Value {
v = reflect.ValueOf(n.val)
}
return func(f *frame) reflect.Value { return v }
case rvalueExpr:
v := n.rval
return func(f *frame) reflect.Value { return v }
default:
if n.rval.IsValid() {
v := n.rval
@@ -180,11 +177,23 @@ func genValueInterface(n *node) func(*frame) reflect.Value {
}
}
func zeroInterfaceValue() reflect.Value {
n := &node{kind: basicLit, typ: &itype{cat: nilT, untyped: true}}
v := reflect.New(reflect.TypeOf((*interface{})(nil)).Elem()).Elem()
return reflect.ValueOf(valueInterface{n, v})
}
func genValueInterfaceValue(n *node) func(*frame) reflect.Value {
value := genValue(n)
return func(f *frame) reflect.Value {
return value(f).Interface().(valueInterface).value
v := value(f)
if v.Interface().(valueInterface).node == nil {
// Uninitialized interface value, set it to a correct zero value.
v.Set(zeroInterfaceValue())
v = value(f)
}
return v.Interface().(valueInterface).value
}
}

View File

@@ -1,5 +1,6 @@
// +build go1.12,!go1.14
// Package stdlib provides wrappers of standard library packages to be imported natively in Yaegi.
package stdlib
import "reflect"

View File

@@ -1,5 +1,6 @@
// +build go1.12,!go1.14
// Package syscall provide wrapper of standard library syscall package for native import in Yaegi.
package syscall
import "reflect"

View File

@@ -1,5 +1,6 @@
// +build go1.12,!go1.14
// Package unsafe provides wrapper of standard library unsafe package to be imported natively in Yaegi.
package unsafe
import "reflect"