Compare commits

...

34 Commits

Author SHA1 Message Date
Marc Vertes
513f5e37aa interp: fix type conversion for constant expressions
The case of a constant arithmetic expression being of float kind because
of quotient was not handled correctly. Simplify constant extraction to
reflect.Value first, then conversion to target type, and let reflect Convert
method panic if conversion is not valid.

Fixes #920.
2020-10-23 10:44:04 +02:00
Marc Vertes
9520a92241 interp: fix type in assign of shift operations
Type checking on shift operands was failing for untyped variable values.

Fix propagation of type in assignment. Optimize assignment of arithmetic
operations on variables by skipping the assign and writing directly to
destination frame value in the operator function.

Skip some slow tests when given -short test option.

Fixes #917.
2020-10-23 10:32:03 +02:00
rsteube
d47821bfaa interp: add indexExpr check for reflect.Array
In typecheck.go, type check was failing for expressions like `[2]int{0, 1}[0:]`.
```
cannot slice type [2]int
```

Fixes #914 .
2020-10-21 23:00:04 +02:00
rsteube
d7ede8ed5c interp: added sliceExpr and starExpr
Type check was failing for expression such as: `&(*tree)[node:][0]`, as in

```go
tree, _ := huffmanTreePool.Get().(*[]huffmanTree)
...

initHuffmanTree(&(*tree)[node:][0], histogram[l], -1, int16(l))
```

see c3da72aa01/brotli_bit_stream.go (L469)
2020-10-21 18:14:03 +02:00
Marc Vertes
22c63b225c interp: fix array size definition in case of forward declared constant
In type.go, the symbol lookup on constant id was not performed. Handle
the ident kind explicitely to allow that.

Fixes #911.
2020-10-21 13:08:03 +02:00
mpl
c0eaab0891 interp: fix assignable check
The assignable check used to be too strict as it lacked the property that
if an untyped const can be represented as a T, then it is assignable to T.

And we can now use that fixed check to add a missing check: in a return
statement, we now make sure that any of the returned elements are
assignable to what the signature tells us they should be.
2020-10-21 10:06:03 +02:00
rsteube
c74d050c5a interp: added kind to log for typecheck (#910) 2020-10-21 09:46:05 +02:00
Marc Vertes
804664c631 interp: fix type check in constant unary operations
In unary constant operations, the test for unsigned was defeated by
testing for int first, which is true also for unsigned. Make sure that
testing for unsigned precedes testing for int.

Fixes #907.
2020-10-19 10:32:03 +02:00
Kamil Samigullin
f6d8261a8a chore: add homebrew-tap recipe
Publish homebrew-tap recipe to https://github.com/traefik/homebrew-tap
2020-10-17 05:54:04 +02:00
Marc Vertes
68c02ce054 feature: expose extract package, previously internal 2020-10-15 18:58:03 +02:00
Marc Vertes
4b3e9ee231 feature: use environment in addition to flags to control test options
This applies to -syscall, -unsafe and -unrestricted flags with the
corresponding env variables YAEGI_SYSCALL, YAEGI_UNSAFE and
YAEGI_UNRESTRICTED, already used in the same way for the run
sub-command.
2020-10-15 18:42:04 +02:00
mpl
8916618a81 interp: API change for Symbols method
Often enough when debugging, one does not know exactly what argument
should be given to Symbols, as it's not always clear in which
scope/namespace the symbol one is looking for is stored in.

Therefore, this change enables the Symbols method to now take the empty
string as an argument, which results in all the known symbols to be
returned (keyed by import path).

As a consequence, when an non-empty argument is given, the returned
result should be similar to what we had before, except it is now
returned as the sole entry of an encompassing map.

In addition, the "binary" symbols (i.e. the ones ingested through a
Use call), are now also taken into account.
2020-10-15 10:02:04 +02:00
mpl
57b49f40d6 interp: make Symbols method take into account type objects too 2020-10-14 17:44:09 +02:00
Marc Vertes
190dade469 fix: detect a wrong number of arguments at return
Also fix error ouptut in the run command to avoid displaying twice
the same error.

Fixes #819.
2020-10-14 17:30:04 +02:00
Marc Vertes
6b652ea485 feature: provide a version sub-command
A version sub-command is implemented.
A local install target is added to Makefile, to set version from git at build.

Fixes #840.
2020-10-14 16:24:05 +02:00
Marc Vertes
473bc63588 fix: behavior of select and time ticker
There was several issues:
- access to field on pointer to struct from runtime: fix in
  lookupBinField
- assign operation was skipped when performed in a comm clause
- the direction of comm clause was wrong if a channel send operation was
  performed in a body of a receive comm clause

Fixes #884.
2020-10-14 15:50:04 +02:00
Marc Vertes
e32da38ad0 fix: append from / to runtime defined array
Check first for runtime defined array (typ.cat of valueT)
to avoid checking inexisting properties (an panic), when
deciding to use appendSlice or not.

Fixes #880.
2020-10-13 17:14:04 +02:00
Marc Vertes
b2b519c2fd fix: correctly return constant expressions in functions
In the case of a function returning a single value, a constant
result could be ignored, and the function would return a zero value.

Fixes #889.
2020-10-13 17:02:04 +02:00
Marc Vertes
9491e58920 fix: correct handling of select comm clause with empty body
Fixes #887.
2020-10-13 11:24:04 +02:00
Marc Vertes
f362237ac5 fix: perform constant type conversion when required
Fixes #893.
2020-10-12 19:06:03 +02:00
Marc Vertes
a83ec1f925 fix: allow yaegi command to interpret itself
Since the introduction of restricted stdlib and syscall symbols, the
capability of yaegi to interpret itself was broken.
The use of unrestricted symbols is now also controlled by environment
variables, to allow propagation accross nested interpreters.
The interpreter Panic symbol was not wrapped, this is fixed now.
the import path resolution was failing if the working directory was
outside of GOPATH.
The documentation and readme have been ajusted.

Fixes #890.
2020-10-09 11:48:04 +02:00
mpl
155ca4e6ad interp: support implicit-type slice of pointers of struct composite literal
Also fix same case for implicit-type maps of etc.

Fixes #883
2020-10-09 10:32:03 +02:00
mpl
ca196a5768 interp: fix implicit type for bin composite lit case
When the type is implicit, the first element in the list of children is
not the type of the composite literal, but it is one of the actual
children, so it should not be discarded.

Fixes #862
2020-10-05 16:42:03 +02:00
Marc Vertes
b78d55c66b fix: compute array type size from constant expression
Fixes #875.
2020-10-05 16:12:04 +02:00
mpl
16f5586a11 interp: apply integer division when appropriate
When working with an untyped const expression involving a division, if
the default type of the result should be an int (for example because the
default types of all the operands are ints as well), then we should make
sure that the operation that is applied is indeed an integer division,
and that the type of the result is not a float.

This is achieved by using the QUO_ASSIGN operator, instead of the QUO
operator.

This should fix several problems lurking around, and it notably fixes
one of the visible consequences, which is a systematic panic when using
the REPL as a "calculator".

This incidentally also allows us to revert what was done in
5dfc3b86dc since it now turns out it was
just a hack to fix one of the symptoms.

Fixes #864
2020-10-05 10:50:03 +02:00
Julien Breux
c3cf301c60 fix: replace playground link in docs 2020-09-30 10:26:03 +02:00
Marc Vertes
0dde990d0b feature: improve extract, add unrestricted syscalls 2020-09-29 18:42:05 +02:00
Marc Vertes
4cfeb1946e fix: concurrent eval test 2020-09-29 15:22:04 +02:00
Nicholas Wiersma
ec64b006cf fix: convert struct tags properly 2020-09-29 09:22:04 +02:00
Nicholas Wiersma
f36d4e01eb fix: yaegi os.Args should contain the script name 2020-09-28 10:54:05 +02:00
mpl
5dfc3b86dc interp: fix division for const 2020-09-28 10:42:05 +02:00
mpl
3ae01a2af3 interp: refactor doComposite cases 2020-09-22 17:26:03 +02:00
Marc Vertes
c06f83f34a fix: correct access to parameter type for variadic binary methods 2020-09-22 15:18:03 +02:00
Ludovic Fernandez
a6d9c84a30 chore: adds a downloader script. 2020-09-20 18:26:03 +02:00
509 changed files with 4884 additions and 2348 deletions

2
.gitignore vendored
View File

@@ -3,7 +3,7 @@
*.dot
.idea/
/yaegi
cmd/goexports/goexports
internal/cmd/extract/extract
example/inception/inception
_test/tmp/
/dist

View File

@@ -26,31 +26,6 @@ builds:
- goos: darwin
goarch: 386
- id: goexports
binary: goexports
main: ./cmd/goexports/
goos:
- darwin
- linux
# - windows
- freebsd
- openbsd
- solaris
goarch:
- amd64
- 386
- arm
- arm64
goarm:
- 7
- 6
- 5
ignore:
- goos: darwin
goarch: 386
changelog:
sort: asc
filters:
@@ -70,3 +45,19 @@ archives:
format: zip
files:
- LICENSE
brews:
- github:
owner: traefik
name: homebrew-tap
commit_author:
name: traefiker
email: 30906710+traefiker@users.noreply.github.com
folder: Formula
homepage: https://github.com/traefik/yaegi
description: |
Yaegi is Another Elegant Go Interpreter.
It powers executable Go scripts and plugins, in embedded interpreters
or interactive shells, on top of the Go runtime.
test: |
system "#{bin}/yaegi version"

View File

@@ -3,21 +3,28 @@ check:
golangci-lint run
# Generate stdlib/syscall/syscall_GOOS_GOARCH.go for all platforms
gen_all_syscall: cmd/goexports/goexports
@cd stdlib/syscall && \
for v in $$(go tool dist list); do \
gen_all_syscall: internal/cmd/extract/extract
@for v in $$(go tool dist list); do \
echo syscall_$${v%/*}_$${v#*/}.go; \
GOOS=$${v%/*} GOARCH=$${v#*/} go generate; \
GOOS=$${v%/*} GOARCH=$${v#*/} go generate ./stdlib/syscall ./stdlib/unrestricted; \
done
cmd/goexports/goexports: cmd/goexports/goexports.go
go generate cmd/goexports/goexports.go
internal/cmd/extract/extract:
rm -f internal/cmd/extract/extract
go generate ./internal/cmd/extract
generate: gen_all_syscall
go generate
tests:
GO111MODULE=off go test -v ./...
GO111MODULE=off go test -race ./interp
install:
GOFLAGS=-ldflags=-X=main.version=$$(git describe --tags) go install ./...
.PHONY: check gen_all_syscall gen_tests
tests:
go test -v ./...
go test -race ./interp
# https://github.com/goreleaser/godownloader
install.sh: .goreleaser.yml
godownloader --repo=traefik/yaegi -o install.sh .goreleaser.yml
.PHONY: check gen_all_syscall gen_tests generate_downloader internal/cmd/extract/extract install

View File

@@ -37,6 +37,12 @@ go get -u github.com/traefik/yaegi/cmd/yaegi
Note that you can use [rlwrap](https://github.com/hanslub42/rlwrap) (install with your favorite package manager),
and alias the `yaegi` command in `alias yaegi='rlwrap yaegi'` in your `~/.bashrc`, to have history and command line edition.
### CI Integration
```bash
curl -sfL https://raw.githubusercontent.com/traefik/yaegi/master/install.sh | bash -s -- -b $GOPATH/bin v0.9.0
```
## Usage
### As an embedded interpreter
@@ -68,7 +74,7 @@ func main() {
}
```
[Go Playground](https://play.golang.org/p/zzvw4VlerLP)
[Go Playground](https://play.golang.org/p/2n-EpZbMYI9)
### As a dynamic extension framework
@@ -106,7 +112,7 @@ func main() {
}
```
[Go Playground](https://play.golang.org/p/6SEAoaO7n0U)
[Go Playground](https://play.golang.org/p/WvwH4JqrU-p)
### As a command-line interpreter
@@ -122,10 +128,20 @@ Hello World
>
```
Or interpret Go files:
Note that in interactive mode, all stdlib package are pre-imported,
you can use them directly:
```console
$ yaegi cmd/yaegi/yaegi.go
$ yaegi
> reflect.TypeOf(time.Date)
: func(int, time.Month, int, int, int, int, int, *time.Location) time.Time
>
```
Or interpret Go packages, directories or files, including itself:
```console
$ yaegi -syscall -unsafe -unrestricted github.com/traefik/yaegi/cmd/yaegi
>
```

17
_test/a43.go Normal file
View File

@@ -0,0 +1,17 @@
package main
import "fmt"
type T [l1 + l2]int
const (
l1 = 2
l2 = 3
)
func main() {
fmt.Println(T{})
}
// Output:
// [0 0 0 0 0]

12
_test/a44.go Normal file
View File

@@ -0,0 +1,12 @@
package main
var a = [max]int{}
const max = 32
func main() {
println(len(a))
}
// Output:
// 32

22
_test/addr1.go Normal file
View File

@@ -0,0 +1,22 @@
package main
import "fmt"
type T struct {
A int
B int
}
func main() {
a := &[]T{
{1, 2},
{3, 4},
}
fmt.Println("a:", a)
x := &(*a)[1:][0]
fmt.Println("x:", x)
}
// Output:
// a: &[{1 2} {3 4}]
// x: &{3 4}

15
_test/append1.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import (
"bufio"
"bytes"
)
func main() {
s := bufio.NewScanner(bytes.NewReader([]byte("Hello\nTest\nLine3")))
s.Scan()
println(string(append(s.Bytes(), []byte(" World")...)))
}
// Output:
// Hello World

15
_test/append2.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import (
"bufio"
"bytes"
)
func main() {
s := bufio.NewScanner(bytes.NewReader([]byte("Hello\nTest\nLine3")))
s.Scan()
println(string(append(s.Bytes(), " World"...)))
}
// Output:
// Hello World

15
_test/assign16.go Normal file
View File

@@ -0,0 +1,15 @@
package main
type H struct {
bits uint
}
func main() {
h := &H{8}
var x uint = (1 << h.bits) >> 6
println(x)
}
// Output:
// 4

View File

@@ -0,0 +1,17 @@
package main
import (
"fmt"
"image"
)
func main() {
v := map[string]*image.Point{
"foo": {X: 3, Y: 2},
"bar": {X: 4, Y: 5},
}
fmt.Println(v["foo"], v["bar"])
}
// Output:
// (3,2) (4,5)

View File

@@ -0,0 +1,17 @@
package main
import (
"fmt"
"image"
)
func main() {
v := []*image.Point{
{X: 3, Y: 2},
{X: 4, Y: 5},
}
fmt.Println(v)
}
// Output:
// [(3,2) (4,5)]

16
_test/binstruct_slice0.go Normal file
View File

@@ -0,0 +1,16 @@
package main
import (
"fmt"
"image"
)
func main() {
v := []image.Point{
{X: 3, Y: 2},
}
fmt.Println(v)
}
// Output:
// [(3,2)]

19
_test/composite12.go Normal file
View File

@@ -0,0 +1,19 @@
package main
type A struct {
C D
}
type D struct {
E string
}
func main() {
a := A{}
a.C = D{"bb"}
println(a.C.E)
}
// Output:
// bb

19
_test/composite13.go Normal file
View File

@@ -0,0 +1,19 @@
package main
type A struct {
C D
}
type D struct {
E string
}
func main() {
a := A{}
a.C = D{E: "bb"}
println(a.C.E)
}
// Output:
// bb

16
_test/composite8bis.go Normal file
View File

@@ -0,0 +1,16 @@
package main
type T struct{ I int }
func main() {
t := []*T{}
s := []int{1, 2}
for _, e := range s {
x := &T{I: e}
t = append(t, x)
}
println(t[0].I, t[1].I)
}
// Output:
// 1 2

12
_test/const16.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import (
"fmt"
)
func main() {
fmt.Println(7 / 3)
}
// Output:
// 2

14
_test/const17.go Normal file
View File

@@ -0,0 +1,14 @@
package main
import "fmt"
var t [7/3]int
func main() {
t[0] = 3/2
t[1] = 5/2
fmt.Println(t)
}
// Output:
// [1 2]

11
_test/const18.go Normal file
View File

@@ -0,0 +1,11 @@
package main
import "time"
func main() {
a := int64(time.Second)
println(a)
}
// Output:
// 1000000000

17
_test/const19.go Normal file
View File

@@ -0,0 +1,17 @@
package main
import (
"fmt"
"time"
)
func get10Hours() time.Duration {
return 10 * time.Hour
}
func main() {
fmt.Println(get10Hours().String())
}
// Output:
// 10h0m0s

12
_test/const20.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import "fmt"
const maxLen = int64(int(^uint(0) >> 1))
func main() {
fmt.Println(maxLen)
}
// Output:
// 9223372036854775807

12
_test/const21.go Normal file
View File

@@ -0,0 +1,12 @@
package main
const a = 64
var b uint = a * a / 2
func main() {
println(b)
}
// Output:
// 2048

10
_test/fun23.go Normal file
View File

@@ -0,0 +1,10 @@
package main
func f(x int) { return x }
func main() {
print("hello")
}
// Error:
// 3:17: too many arguments to return

10
_test/fun24.go Normal file
View File

@@ -0,0 +1,10 @@
package main
func f(x int) (int, int) { return x, "foo" }
func main() {
print("hello")
}
// Error:
// cannot use "foo" (type stringT) as type intT in return argument

10
_test/fun25.go Normal file
View File

@@ -0,0 +1,10 @@
package main
func f(x string) (a int, b int) { return x, 5 }
func main() {
print("hello")
}
// Error:
// cannot use x (type stringT) as type intT in return argument

23
_test/issue-880.go Normal file
View File

@@ -0,0 +1,23 @@
package main
import (
"bufio"
"bytes"
)
func main() {
var buf1 = make([]byte, 1024)
var buf2 []byte
buf1 = []byte("Hallo\nTest\nLine3")
s := bufio.NewScanner(bytes.NewReader(buf1))
for s.Scan() {
buf2 = append(buf2, append(s.Bytes(), []byte("\n")...)...)
}
print(string(buf2))
}
// Output:
// Hallo
// Test
// Line3

7
_test/m2/m2_test.go Normal file
View File

@@ -0,0 +1,7 @@
package m2
import "testing"
func TestM2(t *testing.T) {
t.Errorf("got %s, want %s", "AAA", "BBB")
}

View File

@@ -1,18 +1,20 @@
package main
import "time"
import "fmt"
import (
"fmt"
"time"
)
func main() {
c1 := make(chan string)
c2 := make(chan string)
go func() {
time.Sleep(1e9)
time.Sleep(1e7)
c1 <- "one"
}()
go func() {
time.Sleep(2e9)
time.Sleep(2e7)
c2 <- "two"
}()

44
_test/select14.go Normal file
View File

@@ -0,0 +1,44 @@
package main
import (
"fmt"
"time"
)
const (
period = 100 * time.Millisecond
precision = 5 * time.Millisecond
)
func main() {
counter := 0
p := time.Now()
ticker := time.NewTicker(period)
ch := make(chan int)
go func() {
for i := 0; i < 3; i++ {
select {
case t := <-ticker.C:
counter = counter + 1
ch <- counter
if d := t.Sub(p) - period; d < -precision || d > precision {
fmt.Println("wrong delay", d)
}
p = t
}
}
ch <- 0
}()
for c := range ch {
if c == 0 {
break
}
println(c)
}
}
// Output:
// 1
// 2
// 3

23
_test/select15.go Normal file
View File

@@ -0,0 +1,23 @@
package main
type T struct {
c1 chan string
c2 chan string
}
func main() {
t := &T{}
t.c2 = make(chan string)
go func(c chan string) { c <- "done" }(t.c2)
select {
case msg := <-t.c1:
println("received from c1:", msg)
case <-t.c2:
}
println("Bye")
}
// Output:
// Bye

11
_test/slice.go Normal file
View File

@@ -0,0 +1,11 @@
package main
import "fmt"
func main() {
a := [2][2]int{{0, 1}, {2, 3}}
fmt.Println(a[0][0:])
}
// Output:
// [0 1]

24
_test/struct58.go Normal file
View File

@@ -0,0 +1,24 @@
package main
import (
"fmt"
"reflect"
)
type A struct {
Test string `tag:"test"`
}
func main() {
a := A{}
t := reflect.TypeOf(a)
f, ok := t.FieldByName("Test")
if !ok {
return
}
fmt.Println(f.Tag.Get("tag"))
}
// Output:
// test

16
_test/variadic10.go Normal file
View File

@@ -0,0 +1,16 @@
package main
import (
"log"
"os"
)
func main() {
logger := log.New(os.Stdout, "test ", log.Lmsgprefix)
logger.Printf("args: %v %v", 1, "truc")
logger.Printf("args: %v %v %v", 1, "truc", 2)
}
// Output:
// test args: 1 truc
// test args: 1 truc 2

View File

@@ -1,137 +0,0 @@
//go:generate go build
/*
Goexports generates wrappers of package exported symbols.
Output files are written in the current directory, and prefixed with the go version.
Usage:
goexports package...
Example:
goexports github.com/traefik/yaegi/interp
The same goexport program is used for all target operating systems and architectures.
The GOOS and GOARCH environment variables set the desired target.
*/
package main
import (
"bufio"
"bytes"
"flag"
"fmt"
"io"
"log"
"os"
"path"
"runtime"
"strings"
"github.com/traefik/yaegi/extract"
)
// genLicense generates the correct LICENSE header text from the provided
// path to a LICENSE file.
func genLicense(fname string) (string, error) {
if fname == "" {
return "", nil
}
f, err := os.Open(fname)
if err != nil {
return "", fmt.Errorf("could not open LICENSE file: %v", err)
}
defer func() { _ = f.Close() }()
license := new(strings.Builder)
sc := bufio.NewScanner(f)
for sc.Scan() {
txt := sc.Text()
if txt != "" {
txt = " " + txt
}
license.WriteString("//" + txt + "\n")
}
if sc.Err() != nil {
return "", fmt.Errorf("could not scan LICENSE file: %v", err)
}
return license.String(), nil
}
var (
licenseFlag = flag.String("license", "", "path to a LICENSE file")
// TODO: deal with a module that has several packages (so there's only one go.mod file at the root of the project).
importPathFlag = flag.String("import_path", "", "the namespace for the symbols extracted from the argument. Not needed if the argument is from the stdlib, or if the name can be found in a go.mod")
)
func main() {
flag.Parse()
if flag.NArg() == 0 {
flag.Usage()
log.Fatalf("missing package path")
}
license, err := genLicense(*licenseFlag)
if err != nil {
log.Fatal(err)
}
wd, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
ext := extract.Extractor{
Dest: path.Base(wd),
License: license,
}
goos, goarch := os.Getenv("GOOS"), os.Getenv("GOARCH")
skip := map[string]bool{}
if goos == "solaris" {
skip["syscall.RawSyscall6"] = true
skip["syscall.Syscall6"] = true
}
ext.Skip = skip
for _, pkgIdent := range flag.Args() {
var buf bytes.Buffer
importPath, err := ext.Extract(pkgIdent, *importPathFlag, &buf)
if err != nil {
log.Println(err)
continue
}
var oFile string
if pkgIdent == "syscall" {
oFile = strings.ReplaceAll(importPath, "/", "_") + "_" + goos + "_" + goarch + ".go"
} else {
oFile = strings.ReplaceAll(importPath, "/", "_") + ".go"
}
prefix := runtime.Version()
if runtime.Version() != "devel" {
parts := strings.Split(runtime.Version(), ".")
prefix = parts[0] + "_" + extract.GetMinor(parts[1])
}
f, err := os.Create(prefix + "_" + oFile)
if err != nil {
log.Fatal(err)
}
if _, err := io.Copy(f, &buf); err != nil {
_ = f.Close()
log.Fatal(err)
}
if err := f.Close(); err != nil {
log.Fatal(err)
}
}
}

View File

@@ -15,11 +15,15 @@ import (
func extractCmd(arg []string) error {
var licensePath string
var importPath string
var name string
var exclude string
var include string
eflag := flag.NewFlagSet("run", flag.ContinueOnError)
eflag.StringVar(&licensePath, "license", "", "path to a LICENSE file")
eflag.StringVar(&importPath, "import_path", "", "the namespace for the extracted symbols")
eflag.StringVar(&name, "name", "", "the namespace for the extracted symbols")
eflag.StringVar(&exclude, "exclude", "", "comma separated list of regexp matching symbols to exclude")
eflag.StringVar(&include, "include", "", "comma separated list of regexp matching symbols to include")
eflag.Usage = func() {
fmt.Println("Usage: yaegi extract [options] packages...")
fmt.Println("Options:")
@@ -45,20 +49,32 @@ func extractCmd(arg []string) error {
return err
}
if name == "" {
name = path.Base(wd)
}
ext := extract.Extractor{
Dest: path.Base(wd),
Dest: name,
License: license,
}
if exclude != "" {
ext.Exclude = strings.Split(exclude, ",")
}
if include != "" {
ext.Include = strings.Split(include, ",")
}
r := strings.NewReplacer("/", "-", ".", "_")
for _, pkgIdent := range args {
var buf bytes.Buffer
importPath, err := ext.Extract(pkgIdent, importPath, &buf)
importPath, err := ext.Extract(pkgIdent, name, &buf)
if err != nil {
fmt.Fprintln(os.Stderr, err)
continue
}
oFile := strings.ReplaceAll(importPath, "/", "_") + ".go"
oFile := r.Replace(importPath) + ".go"
f, err := os.Create(oFile)
if err != nil {
return err

View File

@@ -14,6 +14,7 @@ The commands are:
help print usage information
run execute a Go program from source
test execute test functions in a Go package
version print version
Use "yaegi help <command>" for more information about a command.
@@ -37,6 +38,9 @@ func help(arg []string) error {
return run([]string{"-h"})
case Test:
return test([]string{"-h"})
case Version:
fmt.Println("Usage: yaegi version")
return nil
default:
return fmt.Errorf("help: invalid yaegi command: %v", cmd)
}

View File

@@ -6,6 +6,7 @@ import (
"go/build"
"io/ioutil"
"os"
"strconv"
"strings"
"github.com/traefik/yaegi/interp"
@@ -17,19 +18,21 @@ import (
func run(arg []string) error {
var interactive bool
var useSyscall bool
var useUnrestricted bool
var useUnsafe bool
var tags string
var cmd string
var err error
// The following flags are initialized from environment.
useSyscall, _ := strconv.ParseBool(os.Getenv("YAEGI_SYSCALL"))
useUnrestricted, _ := strconv.ParseBool(os.Getenv("YAEGI_UNRESTRICTED"))
useUnsafe, _ := strconv.ParseBool(os.Getenv("YAEGI_UNSAFE"))
rflag := flag.NewFlagSet("run", flag.ContinueOnError)
rflag.BoolVar(&interactive, "i", false, "start an interactive REPL")
rflag.BoolVar(&useSyscall, "syscall", false, "include syscall symbols")
rflag.BoolVar(&useUnrestricted, "unrestricted", false, "include unrestricted symbols")
rflag.BoolVar(&useSyscall, "syscall", useSyscall, "include syscall symbols")
rflag.BoolVar(&useUnrestricted, "unrestricted", useUnrestricted, "include unrestricted symbols")
rflag.StringVar(&tags, "tags", "", "set a list of build tags")
rflag.BoolVar(&useUnsafe, "unsafe", false, "include usafe symbols")
rflag.BoolVar(&useUnsafe, "unsafe", useUnsafe, "include unsafe symbols")
rflag.StringVar(&cmd, "e", "", "set the command to be executed (instead of script or/and shell)")
rflag.Usage = func() {
fmt.Println("Usage: yaegi run [options] [path] [args]")
@@ -46,31 +49,40 @@ func run(arg []string) error {
i.Use(interp.Symbols)
if useSyscall {
i.Use(syscall.Symbols)
// Using a environment var allows a nested interpreter to import the syscall package.
if err := os.Setenv("YAEGI_SYSCALL", "1"); err != nil {
return err
}
}
if useUnsafe {
i.Use(unsafe.Symbols)
if err := os.Setenv("YAEGI_UNSAFE", "1"); err != nil {
return err
}
}
if useUnrestricted {
// Use of unrestricted symbols should always follow use of stdlib symbols, to update them.
// Use of unrestricted symbols should always follow stdlib and syscall symbols, to update them.
i.Use(unrestricted.Symbols)
if err := os.Setenv("YAEGI_UNRESTRICTED", "1"); err != nil {
return err
}
}
if cmd != "" {
_, err = i.Eval(cmd)
showError(err)
}
if len(args) == 0 {
if interactive || cmd == "" {
_, err = i.REPL()
showError(err)
_, err = i.REPL()
}
return err
}
// Skip first os arg to set command line as expected by interpreted main
path := args[0]
os.Args = arg[1:]
os.Args = arg
flag.CommandLine = flag.NewFlagSet(path, flag.ExitOnError)
if isFile(path) {
@@ -78,7 +90,6 @@ func run(arg []string) error {
} else {
_, err = i.EvalPath(path)
}
showError(err)
if err != nil {
return err
@@ -86,7 +97,6 @@ func run(arg []string) error {
if interactive {
_, err = i.REPL()
showError(err)
}
return err
}

View File

@@ -1,11 +1,13 @@
package main
import (
"errors"
"flag"
"fmt"
"go/build"
"os"
"regexp"
"strconv"
"strings"
"testing"
@@ -27,13 +29,15 @@ func test(arg []string) (err error) {
run string
short bool
tags string
useUnrestricted bool
useUnsafe bool
useSyscall bool
timeout string
verbose bool
)
// The following flags are initialized from environment.
useSyscall, _ := strconv.ParseBool(os.Getenv("YAEGI_SYSCALL"))
useUnrestricted, _ := strconv.ParseBool(os.Getenv("YAEGI_UNRESTRICTED"))
useUnsafe, _ := strconv.ParseBool(os.Getenv("YAEGI_UNSAFE"))
tflag := flag.NewFlagSet("test", flag.ContinueOnError)
tflag.StringVar(&bench, "bench", "", "Run only those benchmarks matching a regular expression.")
tflag.BoolVar(&benchmem, "benchmem", false, "Print memory allocation statistics for benchmarks.")
@@ -45,9 +49,9 @@ func test(arg []string) (err error) {
tflag.BoolVar(&short, "short", false, "Tell long-running tests to shorten their run time.")
tflag.StringVar(&tags, "tags", "", "Set a list of build tags.")
tflag.StringVar(&timeout, "timeout", "", "If a test binary runs longer than duration d, panic.")
tflag.BoolVar(&useUnrestricted, "unrestricted", false, "Include unrestricted symbols.")
tflag.BoolVar(&useUnsafe, "unsafe", false, "Include usafe symbols.")
tflag.BoolVar(&useSyscall, "syscall", false, "Include syscall symbols.")
tflag.BoolVar(&useUnrestricted, "unrestricted", useUnrestricted, "Include unrestricted symbols.")
tflag.BoolVar(&useUnsafe, "unsafe", useUnsafe, "Include usafe symbols.")
tflag.BoolVar(&useSyscall, "syscall", useSyscall, "Include syscall symbols.")
tflag.BoolVar(&verbose, "v", false, "Verbose output: log all tests as they are run.")
tflag.Usage = func() {
fmt.Println("Usage: yaegi test [options] [path]")
@@ -104,12 +108,22 @@ func test(arg []string) (err error) {
i.Use(interp.Symbols)
if useSyscall {
i.Use(syscall.Symbols)
// Using a environment var allows a nested interpreter to import the syscall package.
if err := os.Setenv("YAEGI_SYSCALL", "1"); err != nil {
return err
}
}
if useUnrestricted {
i.Use(unrestricted.Symbols)
if err := os.Setenv("YAEGI_UNRESTRICTED", "1"); err != nil {
return err
}
}
if useUnsafe {
i.Use(unsafe.Symbols)
if err := os.Setenv("YAEGI_UNSAFE", "1"); err != nil {
return err
}
}
if err = i.EvalTest(path); err != nil {
return err
@@ -117,7 +131,11 @@ func test(arg []string) (err error) {
benchmarks := []testing.InternalBenchmark{}
tests := []testing.InternalTest{}
for name, sym := range i.Symbols(path) {
syms, ok := i.Symbols(path)[path]
if !ok {
return errors.New("No tests found")
}
for name, sym := range syms {
switch fun := sym.Interface().(type) {
case func(*testing.B):
benchmarks = append(benchmarks, testing.InternalBenchmark{name, fun})

View File

@@ -71,7 +71,16 @@ Options:
-unsafe
include unsafe symbols.
Debugging support (may be removed at any time):
Environment variables:
YAEGI_SYSCALL=1
Include syscall symbols (same as -syscall flag).
YAEGI_UNRESTRICTED=1
Include unrestricted symbols (same as -unrestricted flag).
YAEGI_UNSAFE=1
Include unsafe symbols (same as -unsafe flag).
YAEGI_PROMPT=1
Force enable the printing of the REPL prompt and the result of last instruction,
even if stdin is not a terminal.
YAEGI_AST_DOT=1
Generate and display graphviz dot of AST with dotty(1)
YAEGI_CFG_DOT=1
@@ -90,6 +99,8 @@ import (
"fmt"
"log"
"os"
"github.com/traefik/yaegi/interp"
)
const (
@@ -97,8 +108,11 @@ const (
Help = "help"
Run = "run"
Test = "test"
Version = "version"
)
var version = "devel" // This may be overwritten at build time.
func main() {
var cmd string
var err error
@@ -119,6 +133,8 @@ func main() {
err = run(os.Args[2:])
case Test:
err = test(os.Args[2:])
case Version:
fmt.Println(version)
default:
// If no command is given, fallback to default "run" command.
// This allows scripts starting with "#!/usr/bin/env yaegi",
@@ -129,8 +145,10 @@ func main() {
}
if err != nil && !errors.Is(err, flag.ErrHelp) {
err = fmt.Errorf("%s: %w", cmd, err)
fmt.Fprintln(os.Stderr, err)
fmt.Fprintln(os.Stderr, fmt.Errorf("%s: %w", cmd, err))
if p, ok := err.(interp.Panic); ok {
fmt.Fprintln(os.Stderr, string(p.Stack))
}
exitCode = 1
}
os.Exit(exitCode)

View File

@@ -18,13 +18,14 @@ import (
"os"
"path"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
"text/template"
)
const model = `// Code generated by 'github.com/traefik/yaegi/extract {{.PkgName}}'. DO NOT EDIT.
const model = `// Code generated by 'yaegi extract {{.PkgName}}'. DO NOT EDIT.
{{.License}}
@@ -111,7 +112,17 @@ var restricted = map[string]bool{
"logNew": true,
}
func genContent(dest, importPath, license string, p *types.Package, skip map[string]bool) ([]byte, error) {
func matchList(name string, list []string) (match bool, err error) {
for _, re := range list {
match, err = regexp.MatchString(re, name)
if err != nil || match {
return
}
}
return
}
func (e *Extractor) genContent(importPath string, p *types.Package) ([]byte, error) {
prefix := "_" + importPath + "_"
prefix = strings.NewReplacer("/", "_", "-", "_", ".", "_").Replace(prefix)
@@ -137,11 +148,26 @@ func genContent(dest, importPath, license string, p *types.Package, skip map[str
continue
}
pname := path.Base(importPath) + "." + name
if skip[pname] {
if len(e.Include) > 0 {
match, err := matchList(name, e.Include)
if err != nil {
return nil, err
}
if !match {
// Explicitly defined include expressions force non matching symbols to be skipped.
continue
}
}
match, err := matchList(name, e.Exclude)
if err != nil {
return nil, err
}
if match {
continue
}
pname := path.Base(importPath) + "." + name
if rname := path.Base(importPath) + name; restricted[rname] {
// Restricted symbol, locally provided by stdlib wrapper.
pname = rname
@@ -150,7 +176,7 @@ func genContent(dest, importPath, license string, p *types.Package, skip map[str
switch o := o.(type) {
case *types.Const:
if b, ok := o.Type().(*types.Basic); ok && (b.Info()&types.IsUntyped) != 0 {
// convert untyped constant to right type to avoid overflow
// Convert untyped constant to right type to avoid overflow.
val[name] = Val{fixConst(pname, o.Val(), imports), false}
} else {
val[name] = Val{pname, false}
@@ -201,12 +227,18 @@ func genContent(dest, importPath, license string, p *types.Package, skip map[str
}
}
buildTags, err := buildTags()
// Generate buildTags with Go version only for stdlib packages.
// Third party packages do not depend on Go compiler version by default.
var buildTags string
if isInStdlib(importPath) {
var err error
buildTags, err = genBuildTags()
if err != nil {
return nil, err
}
}
base := template.New("goexports")
base := template.New("extract")
parse, err := base.Parse(model)
if err != nil {
return nil, fmt.Errorf("template parsing error: %v", err)
@@ -231,14 +263,14 @@ func genContent(dest, importPath, license string, p *types.Package, skip map[str
b := new(bytes.Buffer)
data := map[string]interface{}{
"Dest": dest,
"Dest": e.Dest,
"Imports": imports,
"PkgName": importPath,
"Val": val,
"Typ": typ,
"Wrap": wrap,
"BuildTags": buildTags,
"License": license,
"License": e.License,
}
err = parse.Execute(b, data)
if err != nil {
@@ -288,12 +320,20 @@ func fixConst(name string, val constant.Value, imports map[string]bool) string {
return fmt.Sprintf("constant.MakeFromLiteral(%q, token.%s, 0)", str, tok)
}
// Extractor creates a package with all the symbols from a dependency package.
type Extractor struct {
Dest string // The name of the created package.
License string // License text to be included in the created package, optional.
Exclude []string // Comma separated list of regexp matching symbols to exclude.
Include []string // Comma separated list of regexp matching symbols to include.
}
// importPath checks whether pkgIdent is an existing directory relative to
// e.WorkingDir. If yes, it returns the actual import path of the Go package
// located in the directory. If it is definitely a relative path, but it does not
// exist, an error is returned. Otherwise, it is assumed to be an import path, and
// pkgIdent is returned.
func (e Extractor) importPath(pkgIdent, importPath string) (string, error) {
func (e *Extractor) importPath(pkgIdent, importPath string) (string, error) {
wd, err := os.Getwd()
if err != nil {
return "", err
@@ -353,20 +393,13 @@ func (e Extractor) importPath(pkgIdent, importPath string) (string, error) {
return parts[1], nil
}
// Extractor creates a package with all the symbols from a dependency package.
type Extractor struct {
Dest string // the name of the created package.
License string // license text to be included in the created package, optional.
Skip map[string]bool
}
// Extract writes to rw a Go package with all the symbols found at pkgIdent.
// pkgIdent can be an import path, or a local path, relative to e.WorkingDir. In
// the latter case, Extract returns the actual import path of the package found at
// pkgIdent, otherwise it just returns pkgIdent.
// If pkgIdent is an import path, it is looked up in GOPATH. Vendoring is not
// supported yet, and the behavior is only defined for GO111MODULE=off.
func (e Extractor) Extract(pkgIdent, importPath string, rw io.Writer) (string, error) {
func (e *Extractor) Extract(pkgIdent, importPath string, rw io.Writer) (string, error) {
ipp, err := e.importPath(pkgIdent, importPath)
if err != nil {
return "", err
@@ -377,7 +410,7 @@ func (e Extractor) Extract(pkgIdent, importPath string, rw io.Writer) (string, e
return "", err
}
content, err := genContent(e.Dest, ipp, e.License, pkg, e.Skip)
content, err := e.genContent(ipp, pkg)
if err != nil {
return "", err
}
@@ -403,7 +436,7 @@ func GetMinor(part string) string {
return minor
}
func buildTags() (string, error) {
func genBuildTags() (string, error) {
version := runtime.Version()
if version == "devel" {
return "", nil
@@ -423,3 +456,5 @@ func buildTags() (string, error) {
return currentGoVersion + ",!" + nextGoVersion, nil
}
func isInStdlib(path string) bool { return !strings.Contains(path, ".") }

View File

@@ -8,9 +8,7 @@ import (
"testing"
)
var expectedOutput = `// Code generated by 'github.com/traefik/yaegi/extract guthib.com/baz'. DO NOT EDIT.
// +build BUILD_TAGS
var expectedOutput = `// Code generated by 'yaegi extract guthib.com/baz'. DO NOT EDIT.
package bar
@@ -27,14 +25,6 @@ func init() {
}
`
func init() {
buildTags, err := buildTags()
if err != nil {
panic(err)
}
expectedOutput = strings.Replace(expectedOutput, "BUILD_TAGS", buildTags, 1)
}
func TestPackages(t *testing.T) {
testCases := []struct {
desc string

View File

@@ -1,7 +1,7 @@
package yaegi
//go:generate go generate github.com/traefik/yaegi/internal/cmd/extract
//go:generate go generate github.com/traefik/yaegi/interp
//go:generate go generate github.com/traefik/yaegi/cmd/goexports
//go:generate go generate github.com/traefik/yaegi/stdlib
//go:generate go generate github.com/traefik/yaegi/stdlib/syscall
//go:generate go generate github.com/traefik/yaegi/stdlib/unsafe

401
install.sh Normal file
View File

@@ -0,0 +1,401 @@
#!/bin/sh
set -e
# Code generated by godownloader on 2020-09-27T12:53:27Z. DO NOT EDIT.
#
usage() {
this=$1
cat <<EOF
$this: download go binaries for traefik/yaegi
Usage: $this [-b] bindir [-d] [tag]
-b sets bindir or installation directory, Defaults to ./bin
-d turns on debug logging
[tag] is a tag from
https://github.com/traefik/yaegi/releases
If tag is missing, then the latest will be used.
Generated by godownloader
https://github.com/goreleaser/godownloader
EOF
exit 2
}
parse_args() {
#BINDIR is ./bin unless set be ENV
# over-ridden by flag below
BINDIR=${BINDIR:-./bin}
while getopts "b:dh?x" arg; do
case "$arg" in
b) BINDIR="$OPTARG" ;;
d) log_set_priority 10 ;;
h | \?) usage "$0" ;;
x) set -x ;;
esac
done
shift $((OPTIND - 1))
TAG=$1
}
# this function wraps all the destructive operations
# if a curl|bash cuts off the end of the script due to
# network, either nothing will happen or will syntax error
# out preventing half-done work
execute() {
tmpdir=$(mktemp -d)
log_debug "downloading files into ${tmpdir}"
http_download "${tmpdir}/${TARBALL}" "${TARBALL_URL}"
http_download "${tmpdir}/${CHECKSUM}" "${CHECKSUM_URL}"
hash_sha256_verify "${tmpdir}/${TARBALL}" "${tmpdir}/${CHECKSUM}"
srcdir="${tmpdir}"
(cd "${tmpdir}" && untar "${TARBALL}")
test ! -d "${BINDIR}" && install -d "${BINDIR}"
for binexe in $BINARIES; do
if [ "$OS" = "windows" ]; then
binexe="${binexe}.exe"
fi
install "${srcdir}/${binexe}" "${BINDIR}/"
log_info "installed ${BINDIR}/${binexe}"
done
rm -rf "${tmpdir}"
}
get_binaries() {
case "$PLATFORM" in
darwin/amd64) BINARIES="yaegi" ;;
darwin/arm64) BINARIES="yaegi" ;;
darwin/armv5) BINARIES="yaegi" ;;
darwin/armv6) BINARIES="yaegi" ;;
darwin/armv7) BINARIES="yaegi" ;;
freebsd/386) BINARIES="yaegi" ;;
freebsd/amd64) BINARIES="yaegi" ;;
freebsd/arm64) BINARIES="yaegi" ;;
freebsd/armv5) BINARIES="yaegi" ;;
freebsd/armv6) BINARIES="yaegi" ;;
freebsd/armv7) BINARIES="yaegi" ;;
linux/386) BINARIES="yaegi" ;;
linux/amd64) BINARIES="yaegi" ;;
linux/arm64) BINARIES="yaegi" ;;
linux/armv5) BINARIES="yaegi" ;;
linux/armv6) BINARIES="yaegi" ;;
linux/armv7) BINARIES="yaegi" ;;
openbsd/386) BINARIES="yaegi" ;;
openbsd/amd64) BINARIES="yaegi" ;;
openbsd/arm64) BINARIES="yaegi" ;;
openbsd/armv5) BINARIES="yaegi" ;;
openbsd/armv6) BINARIES="yaegi" ;;
openbsd/armv7) BINARIES="yaegi" ;;
solaris/386) BINARIES="yaegi" ;;
solaris/amd64) BINARIES="yaegi" ;;
solaris/arm64) BINARIES="yaegi" ;;
solaris/armv5) BINARIES="yaegi" ;;
solaris/armv6) BINARIES="yaegi" ;;
solaris/armv7) BINARIES="yaegi" ;;
*)
log_crit "platform $PLATFORM is not supported. Make sure this script is up-to-date and file request at https://github.com/${PREFIX}/issues/new"
exit 1
;;
esac
}
tag_to_version() {
if [ -z "${TAG}" ]; then
log_info "checking GitHub for latest tag"
else
log_info "checking GitHub for tag '${TAG}'"
fi
REALTAG=$(github_release "$OWNER/$REPO" "${TAG}") && true
if test -z "$REALTAG"; then
log_crit "unable to find '${TAG}' - use 'latest' or see https://github.com/${PREFIX}/releases for details"
exit 1
fi
# if version starts with 'v', remove it
TAG="$REALTAG"
VERSION=${TAG#v}
}
adjust_format() {
# change format (tar.gz or zip) based on OS
case ${OS} in
windows) FORMAT=zip ;;
esac
true
}
adjust_os() {
# adjust archive name based on OS
true
}
adjust_arch() {
# adjust archive name based on ARCH
true
}
cat /dev/null <<EOF
------------------------------------------------------------------------
https://github.com/client9/shlib - portable posix shell functions
Public domain - http://unlicense.org
https://github.com/client9/shlib/blob/master/LICENSE.md
but credit (and pull requests) appreciated.
------------------------------------------------------------------------
EOF
is_command() {
command -v "$1" >/dev/null
}
echoerr() {
echo "$@" 1>&2
}
log_prefix() {
echo "$0"
}
_logp=6
log_set_priority() {
_logp="$1"
}
log_priority() {
if test -z "$1"; then
echo "$_logp"
return
fi
[ "$1" -le "$_logp" ]
}
log_tag() {
case $1 in
0) echo "emerg" ;;
1) echo "alert" ;;
2) echo "crit" ;;
3) echo "err" ;;
4) echo "warning" ;;
5) echo "notice" ;;
6) echo "info" ;;
7) echo "debug" ;;
*) echo "$1" ;;
esac
}
log_debug() {
log_priority 7 || return 0
echoerr "$(log_prefix)" "$(log_tag 7)" "$@"
}
log_info() {
log_priority 6 || return 0
echoerr "$(log_prefix)" "$(log_tag 6)" "$@"
}
log_err() {
log_priority 3 || return 0
echoerr "$(log_prefix)" "$(log_tag 3)" "$@"
}
log_crit() {
log_priority 2 || return 0
echoerr "$(log_prefix)" "$(log_tag 2)" "$@"
}
uname_os() {
os=$(uname -s | tr '[:upper:]' '[:lower:]')
case "$os" in
cygwin_nt*) os="windows" ;;
mingw*) os="windows" ;;
msys_nt*) os="windows" ;;
esac
echo "$os"
}
uname_arch() {
arch=$(uname -m)
case $arch in
x86_64) arch="amd64" ;;
x86) arch="386" ;;
i686) arch="386" ;;
i386) arch="386" ;;
aarch64) arch="arm64" ;;
armv5*) arch="armv5" ;;
armv6*) arch="armv6" ;;
armv7*) arch="armv7" ;;
esac
echo ${arch}
}
uname_os_check() {
os=$(uname_os)
case "$os" in
darwin) return 0 ;;
dragonfly) return 0 ;;
freebsd) return 0 ;;
linux) return 0 ;;
android) return 0 ;;
nacl) return 0 ;;
netbsd) return 0 ;;
openbsd) return 0 ;;
plan9) return 0 ;;
solaris) return 0 ;;
windows) return 0 ;;
esac
log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib"
return 1
}
uname_arch_check() {
arch=$(uname_arch)
case "$arch" in
386) return 0 ;;
amd64) return 0 ;;
arm64) return 0 ;;
armv5) return 0 ;;
armv6) return 0 ;;
armv7) return 0 ;;
ppc64) return 0 ;;
ppc64le) return 0 ;;
mips) return 0 ;;
mipsle) return 0 ;;
mips64) return 0 ;;
mips64le) return 0 ;;
s390x) return 0 ;;
amd64p32) return 0 ;;
esac
log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value. Please file bug report at https://github.com/client9/shlib"
return 1
}
untar() {
tarball=$1
case "${tarball}" in
*.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" ;;
*.tar) tar --no-same-owner -xf "${tarball}" ;;
*.zip) unzip "${tarball}" ;;
*)
log_err "untar unknown archive format for ${tarball}"
return 1
;;
esac
}
http_download_curl() {
local_file=$1
source_url=$2
header=$3
if [ -z "$header" ]; then
code=$(curl -w '%{http_code}' -sL -o "$local_file" "$source_url")
else
code=$(curl -w '%{http_code}' -sL -H "$header" -o "$local_file" "$source_url")
fi
if [ "$code" != "200" ]; then
log_debug "http_download_curl received HTTP status $code"
return 1
fi
return 0
}
http_download_wget() {
local_file=$1
source_url=$2
header=$3
if [ -z "$header" ]; then
wget -q -O "$local_file" "$source_url"
else
wget -q --header "$header" -O "$local_file" "$source_url"
fi
}
http_download() {
log_debug "http_download $2"
if is_command curl; then
http_download_curl "$@"
return
elif is_command wget; then
http_download_wget "$@"
return
fi
log_crit "http_download unable to find wget or curl"
return 1
}
http_copy() {
tmp=$(mktemp)
http_download "${tmp}" "$1" "$2" || return 1
body=$(cat "$tmp")
rm -f "${tmp}"
echo "$body"
}
github_release() {
owner_repo=$1
version=$2
test -z "$version" && version="latest"
giturl="https://github.com/${owner_repo}/releases/${version}"
json=$(http_copy "$giturl" "Accept:application/json")
test -z "$json" && return 1
version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//')
test -z "$version" && return 1
echo "$version"
}
hash_sha256() {
TARGET=${1:-/dev/stdin}
if is_command gsha256sum; then
hash=$(gsha256sum "$TARGET") || return 1
echo "$hash" | cut -d ' ' -f 1
elif is_command sha256sum; then
hash=$(sha256sum "$TARGET") || return 1
echo "$hash" | cut -d ' ' -f 1
elif is_command shasum; then
hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1
echo "$hash" | cut -d ' ' -f 1
elif is_command openssl; then
hash=$(openssl -dst openssl dgst -sha256 "$TARGET") || return 1
echo "$hash" | cut -d ' ' -f a
else
log_crit "hash_sha256 unable to find command to compute sha-256 hash"
return 1
fi
}
hash_sha256_verify() {
TARGET=$1
checksums=$2
if [ -z "$checksums" ]; then
log_err "hash_sha256_verify checksum file not specified in arg2"
return 1
fi
BASENAME=${TARGET##*/}
want=$(grep "${BASENAME}" "${checksums}" 2>/dev/null | tr '\t' ' ' | cut -d ' ' -f 1)
if [ -z "$want" ]; then
log_err "hash_sha256_verify unable to find checksum for '${TARGET}' in '${checksums}'"
return 1
fi
got=$(hash_sha256 "$TARGET")
if [ "$want" != "$got" ]; then
log_err "hash_sha256_verify checksum for '$TARGET' did not verify ${want} vs $got"
return 1
fi
}
cat /dev/null <<EOF
------------------------------------------------------------------------
End of functions from https://github.com/client9/shlib
------------------------------------------------------------------------
EOF
PROJECT_NAME="yaegi"
OWNER=traefik
REPO="yaegi"
BINARY=yaegi
FORMAT=tar.gz
OS=$(uname_os)
ARCH=$(uname_arch)
PREFIX="$OWNER/$REPO"
# use in logging routines
log_prefix() {
echo "$PREFIX"
}
PLATFORM="${OS}/${ARCH}"
GITHUB_DOWNLOAD=https://github.com/${OWNER}/${REPO}/releases/download
uname_os_check "$OS"
uname_arch_check "$ARCH"
parse_args "$@"
get_binaries
tag_to_version
adjust_format
adjust_os
adjust_arch
log_info "found version: ${VERSION} for ${TAG}/${OS}/${ARCH}"
NAME=${PROJECT_NAME}_v${VERSION}_${OS}_${ARCH}
TARBALL=${NAME}.${FORMAT}
TARBALL_URL=${GITHUB_DOWNLOAD}/${TAG}/${TARBALL}
CHECKSUM=${PROJECT_NAME}_${VERSION}_checksums.txt
CHECKSUM_URL=${GITHUB_DOWNLOAD}/${TAG}/${CHECKSUM}
execute

View File

@@ -0,0 +1,107 @@
//go:generate go build
/*
extract generates wrappers of stdlib package exported symbols. This command
is reserved for internal use in yaegi project.
For a similar purpose with third party packages, see the yaegi extract subcommand,
based on the same code.
Output files are written in the current directory, and prefixed with the go version.
Usage:
extract package...
The same program is used for all target operating systems and architectures.
The GOOS and GOARCH environment variables set the desired target.
*/
package main
import (
"bytes"
"flag"
"io"
"log"
"os"
"path"
"runtime"
"strings"
"github.com/traefik/yaegi/extract"
)
var (
exclude = flag.String("exclude", "", "comma separated list of regexp matching symbols to exclude")
include = flag.String("include", "", "comma separated list of regexp matching symbols to include")
)
func main() {
flag.Parse()
if flag.NArg() == 0 {
flag.Usage()
log.Fatalf("missing package path")
}
wd, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
ext := extract.Extractor{
Dest: path.Base(wd),
}
goos, goarch := os.Getenv("GOOS"), os.Getenv("GOARCH")
if *exclude != "" {
ext.Exclude = strings.Split(*exclude, ",")
}
if *include != "" {
ext.Include = strings.Split(*include, ",")
}
for _, pkgIdent := range flag.Args() {
var buf bytes.Buffer
if pkgIdent == "syscall" && goos == "solaris" {
// Syscall6 is broken on solaris (https://github.com/golang/go/issues/24357),
// it breaks build, skip related symbols.
ext.Exclude = append(ext.Exclude, "Syscall6")
}
importPath, err := ext.Extract(pkgIdent, "", &buf)
if err != nil {
log.Fatal(err)
}
var oFile string
if pkgIdent == "syscall" {
oFile = strings.ReplaceAll(importPath, "/", "_") + "_" + goos + "_" + goarch + ".go"
} else {
oFile = strings.ReplaceAll(importPath, "/", "_") + ".go"
}
prefix := runtime.Version()
if runtime.Version() != "devel" {
parts := strings.Split(runtime.Version(), ".")
prefix = parts[0] + "_" + extract.GetMinor(parts[1])
}
f, err := os.Create(prefix + "_" + oFile)
if err != nil {
log.Fatal(err)
}
if _, err := io.Copy(f, &buf); err != nil {
_ = f.Close()
log.Fatal(err)
}
if err := f.Close(); err != nil {
log.Fatal(err)
}
}
}

View File

@@ -196,6 +196,18 @@ func {{$name}}Const(n *node) {
{{- if $op.Shift}}
v := constant.Shift(vConstantValue(v0), token.{{tokenFromName $name}}, uint(vUint(v1)))
n.rval.Set(reflect.ValueOf(v))
{{- else if (eq $op.Name "/")}}
var operator token.Token
// When the result of the operation is expected to be an int (because both
// operands are ints), we want to force the type of the whole expression to be an
// int (and not a float), which is achieved by using the QUO_ASSIGN operator.
if n.typ.untyped && isInt(n.typ.rtype) {
operator = token.QUO_ASSIGN
} else {
operator = token.QUO
}
v := constant.BinaryOp(vConstantValue(v0), operator, vConstantValue(v1))
n.rval.Set(reflect.ValueOf(v))
{{- else}}
v := constant.BinaryOp(vConstantValue(v0), token.{{tokenFromName $name}}, vConstantValue(v1))
n.rval.Set(reflect.ValueOf(v))
@@ -395,10 +407,10 @@ func {{$name}}Const(n *node) {
case isConst:
v := constant.UnaryOp(token.{{tokenFromName $name}}, vConstantValue(v0), 0)
n.rval.Set(reflect.ValueOf(v))
case isInt(t):
n.rval.SetInt({{$op.Name}} v0.Int())
case isUint(t):
n.rval.SetUint({{$op.Name}} v0.Uint())
case isInt(t):
n.rval.SetInt({{$op.Name}} v0.Int())
{{- if $op.Float}}
case isFloat(t):
n.rval.SetFloat({{$op.Name}} v0.Float())
@@ -931,7 +943,7 @@ type Op struct {
}
func main() {
base := template.New("goexports")
base := template.New("genop")
base.Funcs(template.FuncMap{
"tokenFromName": func(name string) string {
switch name {

View File

@@ -443,10 +443,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
n.gen = nop
break
}
if n.anc.kind == commClause {
n.gen = nop
break
}
var atyp *itype
if n.nleft+n.nright < len(n.child) {
if atyp, err = nodeType(interp, sc, n.child[n.nleft]); err != nil {
@@ -543,11 +540,25 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
// which require and additional operation to set the value
break
}
if dest.action == aGetIndex {
// optimization does not work when assigning to a struct field. Maybe we're not
// setting the right frame index or something, and we would end up not writing at
// the right place. So disabling it for now.
break
}
// Skip the assign operation entirely, the source frame index is set
// to destination index, avoiding extra memory alloc and duplication.
n.gen = nop
src.findex = dest.findex
src.level = level
case n.action == aAssign && len(n.child) < 4 && !src.rval.IsValid() && isArithmeticAction(src):
// Optimize single assignments from some arithmetic operations.
// Skip the assign operation entirely, the source frame index is set
// to destination index, avoiding extra memory alloc and duplication.
src.typ = dest.typ
src.findex = dest.findex
src.level = level
n.gen = nop
case src.kind == basicLit && !src.rval.IsValid():
// Assign to nil.
src.rval = reflect.New(dest.typ.TypeOf()).Elem()
@@ -849,7 +860,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
n.gen = nop
n.findex = -1
n.typ = c0.typ
n.rval = c1.rval
n.rval = c1.rval.Convert(c0.typ.rtype)
default:
n.gen = convert
n.typ = c0.typ
@@ -976,7 +987,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
n.findex = sc.add(n.typ)
// TODO: Check that composite literal expr matches corresponding type
n.gen = compositeGenerator(n, n.typ)
n.gen = compositeGenerator(n, n.typ, nil)
case fallthroughtStmt:
if n.anc.kind != caseBody {
@@ -1123,7 +1134,6 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
sym, level, found := sc.lookup(n.ident)
if !found {
// retry with the filename, in case ident is a package name.
// TODO(mpl): maybe we improve lookup itself so it can deal with that.
sym, level, found = sc.lookup(filepath.Join(n.ident, baseName))
if !found {
err = n.cfgErrorf("undefined: %s", n.ident)
@@ -1296,7 +1306,12 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
}
case returnStmt:
if mustReturnValue(sc.def.child[2]) {
if len(n.child) > sc.def.typ.numOut() {
err = n.cfgErrorf("too many arguments to return")
break
}
returnSig := sc.def.child[2]
if mustReturnValue(returnSig) {
nret := len(n.child)
if nret == 1 && isCall(n.child[0]) {
nret = n.child[0].child[0].typ.numOut()
@@ -1310,13 +1325,19 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
n.tnext = nil
n.val = sc.def
for i, c := range n.child {
if c.typ.cat == nilT {
// 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].fieldType(i)); err != nil {
typ, err = nodeType(interp, sc, returnSig.child[1].fieldType(i))
if err != nil {
return
}
// TODO(mpl): move any of that code to typecheck?
c.typ.node = c
if !c.typ.assignableTo(typ) {
err = fmt.Errorf("cannot use %v (type %v) as type %v in return argument", c.ident, c.typ.cat, typ.cat)
return
}
if c.typ.cat == nilT {
// nil: Set node value to zero of return type
if typ.cat == funcT {
// Wrap the typed nil value in a node, as per other interpreter functions
c.rval = reflect.ValueOf(&node{kind: basicLit, rval: reflect.New(typ.TypeOf()).Elem()})
@@ -2354,10 +2375,10 @@ func gotoLabel(s *symbol) {
}
}
func compositeGenerator(n *node, typ *itype) (gen bltnGenerator) {
func compositeGenerator(n *node, typ *itype, rtyp reflect.Type) (gen bltnGenerator) {
switch typ.cat {
case aliasT, ptrT:
gen = compositeGenerator(n, n.typ.val)
gen = compositeGenerator(n, n.typ.val, rtyp)
case arrayT:
gen = arrayLit
case mapT:
@@ -2368,9 +2389,9 @@ func compositeGenerator(n *node, typ *itype) (gen bltnGenerator) {
gen = compositeLitNotype
case n.lastChild().kind == keyValueExpr:
if n.nleft == 1 {
gen = compositeSparse
gen = compositeLitKeyed
} else {
gen = compositeSparseNotype
gen = compositeLitKeyedNotype
}
default:
if n.nleft == 1 {
@@ -2380,11 +2401,21 @@ func compositeGenerator(n *node, typ *itype) (gen bltnGenerator) {
}
}
case valueT:
switch k := n.typ.rtype.Kind(); k {
if rtyp == nil {
rtyp = n.typ.rtype
}
switch k := rtyp.Kind(); k {
case reflect.Struct:
if n.nleft == 1 {
gen = compositeBinStruct
} else {
gen = compositeBinStructNotype
}
case reflect.Map:
// TODO(mpl): maybe needs a NoType version too
gen = compositeBinMap
case reflect.Ptr:
gen = compositeGenerator(n, typ, n.typ.val.rtype)
default:
log.Panic(n.cfgErrorf("compositeGenerator not implemented for type kind: %s", k))
}
@@ -2426,3 +2457,13 @@ func isValueUntyped(v reflect.Value) bool {
}
return t.String() == t.Kind().String()
}
// isArithmeticAction returns true if the node action is an arithmetic operator.
func isArithmeticAction(n *node) bool {
switch n.action {
case aAdd, aAnd, aAndNot, aBitNot, aMul, aQuo, aRem, aShl, aShr, aSub, aXor:
return true
default:
return false
}
}

View File

@@ -13,8 +13,8 @@ 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.
executable, and exposed to scripts with the Use method. The extract
subcommand of yaegi can be used to generate package wrappers.
Custom build tags

View File

@@ -248,6 +248,8 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
typeName := n.child[0].ident
var typ *itype
if typ, err = nodeType(interp, sc, n.child[1]); err != nil {
err = nil
revisit = append(revisit, n)
return false
}

View File

@@ -2,8 +2,6 @@ package interp
import "reflect"
const hooksPath = "github.com/traefik/yaegi"
// convertFn is the signature of a symbol converter.
type convertFn func(from, to reflect.Type) func(src, dest reflect.Value)

View File

@@ -60,7 +60,7 @@ type receiver struct {
// frame contains values for the current execution level (a function context).
type frame struct {
// id is an atomic counter used for cancellation, only access
// id is an atomic counter used for cancellation, only accessed
// via newFrame/runid/setrunid/clone.
// Located at start of struct to ensure proper aligment.
id uint64
@@ -156,7 +156,8 @@ type Interpreter struct {
const (
mainID = "main"
selfPath = "github.com/traefik/yaegi/interp"
selfPrefix = "github.com/traefik/yaegi"
selfPath = selfPrefix + "/interp"
// DefaultSourceName is the name used by default when the name of the input
// source file has not been specified for an Eval.
// TODO(mpl): something even more special as a name?
@@ -175,6 +176,7 @@ var Symbols = Exports{
"Interpreter": reflect.ValueOf((*Interpreter)(nil)),
"Options": reflect.ValueOf((*Options)(nil)),
"Panic": reflect.ValueOf((*Panic)(nil)),
},
}
@@ -401,32 +403,59 @@ func (interp *Interpreter) EvalTest(path string) error {
return err
}
// Symbols returns a map of interpreter exported symbol values for the given path.
func (interp *Interpreter) Symbols(path string) map[string]reflect.Value {
m := map[string]reflect.Value{}
// Symbols returns a map of interpreter exported symbol values for the given
// import path. If the argument is the empty string, all known symbols are
// returned.
func (interp *Interpreter) Symbols(importPath string) Exports {
m := map[string]map[string]reflect.Value{}
interp.mutex.RLock()
if interp.scopes[path] == nil {
interp.mutex.RUnlock()
return m
}
sym := interp.scopes[path].sym
interp.mutex.RUnlock()
defer interp.mutex.RUnlock()
for n, s := range sym {
for k, v := range interp.srcPkg {
if importPath != "" && k != importPath {
continue
}
syms := map[string]reflect.Value{}
for n, s := range v {
if !canExport(n) {
// Skip private non-exported symbols.
continue
}
switch s.kind {
case constSym:
m[n] = s.rval
syms[n] = s.rval
case funcSym:
m[n] = genFunctionWrapper(s.node)(interp.frame)
syms[n] = genFunctionWrapper(s.node)(interp.frame)
case varSym:
m[n] = interp.frame.data[s.index]
syms[n] = interp.frame.data[s.index]
case typeSym:
syms[n] = reflect.New(s.typ.TypeOf())
}
}
if len(syms) > 0 {
m[k] = syms
}
if importPath != "" {
return m
}
}
if importPath != "" && len(m) > 0 {
return m
}
for k, v := range interp.binPkg {
if importPath != "" && k != importPath {
continue
}
m[k] = v
if importPath != "" {
return m
}
}
return m
}
@@ -603,7 +632,7 @@ func (interp *Interpreter) getWrapper(t reflect.Type) reflect.Type {
// they can be used in interpreted code.
func (interp *Interpreter) Use(values Exports) {
for k, v := range values {
if k == hooksPath {
if k == selfPrefix {
interp.hooks.Parse(v)
continue
}
@@ -785,20 +814,28 @@ func (interp *Interpreter) REPL() (reflect.Value, error) {
}
}
// getPrompt returns a function which prints a prompt only if input is a terminal.
func getPrompt(in io.Reader, out io.Writer) func(reflect.Value) {
s, ok := in.(interface{ Stat() (os.FileInfo, error) })
if !ok {
return func(reflect.Value) {}
}
stat, err := s.Stat()
if err == nil && stat.Mode()&os.ModeCharDevice != 0 {
func doPrompt(out io.Writer) func(v reflect.Value) {
return func(v reflect.Value) {
if v.IsValid() {
fmt.Fprintln(out, ":", v)
}
fmt.Fprint(out, "> ")
}
}
// getPrompt returns a function which prints a prompt only if input is a terminal.
func getPrompt(in io.Reader, out io.Writer) func(reflect.Value) {
forcePrompt, _ := strconv.ParseBool(os.Getenv("YAEGI_PROMPT"))
if forcePrompt {
return doPrompt(out)
}
s, ok := in.(interface{ Stat() (os.FileInfo, error) })
if !ok {
return func(reflect.Value) {}
}
stat, err := s.Stat()
if err == nil && stat.Mode()&os.ModeCharDevice != 0 {
return doPrompt(out)
}
return func(reflect.Value) {}
}

View File

@@ -43,6 +43,9 @@ func TestInterpConsistencyBuild(t *testing.T) {
file.Name() == "for7.go" || // expect error
file.Name() == "fun21.go" || // expect error
file.Name() == "fun22.go" || // expect error
file.Name() == "fun23.go" || // expect error
file.Name() == "fun24.go" || // expect error
file.Name() == "fun25.go" || // expect error
file.Name() == "if2.go" || // expect error
file.Name() == "import6.go" || // expect error
file.Name() == "init1.go" || // expect error
@@ -201,6 +204,11 @@ func TestInterpErrorConsistency(t *testing.T) {
expectedInterp: "6:2: not enough arguments in call to time.Date",
expectedExec: "6:11: not enough arguments in call to time.Date",
},
{
fileName: "fun23.go",
expectedInterp: "3:17: too many arguments to return",
expectedExec: "3:17: too many arguments to return",
},
{
fileName: "op1.go",
expectedInterp: "5:2: invalid operation: mismatched types int and float64",

View File

@@ -956,6 +956,9 @@ func TestConcurrentEvals(t *testing.T) {
hello1 = true
case "hello world2":
hello2 = true
case "hello world1hello world2", "hello world2hello world1":
hello1 = true
hello2 = true
default:
c <- fmt.Errorf("unexpected output: %v", l)
return
@@ -983,6 +986,9 @@ func TestConcurrentEvals(t *testing.T) {
// called by EvalWithContext is sequential. And that there is no data race for the
// interp package global vars or the interpreter fields in this case.
func TestConcurrentEvals2(t *testing.T) {
if testing.Short() {
return
}
pin, pout := io.Pipe()
defer func() {
_ = pin.Close()
@@ -1042,6 +1048,9 @@ func TestConcurrentEvals2(t *testing.T) {
// - when calling Interpreter.Use, the symbols given as argument should be
// copied when being inserted into interp.binPkg, and not directly used as-is.
func TestConcurrentEvals3(t *testing.T) {
if testing.Short() {
return
}
allDone := make(chan bool)
runREPL := func() {
done := make(chan error)
@@ -1120,6 +1129,9 @@ func TestConcurrentComposite2(t *testing.T) {
}
func testConcurrentComposite(t *testing.T, filePath string) {
if testing.Short() {
return
}
pin, pout := io.Pipe()
i := interp.New(interp.Options{Stdout: pout})
i.Use(stdlib.Symbols)
@@ -1157,6 +1169,9 @@ func testConcurrentComposite(t *testing.T, filePath string) {
}
func TestEvalScanner(t *testing.T) {
if testing.Short() {
return
}
type testCase struct {
desc string
src []string
@@ -1328,3 +1343,94 @@ func applyCIMultiplier(timeout time.Duration) time.Duration {
}
return time.Duration(float64(timeout) * CITimeoutMultiplier)
}
func TestREPLDivision(t *testing.T) {
if testing.Short() {
return
}
_ = os.Setenv("YAEGI_PROMPT", "1")
defer func() {
_ = os.Setenv("YAEGI_PROMPT", "0")
}()
allDone := make(chan bool)
runREPL := func() {
done := make(chan error)
pinin, poutin := io.Pipe()
pinout, poutout := io.Pipe()
i := interp.New(interp.Options{Stdin: pinin, Stdout: poutout})
i.Use(stdlib.Symbols)
go func() {
_, _ = i.REPL()
}()
defer func() {
_ = pinin.Close()
_ = poutin.Close()
_ = pinout.Close()
_ = poutout.Close()
allDone <- true
}()
input := []string{
`1/1`,
`7/3`,
`16/5`,
`3./2`, // float
}
output := []string{
`1`,
`2`,
`3`,
`1.5`,
}
go func() {
sc := bufio.NewScanner(pinout)
k := 0
for sc.Scan() {
l := sc.Text()
if l != "> : "+output[k] {
done <- fmt.Errorf("unexpected output, want %q, got %q", output[k], l)
return
}
k++
if k > 3 {
break
}
}
done <- nil
}()
for _, v := range input {
in := strings.NewReader(v + "\n")
if _, err := io.Copy(poutin, in); err != nil {
t.Fatal(err)
}
select {
case err := <-done:
if err != nil {
t.Fatal(err)
}
return
default:
time.Sleep(time.Second)
}
}
if err := <-done; err != nil {
t.Fatal(err)
}
}
go func() {
runREPL()
}()
timeout := time.NewTimer(10 * time.Second)
select {
case <-allDone:
case <-timeout.C:
t.Fatal("timeout")
}
}

View File

@@ -701,7 +701,16 @@ func quoConst(n *node) {
n.rval = reflect.New(t).Elem()
switch {
case isConst:
v := constant.BinaryOp(vConstantValue(v0), token.QUO, vConstantValue(v1))
var operator token.Token
// When the result of the operation is expected to be an int (because both
// operands are ints), we want to force the type of the whole expression to be an
// int (and not a float), which is achieved by using the QUO_ASSIGN operator.
if n.typ.untyped && isInt(n.typ.rtype) {
operator = token.QUO_ASSIGN
} else {
operator = token.QUO
}
v := constant.BinaryOp(vConstantValue(v0), operator, vConstantValue(v1))
n.rval.Set(reflect.ValueOf(v))
case isComplex(t):
n.rval.SetComplex(vComplex(v0) / vComplex(v1))
@@ -1957,10 +1966,10 @@ func bitNotConst(n *node) {
case isConst:
v := constant.UnaryOp(token.XOR, vConstantValue(v0), 0)
n.rval.Set(reflect.ValueOf(v))
case isInt(t):
n.rval.SetInt(^v0.Int())
case isUint(t):
n.rval.SetUint(^v0.Uint())
case isInt(t):
n.rval.SetInt(^v0.Int())
}
}
@@ -1976,10 +1985,10 @@ func negConst(n *node) {
case isConst:
v := constant.UnaryOp(token.SUB, vConstantValue(v0), 0)
n.rval.Set(reflect.ValueOf(v))
case isInt(t):
n.rval.SetInt(-v0.Int())
case isUint(t):
n.rval.SetUint(-v0.Uint())
case isInt(t):
n.rval.SetInt(-v0.Int())
case isFloat(t):
n.rval.SetFloat(-v0.Float())
case isComplex(t):
@@ -2015,10 +2024,10 @@ func posConst(n *node) {
case isConst:
v := constant.UnaryOp(token.ADD, vConstantValue(v0), 0)
n.rval.Set(reflect.ValueOf(v))
case isInt(t):
n.rval.SetInt(+v0.Int())
case isUint(t):
n.rval.SetUint(+v0.Uint())
case isInt(t):
n.rval.SetInt(+v0.Int())
case isFloat(t):
n.rval.SetFloat(+v0.Float())
case isComplex(t):

View File

@@ -1,6 +1,6 @@
package interp
//go:generate go run ../internal/genop/genop.go
//go:generate go run ../internal/cmd/genop/genop.go
import (
"fmt"
@@ -1039,14 +1039,6 @@ func call(n *node) {
}
}
// pindex returns definition parameter index for function call.
func pindex(i, variadic int) int {
if variadic < 0 || i <= variadic {
return i
}
return variadic
}
func getFrame(f *frame, l int) *frame {
switch l {
case 0:
@@ -1077,7 +1069,7 @@ func callBin(n *node) {
// A method signature obtained from reflect.Type includes receiver as 1st arg, except for interface types.
rcvrOffset := 0
if recv := n.child[0].recv; recv != nil && !isInterface(recv.node.typ) {
if funcType.NumIn() > len(child) {
if variadic > 0 || funcType.NumIn() > len(child) {
rcvrOffset = 1
}
}
@@ -1089,7 +1081,13 @@ func callBin(n *node) {
}
for i, c := range child {
defType := funcType.In(rcvrOffset + pindex(i, variadic))
var defType reflect.Type
if variadic >= 0 && i >= variadic {
defType = funcType.In(variadic)
} else {
defType = funcType.In(rcvrOffset + i)
}
switch {
case isBinCall(c):
// Handle nested function calls: pass returned values as arguments
@@ -1871,7 +1869,10 @@ func _return(n *node) {
case 0:
n.exec = nil
case 1:
if child[0].kind == binaryExpr || isCall(child[0]) {
// This is an optimisation that is applied for binary expressions or function
// calls, but not for (binary) expressions involving const, as the values are not
// stored in the frame in that case.
if !child[0].rval.IsValid() && child[0].kind == binaryExpr || isCall(child[0]) {
n.exec = nil
} else {
v := values[0]
@@ -2012,12 +2013,18 @@ func compositeBinMap(n *node) {
}
}
// compositeBinStruct creates and populates a struct object from a binary type.
func compositeBinStruct(n *node) {
// doCompositeBinStruct creates and populates a struct object from a binary type.
func doCompositeBinStruct(n *node, hasType bool) {
next := getExec(n.tnext)
value := valueGenerator(n, n.findex)
typ := n.typ.rtype
child := n.child[1:]
if n.typ.cat == ptrT || n.typ.cat == aliasT {
typ = n.typ.val.rtype
}
child := n.child
if hasType {
child = n.child[1:]
}
values := make([]func(*frame) reflect.Value, len(child))
fieldIndex := make([][]int, len(child))
for i, c := range child {
@@ -2033,7 +2040,7 @@ func compositeBinStruct(n *node) {
}
} else {
fieldIndex[i] = []int{i}
if c.typ.cat == funcT {
if c.typ.cat == funcT && len(c.child) > 1 {
convertLiteralValue(c.child[1], typ.Field(i).Type)
values[i] = genFunctionWrapper(c.child[1])
} else {
@@ -2048,11 +2055,20 @@ func compositeBinStruct(n *node) {
for i, v := range values {
s.FieldByIndex(fieldIndex[i]).Set(v(f))
}
value(f).Set(s)
d := value(f)
switch {
case d.Type().Kind() == reflect.Ptr:
d.Set(s.Addr())
default:
d.Set(s)
}
return next
}
}
func compositeBinStruct(n *node) { doCompositeBinStruct(n, true) }
func compositeBinStructNotype(n *node) { doCompositeBinStruct(n, false) }
func destType(n *node) *itype {
switch n.anc.kind {
case assignStmt, defineStmt:
@@ -2062,68 +2078,7 @@ func destType(n *node) *itype {
}
}
// doCompositeLit creates and populates a struct object.
func doCompositeLit(n *node, hasType bool) {
value := valueGenerator(n, n.findex)
next := getExec(n.tnext)
typ := n.typ
if typ.cat == ptrT || typ.cat == aliasT {
typ = typ.val
}
var mu sync.Mutex
typ.mu = &mu
child := n.child
if hasType {
child = n.child[1:]
}
destInterface := destType(n).cat == interfaceT
values := make([]func(*frame) reflect.Value, len(child))
for i, c := range child {
convertLiteralValue(c, typ.field[i].typ.TypeOf())
switch {
case c.typ.cat == funcT:
values[i] = genFunctionWrapper(c)
case isArray(c.typ) && c.typ.val != nil && c.typ.val.cat == interfaceT:
values[i] = genValueInterfaceArray(c)
case isRecursiveType(typ.field[i].typ, typ.field[i].typ.rtype):
values[i] = genValueRecursiveInterface(c, typ.field[i].typ.rtype)
case isInterface(typ.field[i].typ):
values[i] = genInterfaceWrapper(c, typ.field[i].typ.rtype)
default:
values[i] = genValue(c)
}
}
i := n.findex
l := n.level
n.exec = func(f *frame) bltn {
// TODO: it seems fishy that the typ might be modified post-compilation, and
// hence that several goroutines might be using the same typ that they all modify.
// We probably need to revisit that.
typ.mu.Lock()
a := reflect.New(typ.TypeOf()).Elem()
typ.mu.Unlock()
for i, v := range values {
a.Field(i).Set(v(f))
}
switch d := value(f); {
case d.Type().Kind() == reflect.Ptr:
d.Set(a.Addr())
case destInterface:
d.Set(reflect.ValueOf(valueInterface{n, a}))
default:
getFrame(f, l).data[i] = a
}
return next
}
}
func compositeLit(n *node) { doCompositeLit(n, true) }
func compositeLitNotype(n *node) { doCompositeLit(n, false) }
// doCompositeSparse creates a struct Object, filling fields from sparse key-values.
func doCompositeSparse(n *node, hasType bool) {
func doComposite(n *node, hasType bool, keyed bool) {
value := valueGenerator(n, n.findex)
next := getExec(n.tnext)
typ := n.typ
@@ -2139,27 +2094,37 @@ func doCompositeSparse(n *node, hasType bool) {
destInterface := destType(n).cat == interfaceT
values := make(map[int]func(*frame) reflect.Value)
for _, c := range child {
c1 := c.child[1]
field := typ.fieldIndex(c.child[0].ident)
convertLiteralValue(c1, typ.field[field].typ.TypeOf())
for i, c := range child {
var val *node
var fieldIndex int
if keyed {
val = c.child[1]
fieldIndex = typ.fieldIndex(c.child[0].ident)
} else {
val = c
fieldIndex = i
}
convertLiteralValue(val, typ.field[fieldIndex].typ.TypeOf())
switch {
case c1.typ.cat == funcT:
values[field] = genFunctionWrapper(c1)
case isArray(c1.typ) && c1.typ.val != nil && c1.typ.val.cat == interfaceT:
values[field] = genValueInterfaceArray(c1)
case isRecursiveType(typ.field[field].typ, typ.field[field].typ.rtype):
values[field] = genValueRecursiveInterface(c1, typ.field[field].typ.rtype)
case isInterface(typ.field[field].typ):
values[field] = genInterfaceWrapper(c1, typ.field[field].typ.rtype)
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)
default:
values[field] = genValue(c1)
values[fieldIndex] = genValue(val)
}
}
frameIndex := n.findex
l := n.level
n.exec = func(f *frame) bltn {
typ.mu.Lock()
a, _ := typ.zero()
// No need to call zero() as doComposite is only called for a structT
a := reflect.New(typ.TypeOf()).Elem()
typ.mu.Unlock()
for i, v := range values {
a.Field(i).Set(v(f))
@@ -2171,14 +2136,27 @@ func doCompositeSparse(n *node, hasType bool) {
case destInterface:
d.Set(reflect.ValueOf(valueInterface{n, a}))
default:
d.Set(a)
getFrame(f, l).data[frameIndex] = a
}
return next
}
}
func compositeSparse(n *node) { doCompositeSparse(n, true) }
func compositeSparseNotype(n *node) { doCompositeSparse(n, false) }
// doCompositeLit creates and populates a struct object.
func doCompositeLit(n *node, hasType bool) {
doComposite(n, hasType, false)
}
func compositeLit(n *node) { doCompositeLit(n, true) }
func compositeLitNotype(n *node) { doCompositeLit(n, false) }
// doCompositeLitKeyed creates a struct Object, filling fields from sparse key-values.
func doCompositeLitKeyed(n *node, hasType bool) {
doComposite(n, hasType, true)
}
func compositeLitKeyed(n *node) { doCompositeLitKeyed(n, true) }
func compositeLitKeyedNotype(n *node) { doCompositeLitKeyed(n, false) }
func empty(n *node) {}
@@ -2452,11 +2430,16 @@ func appendSlice(n *node) {
}
func _append(n *node) {
if c1, c2 := n.child[1], n.child[2]; len(n.child) == 3 && c2.typ.cat == arrayT && c2.typ.val.id() == n.typ.val.id() ||
if len(n.child) == 3 {
c1, c2 := n.child[1], n.child[2]
if (c1.typ.cat == valueT || c2.typ.cat == valueT) && c1.typ.rtype == c2.typ.rtype ||
c2.typ.cat == arrayT && c2.typ.val.id() == n.typ.val.id() ||
isByteArray(c1.typ.TypeOf()) && isString(c2.typ.TypeOf()) {
appendSlice(n)
return
}
}
dest := genValueOutput(n, n.typ.rtype)
value := genValue(n.child[1])
next := getExec(n.tnext)
@@ -2853,44 +2836,8 @@ func convertConstantValue(n *node) {
return
}
v := n.rval
typ := n.typ.TypeOf()
kind := typ.Kind()
switch kind {
case reflect.Bool:
v = reflect.ValueOf(constant.BoolVal(c)).Convert(typ)
case reflect.String:
v = reflect.ValueOf(constant.StringVal(c)).Convert(typ)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
i, _ := constant.Int64Val(c)
l := constant.BitLen(c)
if l > bitlen[kind] {
panic(fmt.Sprintf("constant %s overflows int%d", c.ExactString(), bitlen[kind]))
}
v = reflect.ValueOf(i).Convert(typ)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
i, _ := constant.Uint64Val(c)
l := constant.BitLen(c)
if l > bitlen[kind] {
panic(fmt.Sprintf("constant %s overflows uint%d", c.ExactString(), bitlen[kind]))
}
v = reflect.ValueOf(i).Convert(typ)
case reflect.Float32:
f, _ := constant.Float32Val(c)
v = reflect.ValueOf(f).Convert(typ)
case reflect.Float64:
f, _ := constant.Float64Val(c)
v = reflect.ValueOf(f).Convert(typ)
case reflect.Complex64:
r, _ := constant.Float32Val(constant.Real(c))
i, _ := constant.Float32Val(constant.Imag(c))
v = reflect.ValueOf(complex(r, i)).Convert(typ)
case reflect.Complex128:
r, _ := constant.Float64Val(constant.Real(c))
i, _ := constant.Float64Val(constant.Imag(c))
v = reflect.ValueOf(complex(r, i)).Convert(typ)
default:
// Type kind is from internal constant representation. Only use default types here.
var v reflect.Value
switch c.Kind() {
case constant.Bool:
v = reflect.ValueOf(constant.BoolVal(c))
@@ -2910,8 +2857,8 @@ func convertConstantValue(n *node) {
i, _ := constant.Float64Val(constant.Imag(c))
v = reflect.ValueOf(complex(r, i))
}
}
n.rval = v
n.rval = v.Convert(n.typ.TypeOf())
}
// Write to a channel.
@@ -2991,16 +2938,22 @@ func _select(n *node) {
next := getExec(n.tnext)
for i := 0; i < nbClause; i++ {
if len(n.child[i].child) == 0 {
// The comm clause is an empty default, exit select.
cl := n.child[i]
if cl.kind == commClauseDefault {
cases[i].Dir = reflect.SelectDefault
if len(cl.child) == 0 {
clause[i] = func(*frame) bltn { return next }
} else {
switch c0 := n.child[i].child[0]; {
case len(n.child[i].child) > 1:
clause[i] = getExec(cl.child[0].start)
}
continue
}
// The comm clause is in send or recv direction.
switch c0 := cl.child[0]; {
case len(cl.child) > 1:
// The comm clause contains a channel operation and a clause body.
clause[i] = getExec(n.child[i].child[1].start)
chans[i], assigned[i], ok[i], cases[i].Dir = clauseChanDir(n.child[i])
clause[i] = getExec(cl.child[1].start)
chans[i], assigned[i], ok[i], cases[i].Dir = clauseChanDir(c0)
chanValues[i] = genValue(chans[i])
if assigned[i] != nil {
assignedValues[i] = genValue(assigned[i])
@@ -3012,16 +2965,13 @@ func _select(n *node) {
// The comm clause has an empty body clause after channel receive.
chanValues[i] = genValue(c0.child[0].child[0])
cases[i].Dir = reflect.SelectRecv
clause[i] = func(*frame) bltn { return next }
case c0.kind == sendStmt:
// The comm clause as an empty body clause after channel send.
chanValues[i] = genValue(c0.child[0])
cases[i].Dir = reflect.SelectSend
assignedValues[i] = genValue(c0.child[1])
default:
// The comm clause has a default clause.
clause[i] = getExec(c0.start)
cases[i].Dir = reflect.SelectDefault
}
clause[i] = func(*frame) bltn { return next }
}
}

View File

@@ -33,17 +33,12 @@ func (interp *Interpreter) importSrc(rPath, importPath string, skipTest bool) (s
rPath = "."
}
dir = filepath.Join(filepath.Dir(interp.name), rPath, importPath)
} else {
var root string
if rPath == mainID {
root, err = interp.rootFromSourceLocation()
if err != nil {
} else if dir, rPath, err = pkgDir(interp.context.GOPATH, rPath, importPath); err != nil {
// Try again, assuming a root dir at the source location.
if rPath, err = interp.rootFromSourceLocation(); err != nil {
return "", err
}
} else {
root = rPath
}
if dir, rPath, err = pkgDir(interp.context.GOPATH, root, importPath); err != nil {
if dir, rPath, err = pkgDir(interp.context.GOPATH, rPath, importPath); err != nil {
return "", err
}
}

View File

@@ -166,56 +166,68 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
case arrayType:
t.cat = arrayT
if len(n.child) > 1 {
v := n.child[0].rval
switch {
c0 := n.child[0]
if len(n.child) == 1 {
// Array size is not defined.
if t.val, err = nodeType(interp, sc, c0); err != nil {
return nil, err
}
t.incomplete = t.val.incomplete
break
}
// Array size is defined.
switch v := c0.rval; {
case v.IsValid():
// constant size
// Size if defined by a constant litteral value.
if isConstantValue(v.Type()) {
c := v.Interface().(constant.Value)
t.size = constToInt(c)
} else {
t.size = int(v.Int())
}
case n.child[0].kind == ellipsisExpr:
// [...]T expression
case c0.kind == ellipsisExpr:
// [...]T expression, get size from the length of composite array.
t.size = arrayTypeLen(n.anc)
default:
if sym, _, ok := sc.lookup(n.child[0].ident); ok {
if sym.kind != constSym {
return nil, n.child[0].cfgErrorf("non-constant array bound %q", n.child[0].ident)
case c0.kind == identExpr:
sym, _, ok := sc.lookup(c0.ident)
if !ok {
t.incomplete = true
break
}
// Size is defined by a symbol which must be a constant integer.
if sym.kind != constSym {
return nil, c0.cfgErrorf("non-constant array bound %q", c0.ident)
}
if sym.typ == nil || sym.typ.cat != intT {
t.incomplete = true
break
}
// Resolve symbol to get size value
if sym.typ != nil && sym.typ.cat == intT {
if v, ok := sym.rval.Interface().(int); ok {
t.size = v
} else if c, ok := sym.rval.Interface().(constant.Value); ok {
break
}
if c, ok := sym.rval.Interface().(constant.Value); ok {
t.size = constToInt(c)
} else {
t.incomplete = true
break
}
} else {
t.incomplete = true
}
} else {
// Evaluate constant array size expression
if _, err = interp.cfg(n.child[0], sc.pkgID); err != nil {
default:
// Size is defined by a numeric constant expression.
if _, err = interp.cfg(c0, sc.pkgID); err != nil {
return nil, err
}
v, ok := c0.rval.Interface().(constant.Value)
if !ok {
t.incomplete = true
break
}
t.size = constToInt(v)
}
if t.val, err = nodeType(interp, sc, n.child[1]); err != nil {
return nil, err
}
t.sizedef = true
t.incomplete = t.incomplete || t.val.incomplete
} else {
if t.val, err = nodeType(interp, sc, n.child[0]); err != nil {
return nil, err
}
t.incomplete = t.val.incomplete
}
case basicLit:
switch v := n.rval.Interface().(type) {
@@ -550,7 +562,6 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
}
} else {
err = n.cfgErrorf("undefined selector %s.%s", lt.path, name)
panic(err)
}
case srcPkgT:
pkg := interp.srcPkg[lt.path]
@@ -604,7 +615,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
t.field = append(t.field, structField{name: fieldName(c.child[0]), embed: true, typ: typ})
incomplete = incomplete || typ.incomplete
case len(c.child) == 2 && c.child[1].kind == basicLit:
tag := c.child[1].rval.String()
tag := vString(c.child[1].rval)
typ, err := nodeType(interp, sc, c.child[0])
if err != nil {
return nil, err
@@ -615,7 +626,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
var tag string
l := len(c.child)
if c.lastChild().kind == basicLit {
tag = c.lastChild().rval.String()
tag = vString(c.lastChild().rval)
l--
}
typ, err := nodeType(interp, sc, c.child[l-1])
@@ -915,7 +926,23 @@ func (t *itype) assignableTo(o *itype) bool {
if t.isNil() && o.hasNil() || o.isNil() && t.hasNil() {
return true
}
return t.TypeOf().AssignableTo(o.TypeOf())
if t.TypeOf().AssignableTo(o.TypeOf()) {
return true
}
n := t.node
if n == nil || !n.rval.IsValid() {
return false
}
con, ok := n.rval.Interface().(constant.Value)
if !ok {
return false
}
if con == nil || !isConstType(o) {
return false
}
return representableConst(con, o.TypeOf())
}
// convertibleTo returns true if t is convertible to o.
@@ -924,7 +951,7 @@ func (t *itype) convertibleTo(o *itype) bool {
return true
}
// unsafe checkes
// unsafe checks
tt, ot := t.TypeOf(), o.TypeOf()
if (tt.Kind() == reflect.Ptr || tt.Kind() == reflect.Uintptr) && ot.Kind() == reflect.UnsafePointer {
return true
@@ -1180,7 +1207,11 @@ func (t *itype) lookupBinField(name string) (s reflect.StructField, index []int,
if !isStruct(t) {
return
}
s, ok = t.TypeOf().FieldByName(name)
rt := t.rtype
if t.cat == valueT && rt.Kind() == reflect.Ptr {
rt = rt.Elem()
}
s, ok = rt.FieldByName(name)
if !ok {
for i, f := range t.field {
if f.embed {

View File

@@ -91,7 +91,10 @@ func (check typecheck) addressExpr(n *node) error {
case selectorExpr:
c0 = c0.child[1]
continue
case indexExpr:
case starExpr:
c0 = c0.child[0]
continue
case indexExpr, sliceExpr:
c := c0.child[0]
if isArray(c.typ) || isMap(c.typ) {
c0 = c
@@ -101,7 +104,7 @@ func (check typecheck) addressExpr(n *node) error {
found = true
continue
}
return n.cfgErrorf("invalid operation: cannot take address of %s", c0.typ.id())
return n.cfgErrorf("invalid operation: cannot take address of %s [kind: %s]", c0.typ.id(), kinds[c0.kind])
}
return nil
}
@@ -148,7 +151,7 @@ func (check typecheck) shift(n *node) error {
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
var v0 constant.Value
if c0.typ.untyped {
if c0.typ.untyped && c0.rval.IsValid() {
v0 = constant.ToInt(c0.rval.Interface().(constant.Value))
c0.rval = reflect.ValueOf(v0)
}
@@ -483,7 +486,7 @@ func (check typecheck) sliceExpr(n *node) error {
case reflect.Array:
valid = true
l = t.Len()
if c.kind != selectorExpr && (c.sym == nil || c.sym.kind != varSym) {
if c.kind != indexExpr && c.kind != selectorExpr && (c.sym == nil || c.sym.kind != varSym) {
return c.cfgErrorf("cannot slice type %s", c.typ.id())
}
case reflect.Slice:

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract archive/tar'. DO NOT EDIT.
// Code generated by 'yaegi extract archive/tar'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract archive/zip'. DO NOT EDIT.
// Code generated by 'yaegi extract archive/zip'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract bufio'. DO NOT EDIT.
// Code generated by 'yaegi extract bufio'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract bytes'. DO NOT EDIT.
// Code generated by 'yaegi extract bytes'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract compress/bzip2'. DO NOT EDIT.
// Code generated by 'yaegi extract compress/bzip2'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract compress/flate'. DO NOT EDIT.
// Code generated by 'yaegi extract compress/flate'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract compress/gzip'. DO NOT EDIT.
// Code generated by 'yaegi extract compress/gzip'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract compress/lzw'. DO NOT EDIT.
// Code generated by 'yaegi extract compress/lzw'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract compress/zlib'. DO NOT EDIT.
// Code generated by 'yaegi extract compress/zlib'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract container/heap'. DO NOT EDIT.
// Code generated by 'yaegi extract container/heap'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract container/list'. DO NOT EDIT.
// Code generated by 'yaegi extract container/list'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract container/ring'. DO NOT EDIT.
// Code generated by 'yaegi extract container/ring'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract context'. DO NOT EDIT.
// Code generated by 'yaegi extract context'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract crypto'. DO NOT EDIT.
// Code generated by 'yaegi extract crypto'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract crypto/aes'. DO NOT EDIT.
// Code generated by 'yaegi extract crypto/aes'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract crypto/cipher'. DO NOT EDIT.
// Code generated by 'yaegi extract crypto/cipher'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract crypto/des'. DO NOT EDIT.
// Code generated by 'yaegi extract crypto/des'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract crypto/dsa'. DO NOT EDIT.
// Code generated by 'yaegi extract crypto/dsa'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract crypto/ecdsa'. DO NOT EDIT.
// Code generated by 'yaegi extract crypto/ecdsa'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract crypto/ed25519'. DO NOT EDIT.
// Code generated by 'yaegi extract crypto/ed25519'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract crypto/elliptic'. DO NOT EDIT.
// Code generated by 'yaegi extract crypto/elliptic'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract crypto/hmac'. DO NOT EDIT.
// Code generated by 'yaegi extract crypto/hmac'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract crypto/md5'. DO NOT EDIT.
// Code generated by 'yaegi extract crypto/md5'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract crypto/rand'. DO NOT EDIT.
// Code generated by 'yaegi extract crypto/rand'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract crypto/rc4'. DO NOT EDIT.
// Code generated by 'yaegi extract crypto/rc4'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract crypto/rsa'. DO NOT EDIT.
// Code generated by 'yaegi extract crypto/rsa'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract crypto/sha1'. DO NOT EDIT.
// Code generated by 'yaegi extract crypto/sha1'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract crypto/sha256'. DO NOT EDIT.
// Code generated by 'yaegi extract crypto/sha256'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract crypto/sha512'. DO NOT EDIT.
// Code generated by 'yaegi extract crypto/sha512'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract crypto/subtle'. DO NOT EDIT.
// Code generated by 'yaegi extract crypto/subtle'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract crypto/tls'. DO NOT EDIT.
// Code generated by 'yaegi extract crypto/tls'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract crypto/x509'. DO NOT EDIT.
// Code generated by 'yaegi extract crypto/x509'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract crypto/x509/pkix'. DO NOT EDIT.
// Code generated by 'yaegi extract crypto/x509/pkix'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract database/sql'. DO NOT EDIT.
// Code generated by 'yaegi extract database/sql'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract database/sql/driver'. DO NOT EDIT.
// Code generated by 'yaegi extract database/sql/driver'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract debug/dwarf'. DO NOT EDIT.
// Code generated by 'yaegi extract debug/dwarf'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract debug/elf'. DO NOT EDIT.
// Code generated by 'yaegi extract debug/elf'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract debug/gosym'. DO NOT EDIT.
// Code generated by 'yaegi extract debug/gosym'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract debug/macho'. DO NOT EDIT.
// Code generated by 'yaegi extract debug/macho'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract debug/pe'. DO NOT EDIT.
// Code generated by 'yaegi extract debug/pe'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract debug/plan9obj'. DO NOT EDIT.
// Code generated by 'yaegi extract debug/plan9obj'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract encoding'. DO NOT EDIT.
// Code generated by 'yaegi extract encoding'. DO NOT EDIT.
// +build go1.14,!go1.15

View File

@@ -1,4 +1,4 @@
// Code generated by 'github.com/traefik/yaegi/extract encoding/ascii85'. DO NOT EDIT.
// Code generated by 'yaegi extract encoding/ascii85'. DO NOT EDIT.
// +build go1.14,!go1.15

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