Compare commits

...

24 Commits

Author SHA1 Message Date
Marc Vertes
92d65c22f0 interp: remove incorrect type check on array object
The type check was generating false negatives. A correct test to check the
adressable status of an array is more complex to implement, and will
be done later.

Fixes #973.
2020-12-02 17:26:03 +01:00
mpl
2db4579b6f interp: fix short-form type assertions
The long-form (with comma-ok) ones were already fixed but the short-form
ones were not because they were in a completely different code path.

This PR also refactors the code so that both short-form and long-form
are now merged in the same function.

N.B: even though most (all?) cases seem to now be supported, one of them
still yields a result that does not satisfy reflect's Implements method
yet. It does not prevent the resulting assertion to be usable though.

N.B2: the code path for the third-form (_, ok) hasn't been fixed and/or
refactored yet.

Fixes #919
2020-12-02 14:46:03 +01:00
mpl
101633c380 interp: support two more type assertion cases
Fixes #967
2020-12-01 15:50:04 +01:00
mpl
1e0f6ece6e interp: support more type assertion cases
Fixes #955
2020-11-30 18:00:04 +01:00
Marc Vertes
662d2a6afe interp: fix parsing of assign to dereferenced pointer
Fixes #969.
2020-11-30 17:46:05 +01:00
Marc Vertes
b25ee3f809 interp: fix method lookup on aliased types
In aliased type declarations, when the target type was imported from
an external package rather than declared locally, the aliased type was
overwritten by target, loosing ability to lookup methods on the aliased
type. Aliasing on imported types is now properly detected and handled.

Fixes #971.
2020-11-30 15:44:04 +01:00
Marc Vertes
81e1e5f206 interp: handle getting address of interpreter interface value
Fixes #963.
2020-11-30 11:48:04 +01:00
Marc Vertes
81d8339132 test: chdir to package directory prior to launch tests
Because this is what `go test` does, and some packages depend on that,
for example `github.com/jjcollinge/servicefabric`.
2020-11-25 16:10:04 +01:00
Marc Vertes
d494f9e420 interp: support calling goto from sub-scope
As opposed to other symbols, goto labels must be searched in included
scopes, not upper ones. Implement scope.lookdown to perform this,
to allow calls to goto to be embedded in included scopes where label
is defined.

Fixes #953.
2020-11-19 12:48:03 +01:00
Marc Vertes
6da1107c39 fix: do not confuse function call with type conversion
Use node action to better discriminate between function call and type
conversion which have the same pattern at AST level.

Fixes #960.
2020-11-18 14:56:05 +01:00
Marc Vertes
38a7331bf9 interp: fix type check on function signature
Perform function declaration type check from the upper level scope (the scope where the
function is declared), to avoid possible collisions of local variables with package names.

Fixes #957.
2020-11-13 18:02:04 +01:00
Marc Vertes
13783889cb interp: avoid useless interface wrapping
in `callBin`, call arguments are converted to the corresponding
parameter type. In a case of an interface, the original concrete type
should be preserved instead, and only wrapped to an interface type for
internal interpreter types, as runtime values should already implement the
interface.

This change removes the interface wrapping when parameter is a runtime
value (valueT or ptrT to valueT).

This removes some overhead when handling runtime values, and keep a
similar behavior between interpreted and pre-compiled code. For
example, `io.Copy` preserves its internal optimisations when passed a
`bytes.Buffer`.
2020-11-12 10:48:04 +01:00
mpl
ed626f3fb9 interp: support conversion of runtime func into interp func
Conversion of interp func into runtime func already worked, but added a
test anyway to watch out for regressions.

Fixes #941
2020-11-10 00:40:04 +01:00
Marc Vertes
d0a34d467b interp: fix getting unsigned constant value
The function vUint, used to get the unsigned integer value of a value,
variable (frame) or constant, was broken for constant.Value expression.

Fixes #948.
2020-11-09 17:40:04 +01:00
Nicholas Wiersma
83676577ac fix: use the import path for types
When running GTA, the type `path` was set to `rpath`. This equates to the package path (`github.com/traefik/yaegi`) in most cases. In the vendored case the `rpath` is the sub package path `something/vendor/github.com/traefik/yaegi` causing issues in typecheck and likely further down the line. By using the `importPath` it makes this consistent.

**Note:** I have no clue how to test this decently. I am open to options here.

Potentially Fixes #916
2020-11-05 13:42:03 +01:00
mpl
f0fc907269 extract: support building/running yaegi with Go devel
Fixes #928
2020-11-05 11:40:04 +01:00
Marc Vertes
61f4704925 interp: fix CFG in case of for loop with empty init and cond
Refactor `for` variants for clarity. Ensure all possible 8 combinations
are covered.

Fixes #942.
2020-11-05 11:00:04 +01:00
Marc Vertes
b1ccfbf47f interp: apply type conversion on untyped variable at run
Fixes #938.
2020-11-04 18:16:04 +01:00
Marc Vertes
0ed4b362dc interp: implement conversion for interpreter function types
Interpreter function types are represented internally by the AST node
of their definition. The conversion operation creates a new node with
the type field pointing to the target type.

Fixes #936.
2020-11-03 17:48:03 +01:00
Marc Vertes
98807387a4 interp: resolve type for untyped shift expressions
A non-constant shift expression can be untyped, requiring to apply a
type from inherited context. This change insures that such context is
propagated during CFG pre-order walk, to be used if necessary.
    
Fixes #927.
2020-11-02 18:08:04 +01:00
Marc Vertes
c817823ba1 interp: fix incorrect infinite loop on for statement
Add a for statement variant for the case of a "for" with an init, no
condition and no post-increment.

Fixes #933.
2020-11-02 17:52:03 +01:00
mpl
3cb8bca81a interp: on panic, look for node where offending exec originated from
The execution flow is such that a node can end up running several chained exec
funcs, some of which actually originate from other nodes. For example, in:

var m []int // L0
println("hello world") // L1
m[0] = 1 // L2

the offending code is on a node on line 2 (out of range error). However, since
the assignment to m is part of the execution flow of the variable m, we'll get
the panic when running all the chained exec funcs attached to the node for m on
line 0.

Which is why, when that happens, we need to look for the actual node (the one on
L2) where the offending instruction originates from, in order to
properly report the origin of the panic.

Fixes #546
2020-11-02 10:26:04 +01:00
Marc Vertes
a38d19288f interp: fix testing for nil interface values
Fixes #924.
2020-10-30 16:20:04 +01:00
Marc Vertes
7f8ffa6719 interp: handle explicit nil values in literal composite values
Fixes #922.
2020-10-27 11:24:04 +01:00
254 changed files with 1305 additions and 449 deletions

29
_test/addr2.go Normal file
View File

@@ -0,0 +1,29 @@
package main
import (
"encoding/xml"
"fmt"
)
type Email struct {
Where string `xml:"where,attr"`
Addr string
}
func f(s string, r interface{}) error {
return xml.Unmarshal([]byte(s), &r)
}
func main() {
data := `
<Email where='work'>
<Addr>bob@work.com</Addr>
</Email>
`
v := Email{}
err := f(data, &v)
fmt.Println(err, v)
}
// Ouput:
// <nil> {work bob@work.com}

96
_test/assert0.go Normal file
View File

@@ -0,0 +1,96 @@
package main
import (
"fmt"
"reflect"
"time"
)
type MyWriter interface {
Write(p []byte) (i int, err error)
}
type TestStruct struct{}
func (t TestStruct) Write(p []byte) (n int, err error) {
return len(p), nil
}
func usesWriter(w MyWriter) {
n, _ := w.Write([]byte("hello world"))
fmt.Println(n)
}
type MyStringer interface {
String() string
}
func usesStringer(s MyStringer) {
fmt.Println(s.String())
}
func main() {
aType := reflect.TypeOf((*MyWriter)(nil)).Elem()
var t interface{}
t = TestStruct{}
var tw MyWriter
var ok bool
tw, ok = t.(MyWriter)
if !ok {
fmt.Println("TestStruct does not implement MyWriter")
} else {
fmt.Println("TestStruct implements MyWriter")
usesWriter(tw)
}
n, _ := t.(MyWriter).Write([]byte("hello world"))
fmt.Println(n)
bType := reflect.TypeOf(TestStruct{})
fmt.Println(bType.Implements(aType))
t = 42
foo, ok := t.(MyWriter)
if !ok {
fmt.Println("42 does not implement MyWriter")
} else {
fmt.Println("42 implements MyWriter")
}
_ = foo
var tt interface{}
tt = time.Nanosecond
var myD MyStringer
myD, ok = tt.(MyStringer)
if !ok {
fmt.Println("time.Nanosecond does not implement MyStringer")
} else {
fmt.Println("time.Nanosecond implements MyStringer")
usesStringer(myD)
}
fmt.Println(tt.(MyStringer).String())
cType := reflect.TypeOf((*MyStringer)(nil)).Elem()
dType := reflect.TypeOf(time.Nanosecond)
fmt.Println(dType.Implements(cType))
tt = 42
bar, ok := tt.(MyStringer)
if !ok {
fmt.Println("42 does not implement MyStringer")
} else {
fmt.Println("42 implements MyStringer")
}
_ = bar
}
// Output:
// TestStruct implements MyWriter
// 11
// 11
// true
// 42 does not implement MyWriter
// time.Nanosecond implements MyStringer
// 1ns
// 1ns
// true
// 42 does not implement MyStringer

60
_test/assert1.go Normal file
View File

@@ -0,0 +1,60 @@
package main
import (
"fmt"
"reflect"
"time"
)
type TestStruct struct{}
func (t TestStruct) String() string {
return "hello world"
}
func main() {
aType := reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
var t interface{}
t = time.Nanosecond
s, ok := t.(fmt.Stringer)
if !ok {
fmt.Println("time.Nanosecond does not implement fmt.Stringer")
return
}
fmt.Println(s.String())
fmt.Println(t.(fmt.Stringer).String())
bType := reflect.TypeOf(time.Nanosecond)
fmt.Println(bType.Implements(aType))
t = 42
foo, ok := t.(fmt.Stringer)
if !ok {
fmt.Println("42 does not implement fmt.Stringer")
} else {
fmt.Println("42 implements fmt.Stringer")
}
_ = foo
var tt interface{}
tt = TestStruct{}
ss, ok := tt.(fmt.Stringer)
if !ok {
fmt.Println("TestStuct does not implement fmt.Stringer")
return
}
fmt.Println(ss.String())
fmt.Println(tt.(fmt.Stringer).String())
// TODO(mpl): uncomment when fixed
// cType := reflect.TypeOf(TestStruct{})
// fmt.Println(cType.Implements(aType))
}
// Output:
// 1ns
// 1ns
// true
// 42 does not implement fmt.Stringer
// hello world
// hello world

15
_test/composite14.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import "fmt"
type T struct {
b []byte
}
func main() {
t := T{nil}
fmt.Println(t)
}
// Output:
// {[]}

21
_test/convert0.go Normal file
View File

@@ -0,0 +1,21 @@
package main
type T struct {
v int
}
type comparator func(T, T) bool
func sort(items []T, comp comparator) {
println("in sort")
}
func compT(t0, t1 T) bool { return t0.v < t1.v }
func main() {
a := []T{}
sort(a, comparator(compT))
}
// Output:
// in sort

17
_test/convert1.go Normal file
View File

@@ -0,0 +1,17 @@
package main
import "strconv"
type atoidef func(s string) (int, error)
func main() {
stdatoi := atoidef(strconv.Atoi)
n, err := stdatoi("7")
if err != nil {
panic(err)
}
println(n)
}
// Output:
// 7

19
_test/convert2.go Normal file
View File

@@ -0,0 +1,19 @@
package main
import "bufio"
func fakeSplitFunc(data []byte, atEOF bool) (advance int, token []byte, err error) {
return 7, nil, nil
}
func main() {
splitfunc := bufio.SplitFunc(fakeSplitFunc)
n, _, err := splitfunc(nil, true)
if err != nil {
panic(err)
}
println(n)
}
// Output:
// 7

16
_test/for15.go Normal file
View File

@@ -0,0 +1,16 @@
package main
func f() int { println("in f"); return 1 }
func main() {
for i := f(); ; {
println("in loop")
if i > 0 {
break
}
}
}
// Output:
// in f
// in loop

16
_test/for16.go Normal file
View File

@@ -0,0 +1,16 @@
package main
func main() {
max := 1
for ; ; max-- {
if max == 0 {
break
}
println("in for")
}
println("bye")
}
// Output:
// in for
// bye

17
_test/fun26.go Normal file
View File

@@ -0,0 +1,17 @@
package main
type F func() (int, error)
func f1() (int, error) { return 3, nil }
func f2(a string, f F) {
c, _ := f()
println(a, c)
}
func main() {
f2("hello", F(f1))
}
// Output:
// hello 3

12
_test/goto1.go Normal file
View File

@@ -0,0 +1,12 @@
package main
func main() {
if true {
goto here
}
here:
println("ok")
}
// Output:
// ok

28
_test/nil3.go Normal file
View File

@@ -0,0 +1,28 @@
package main
type I interface {
Hello()
}
type T struct {
h I
}
func (t *T) Hello() { println("Hello") }
func main() {
t := &T{}
println(t.h != nil)
println(t.h == nil)
t.h = t
println(t.h != nil)
println(t.h == nil)
t.h.Hello()
}
// Output:
// false
// true
// true
// false
// Hello

20
_test/time14.go Normal file
View File

@@ -0,0 +1,20 @@
package main
import (
"fmt"
"time"
)
var t time.Time
func f() time.Time {
time := t
return time
}
func main() {
fmt.Println(f())
}
// Output:
// 0001-01-01 00:00:00 +0000 UTC

15
_test/time15.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import "time"
type TimeValue time.Time
func (v *TimeValue) decode() { println("in decode") }
func main() {
var tv TimeValue
tv.decode()
}
// Output:
// in decode

View File

@@ -6,6 +6,7 @@ import (
"fmt"
"go/build"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
@@ -102,6 +103,18 @@ func test(arg []string) (err error) {
testing.Init()
os.Args = tf
flag.Parse()
path += string(filepath.Separator)
var dir string
switch strings.Split(path, string(filepath.Separator))[0] {
case ".", "..", string(filepath.Separator):
dir = path
default:
dir = filepath.Join(build.Default.GOPATH, "src", path)
}
if err = os.Chdir(dir); err != nil {
return err
}
i := interp.New(interp.Options{GoPath: build.Default.GOPATH, BuildTags: strings.Split(tags, ",")})
i.Use(stdlib.Symbols)

View File

@@ -0,0 +1,13 @@
package main
import (
"fmt"
"guthib.com/bat/baz"
)
func main() {
t := baz.NewT()
fmt.Printf("%s", t.A3)
}

View File

@@ -0,0 +1,22 @@
package baz
func NewT() *T {
return &T{
A1: make([]U, 0),
A3: "foobar",
}
}
type T struct {
A1 []U
A3 string
}
type U struct {
B1 V
B2 V
}
type V struct {
C1 string
}

View File

@@ -93,6 +93,12 @@ func TestPackages(t *testing.T) {
expected: "Yo hello",
evalFile: "./_pkg12/src/guthib.com/foo/main.go",
},
{
desc: "eval main with vendor",
goPath: "./_pkg13/",
expected: "foobar",
evalFile: "./_pkg13/src/guthib.com/foo/bar/main.go",
},
}
for _, test := range testCases {

View File

@@ -436,10 +436,12 @@ func GetMinor(part string) string {
return minor
}
const defaultMinorVersion = 15
func genBuildTags() (string, error) {
version := runtime.Version()
if version == "devel" {
return "", nil
if strings.HasPrefix(version, "devel") {
return "", fmt.Errorf("extracting only supported with stable releases of Go, not %v", version)
}
parts := strings.Split(version, ".")
@@ -452,6 +454,11 @@ func genBuildTags() (string, error) {
return "", fmt.Errorf("failed to parse version: %v", err)
}
// Only append an upper bound if we are not on the latest go
if minor >= defaultMinorVersion {
return currentGoVersion, nil
}
nextGoVersion := parts[0] + "." + strconv.Itoa(minor+1)
return currentGoVersion + ",!" + nextGoVersion, nil

View File

@@ -84,11 +84,12 @@ func main() {
oFile = strings.ReplaceAll(importPath, "/", "_") + ".go"
}
prefix := runtime.Version()
if runtime.Version() != "devel" {
parts := strings.Split(runtime.Version(), ".")
prefix = parts[0] + "_" + extract.GetMinor(parts[1])
version := runtime.Version()
if strings.HasPrefix(version, "devel") {
log.Fatalf("extracting only supported with stable releases of Go, not %v", version)
}
parts := strings.Split(version, ".")
prefix := parts[0] + "_" + extract.GetMinor(parts[1])
f, err := os.Create(prefix + "_" + oFile)
if err != nil {

View File

@@ -51,12 +51,14 @@ const (
fieldList
fileStmt
forStmt0 // for {}
forStmt1 // for cond {}
forStmt2 // for init; cond; {}
forStmt3 // for ; cond; post {}
forStmt3a // for init; ; post {}
forStmt4 // for init; cond; post {}
forRangeStmt // for range
forStmt1 // for init; ; {}
forStmt2 // for cond {}
forStmt3 // for init; cond; {}
forStmt4 // for ; ; post {}
forStmt5 // for ; cond; post {}
forStmt6 // for init; ; post {}
forStmt7 // for init; cond; post {}
forRangeStmt // for range {}
funcDecl
funcLit
funcType
@@ -134,8 +136,10 @@ var kinds = [...]string{
forStmt1: "forStmt1",
forStmt2: "forStmt2",
forStmt3: "forStmt3",
forStmt3a: "forStmt3a",
forStmt4: "forStmt4",
forStmt5: "forStmt5",
forStmt6: "forStmt6",
forStmt7: "forStmt7",
forRangeStmt: "forRangeStmt",
funcDecl: "funcDecl",
funcType: "funcType",
@@ -654,23 +658,23 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
case *ast.ForStmt:
// Disambiguate variants of FOR statements with a node kind per variant
var kind nkind
if a.Cond == nil {
if a.Init != nil && a.Post != nil {
kind = forStmt3a
} else {
kind = forStmt0
}
} else {
switch {
case a.Init == nil && a.Post == nil:
kind = forStmt1
case a.Init != nil && a.Post == nil:
kind = forStmt2
case a.Init == nil && a.Post != nil:
kind = forStmt3
default:
kind = forStmt4
}
switch {
case a.Cond == nil && a.Init == nil && a.Post == nil:
kind = forStmt0
case a.Cond == nil && a.Init != nil && a.Post == nil:
kind = forStmt1
case a.Cond != nil && a.Init == nil && a.Post == nil:
kind = forStmt2
case a.Cond != nil && a.Init != nil && a.Post == nil:
kind = forStmt3
case a.Cond == nil && a.Init == nil && a.Post != nil:
kind = forStmt4
case a.Cond != nil && a.Init == nil && a.Post != nil:
kind = forStmt5
case a.Cond == nil && a.Init != nil && a.Post != nil:
kind = forStmt6
case a.Cond != nil && a.Init != nil && a.Post != nil:
kind = forStmt7
}
st.push(addChild(&root, anc, pos, kind, aNop), nod)

View File

@@ -66,6 +66,44 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
return false
}
switch n.kind {
case binaryExpr, unaryExpr, parenExpr:
if isBoolAction(n) {
break
}
// Gather assigned type if set, to give context for type propagation at post-order.
switch n.anc.kind {
case assignStmt, defineStmt:
a := n.anc
i := childPos(n) - a.nright
if i < 0 {
break
}
if len(a.child) > a.nright+a.nleft {
i--
}
dest := a.child[i]
if dest.typ != nil && !isInterface(dest.typ) {
// Interface type are not propagated, and will be resolved at post-order.
n.typ = dest.typ
}
case binaryExpr, unaryExpr, parenExpr:
n.typ = n.anc.typ
}
case defineStmt:
// Determine type of variables initialized at declaration, so it can be propagated.
if n.nleft+n.nright == len(n.child) {
// No type was specified on the left hand side, it will resolved at post-order.
break
}
n.typ, err = nodeType(interp, sc, n.child[n.nleft])
if err != nil {
break
}
for i := 0; i < n.nleft; i++ {
n.child[i].typ = n.typ
}
case blockStmt:
if n.anc != nil && n.anc.kind == rangeStmt {
// For range block: ensure that array or map type is propagated to iterators
@@ -159,7 +197,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
case breakStmt, continueStmt, gotoStmt:
if len(n.child) > 0 {
// Handle labeled statements
// Handle labeled statements.
label := n.child[0].ident
if sym, _, ok := sc.lookup(label); ok {
if sym.kind != labelSym {
@@ -176,25 +214,23 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
case labeledStmt:
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
}
// 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
n.sym = sym
} else {
n.sym = &symbol{kind: labelSym, node: n, index: -1}
sc.sym[label] = n.sym
}
sc.sym[label] = n.sym
case caseClause:
sc = sc.pushBloc()
if sn := n.anc.anc; sn.kind == typeSwitch && sn.child[1].action == aAssign {
// Type switch clause with a var defined in switch guard
// Type switch clause with a var defined in switch guard.
var typ *itype
if len(n.child) == 2 {
// 1 type in clause: define the var with this type in the case clause scope
// 1 type in clause: define the var with this type in the case clause scope.
switch {
case n.child[0].ident == nilIdent:
typ = sc.getType("interface{}")
@@ -204,7 +240,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
typ, err = nodeType(interp, sc, n.child[0])
}
} else {
// define the var with the type in the switch guard expression
// Define the var with the type in the switch guard expression.
typ = sn.child[1].child[1].child[0].typ
}
if err != nil {
@@ -285,11 +321,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
}
}
case forStmt0, forRangeStmt:
sc = sc.pushBloc()
sc.loop, sc.loopRestart = n, n.child[0]
case forStmt1, forStmt2, forStmt3, forStmt3a, forStmt4:
case forStmt0, forStmt1, forStmt2, forStmt3, forStmt4, forStmt5, forStmt6, forStmt7, forRangeStmt:
sc = sc.pushBloc()
sc.loop, sc.loopRestart = n, n.lastChild()
@@ -386,9 +418,10 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
return false
}
if n.child[1].kind == identExpr {
switch n.child[1].kind {
case identExpr, selectorExpr:
n.typ = &itype{cat: aliasT, val: typ, name: typeName}
} else {
default:
n.typ = typ
n.typ.name = typeName
}
@@ -447,7 +480,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
var atyp *itype
if n.nleft+n.nright < len(n.child) {
if atyp, err = nodeType(interp, sc, n.child[n.nleft]); err != nil {
return
break
}
}
@@ -644,7 +677,12 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
}
switch n.action {
case aRem, aShl, aShr:
case aRem:
n.typ = c0.typ
case aShl, aShr:
if c0.typ.untyped {
break
}
n.typ = c0.typ
case aEqual, aNotEqual:
n.typ = sc.getType("bool")
@@ -860,7 +898,12 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
n.gen = nop
n.findex = -1
n.typ = c0.typ
n.rval = c1.rval.Convert(c0.typ.rtype)
if c, ok := c1.rval.Interface().(constant.Value); ok {
i, _ := constant.Int64Val(constant.ToInt(c))
n.rval = reflect.ValueOf(i).Convert(c0.typ.rtype)
} else {
n.rval = c1.rval.Convert(c0.typ.rtype)
}
default:
n.gen = convert
n.typ = c0.typ
@@ -1005,7 +1048,14 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
body.tnext = n.start
sc = sc.pop()
case forStmt1: // for cond {}
case forStmt1: // for init; ; {}
init, body := n.child[0], n.child[1]
n.start = init.start
init.tnext = body.start
body.tnext = n.start
sc = sc.pop()
case forStmt2: // for cond {}
cond, body := n.child[0], n.child[1]
if !isBool(cond.typ) {
err = cond.cfgErrorf("non-bool used as for condition")
@@ -1024,7 +1074,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
setFNext(cond, n)
sc = sc.pop()
case forStmt2: // for init; cond; {}
case forStmt3: // for init; cond; {}
init, cond, body := n.child[0], n.child[1], n.child[2]
if !isBool(cond.typ) {
err = cond.cfgErrorf("non-bool used as for condition")
@@ -1046,7 +1096,14 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
setFNext(cond, n)
sc = sc.pop()
case forStmt3: // for ; cond; post {}
case forStmt4: // for ; ; post {}
post, body := n.child[0], n.child[1]
n.start = body.start
post.tnext = body.start
body.tnext = post.start
sc = sc.pop()
case forStmt5: // for ; cond; post {}
cond, post, body := n.child[0], n.child[1], n.child[2]
if !isBool(cond.typ) {
err = cond.cfgErrorf("non-bool used as for condition")
@@ -1066,7 +1123,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
body.tnext = post.start
sc = sc.pop()
case forStmt3a: // for init; ; post {}
case forStmt6: // for init; ; post {}
init, post, body := n.child[0], n.child[1], n.child[2]
n.start = init.start
init.tnext = body.start
@@ -1074,7 +1131,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
post.tnext = body.start
sc = sc.pop()
case forStmt4: // for init; cond; post {}
case forStmt7: // for init; cond; post {}
init, cond, post, body := n.child[0], n.child[1], n.child[2], n.child[3]
if !isBool(cond.typ) {
err = cond.cfgErrorf("non-bool used as for condition")
@@ -1326,7 +1383,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
n.val = sc.def
for i, c := range n.child {
var typ *itype
typ, err = nodeType(interp, sc, returnSig.child[1].fieldType(i))
typ, err = nodeType(interp, sc.upperLevel(), returnSig.child[1].fieldType(i))
if err != nil {
return
}
@@ -2278,7 +2335,7 @@ func isCall(n *node) bool {
}
func isBinCall(n *node) bool {
return n.kind == callExpr && n.child[0].typ.cat == valueT && n.child[0].typ.rtype.Kind() == reflect.Func
return isCall(n) && n.child[0].typ.cat == valueT && n.child[0].typ.rtype.Kind() == reflect.Func
}
func mustReturnValue(n *node) bool {
@@ -2294,7 +2351,7 @@ func mustReturnValue(n *node) bool {
}
func isRegularCall(n *node) bool {
return n.kind == callExpr && n.child[0].typ.cat == funcT
return isCall(n) && n.child[0].typ.cat == funcT
}
func variadicPos(n *node) int {
@@ -2467,3 +2524,12 @@ func isArithmeticAction(n *node) bool {
return false
}
}
func isBoolAction(n *node) bool {
switch n.action {
case aEqual, aGreater, aGreaterEqual, aLand, aLor, aLower, aLowerEqual, aNot, aNotEqual:
return true
default:
return false
}
}

View File

@@ -145,7 +145,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
elementType := sc.getType(typeName)
if elementType == nil {
// Add type if necessary, so method can be registered
sc.sym[typeName] = &symbol{kind: typeSym, typ: &itype{name: typeName, path: rpath, incomplete: true, node: rtn.child[0], scope: sc}}
sc.sym[typeName] = &symbol{kind: typeSym, typ: &itype{name: typeName, path: importPath, incomplete: true, node: rtn.child[0], scope: sc}}
elementType = sc.sym[typeName].typ
}
rcvrtype = &itype{cat: ptrT, val: elementType, incomplete: elementType.incomplete, node: rtn, scope: sc}
@@ -154,7 +154,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
rcvrtype = sc.getType(typeName)
if rcvrtype == nil {
// Add type if necessary, so method can be registered
sc.sym[typeName] = &symbol{kind: typeSym, typ: &itype{name: typeName, path: rpath, incomplete: true, node: rtn, scope: sc}}
sc.sym[typeName] = &symbol{kind: typeSym, typ: &itype{name: typeName, path: importPath, incomplete: true, node: rtn, scope: sc}}
rcvrtype = sc.sym[typeName].typ
}
}
@@ -253,13 +253,14 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
return false
}
if n.child[1].kind == identExpr {
n.typ = &itype{cat: aliasT, val: typ, name: typeName, path: rpath, field: typ.field, incomplete: typ.incomplete, scope: sc, node: n.child[0]}
switch n.child[1].kind {
case identExpr, selectorExpr:
n.typ = &itype{cat: aliasT, val: typ, name: typeName, path: importPath, field: typ.field, incomplete: typ.incomplete, scope: sc, node: n.child[0]}
copy(n.typ.method, typ.method)
} else {
default:
n.typ = typ
n.typ.name = typeName
n.typ.path = rpath
n.typ.path = importPath
}
asImportName := filepath.Join(typeName, baseName)

View File

@@ -71,6 +71,27 @@ func TestEvalArithmetic(t *testing.T) {
})
}
func TestEvalShift(t *testing.T) {
i := interp.New(interp.Options{})
runTests(t, i, []testCase{
{src: "a, b, m := uint32(1), uint32(2), uint32(0); m = a + (1 << b)", res: "5"},
{src: "c := uint(1); d := uint(+(-(1 << c)))", res: "18446744073709551614"},
{src: "e, f := uint32(0), uint32(0); f = 1 << -(e * 2)", res: "1"},
{src: "p := uint(0xdead); byte((1 << (p & 7)) - 1)", res: "31"},
{pre: func() { eval(t, i, "const k uint = 1 << 17") }, src: "int(k)", res: "131072"},
})
}
func TestOpVarConst(t *testing.T) {
i := interp.New(interp.Options{})
runTests(t, i, []testCase{
{pre: func() { eval(t, i, "const a uint = 8 + 2") }, src: "a", res: "10"},
{src: "b := uint(5); a+b", res: "15"},
{src: "b := uint(5); b+a", res: "15"},
{src: "b := uint(5); b>a", res: "false"},
})
}
func TestEvalStar(t *testing.T) {
i := interp.New(interp.Options{})
runTests(t, i, []testCase{
@@ -89,6 +110,7 @@ func TestEvalAssign(t *testing.T) {
{src: "f := int64(3.2)", err: "1:39: cannot convert expression of type float64 to type int64"},
{src: "g := 1; g <<= 8", res: "256"},
{src: "h := 1; h >>= 8", res: "0"},
{src: "i := 1; j := &i; (*j) = 2", res: "2"},
})
}
@@ -423,19 +445,15 @@ func TestEvalSliceExpression(t *testing.T) {
{src: `a := []int{0,1,2}[:]`, res: "[0 1 2]"},
{src: `a := []int{0,1,2,3}[1:3:4]`, res: "[1 2]"},
{src: `a := []int{0,1,2,3}[:3:4]`, res: "[0 1 2]"},
{src: `ar := [3]int{0,1,2}
a := ar[1:3]`, res: "[1 2]"},
{src: `ar := [3]int{0,1,2}; a := ar[1:3]`, res: "[1 2]"},
{src: `a := (&[3]int{0,1,2})[1:3]`, res: "[1 2]"},
{src: `a := (&[3]int{0,1,2})[1:3]`, res: "[1 2]"},
{src: `s := "hello"[1:3]`, res: "el"},
{src: `str := "hello"
s := str[1:3]`, res: "el"},
{src: `str := "hello"; s := str[1:3]`, res: "el"},
{src: `a := int(1)[0:1]`, err: "1:33: cannot slice type int"},
{src: `a := ([3]int{0,1,2})[1:3]`, err: "1:33: cannot slice type [3]int"},
{src: `a := (&[]int{0,1,2,3})[1:3]`, err: "1:33: cannot slice type *[]int"},
{src: `a := "hello"[1:3:4]`, err: "1:45: invalid operation: 3-index slice of string"},
{src: `ar := [3]int{0,1,2}
a := ar[:4]`, err: "2:16: index int is out of bounds"},
{src: `ar := [3]int{0,1,2}; a := ar[:4]`, err: "1:58: index int is out of bounds"},
{src: `a := []int{0,1,2,3}[1::4]`, err: "1:49: 2nd index required in 3-index slice"},
{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"},

View File

@@ -3,10 +3,12 @@ package interp
//go:generate go run ../internal/cmd/genop/genop.go
import (
"errors"
"fmt"
"go/constant"
"log"
"reflect"
"regexp"
"sync"
"unsafe"
)
@@ -67,11 +69,22 @@ var builtin = [...]bltnGenerator{
aStar: deref,
aSub: sub,
aSubAssign: subAssign,
aTypeAssert: typeAssert,
aTypeAssert: typeAssert1,
aXor: xor,
aXorAssign: xorAssign,
}
var receiverStripperRxp *regexp.Regexp
func init() {
re := `func\(((.*?(, |\)))(.*))`
var err error
receiverStripperRxp, err = regexp.Compile(re)
if err != nil {
panic(err)
}
}
type valueInterface struct {
node *node
value reflect.Value
@@ -108,10 +121,53 @@ func (interp *Interpreter) run(n *node, cf *frame) {
runCfg(n.start, f)
}
// originalExecNode looks in the tree of nodes for the node which has exec,
// aside from n, in order to know where n "inherited" that exec from.
func originalExecNode(n *node, exec bltn) *node {
execAddr := reflect.ValueOf(exec).Pointer()
var originalNode *node
seen := make(map[int64]struct{})
root := n
for {
root = root.anc
if root == nil {
break
}
if _, ok := seen[root.index]; ok {
continue
}
root.Walk(func(wn *node) bool {
if _, ok := seen[wn.index]; ok {
return true
}
seen[wn.index] = struct{}{}
if wn.index == n.index {
return true
}
if wn.exec == nil {
return true
}
if reflect.ValueOf(wn.exec).Pointer() == execAddr {
originalNode = wn
return false
}
return true
}, nil)
if originalNode != nil {
break
}
}
return originalNode
}
// Functions set to run during execution of CFG.
// runCfg executes a node AST by walking its CFG and running node builtin at each step.
func runCfg(n *node, f *frame) {
var exec bltn
defer func() {
f.mutex.Lock()
f.recovered = recover()
@@ -119,14 +175,18 @@ func runCfg(n *node, f *frame) {
val[0].Call(val[1:])
}
if f.recovered != nil {
fmt.Println(n.cfgErrorf("panic"))
oNode := originalExecNode(n, exec)
if oNode == nil {
oNode = n
}
fmt.Println(oNode.cfgErrorf("panic"))
f.mutex.Unlock()
panic(f.recovered)
}
f.mutex.Unlock()
}()
for exec := n.exec; exec != nil && f.runid() == n.interp.runid(); {
for exec = n.exec; exec != nil && f.runid() == n.interp.runid(); {
exec = exec(f)
}
}
@@ -170,79 +230,39 @@ func typeAssertStatus(n *node) {
}
}
func typeAssert(n *node) {
c0, c1 := n.child[0], n.child[1]
value := genValue(c0) // input value
value0 := genValue(n) // returned result
next := getExec(n.tnext)
switch {
case isInterfaceSrc(c1.typ):
typ := n.child[1].typ
typID := n.child[1].typ.id()
n.exec = func(f *frame) bltn {
v := value(f)
vi, ok := v.Interface().(valueInterface)
if !ok {
panic(n.cfgErrorf("interface conversion: nil is not %v", typID))
}
if !vi.node.typ.implements(typ) {
panic(n.cfgErrorf("interface conversion: %v is not %v", vi.node.typ.id(), typID))
}
value0(f).Set(v)
return next
}
case isInterface(c1.typ):
n.exec = func(f *frame) bltn {
v := value(f).Elem()
typ := value0(f).Type()
if !v.IsValid() {
panic(fmt.Sprintf("interface conversion: interface {} is nil, not %s", typ.String()))
}
if !canAssertTypes(v.Type(), typ) {
method := firstMissingMethod(v.Type(), typ)
panic(fmt.Sprintf("interface conversion: %s is not %s: missing method %s", v.Type().String(), typ.String(), method))
}
value0(f).Set(v)
return next
}
case c0.typ.cat == valueT || c0.typ.cat == errorT:
n.exec = func(f *frame) bltn {
v := value(f).Elem()
typ := value0(f).Type()
if !v.IsValid() {
panic(fmt.Sprintf("interface conversion: interface {} is nil, not %s", typ.String()))
}
if !canAssertTypes(v.Type(), typ) {
method := firstMissingMethod(v.Type(), typ)
panic(fmt.Sprintf("interface conversion: %s is not %s: missing method %s", v.Type().String(), typ.String(), method))
}
value0(f).Set(v)
return next
}
default:
n.exec = func(f *frame) bltn {
v := value(f).Interface().(valueInterface)
typ := value0(f).Type()
if !v.value.IsValid() {
panic(fmt.Sprintf("interface conversion: interface {} is nil, not %s", typ.String()))
}
if !canAssertTypes(v.value.Type(), typ) {
panic(fmt.Sprintf("interface conversion: interface {} is %s, not %s", v.value.Type().String(), typ.String()))
}
value0(f).Set(v.value)
return next
}
func stripReceiverFromArgs(signature string) (string, error) {
fields := receiverStripperRxp.FindStringSubmatch(signature)
if len(fields) < 5 {
return "", errors.New("error while matching method signature")
}
if fields[3] == ")" {
return fmt.Sprintf("func()%s", fields[4]), nil
}
return fmt.Sprintf("func(%s", fields[4]), nil
}
func typeAssert1(n *node) {
typeAssert(n, false)
}
func typeAssert2(n *node) {
typeAssert(n, true)
}
func typeAssert(n *node, withOk bool) {
c0, c1 := n.child[0], n.child[1]
value := genValue(c0) // input value
value0 := genValue(n.anc.child[0]) // returned result
value1 := genValue(n.anc.child[1]) // returned status
setStatus := n.anc.child[1].ident != "_" // do not assign status to "_"
typ := c1.typ // type to assert or convert to
value := genValue(c0) // input value
var value0, value1 func(*frame) reflect.Value
setStatus := false
if withOk {
value0 = genValue(n.anc.child[0]) // returned result
value1 = genValue(n.anc.child[1]) // returned status
setStatus = n.anc.child[1].ident != "_" // do not assign status to "_"
} else {
value0 = genValue(n) // returned result
}
typ := c1.typ // type to assert or convert to
typID := typ.id()
rtype := typ.rtype // type to assert
next := getExec(n.tnext)
@@ -251,50 +271,179 @@ func typeAssert2(n *node) {
case isInterfaceSrc(typ):
n.exec = func(f *frame) bltn {
v, ok := value(f).Interface().(valueInterface)
if ok && v.node.typ.id() == typID {
value0(f).Set(value(f))
} else {
ok = false
}
if setStatus {
value1(f).SetBool(ok)
defer func() {
value1(f).SetBool(ok)
}()
}
if !ok {
if !withOk {
panic(n.cfgErrorf("interface conversion: nil is not %v", typID))
}
return next
}
if v.node.typ.id() == typID {
value0(f).Set(value(f))
return next
}
m0 := v.node.typ.methods()
m1 := typ.methods()
if len(m0) < len(m1) {
ok = false
if !withOk {
panic(n.cfgErrorf("interface conversion: %v is not %v", v.node.typ.id(), typID))
}
return next
}
for k, meth1 := range m1 {
var meth0 string
meth0, ok = m0[k]
if !ok {
return next
}
// As far as we know this equality check can fail because they are two ways to
// represent the signature of a method: one where the receiver appears before the
// func keyword, and one where it is just a func signature, and the receiver is
// seen as the first argument. That's why if that equality fails, we try harder to
// compare them afterwards. Hopefully that is the only reason this equality can fail.
if meth0 == meth1 {
continue
}
tm := lookupFieldOrMethod(v.node.typ, k)
if tm == nil {
ok = false
return next
}
var err error
meth0, err = stripReceiverFromArgs(meth0)
if err != nil {
ok = false
return next
}
if meth0 != meth1 {
ok = false
return next
}
}
value0(f).Set(value(f))
return next
}
case isInterface(typ):
n.exec = func(f *frame) bltn {
v := value(f).Elem()
ok := v.IsValid() && canAssertTypes(v.Type(), rtype)
if ok {
value0(f).Set(v)
}
var leftType reflect.Type
v := value(f)
val, ok := v.Interface().(valueInterface)
if setStatus {
value1(f).SetBool(ok)
defer func() {
value1(f).SetBool(ok)
}()
}
if ok && val.node.typ.cat != valueT {
m0 := val.node.typ.methods()
m1 := typ.methods()
if len(m0) < len(m1) {
ok = false
return next
}
for k, meth1 := range m1 {
var meth0 string
meth0, ok = m0[k]
if !ok {
return next
}
if meth0 != meth1 {
ok = false
return next
}
}
// TODO(mpl): make this case compliant with reflect's Implements.
v = genInterfaceWrapper(val.node, rtype)(f)
value0(f).Set(v)
ok = true
return next
}
if ok {
v = val.value
leftType = val.node.typ.rtype
} else {
v = v.Elem()
leftType = v.Type()
ok = true
}
ok = v.IsValid()
if !ok {
if !withOk {
panic(fmt.Sprintf("interface conversion: interface {} is nil, not %s", rtype.String()))
}
return next
}
ok = canAssertTypes(leftType, rtype)
if !ok {
if !withOk {
method := firstMissingMethod(leftType, rtype)
panic(fmt.Sprintf("interface conversion: %s is not %s: missing method %s", leftType.String(), rtype.String(), method))
}
return next
}
value0(f).Set(v)
return next
}
case n.child[0].typ.cat == valueT || n.child[0].typ.cat == errorT:
n.exec = func(f *frame) bltn {
v := value(f).Elem()
ok := v.IsValid() && canAssertTypes(v.Type(), rtype)
if ok {
value0(f).Set(v)
}
ok := v.IsValid()
if setStatus {
value1(f).SetBool(ok)
defer func() {
value1(f).SetBool(ok)
}()
}
if !ok {
if !withOk {
panic(fmt.Sprintf("interface conversion: interface {} is nil, not %s", rtype.String()))
}
return next
}
ok = canAssertTypes(v.Type(), rtype)
if !ok {
if !withOk {
method := firstMissingMethod(v.Type(), rtype)
panic(fmt.Sprintf("interface conversion: %s is not %s: missing method %s", v.Type().String(), rtype.String(), method))
}
return next
}
value0(f).Set(v)
return next
}
default:
n.exec = func(f *frame) bltn {
v, ok := value(f).Interface().(valueInterface)
ok = ok && v.value.IsValid() && canAssertTypes(v.value.Type(), rtype)
if ok {
value0(f).Set(v.value)
}
if setStatus {
value1(f).SetBool(ok)
defer func() {
value1(f).SetBool(ok)
}()
}
if !ok || !v.value.IsValid() {
ok = false
if !withOk {
panic(fmt.Sprintf("interface conversion: interface {} is nil, not %s", rtype.String()))
}
return next
}
ok = canAssertTypes(v.value.Type(), rtype)
if !ok {
if !withOk {
panic(fmt.Sprintf("interface conversion: interface {} is %s, not %s", v.value.Type().String(), rtype.String()))
}
return next
}
value0(f).Set(v.value)
return next
}
}
@@ -343,10 +492,30 @@ func convert(n *node) {
return
}
if n.child[0].typ.cat == funcT && c.typ.cat == funcT {
value := genValue(c)
n.exec = func(f *frame) bltn {
n, ok := value(f).Interface().(*node)
if !ok || !n.typ.convertibleTo(c.typ) {
panic("cannot convert")
}
n1 := *n
n1.typ = c.typ
dest(f).Set(reflect.ValueOf(&n1))
return next
}
return
}
doConvert := true
var value func(*frame) reflect.Value
if c.typ.cat == funcT {
switch {
case c.typ.cat == funcT:
value = genFunctionWrapper(c)
} else {
case n.child[0].typ.cat == funcT && c.typ.cat == valueT:
doConvert = false
value = genValueNode(c)
default:
value = genValue(c)
}
@@ -367,7 +536,11 @@ func convert(n *node) {
}
n.exec = func(f *frame) bltn {
dest(f).Set(value(f).Convert(typ))
if doConvert {
dest(f).Set(value(f).Convert(typ))
} else {
dest(f).Set(value(f))
}
return next
}
}
@@ -517,12 +690,23 @@ func not(n *node) {
func addr(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).Addr())
return next
c0 := n.child[0]
value := genValue(c0)
switch c0.typ.cat {
case interfaceT:
i := n.findex
l := n.level
n.exec = func(f *frame) bltn {
v := value(f).Interface().(valueInterface).value
getFrame(f, l).data[i] = reflect.ValueOf(v.Interface())
return next
}
default:
n.exec = func(f *frame) bltn {
dest(f).Set(value(f).Addr())
return next
}
}
}
@@ -692,6 +876,9 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
if src.Type().Kind() != dest.Type().Kind() {
dest.Set(src.Addr())
} else {
if wrappedSrc, ok := src.Interface().(valueInterface); ok {
src = wrappedSrc.value
}
dest.Set(src)
}
d = d[numRet+1:]
@@ -797,7 +984,10 @@ func call(n *node) {
var method bool
value := genValue(n.child[0])
var values []func(*frame) reflect.Value
if n.child[0].recv != nil {
recvIndexLater := false
switch {
case n.child[0].recv != nil:
// Compute method receiver value.
if isRecursiveType(n.child[0].recv.node.typ, n.child[0].recv.node.typ.rtype) {
values = append(values, genValueRecvInterfacePtr(n.child[0]))
@@ -805,11 +995,17 @@ func call(n *node) {
values = append(values, genValueRecv(n.child[0]))
}
method = true
} else if n.child[0].action == aMethod {
case len(n.child[0].child) > 0 && n.child[0].child[0].typ != nil && n.child[0].child[0].typ.cat == interfaceT:
recvIndexLater = true
values = append(values, genValueBinRecv(n.child[0], &receiver{node: n.child[0].child[0]}))
value = genValueBinMethodOnInterface(n, value)
method = true
case n.child[0].action == aMethod:
// Add a place holder for interface method receiver.
values = append(values, nil)
method = true
}
numRet := len(n.child[0].typ.ret)
variadic := variadicPos(n)
child := n.child[1:]
@@ -919,6 +1115,7 @@ func call(n *node) {
n.exec = func(f *frame) bltn {
var def *node
var ok bool
bf := value(f)
if def, ok = bf.Interface().(*node); ok {
bf = def.rval
@@ -988,16 +1185,16 @@ func call(n *node) {
var src reflect.Value
if v == nil {
src = def.recv.val
if len(def.recv.index) > 0 {
if src.Kind() == reflect.Ptr {
src = src.Elem().FieldByIndex(def.recv.index)
} else {
src = src.FieldByIndex(def.recv.index)
}
}
} else {
src = v(f)
}
if recvIndexLater && def.recv != nil && len(def.recv.index) > 0 {
if src.Kind() == reflect.Ptr {
src = src.Elem().FieldByIndex(def.recv.index)
} else {
src = src.FieldByIndex(def.recv.index)
}
}
// Accommodate to receiver type
d := dest[0]
if ks, kd := src.Kind(), d.Kind(); ks != kd {
@@ -1116,6 +1313,7 @@ func callBin(n *node) {
c.val = reflect.Zero(argType)
}
}
switch c.typ.cat {
case funcT:
values = append(values, genFunctionWrapper(c))
@@ -1128,6 +1326,14 @@ func callBin(n *node) {
default:
values = append(values, genInterfaceWrapper(c, defType))
}
case ptrT:
if c.typ.val.cat == valueT {
values = append(values, genValue(c))
} else {
values = append(values, genInterfaceWrapper(c, defType))
}
case valueT:
values = append(values, genValue(c))
default:
values = append(values, genInterfaceWrapper(c, defType))
}
@@ -1528,6 +1734,15 @@ func getMethodByName(n *node) {
n.exec = func(f *frame) bltn {
val := value0(f).Interface().(valueInterface)
typ := val.node.typ
if typ.node == nil && typ.cat == valueT {
// happens with a var of empty interface type, that has value of concrete type
// from runtime, being asserted to "user-defined" interface.
if _, ok := typ.rtype.MethodByName(name); !ok {
panic(fmt.Sprintf("method %s not found", name))
}
return next
}
m, li := val.node.typ.lookupMethod(name)
fr := f.clone()
nod := *m
@@ -1719,6 +1934,11 @@ func neg(n *node) {
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
}
case reflect.Float32, reflect.Float64:
n.exec = func(f *frame) bltn {
dest(f).SetFloat(-value(f).Float())
@@ -2104,16 +2324,20 @@ func doComposite(n *node, hasType bool, keyed bool) {
val = c
fieldIndex = i
}
convertLiteralValue(val, typ.field[fieldIndex].typ.TypeOf())
ft := typ.field[fieldIndex].typ
rft := ft.TypeOf()
convertLiteralValue(val, rft)
switch {
case val.typ.cat == nilT:
values[fieldIndex] = func(*frame) reflect.Value { return reflect.New(rft).Elem() }
case val.typ.cat == funcT:
values[fieldIndex] = genFunctionWrapper(val)
case isArray(val.typ) && val.typ.val != nil && val.typ.val.cat == interfaceT:
values[fieldIndex] = genValueInterfaceArray(val)
case isRecursiveType(typ.field[fieldIndex].typ, typ.field[fieldIndex].typ.rtype):
values[fieldIndex] = genValueRecursiveInterface(val, typ.field[fieldIndex].typ.rtype)
case isInterface(typ.field[fieldIndex].typ):
values[fieldIndex] = genInterfaceWrapper(val, typ.field[fieldIndex].typ.rtype)
case isRecursiveType(ft, rft):
values[fieldIndex] = genValueRecursiveInterface(val, rft)
case isInterface(ft):
values[fieldIndex] = genInterfaceWrapper(val, rft)
default:
values[fieldIndex] = genValue(val)
}
@@ -3087,9 +3311,9 @@ func isNil(n *node) {
fnext := getExec(n.fnext)
if c0.typ.cat == interfaceT {
n.exec = func(f *frame) bltn {
vi := value(f).Interface().(valueInterface)
if (vi == valueInterface{} ||
vi.node.kind == basicLit && vi.node.typ.cat == nilT) {
v := value(f)
vi, ok := v.Interface().(valueInterface)
if ok && (vi == valueInterface{} || vi.node.kind == basicLit && vi.node.typ.cat == nilT) || v.IsNil() {
dest(f).SetBool(true)
return tnext
}
@@ -3109,7 +3333,12 @@ func isNil(n *node) {
} else {
if c0.typ.cat == interfaceT {
n.exec = func(f *frame) bltn {
dest(f).SetBool(value(f).Interface().(valueInterface) == valueInterface{})
v := value(f)
if vi, ok := v.Interface().(valueInterface); ok {
dest(f).SetBool(vi == valueInterface{} || vi.node.kind == basicLit && vi.node.typ.cat == nilT)
} else {
dest(f).SetBool(v.IsNil())
}
return tnext
}
} else {
@@ -3136,9 +3365,9 @@ func isNotNil(n *node) {
fnext := getExec(n.fnext)
if c0.typ.cat == interfaceT {
n.exec = func(f *frame) bltn {
vi := value(f).Interface().(valueInterface)
if (vi == valueInterface{} ||
vi.node.kind == basicLit && vi.node.typ.cat == nilT) {
v := value(f)
vi, ok := v.Interface().(valueInterface)
if ok && (vi == valueInterface{} || vi.node.kind == basicLit && vi.node.typ.cat == nilT) || v.IsNil() {
dest(f).SetBool(false)
return fnext
}
@@ -3158,7 +3387,12 @@ func isNotNil(n *node) {
} else {
if c0.typ.cat == interfaceT {
n.exec = func(f *frame) bltn {
dest(f).SetBool(!(value(f).Interface().(valueInterface) == valueInterface{}))
v := value(f)
if vi, ok := v.Interface().(valueInterface); ok {
dest(f).SetBool(!(vi == valueInterface{} || vi.node.kind == basicLit && vi.node.typ.cat == nilT))
} else {
dest(f).SetBool(!v.IsNil())
}
return tnext
}
} else {

View File

@@ -72,26 +72,28 @@ type symbol struct {
// execution to the index in frame, created exactly from the types layout.
//
type scope struct {
anc *scope // Ancestor upper scope
anc *scope // ancestor upper scope
child []*scope // included scopes
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
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)
iota int // iota value in this scope
}
// push creates a new scope and chain it to the current one.
// push creates a new child scope and chain it to the current one.
func (s *scope) push(indirect bool) *scope {
sc := scope{anc: s, level: s.level, sym: map[string]*symbol{}}
sc := &scope{anc: s, level: s.level, sym: map[string]*symbol{}}
s.child = append(s.child, sc)
if indirect {
sc.types = []reflect.Type{}
sc.level = s.level + 1
} else {
// propagate size, types, def and global as scopes at same level share the same frame
// Propagate size, types, def and global as scopes at same level share the same frame.
sc.types = s.types
sc.def = s.def
sc.global = s.global
@@ -99,7 +101,7 @@ func (s *scope) push(indirect bool) *scope {
}
// inherit loop state and pkgID from ancestor
sc.loop, sc.loopRestart, sc.pkgID = s.loop, s.loopRestart, s.pkgID
return &sc
return sc
}
func (s *scope) pushBloc() *scope { return s.push(false) }
@@ -107,12 +109,20 @@ func (s *scope) pushFunc() *scope { return s.push(true) }
func (s *scope) pop() *scope {
if s.level == s.anc.level {
// propagate size and types, as scopes at same level share the same frame
// Propagate size and types, as scopes at same level share the same frame.
s.anc.types = s.types
}
return s.anc
}
func (s *scope) upperLevel() *scope {
level := s.level
for s != nil && s.level == level {
s = s.anc
}
return s
}
// lookup searches for a symbol in the current scope, and upper ones if not found
// it returns the symbol, the number of indirections level from the current scope
// and status (false if no result).
@@ -130,6 +140,20 @@ 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) {

View File

@@ -557,7 +557,8 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
if v, ok := pkg[name]; ok {
t.cat = valueT
t.rtype = v.Type()
if isBinType(v) { // a bin type is encoded as a pointer on nil value
if isBinType(v) {
// A bin type is encoded as a pointer on a typed nil value.
t.rtype = t.rtype.Elem()
}
} else {
@@ -736,7 +737,7 @@ func (t *itype) finalize() (*itype, error) {
}
// ReferTo returns true if the type contains a reference to a
// full type name. It allows to asses a type recursive status.
// full type name. It allows to assess a type recursive status.
func (t *itype) referTo(name string, seen map[*itype]bool) bool {
if t.path+"/"+t.name == name {
return true

View File

@@ -217,6 +217,7 @@ var binaryOpPredicates = opPredicates{
// binaryExpr type checks a binary expression.
func (check typecheck) binaryExpr(n *node) error {
c0, c1 := n.child[0], n.child[1]
a := n.action
if isAssignAction(a) {
a--
@@ -226,6 +227,21 @@ func (check typecheck) binaryExpr(n *node) error {
return check.shift(n)
}
switch n.action {
case aRem:
if zeroConst(c1) {
return n.cfgErrorf("invalid operation: division by zero")
}
case aQuo:
if zeroConst(c1) {
return n.cfgErrorf("invalid operation: division by zero")
}
if c0.rval.IsValid() && c1.rval.IsValid() {
// Avoid constant conversions below to ensure correct constant integer quotient.
return nil
}
}
_ = check.convertUntyped(c0, c1.typ)
_ = check.convertUntyped(c1, c0.typ)
@@ -241,16 +257,13 @@ func (check typecheck) binaryExpr(n *node) error {
if err := check.op(binaryOpPredicates, a, n, c0, t0); err != nil {
return err
}
switch n.action {
case aQuo, aRem:
if (c0.typ.untyped || isInt(t0)) && c1.typ.untyped && constant.Sign(c1.rval.Interface().(constant.Value)) == 0 {
return n.cfgErrorf("invalid operation: division by zero")
}
}
return nil
}
func zeroConst(n *node) bool {
return n.typ.untyped && constant.Sign(n.rval.Interface().(constant.Value)) == 0
}
func (check typecheck) index(n *node, max int) error {
if err := check.convertUntyped(n, &itype{cat: intT, name: "int"}); err != nil {
return err
@@ -486,9 +499,7 @@ func (check typecheck) sliceExpr(n *node) error {
case reflect.Array:
valid = true
l = t.Len()
if c.kind != indexExpr && c.kind != selectorExpr && (c.sym == nil || c.sym.kind != varSym) {
return c.cfgErrorf("cannot slice type %s", c.typ.id())
}
// TODO(marc): check addressable status of array object (i.e. composite arrays are not).
case reflect.Slice:
valid = true
case reflect.Ptr:
@@ -621,16 +632,13 @@ func (check typecheck) conversion(n *node, typ *itype) error {
if !ok {
return n.cfgErrorf("cannot convert expression of type %s to type %s", n.typ.id(), typ.id())
}
if n.typ.untyped {
if isInterface(typ) || c != nil && !isConstType(typ) {
typ = n.typ.defaultType()
}
if err := check.convertUntyped(n, typ); err != nil {
return err
}
if !n.typ.untyped || c == nil {
return nil
}
return nil
if isInterface(typ) || !isConstType(typ) {
typ = n.typ.defaultType()
}
return check.convertUntyped(n, typ)
}
type param struct {

View File

@@ -33,6 +33,30 @@ func valueOf(data []reflect.Value, i int) reflect.Value {
return reflect.Value{}
}
func genValueBinMethodOnInterface(n *node, defaultGen func(*frame) reflect.Value) func(*frame) reflect.Value {
if n == nil || n.child == nil || n.child[0] == nil ||
n.child[0].child == nil || n.child[0].child[0] == nil {
return defaultGen
}
if n.child[0].child[1] == nil || n.child[0].child[1].ident == "" {
return defaultGen
}
value0 := genValue(n.child[0].child[0])
return func(f *frame) reflect.Value {
val, ok := value0(f).Interface().(valueInterface)
if !ok {
return defaultGen(f)
}
typ := val.node.typ
if typ.node != nil || typ.cat != valueT {
return defaultGen(f)
}
meth, _ := typ.rtype.MethodByName(n.child[0].child[1].ident)
return meth.Func
}
}
func genValueRecvIndirect(n *node) func(*frame) reflect.Value {
v := genValueRecv(n)
return func(f *frame) reflect.Value { return v(f).Elem() }
@@ -55,6 +79,35 @@ func genValueRecv(n *node) func(*frame) reflect.Value {
}
}
func genValueBinRecv(n *node, recv *receiver) func(*frame) reflect.Value {
value := genValue(n)
binValue := genValue(recv.node)
v := func(f *frame) reflect.Value {
if def, ok := value(f).Interface().(*node); ok {
if def != nil && def.recv != nil && def.recv.val.IsValid() {
return def.recv.val
}
}
ival, _ := binValue(f).Interface().(valueInterface)
return ival.value
}
fi := recv.index
if len(fi) == 0 {
return v
}
return func(f *frame) reflect.Value {
r := v(f)
if r.Kind() == reflect.Ptr {
r = r.Elem()
}
return r.FieldByIndex(fi)
}
}
func genValueRecvInterfacePtr(n *node) func(*frame) reflect.Value {
v := genValue(n.recv.node)
fi := n.recv.index
@@ -342,6 +395,10 @@ func vInt(v reflect.Value) (i int64) {
}
func vUint(v reflect.Value) (i uint64) {
if c := vConstantValue(v); c != nil {
i, _ = constant.Uint64Val(constant.ToInt(c))
return i
}
switch v.Type().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
i = uint64(v.Int())

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract archive/tar'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract archive/zip'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract bufio'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract bytes'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract compress/bzip2'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract compress/flate'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract compress/gzip'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract compress/lzw'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract compress/zlib'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract container/heap'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract container/list'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract container/ring'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract context'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract crypto'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract crypto/aes'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract crypto/cipher'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract crypto/des'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract crypto/dsa'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract crypto/ecdsa'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract crypto/ed25519'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract crypto/elliptic'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract crypto/hmac'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract crypto/md5'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract crypto/rand'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract crypto/rc4'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract crypto/rsa'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract crypto/sha1'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract crypto/sha256'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract crypto/sha512'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract crypto/subtle'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract crypto/tls'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract crypto/x509'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract crypto/x509/pkix'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract database/sql'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract database/sql/driver'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract debug/dwarf'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract debug/elf'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract debug/gosym'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract debug/macho'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract debug/pe'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract debug/plan9obj'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract encoding'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract encoding/ascii85'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract encoding/asn1'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract encoding/base32'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract encoding/base64'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract encoding/binary'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract encoding/csv'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract encoding/gob'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract encoding/hex'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract encoding/json'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract encoding/pem'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract encoding/xml'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract errors'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract expvar'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract flag'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract fmt'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract go/ast'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract go/build'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract go/constant'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract go/doc'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract go/format'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract go/importer'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract go/parser'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract go/printer'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract go/scanner'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract go/token'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract go/types'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract hash'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract hash/adler32'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

View File

@@ -1,6 +1,6 @@
// Code generated by 'yaegi extract hash/crc32'. DO NOT EDIT.
// +build go1.15,!go1.16
// +build go1.15
package stdlib

Some files were not shown because too many files have changed in this diff Show More