Compare commits

...

60 Commits

Author SHA1 Message Date
Nicholas Wiersma
3c6df504df fix: dont allow calling init 2020-07-09 14:35:04 +02:00
Nicholas Wiersma
98eacf3610 fix: execute global variables in the correct order
* fix: constant definition loop on out of order vars

* fix: do not wire global varDecl

* fix: wire and execute global vars

* chore: add tests

* fix: refactor and lint
2020-07-09 14:05:03 +02:00
Marc Vertes
16ff52a949 fix: avoid a panic in CFG in case of incomplete type
By returning early in case of incomplete type in CFG, we avoid
a panic, and let a chance to a new attempt after the missing
type has been parsed.

Fixes 763.
2020-07-09 13:05:04 +02:00
Marc Vertes
640d1429e5 fix: type assert when status is _
If the status is _, there is no storage allocated in frame, and
the status assign operation should be skipped.

Fixes #761.
2020-07-09 08:45:03 +02:00
Nicholas Wiersma
659913eebe fix: convert type properly to the correct type 2020-07-08 22:55:03 +02:00
Marc Vertes
b3766509cc feature: restrict symbols which can exit the interpreter process
* feature: restrict symbols which can exit the interpreter process

Some symbols such as os.Exit or log.Fatal, which make the current process
to exit, are now restricted. They are replaced by a version which panics
instead of exiting, as panics are recovered by Eval.

The restricted os.FindProcess version is identical to the original
except it errors when trying to return the self process, in order to
forbid killing or signaling the interpreter process from script.

The os/exec symbols are available only through unrestricted package.

The original symbols are stored in an unrestricted package, which
requires an explicit Use, as for unsafe and syscall packages.

The Use() interpreter method has been slightly modified to allow inplace
updating of package symbols, allowing to replace some symbols but not
the entire imported package.

A command line option -unrestricted has been added to yaegi CLI to use
the unrestricted symbols.

Fixes #486.

* fix: lint
2020-07-08 22:35:04 +02:00
Nicholas Wiersma
bc2b224bae fix: make a copy of defined before detecting recursivness 2020-07-07 12:05:03 +02:00
Nicholas Wiersma
9d4685deea fix: handle interfaces in composite sparce (#749)
Co-authored-by: Marc Vertes <mvertes@free.fr>
2020-07-06 15:41:27 +02:00
Marc Vertes
2a70a71dc2 fix: avoid infinite loop when parsing recursive types
Mark all visited types as such when walking down struct fields.

Fixes #750.
Updates #652.
2020-07-06 15:30:04 +02:00
Nicholas Wiersma
851444453c fix: assert switch type from valueT in struct case (#747)
* fix: switch type from valueT in struct case

In a struct case in type assertion, if the source is a valueT, we still need to take the struct type to allow method and field resolution.

* fix: handle all ptr structs as well
2020-07-06 15:09:48 +02:00
Nicholas Wiersma
a8b1c2a017 fix: a const, non-const equality check must convert 2020-07-06 11:25:03 +02:00
Nicholas Wiersma
d229c2a2c7 fix: handle Func Value in genFunctionWrapper params
* fix: handle Func value in func wrapper params

* fix: lint
2020-07-03 12:25:04 +02:00
Marc Vertes
2f2df7a0f8 fix: avoid memory errors by handling frame indirections (#739)
In all situations where the results are set directly
to the frame, and not using a value helper, the right level of
indirections must be applied, otherwise we may end-up writing
in the wrong frame (the local one, instead of a caller or global).

Fixes #735.
2020-07-03 11:02:46 +02:00
Nicholas Wiersma
4058fd8c44 fix: do type check on assignment (#738)
* fix: do type check on assignment

* fix: check for recursive type rather than field
2020-07-03 10:28:51 +02:00
Nicholas Wiersma
097a745e72 fix: variadic interface conversion in call/callBin 2020-07-02 23:55:03 +02:00
Nicholas Wiersma
1f514e63a8 fix: switch always compare like types 2020-07-02 23:35:03 +02:00
Nicholas Wiersma
a15ecb7176 feature: handle nested recursion
* fix: copy the type in recursion

In more advanced recursive cases, setting the rtype to interface may interfear with typeing. To stop this from happening, instead of setting t.val.rtype to interface in the hope it will be set correctly later, a copy if the type is made, and the rtype of the copy is set to interface{}.

* fix: detect intermediate recursive structs

In the case of a nested recussion, each symbol can have
a different perspective on the recursion. In this case,
it is impossible to move from one struct to the next.
To keep the perspectives the same, any intermediate struct
that contains a recursion should also be set to interface{}.
so that all perspectives are the same.

* fix: handle arb recursion

* chore: refactor dref to be consistent

* fix: invalid recursive struct issue

* fix: handle checkptr issue

* fix: move unsafe into function to stop ptr check

* fix: handle deref in assign
2020-07-02 23:20:03 +02:00
Marc Vertes
d4aa84f729 fix: set frame level in destination nodes to avoid memory corruption (#733)
When operations write their result to a non-local frame, the node
level field must be set accordingly, otherwise they attempt to write
in the wrong frame.

Fixes #730.
2020-07-02 10:03:32 +02:00
Marc Vertes
9977ef6fc6 Revert "fix: make interpreter methods discoverable by runtime (#722)" (#732)
This reverts commit a3b2737b5c.
2020-07-01 16:16:26 +02:00
Marc Vertes
39430c34bb fix: untyped constant converson to default type (#729)
* fix: untyped constant cconverson to default type

In definition assign expression, the source type is propagated to
the assigned value. If the source is an untyped constant, the
destination type must be set to the default type of the constant
definition. A fixType function is provided to perform this.

In addition, the type conversion and check of constants is
refactored for simplifications.

Fixes #727.

* test: fix _test/const14.go
2020-07-01 14:39:47 +02:00
mpl
4f3481b55c interp: support another vendoring case 2020-07-01 10:44:03 +02:00
Marc Vertes
55f2fe396a fix: goexports skisp unimplemented solaris Syscall6 (#726)
The standard library syscall package for Solaris defines unimplemented
symbols Syscall6 and RawSyscall6 which makes the build fails on
Solaris platform, now that yaegi command imports syscall symbols.

As the standard library package is locked down, this will remain
unchanged. We just skip those symbols.

Fixes #725.
2020-06-30 22:50:44 +02:00
Marc Vertes
108b6fd722 feature: add -syscall option to enable use of syscall symbols (#723) 2020-06-29 14:43:43 +02:00
Marc Vertes
a3b2737b5c fix: make interpreter methods discoverable by runtime (#722)
* fix: make interpreter methods discoverable by runtime

When generating an interface wrapper, lookup existing wrappers by method
to get the one with the biggest set of methods implemented by interpreter.

A string method is also added to wrappers, in order to provide a string
representation of the interpreter value rather than the wrapper itself
(at least for %s and %v verbs).

This allows the runtime to pickup an interpreter method automatically
even if the conversion to the interface is not specified in the script. As
in Go spec, it is enough for the type to implement the required methods.

A current limitation is that only single wrappers can be instantiated,
not allowing to compose interfaces.

This limitation can be removed when the Go reflect issue
https://github.com/golang/go/issues/15924 is fixed.

Fixes #435.

* test: add a simpler test
2020-06-29 14:25:14 +02:00
Nicholas Wiersma
d2c4a36c25 fix: dont optimize map index assigns 2020-06-29 09:40:03 +02:00
Nicholas Wiersma
f5f44f7ddd fix: rework compatibility and ordering checks 2020-06-25 09:44:04 +02:00
Nicholas Wiersma
4d013e4686 fix: handle defer in builtins 2020-06-25 09:28:03 +02:00
Julien Levesy
c11d361953 Handle Receive and Send channels 2020-06-23 09:04:04 +02:00
Nicholas Wiersma
c2ad279643 fix: use pragma for ptr checks 2020-06-22 16:40:03 +02:00
Nicholas Wiersma
9627782394 feature: add Sizeof and Alignof to unsafe 2020-06-22 15:24:04 +02:00
Nicholas Wiersma
e00b853971 fix: make reftype func variadic 2020-06-22 13:38:04 +02:00
mpl
7cfa264dbc interp: force root scope for binPkgT selector 2020-06-22 13:24:03 +02:00
Ludovic Fernandez
a6c24a0d13 chore: update linter. (#706)
* chore: update linter.

* chore: remove not needed travis env var.
2020-06-22 12:55:42 +02:00
Nicholas Wiersma
f19b7563ea feature: unsafe type conversion 2020-06-18 18:14:03 +02:00
Nicholas Wiersma
0643762852 fix: allow uint in make len and cap 2020-06-18 15:18:04 +02:00
mpl
7323d97023 interp: global scope redeclaration detection 2020-06-18 15:08:04 +02:00
Julien Levesy
6486909921 fix: range over channels returned by binary calls 2020-06-18 14:22:03 +02:00
Nicholas Wiersma
d252821df3 feature: constant values in bin packages 2020-06-18 13:54:04 +02:00
Nicholas Wiersma
2bef03e253 fix: import non-existant package 2020-06-18 09:44:03 +02:00
Nicholas Wiersma
1fe91be882 feature: refactor numeric constants and detect overflow 2020-06-18 09:20:04 +02:00
Nicholas Wiersma
5cbbf9339c fix: type assertion of non-matching types 2020-06-18 09:06:03 +02:00
Nicholas Wiersma
8365f687e7 fix: composite map assign (#683)
* fix: composite map assign

* reword comment

Co-authored-by: Marc Vertes <mvertes@free.fr>
2020-06-10 12:29:09 +02:00
Nicholas Wiersma
36836cd4f2 fix: return correct package name for parsed package (#679)
Co-authored-by: Marc Vertes <mvertes@free.fr>
2020-06-10 12:17:10 +02:00
Marc Vertes
82b499a1c8 fix: detect default comm clause in select from AST (#678)
* fix: detect default comm clause in select from AST

The heuristic to distinguish a default comm clause was too weak.
Make it robust by using AST.

Fixes #646.

* rename test to avoid conflict
2020-06-10 12:06:59 +02:00
Marc Vertes
0ef7f8fb80 fix: perform send channel action for select (#665)
* fix: perform send channel action for select

The CFG was wrong for select send comm clauses. If an init operation
on channel was required (like a derefence, index operation, ...) it
was skipped. The bug was invisible in case of a local var channel.

A send channel init operation consist to prepare both the data to
send (right subtree of send AST) and the channel itself (left
subtree of send AST). All channel init operation must be performed
prior to call select.

Fixes #647.

* doc: fix comment

* invert test to continue early

please note that this also changes the logic a little bit, since the
line that was:

if pn != nil {

now implictly becomes:

if an != nil && pn != nil {

(which I think is actually more correct).

* explicit chaining of init actions in select

* explicit chaining of init actions in select

Co-authored-by: mpl <mathieu.lonjaret@gmail.com>
2020-06-10 11:37:06 +02:00
Marc Vertes
c1f5005b2a fix: finish support of type assertions which was incomplete (#657)
* fix: finish support of type assertions which was incomplete

TypeAssert was optimistically returning ok without verifying that
value could be converted to the required interface (in case of
type assert of an interface type), or not checking the type in
all conditions. There is now a working implements method for itype.

Fixes #640.

* style: appease lint

* fix: remove useless code block

* doc: improve comments

* avoid test conflict
2020-06-10 11:21:16 +02:00
Nicholas Wiersma
def57d57c2 fix: dont assign receiver on indexExpr 2020-06-03 19:10:04 +02:00
mpl
74479d70e6 Travis: apply timeout multiplier on CI 2020-06-03 17:50:04 +02:00
Nicholas Wiersma
f1cff308e6 fix: handle printf changing nil type 2020-06-03 17:36:04 +02:00
mpl
6f878052f8 interp: detect local redeclaration (#673)
* interp: detect local redecleration

Fixes #666

* make make check happy

* fix typo in filenames, add them to the exclusion list

Co-authored-by: Marc Vertes <mvertes@free.fr>
2020-06-03 17:21:00 +02:00
Nicholas Wiersma
eb25c84797 fix: handle call ellipsis 2020-06-03 16:42:04 +02:00
Nicholas Wiersma
2de0c80d38 fix: handle range on chan function return 2020-06-03 16:30:05 +02:00
Nicholas Wiersma
151a856bf2 fix: composite literal child types for call expressions 2020-06-03 09:44:03 +02:00
Nicholas Wiersma
d5217f7db4 fix: handle nil interface conversion 2020-06-03 00:32:04 +02:00
Nicholas Wiersma
01e4cdea70 fix: recursive struct function call 2020-06-03 00:18:04 +02:00
Nicholas Wiersma
3c88542180 fix: handle empty interface conversion 2020-05-29 13:58:04 +02:00
Nicholas Wiersma
56d88ef89d fix: nil recover case 2020-05-29 13:28:03 +02:00
Nicholas Wiersma
5d78c8ae27 fix: compose recursion 2020-05-28 08:52:03 +02:00
Nicholas Wiersma
184623d81f Fix import source symbol package 2020-05-27 17:16:03 +02:00
mpl
5d56bac8d0 interp: extend dot debugging 2020-05-26 22:38:03 +02:00
291 changed files with 127372 additions and 123233 deletions

View File

@@ -38,6 +38,9 @@
"gocognit",
"stylecheck",
"gomnd",
"testpackage",
"goerr113",
"nestif",
]
[issues]

19
_test/assign13.go Normal file
View File

@@ -0,0 +1,19 @@
package main
import "fmt"
func getStr() string {
return "test"
}
func main() {
m := make(map[string]string, 0)
m["a"] = fmt.Sprintf("%v", 0.1)
m["b"] = string(fmt.Sprintf("%v", 0.1))
m["c"] = getStr()
fmt.Println(m)
}
// Output:
// map[a:0.1 b:0.1 c:test]

16
_test/assign14.go Normal file
View File

@@ -0,0 +1,16 @@
package main
var optionsG map[string]string = nil
var roundG = 30
func main() {
dummy := roundG
roundG = dummy + 1
println(roundG)
println(optionsG == nil)
}
// Output:
// 31
// true

11
_test/assign15.go Normal file
View File

@@ -0,0 +1,11 @@
package main
func main() {
var c chan<- struct{} = make(chan struct{})
var d <-chan struct{} = c
_ = d
}
// Error:
// _test/assign15.go:5:26: cannot use type chan<- struct{} as type <-chan struct{} in assignment

3
_test/baz-bat/baz-bat.go Normal file
View File

@@ -0,0 +1,3 @@
package baz
var Name = "baz-bat"

12
_test/chan10.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import "time"
func main() {
var tick <-chan time.Time = time.Tick(time.Millisecond)
_ = tick
println("success")
}
// Output:
// success

14
_test/comp2.go Normal file
View File

@@ -0,0 +1,14 @@
package main
type delta int32
func main() {
a := delta(-1)
println(a != -1)
println(a == -1)
}
// Output:
// false
// true

14
_test/composite10.go Normal file
View File

@@ -0,0 +1,14 @@
package main
import "fmt"
func main() {
a := []map[int]int{make(map[int]int)}
for _, b := range a {
fmt.Println(b)
}
}
// Output:
// map[]

15
_test/composite11.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import (
"fmt"
"image/color"
)
func main() {
c := color.NRGBA64{1, 1, 1, 1}
fmt.Println(c)
}
// Output:
// {1 1 1 1}

14
_test/composite9.go Normal file
View File

@@ -0,0 +1,14 @@
package main
import "fmt"
func main() {
a := [][]int{make([]int,0)}
for _, b := range a {
fmt.Println(b)
}
}
// Output:
// []

16
_test/const13.go Normal file
View File

@@ -0,0 +1,16 @@
package main
import (
"fmt"
)
func main() {
const tooBig = 1267650600228229401496703205376
const huge = 1 << 100
const large = huge >> 38
fmt.Println(large)
}
// Output:
// 4611686018427387904

13
_test/const14.go Normal file
View File

@@ -0,0 +1,13 @@
package main
import "compress/flate"
func f1(i int) { println("i:", i) }
func main() {
i := flate.BestSpeed
f1(i)
}
// Output:
// i: 1

17
_test/const15.go Normal file
View File

@@ -0,0 +1,17 @@
package main
type T1 t1
type t1 int8
const (
P2 T1 = 2
P3 T1 = 3
)
func main() {
println(P3)
}
// Output:
// 3

32
_test/defer5.go Normal file
View File

@@ -0,0 +1,32 @@
package main
func f1() {
defer println("f1-begin")
f2()
defer println("f1-end")
}
func f2() {
defer println("f2-begin")
f3()
defer println("f2-end")
}
func f3() {
defer println("f3-begin")
println("hello")
defer println("f3-end")
}
func main() {
f1()
}
// Output:
// hello
// f3-end
// f3-begin
// f2-end
// f2-begin
// f1-end
// f1-begin

27
_test/defer6.go Normal file
View File

@@ -0,0 +1,27 @@
package main
func f1() {
defer print("f1-begin ")
f2()
defer print("f1-end ")
}
func f2() {
defer print("f2-begin ")
f3()
defer print("f2-end ")
}
func f3() {
defer print("f3-begin ")
print("hello ")
defer print("f3-end ")
}
func main() {
f1()
println()
}
// Output:
// hello f3-end f3-begin f2-end f2-begin f1-end f1-begin

18
_test/defer7.go Normal file
View File

@@ -0,0 +1,18 @@
package main
import "fmt"
func f1(in, out []string) {
defer copy(out, in)
}
func main() {
in := []string{"foo", "bar"}
out := make([]string, 2)
f1(in, out)
fmt.Println(out)
}
// Output:
// [foo bar]

24
_test/defer8.go Normal file
View File

@@ -0,0 +1,24 @@
package main
import "fmt"
func f1(m map[string]string) {
defer delete(m, "foo")
defer delete(m, "test")
fmt.Println(m)
}
func main() {
m := map[string]string{
"foo": "bar",
"baz": "bat",
}
f1(m)
fmt.Println(m)
}
// Output:
// map[baz:bat foo:bar]
// map[baz:bat]

21
_test/defer9.go Normal file
View File

@@ -0,0 +1,21 @@
package main
import "fmt"
func f1(ch chan string) {
defer close(ch)
ch <- "foo"
}
func main() {
ch := make(chan string, 1)
f1(ch)
for s := range ch {
fmt.Println(s)
}
}
// Output:
// foo

10
_test/import9.go Normal file
View File

@@ -0,0 +1,10 @@
package main
import "github.com/containous/yaegi/_test/baz-bat"
func main() {
println(baz.Name)
}
// Output:
// baz-bat

12
_test/init1.go Normal file
View File

@@ -0,0 +1,12 @@
package main
func init() {
println("here")
}
func main() {
init()
}
// Error:
// _test/init1.go:8:2: undefined: init

12
_test/interface42.go Normal file
View File

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

12
_test/interface43.go Normal file
View File

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

19
_test/interface44.go Normal file
View File

@@ -0,0 +1,19 @@
package main
type S struct {
a int
}
func main() {
var i interface{} = S{a: 1}
s, ok := i.(S)
if !ok {
println("bad")
return
}
println(s.a)
}
// Output:
// 1

13
_test/interface45.go Normal file
View File

@@ -0,0 +1,13 @@
package main
import "fmt"
func main() {
var i interface{} = 1
var s struct{}
s, _ = i.(struct{})
fmt.Println(s)
}
// Output:
// {}

29
_test/issue-735.go Normal file
View File

@@ -0,0 +1,29 @@
package main
import (
"fmt"
"strconv"
)
var optionsG map[string]string
var roundG int = 30
func strToInt(s string, defaultValue int) int {
n, err := strconv.ParseInt(s, 10, 0)
if err != nil {
return defaultValue
}
return int(n)
}
func main() {
optionsG := map[string]string{"round": "12", "b": "one"}
roundG = strToInt(optionsG["round"], 50)
fmt.Println(roundG)
fmt.Println(optionsG)
}
// Output:
// 12
// map[b:one round:12]

12
_test/make2.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import "fmt"
func main() {
var s uint = 4
t := make([]int, s)
fmt.Println(t)
}
// Output:
// [0 0 0 0]

26
_test/map29.go Normal file
View File

@@ -0,0 +1,26 @@
package main
import (
"fmt"
"time"
)
type Item struct {
Object interface{}
Expiry time.Duration
}
func main() {
items := map[string]Item{}
items["test"] = Item{
Object: "test",
Expiry: time.Second,
}
item := items["test"]
fmt.Println(item)
}
// Output:
// {test 1s}

18
_test/method32.go Normal file
View File

@@ -0,0 +1,18 @@
package main
import (
"fmt"
)
func main() {
var a = []func(string){bar}
b := a[0]
b("bar")
}
func bar(a string) {
fmt.Println(a)
}
// Output:
// bar

58
_test/method33.go Normal file
View File

@@ -0,0 +1,58 @@
package main
import (
"fmt"
)
type T1 struct{}
func (t1 T1) f() {
fmt.Println("T1.f()")
}
func (t1 T1) g() {
fmt.Println("T1.g()")
}
type T2 struct {
T1
}
func (t2 T2) f() {
fmt.Println("T2.f()")
}
type I interface {
f()
}
func printType(i I) {
if t1, ok := i.(T1); ok {
println("T1 ok")
t1.f()
t1.g()
}
if t2, ok := i.(T2); ok {
println("T2 ok")
t2.f()
t2.g()
}
}
func main() {
println("T1")
printType(T1{})
println("T2")
printType(T2{})
}
// Output:
// T1
// T1 ok
// T1.f()
// T1.g()
// T2
// T2 ok
// T2.f()
// T1.g()

23
_test/method34.go Normal file
View File

@@ -0,0 +1,23 @@
package main
type Root struct {
Name string
}
type One struct {
Root
}
type Hi interface {
Hello() string
}
func (r *Root) Hello() string { return "Hello " + r.Name }
func main() {
var one interface{} = &One{Root{Name: "test2"}}
println(one.(Hi).Hello())
}
// Output:
// Hello test2

17
_test/op6.go Normal file
View File

@@ -0,0 +1,17 @@
package main
type T int
func (t T) Error() string { return "T: error" }
var invalidT T
func main() {
var err error
if err != invalidT {
println("ok")
}
}
// Output:
// ok

17
_test/op7.go Normal file
View File

@@ -0,0 +1,17 @@
package main
type T int
func (t T) Error() string { return "T: error" }
var invalidT T
func main() {
var err error
if err > invalidT {
println("ok")
}
}
// Error:
// _test/op7.go:11:5: invalid operation: operator > not defined on error

21
_test/op8.go Normal file
View File

@@ -0,0 +1,21 @@
package main
type I interface {
Get() interface{}
}
type T struct{}
func (T) Get() interface{} {
return nil
}
func main() {
var i I = T{}
var ei interface{}
println(i != ei)
}
// Output:
// true

11
_test/op9.go Normal file
View File

@@ -0,0 +1,11 @@
package main
func main() {
var i complex128 = 1i
var f complex128 = 0.4i
print(i > f)
}
// Error:
// _test/op9.go:7:8: invalid operation: operator > not defined on complex128

21
_test/range7.go Normal file
View File

@@ -0,0 +1,21 @@
package main
import (
"fmt"
)
func someChan() <-chan struct{} {
c := make(chan struct{}, 1)
c <- struct{}{}
return c
}
func main() {
for _ = range someChan() {
fmt.Println("success")
return
}
}
// Output:
// success

16
_test/range8.go Normal file
View File

@@ -0,0 +1,16 @@
package main
import (
"fmt"
"time"
)
func main() {
for _ = range time.Tick(time.Millisecond) {
fmt.Println("success")
return
}
}
// Output:
// success

11
_test/range9.go Normal file
View File

@@ -0,0 +1,11 @@
package main
func main() {
var c chan<- struct{} = make(chan struct{})
for _ = range c {
}
}
// Error:
// _test/range9.go:6:16: invalid operation: range c receive from send-only channel

15
_test/recover2.go Normal file
View File

@@ -0,0 +1,15 @@
package main
func main() {
println("hello")
var r interface{} = 1
r = recover()
if r == nil {
println("world")
}
}
// Output:
// hello
// world

22
_test/recover3.go Normal file
View File

@@ -0,0 +1,22 @@
package main
import "fmt"
func main() {
println("hello")
var r interface{} = 1
r = recover()
fmt.Printf("%v\n", r)
if r == nil {
println("world")
}
if r != nil {
println("exception")
}
}
// Output:
// hello
// <nil>
// world

25
_test/recover4.go Normal file
View File

@@ -0,0 +1,25 @@
package main
import "fmt"
func div(a, b int) (result int) {
defer func() {
r := recover()
fmt.Printf("r = %#v\n", r)
if r != nil {
result = 0
}
}()
return a / b
}
func main() {
println(div(30, 2))
}
// Output:
// r = <nil>
// 15

View File

@@ -0,0 +1,14 @@
package main
type time int
var time string
func main() {
time = "hello"
println(time)
}
// Error:
// ../_test/redeclaration-global0.go:5:5: time redeclared in this block
// previous declaration at ../_test/redeclaration-global0.go:3:6

View File

@@ -0,0 +1,12 @@
package main
var time int
type time string
func main() {
var t time = "hello"
println(t)
}
// TODO: expected redeclaration error.

View File

@@ -0,0 +1,14 @@
package main
import (
"time"
"time"
)
func main() {
var t time.Time
println(t.String())
}
// Error:
// ../_test/redeclaration-global2.go:5:2: time/redeclaration-global2.go redeclared in this block

View File

@@ -0,0 +1,15 @@
package main
import (
"time"
)
var time string
func main() {
time = "hello"
println(t)
}
// Error:
// ../_test/redeclaration-global3.go:7:5: time redeclared in this block

View File

@@ -0,0 +1,15 @@
package main
import (
"time"
)
type time string
func main() {
var t time = "hello"
println(t)
}
// Error:
// ../_test/redeclaration-global4.go:7:6: time redeclared in this block

View File

@@ -0,0 +1,15 @@
package main
var time int
func time() string {
return "hello"
}
func main() {
t := time()
println(t)
}
// Error:
// ../_test/redeclaration-global5.go:5:1: time redeclared in this block

View File

@@ -0,0 +1,17 @@
package main
import (
"time"
)
func time() string {
return "hello"
}
func main() {
t := time()
println(t)
}
// Error:
// ../_test/redeclaration-global6.go:7:1: time redeclared in this block

15
_test/redeclaration0.go Normal file
View File

@@ -0,0 +1,15 @@
package main
func main() {
type foo struct {
yolo string
}
var foo int
foo = 2
println(foo)
}
// Error:
// ../_test/redeclaration0.go:8:6: foo redeclared in this block
// previous declaration at ../_test/redeclaration0.go:4:7

12
_test/redeclaration1.go Normal file
View File

@@ -0,0 +1,12 @@
package main
func main() {
var foo string
var foo int
foo = 2
println(foo)
}
// Error:
// ../_test/redeclaration1.go:6:6: foo redeclared in this block

15
_test/redeclaration2.go Normal file
View File

@@ -0,0 +1,15 @@
package main
func main() {
var foo struct {
yolo string
}
var foo int
foo = 2
println(foo)
}
// Error:
// ../_test/redeclaration2.go:8:6: foo redeclared in this block
// previous declaration at ../_test/redeclaration2.go:4:6

13
_test/redeclaration3.go Normal file
View File

@@ -0,0 +1,13 @@
package main
func main() {
var foo int
foo = 2
type foo struct{}
var bar foo
println(bar)
}
// Error:
// ../_test/redeclaration3.go:7:7: foo redeclared in this block

14
_test/redeclaration4.go Normal file
View File

@@ -0,0 +1,14 @@
package main
func main() {
var foo struct{
yolo string
}
type foo struct{}
var bar foo
println(bar)
}
// Error:
// ../_test/redeclaration4.go:8:7: foo redeclared in this block

14
_test/redeclaration5.go Normal file
View File

@@ -0,0 +1,14 @@
package main
func main() {
type foo struct{
yolo string
}
type foo struct{}
var bar foo
println(bar)
}
// Error:
// ../_test/redeclaration5.go:8:7: foo redeclared in this block

18
_test/restricted0.go Normal file
View File

@@ -0,0 +1,18 @@
package main
import (
"fmt"
"log"
)
func main() {
defer func() {
r := recover()
fmt.Println("recover:", r)
}()
log.Fatal("log.Fatal does not exit")
println("not printed")
}
// Output:
// recover: log.Fatal does not exit

18
_test/restricted1.go Normal file
View File

@@ -0,0 +1,18 @@
package main
import (
"fmt"
"os"
)
func main() {
defer func() {
r := recover()
fmt.Println("recover:", r)
}()
os.Exit(1)
println("not printed")
}
// Output:
// recover: os.Exit(1)

14
_test/restricted2.go Normal file
View File

@@ -0,0 +1,14 @@
package main
import (
"fmt"
"os"
)
func main() {
p, err := os.FindProcess(os.Getpid())
fmt.Println(p, err)
}
// Output:
// <nil> restricted

23
_test/restricted3.go Normal file
View File

@@ -0,0 +1,23 @@
package main
import (
"bytes"
"fmt"
"log"
)
var (
buf bytes.Buffer
logger = log.New(&buf, "logger: ", log.Lshortfile)
)
func main() {
defer func() {
r := recover()
fmt.Println("recover:", r, buf.String())
}()
logger.Fatal("test log")
}
// Output:
// recover: test log logger: restricted.go:39: test log

23
_test/select12.go Normal file
View File

@@ -0,0 +1,23 @@
package main
type S struct {
q chan struct{}
}
func (s *S) Send() {
select {
case s.q <- struct{}{}:
println("sent")
default:
println("unexpected")
}
}
func main() {
s := &S{q: make(chan struct{}, 1)}
s.Send()
println("bye")
}
// Output:
// sent
// bye

16
_test/select13.go Normal file
View File

@@ -0,0 +1,16 @@
package main
func main() {
var c interface{} = int64(1)
q := make(chan struct{})
select {
case q <- struct{}{}:
println("unexpected")
default:
_ = c.(int64)
}
println("bye")
}
// Output:
// bye

18
_test/selector-scope0.go Normal file
View File

@@ -0,0 +1,18 @@
package main
import (
"fmt"
"time"
)
func test(time string, t time.Time) string {
return time
}
func main() {
str := test("test", time.Now())
fmt.Println(str)
}
// Output:
// test

View File

@@ -1,15 +1,23 @@
package main
import (
"encoding/json"
"os"
)
type S struct {
Child []*S
Name string
Child []*S
}
func main() {
s := &S{Name: "root"}
s.Child = append(s.Child, &S{Name: "child"})
println(s.Child[0].Name)
a := S{Name: "hello"}
a.Child = append(a.Child, &S{Name: "world"})
json.NewEncoder(os.Stdout).Encode(a)
a.Child[0].Child = append([]*S{}, &S{Name: "sunshine"})
json.NewEncoder(os.Stdout).Encode(a)
}
// Output:
// child
// {"Name":"hello","Child":[{"Name":"world","Child":null}]}
// {"Name":"hello","Child":[{"Name":"world","Child":[{"Name":"sunshine","Child":null}]}]}

21
_test/struct46.go Normal file
View File

@@ -0,0 +1,21 @@
package main
import "fmt"
type A struct {
B string
C D
}
type D struct {
E *A
}
func main() {
a := &A{B: "b"}
a.C = D{E: a}
fmt.Println(a.C.E.B)
}
// Output:
// b

26
_test/struct47.go Normal file
View File

@@ -0,0 +1,26 @@
package main
import "fmt"
type A struct {
B string
C D
}
func (a *A) Test() string {
return "test"
}
type D struct {
E *A
}
func main() {
a := &A{B: "b"}
d := D{E: a}
a.C = d
fmt.Println(a.C.E.Test())
}
// Output:
// test

37
_test/struct48.go Normal file
View File

@@ -0,0 +1,37 @@
package main
type List struct {
Next *List
Num int
}
func add(l *List, n int) *List {
if l == nil {
return &List{Num: n}
}
l.Next = add(l.Next, n)
return l
}
func pr(l *List) {
if l == nil {
println("")
return
}
print(l.Num)
pr(l.Next)
}
func main() {
a := add(nil, 0)
pr(a)
a = add(a, 1)
pr(a)
a = add(a, 2)
pr(a)
}
// Output:
// 0
// 01
// 012

33
_test/struct49.go Normal file
View File

@@ -0,0 +1,33 @@
package main
type S struct {
ts map[string][]*T
}
type T struct {
s *S
}
func (c *S) getT(addr string) (t *T, ok bool) {
cns, ok := c.ts[addr]
if !ok || len(cns) == 0 {
return nil, false
}
t = cns[len(cns)-1]
c.ts[addr] = cns[:len(cns)-1]
return t, true
}
func main() {
s := &S{
ts: map[string][]*T{},
}
s.ts["test"] = append(s.ts["test"], &T{s: s})
t , ok:= s.getT("test")
println(t != nil, ok)
}
// Output:
// true true

20
_test/struct50.go Normal file
View File

@@ -0,0 +1,20 @@
package main
import "fmt"
type Node struct {
Name string
Child []Node
}
func main() {
a := Node{Name: "hello"}
a.Child = append([]Node{}, Node{Name: "world"})
fmt.Println(a)
a.Child[0].Child = append([]Node{}, Node{Name: "sunshine"})
fmt.Println(a)
}
// Output:
// {hello [{world []}]}
// {hello [{world [{sunshine []}]}]}

23
_test/struct51.go Normal file
View File

@@ -0,0 +1,23 @@
package main
import (
"encoding/json"
"os"
)
type Node struct {
Name string
Child [2]*Node
}
func main() {
a := Node{Name: "hello"}
a.Child[0] = &Node{Name: "world"}
json.NewEncoder(os.Stdout).Encode(a)
a.Child[0].Child[0] = &Node{Name: "sunshine"}
json.NewEncoder(os.Stdout).Encode(a)
}
// Output:
// {"Name":"hello","Child":[{"Name":"world","Child":[null,null]},null]}
// {"Name":"hello","Child":[{"Name":"world","Child":[{"Name":"sunshine","Child":[null,null]},null]},null]}

20
_test/struct52.go Normal file
View File

@@ -0,0 +1,20 @@
package main
import "fmt"
type Node struct {
Name string
Child map[string]Node
}
func main() {
a := Node{Name: "hello", Child: map[string]Node{}}
a.Child["1"] = Node{Name: "world", Child: map[string]Node{}}
fmt.Println(a)
a.Child["1"].Child["1"] = Node{Name: "sunshine", Child: map[string]Node{}}
fmt.Println(a)
}
// Output:
// {hello map[1:{world map[]}]}
// {hello map[1:{world map[1:{sunshine map[]}]}]}

23
_test/struct53.go Normal file
View File

@@ -0,0 +1,23 @@
package main
import "fmt"
type T1 struct {
P []*T
}
type T2 struct {
P2 *T
}
type T struct {
*T1
S1 *T
}
func main() {
fmt.Println(T2{})
}
// Output:
// {<nil>}

26
_test/struct54.go Normal file
View File

@@ -0,0 +1,26 @@
package main
type S struct {
t *T
}
func newS() *S {
return &S{
t: &T{u: map[string]*U{}},
}
}
type T struct {
u map[string]*U
}
type U struct {
a int
}
func main() {
s := newS()
_ = s
println("ok")
}

28
_test/switch38.go Normal file
View File

@@ -0,0 +1,28 @@
package main
func isSeparator(c byte) bool {
switch c {
case '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ', '\t':
return true
}
return false
}
func main() {
s := "max-age=20"
for _, c := range []byte(s) {
println(string(c), isSeparator(c))
}
}
// Output:
// m false
// a false
// x false
// - false
// a false
// g false
// e false
// = true
// 2 false
// 0 false

18
_test/time13.go Normal file
View File

@@ -0,0 +1,18 @@
package main
import (
"fmt"
"time"
)
var dummy = 1
var t time.Time = time.Date(2007, time.November, 10, 23, 4, 5, 0, time.UTC)
func main() {
t = time.Date(2009, time.November, 10, 23, 4, 5, 0, time.UTC)
fmt.Println(t.Clock())
}
// Output:
// 23 4 5

38
_test/type23.go Normal file
View File

@@ -0,0 +1,38 @@
package main
import (
"net/http"
"net/http/httptest"
)
func main() {
var v1 interface{} = 1
var v2 interface{}
var v3 http.ResponseWriter = httptest.NewRecorder()
if r1, ok := v1.(string); ok {
_ = r1
println("unexpected")
}
if _, ok := v1.(string); ok {
println("unexpected")
}
if r2, ok := v2.(string); ok {
_ = r2
println("unexpected")
}
if _, ok := v2.(string); ok {
println("unexpected")
}
if r3, ok := v3.(http.Pusher); ok {
_ = r3
println("unexpected")
}
if _, ok := v3.(http.Pusher); ok {
println("unexpected")
}
println("bye")
}
// Output:
// bye

48
_test/type24.go Normal file
View File

@@ -0,0 +1,48 @@
package main
import (
"fmt"
"net/http"
"net/http/httptest"
)
func main() {
assertInt()
assertNil()
assertValue()
}
func assertInt() {
defer func() {
r := recover()
fmt.Println(r)
}()
var v interface{} = 1
println(v.(string))
}
func assertNil() {
defer func() {
r := recover()
fmt.Println(r)
}()
var v interface{}
println(v.(string))
}
func assertValue() {
defer func() {
r := recover()
fmt.Println(r)
}()
var v http.ResponseWriter = httptest.NewRecorder()
println(v.(http.Pusher))
}
// Output:
// interface conversion: interface {} is int, not string
// interface conversion: interface {} is nil, not string
// interface conversion: *httptest.ResponseRecorder is not http.Pusher: missing method Push

44
_test/type25.go Normal file
View File

@@ -0,0 +1,44 @@
package main
import (
"errors"
"sync/atomic"
)
type wrappedError struct {
wrapped error
}
func (e wrappedError) Error() string {
return "some outer error"
}
func (e wrappedError) Unwrap() error {
return e.wrapped
}
var err atomic.Value
func getWrapped() *wrappedError {
if v := err.Load(); v != nil {
err := v.(wrappedError)
if err.wrapped != nil {
return &err
}
}
return nil
}
func main() {
err.Store(wrappedError{wrapped: errors.New("test")})
e := getWrapped()
if e != nil {
println(e.Error())
println(e.wrapped.Error())
}
}
// Output:
// some outer error
// test

44
_test/type26.go Normal file
View File

@@ -0,0 +1,44 @@
package main
import (
"errors"
"sync/atomic"
)
type wrappedError struct {
wrapped error
}
func (e *wrappedError) Error() string {
return "some outer error"
}
func (e *wrappedError) Unwrap() error {
return e.wrapped
}
var err atomic.Value
func getWrapped() *wrappedError {
if v := err.Load(); v != nil {
err := v.(*wrappedError)
if err.wrapped != nil {
return err
}
}
return nil
}
func main() {
err.Store(&wrappedError{wrapped: errors.New("test")})
e := getWrapped()
if e != nil {
println(e.Error())
println(e.wrapped.Error())
}
}
// Output:
// some outer error
// test

15
_test/unsafe0.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import "unsafe"
func main() {
str := "foobar"
p := unsafe.Pointer(&str)
str2 := *(*string)(p)
println(str2)
}
// Output:
// foobar

20
_test/unsafe1.go Normal file
View File

@@ -0,0 +1,20 @@
package main
import "unsafe"
type S struct {
Name string
}
func main() {
s := &S{Name: "foobar"}
p := unsafe.Pointer(s)
s2 := (*S)(p)
println(s2.Name)
}
// Output:
// foobar

20
_test/unsafe2.go Normal file
View File

@@ -0,0 +1,20 @@
package main
import (
"fmt"
"unsafe"
)
func main() {
str := "foobar"
ptr := unsafe.Pointer(&str)
p := uintptr(ptr)
s1 := fmt.Sprintf("%x", ptr)
s2 := fmt.Sprintf("%x", p)
println(s1 == s2)
}
// Output:
// true

26
_test/unsafe3.go Normal file
View File

@@ -0,0 +1,26 @@
package main
import (
"fmt"
"unsafe"
)
const SSize = 16
type S struct {
X int
Y int
}
func main() {
var sBuf [SSize]byte
s := (*S)(unsafe.Pointer(&sBuf[0]))
s.X = 2
s.Y = 4
fmt.Println(sBuf)
}
// Output:
// [2 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0]

29
_test/unsafe4.go Normal file
View File

@@ -0,0 +1,29 @@
package main
import (
"fmt"
"unsafe"
)
const SSize = 24
type S struct {
X int
Y int
Z int
}
func main() {
arr := []S{
{X: 1},
{X: 2},
{X: 3},
}
addr := unsafe.Pointer(&arr[0])
s := *(*S)(unsafe.Pointer(uintptr(addr) + SSize*2))
fmt.Println(s.X)
}
// Output:
// 3

22
_test/unsafe5.go Normal file
View File

@@ -0,0 +1,22 @@
package main
import (
"fmt"
"unsafe"
)
type S struct {
X int
Y int
Z int
}
func main() {
size := unsafe.Sizeof(S{})
align := unsafe.Alignof(S{})
fmt.Println(size, align)
}
// Output:
// 24 8

13
_test/var12.go Normal file
View File

@@ -0,0 +1,13 @@
package main
var (
a = b
b = "hello"
)
func main() {
println(a)
}
// Output:
// hello

23
_test/var13.go Normal file
View File

@@ -0,0 +1,23 @@
package main
var (
a = concat("hello", b)
b = concat(" ", c, "!")
c = d
d = "world"
)
func concat(a ...string) string {
var s string
for _, ss := range a {
s += ss
}
return s
}
func main() {
println(a)
}
// Output:
// hello world!

10
_test/var14.go Normal file
View File

@@ -0,0 +1,10 @@
package main
import "github.com/containous/yaegi/_test/vars"
func main() {
println(vars.A)
}
// Output:
// hello world!

20
_test/variadic7.go Normal file
View File

@@ -0,0 +1,20 @@
package main
import "fmt"
func main() {
var a, b string
pattern := "%s %s"
dest := []interface{}{&a, &b}
n, err := fmt.Sscanf("test1 test2", pattern, dest...)
if err != nil || n != len(dest) {
println("error")
return
}
println(a, b)
}
// Output:
// test1 test2

17
_test/variadic8.go Normal file
View File

@@ -0,0 +1,17 @@
package main
import (
"fmt"
"time"
)
func main() {
fn1 := func(args ...*time.Duration) string {
return ""
}
fmt.Printf("%T\n", fn1)
}
// Output:
// func(...*time.Duration) string

14
_test/variadic9.go Normal file
View File

@@ -0,0 +1,14 @@
package main
import "fmt"
func Sprintf(format string, a ...interface{}) string {
return fmt.Sprintf(format, a...)
}
func main() {
fmt.Println(Sprintf("Hello %s", "World!"))
}
// Output:
// Hello World!

14
_test/vars/first.go Normal file
View File

@@ -0,0 +1,14 @@
package vars
var (
A = concat("hello", B)
C = D
)
func concat(a ...string) string {
var s string
for _, ss := range a {
s += ss
}
return s
}

6
_test/vars/second.go Normal file
View File

@@ -0,0 +1,6 @@
package vars
var (
B = concat(" ", C, "!")
D = "world"
)

View File

@@ -30,6 +30,7 @@ import (
"go/types"
"io/ioutil"
"log"
"math/big"
"os"
"path"
"runtime"
@@ -97,24 +98,35 @@ func init() {
{{end}}
`
// Val store the value name and addressable status of symbols
// Val store the value name and addressable status of symbols.
type Val struct {
Name string // "package.name"
Addr bool // true if symbol is a Var
}
// Method store information for generating interface wrapper method
// Method store information for generating interface wrapper method.
type Method struct {
Name, Param, Result, Arg, Ret string
}
// Wrap store information for generating interface wrapper
// Wrap store information for generating interface wrapper.
type Wrap struct {
Name string
Method []Method
}
func genContent(dest, pkgName, license string) ([]byte, error) {
// restricted map defines symbols for which a special implementation is provided.
var restricted = map[string]bool{
"osExit": true,
"osFindProcess": true,
"logFatal": true,
"logFatalf": true,
"logFatalln": true,
"logLogger": true,
"logNew": true,
}
func genContent(dest, pkgName, license string, skip map[string]bool) ([]byte, error) {
p, err := importer.ForCompiler(token.NewFileSet(), "source", nil).Import(pkgName)
if err != nil {
return nil, err
@@ -146,11 +158,19 @@ func genContent(dest, pkgName, license string) ([]byte, error) {
}
pname := path.Base(pkgName) + "." + name
if skip[pname] {
continue
}
if rname := path.Base(pkgName) + name; restricted[rname] {
// Restricted symbol, locally provided by stdlib wrapper.
pname = rname
}
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
val[name] = Val{fixConst(pname, o.Val()), false}
val[name] = Val{fixConst(pname, o.Val(), imports), false}
} else {
val[name] = Val{pname, false}
}
@@ -252,36 +272,36 @@ func genContent(dest, pkgName, license string) ([]byte, error) {
return source, nil
}
// fixConst checks untyped constant value, converting it if necessary to avoid overflow
func fixConst(name string, val constant.Value) string {
// fixConst checks untyped constant value, converting it if necessary to avoid overflow.
func fixConst(name string, val constant.Value, imports map[string]bool) string {
var (
tok string
str string
)
switch val.Kind() {
case constant.Float:
str := val.ExactString()
if _, err := strconv.ParseFloat(str, 32); err == nil {
return "float32(" + name + ")"
}
return name
case constant.Int:
str := val.ExactString()
i, err := strconv.ParseInt(str, 0, 64)
if err == nil {
switch {
case i == int64(int32(i)):
return name
case i == int64(uint32(i)):
return "uint32(" + name + ")"
default:
return "int64(" + name + ")"
}
tok = "INT"
str = val.ExactString()
case constant.Float:
v := constant.Val(val) // v is *big.Rat or *big.Float
f, ok := v.(*big.Float)
if !ok {
f = new(big.Float).SetRat(v.(*big.Rat))
}
_, err = strconv.ParseUint(str, 0, 64)
if err == nil {
return "uint64(" + name + ")"
}
return name
tok = "FLOAT"
str = f.Text('g', int(f.Prec()))
case constant.Complex:
// TODO: not sure how to parse this case
fallthrough
default:
return name
}
imports["go/constant"] = true
imports["go/token"] = true
return fmt.Sprintf("constant.MakeFromLiteral(\"%s\", token.%s, 0)", str, tok)
}
// genLicense generates the correct LICENSE header text from the provided
@@ -335,20 +355,25 @@ func main() {
dest := path.Base(dir)
for _, pkg := range flag.Args() {
content, err := genContent(dest, pkg, license)
if err != nil {
log.Println(err)
continue
}
var oFile string
skip := map[string]bool{}
if pkg == "syscall" {
goos, arch := os.Getenv("GOOS"), os.Getenv("GOARCH")
oFile = strings.Replace(pkg, "/", "_", -1) + "_" + goos + "_" + arch + ".go"
if goos == "solaris" {
skip["syscall.RawSyscall6"] = true
skip["syscall.Syscall6"] = true
}
} else {
oFile = strings.Replace(pkg, "/", "_", -1) + ".go"
}
content, err := genContent(dest, pkg, license, skip)
if err != nil {
log.Println(err)
continue
}
prefix := runtime.Version()
if runtime.Version() != "devel" {
parts := strings.Split(runtime.Version(), ".")

View File

@@ -63,15 +63,24 @@ Options:
evaluate the string and return.
-i
start an interactive REPL after file execution.
-syscall
include syscall symbols.
-tags tag,list
a comma-separated list of build tags to consider satisfied during
the interpretation.
-unsafe
include unsafe symbols.
Debugging support (may be removed at any time):
YAEGI_AST_DOT=1
Generate and display graphviz dot of AST with dotty(1)
YAEGI_CFG_DOT=1
Generate and display graphviz dot of CFG with dotty(1)
YAEGI_DOT_CMD='dot -Tsvg -ofoo.svg'
Defines how to process the dot code generated whenever YAEGI_AST_DOT and/or
YAEGI_CFG_DOT is enabled. If any of YAEGI_AST_DOT or YAEGI_CFG_DOT is set,
but YAEGI_DOT_CMD is not defined, the default is to write to a .dot file
next to the Go source file.
*/
package main
@@ -86,14 +95,23 @@ import (
"github.com/containous/yaegi/interp"
"github.com/containous/yaegi/stdlib"
"github.com/containous/yaegi/stdlib/syscall"
"github.com/containous/yaegi/stdlib/unrestricted"
"github.com/containous/yaegi/stdlib/unsafe"
)
func main() {
var interactive bool
var useSyscall bool
var useUnrestricted bool
var useUnsafe bool
var tags string
var cmd string
flag.BoolVar(&interactive, "i", false, "start an interactive REPL")
flag.BoolVar(&useSyscall, "syscall", false, "include syscall symbols")
flag.BoolVar(&useUnrestricted, "unrestricted", false, "include unrestricted symbols")
flag.StringVar(&tags, "tags", "", "set a list of build tags")
flag.BoolVar(&useUnsafe, "unsafe", false, "include usafe symbols")
flag.StringVar(&cmd, "e", "", "set the command to be executed (instead of script or/and shell)")
flag.Usage = func() {
fmt.Println("Usage:", os.Args[0], "[options] [script] [args]")
@@ -107,6 +125,16 @@ func main() {
i := interp.New(interp.Options{GoPath: build.Default.GOPATH, BuildTags: strings.Split(tags, ",")})
i.Use(stdlib.Symbols)
i.Use(interp.Symbols)
if useSyscall {
i.Use(syscall.Symbols)
}
if useUnsafe {
i.Use(unsafe.Symbols)
}
if useUnrestricted {
// Use of unrestricted symbols should always follow use of stdlib symbols, to update them.
i.Use(unrestricted.Symbols)
}
if cmd != `` {
i.REPL(strings.NewReader(cmd), os.Stderr)

View File

@@ -6,10 +6,34 @@ import (
"os"
"os/exec"
"path/filepath"
"strconv"
"testing"
"time"
)
const (
// CITimeoutMultiplier is the multiplier for all timeouts in the CI
CITimeoutMultiplier = 3
)
// Sleep pauses the current goroutine for at least the duration d.
func Sleep(d time.Duration) {
d = applyCIMultiplier(d)
time.Sleep(d)
}
func applyCIMultiplier(timeout time.Duration) time.Duration {
ci := os.Getenv("CI")
if ci == "" {
return timeout
}
b, err := strconv.ParseBool(ci)
if err != nil || !b {
return timeout
}
return time.Duration(float64(timeout) * CITimeoutMultiplier)
}
func TestYaegiCmdCancel(t *testing.T) {
tmp, err := ioutil.TempDir("", "yaegi-")
if err != nil {
@@ -56,7 +80,7 @@ func TestYaegiCmdCancel(t *testing.T) {
if err != nil {
t.Errorf("failed pipe test source to yaegi command: %v", err)
}
time.Sleep(100 * time.Millisecond)
Sleep(200 * time.Millisecond)
err = cmd.Process.Signal(os.Interrupt)
if err != nil {
t.Errorf("failed to send os.Interrupt to yaegi command: %v", err)

View File

@@ -0,0 +1,17 @@
package pkg
import (
"fmt"
"guthib.com/containous/fromage"
)
func Here() string {
return "root"
}
func NewSample() func() string {
return func() string {
return fmt.Sprintf("%s %s", Here(), fromage.Hello())
}
}

View File

@@ -0,0 +1,7 @@
package fromage
import "fmt"
func Hello() string {
return fmt.Sprint("Fromage")
}

View File

@@ -0,0 +1,11 @@
package main
import (
"fmt"
"guthib.com/containous/fromage"
)
func main() {
fmt.Print(fromage.Hello())
}

View File

@@ -0,0 +1,7 @@
package fromage
import "fmt"
func Hello() string {
return fmt.Sprint("Fromage")
}

View File

@@ -1,6 +1,11 @@
package pkg
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"testing"
@@ -10,9 +15,11 @@ import (
func TestPackages(t *testing.T) {
testCases := []struct {
desc string
goPath string
expected string
desc string
goPath string
expected string
topImport string
evalFile string
}{
{
desc: "vendor",
@@ -64,6 +71,18 @@ func TestPackages(t *testing.T) {
goPath: "./_pkg8/",
expected: "root Fromage!",
},
{
desc: "at the project root",
goPath: "./_pkg10/",
expected: "root Fromage",
topImport: "github.com/foo",
},
{
desc: "eval main that has vendored dep",
goPath: "./_pkg11/",
expected: "Fromage",
evalFile: "./_pkg11/src/foo/foo.go",
},
}
for _, test := range testCases {
@@ -78,20 +97,61 @@ func TestPackages(t *testing.T) {
i := interp.New(interp.Options{GoPath: goPath})
i.Use(stdlib.Symbols) // Use binary standard library
// Load pkg from sources
if _, err = i.Eval(`import "github.com/foo/pkg"`); err != nil {
t.Fatal(err)
var msg string
if test.evalFile != "" {
// setting i.Name as this is how it's actually done in cmd/yaegi
i.Name = test.evalFile
data, err := ioutil.ReadFile(test.evalFile)
if err != nil {
t.Fatal(err)
}
// TODO(mpl): this is brittle if we do concurrent tests and stuff, do better later.
stdout := os.Stdout
defer func() { os.Stdout = stdout }()
pr, pw, err := os.Pipe()
if err != nil {
t.Fatal(err)
}
os.Stdout = pw
if _, err := i.Eval(string(data)); err != nil {
t.Fatal(err)
}
var buf bytes.Buffer
errC := make(chan error)
go func() {
_, err := io.Copy(&buf, pr)
errC <- err
}()
if err := pw.Close(); err != nil {
t.Fatal(err)
}
if err := <-errC; err != nil {
t.Fatal(err)
}
msg = buf.String()
} else {
// Load pkg from sources
topImport := "github.com/foo/pkg"
if test.topImport != "" {
topImport = test.topImport
}
if _, err = i.Eval(fmt.Sprintf(`import "%s"`, topImport)); err != nil {
t.Fatal(err)
}
value, err := i.Eval(`pkg.NewSample()`)
if err != nil {
t.Fatal(err)
}
fn := value.Interface().(func() string)
msg = fn()
}
value, err := i.Eval(`pkg.NewSample()`)
if err != nil {
t.Fatal(err)
}
fn := value.Interface().(func() string)
msg := fn()
if msg != test.expected {
t.Errorf("Got %q, want %q", msg, test.expected)
}

View File

@@ -5,6 +5,7 @@ import (
"go/format"
"io/ioutil"
"log"
"strings"
"text/template"
)
@@ -12,7 +13,11 @@ const model = `package interp
// Code generated by 'go run ../internal/genop/genop.go'. DO NOT EDIT.
import "reflect"
import (
"go/constant"
"go/token"
"reflect"
)
// Arithmetic operators
{{range $name, $op := .Arithmetic}}
@@ -176,9 +181,22 @@ func {{$name}}(n *node) {
func {{$name}}Const(n *node) {
v0, v1 := n.child[0].rval, n.child[1].rval
isConst := (v0.IsValid() && isConstantValue(v0.Type())) && (v1.IsValid() && isConstantValue(v1.Type()))
t := n.typ.rtype
if isConst {
t = constVal
}
n.rval = reflect.New(t).Elem()
switch {
case isConst:
{{- if $op.Shift}}
s, _ := constant.Uint64Val(vConstantValue(v1))
v := constant.Shift(vConstantValue(v0), token.{{tokenFromName $name}}, uint(s))
n.rval.Set(reflect.ValueOf(v))
{{- else}}
v := constant.BinaryOp(vConstantValue(v0), token.{{tokenFromName $name}}, vConstantValue(v1))
n.rval.Set(reflect.ValueOf(v))
{{- end}}
{{- if $op.Str}}
case isString(t):
n.rval.SetString(v0.String() {{$op.Name}} v1.String())
@@ -354,23 +372,35 @@ func {{$name}}(n *node) {
{{end}}
{{range $name, $op := .Unary}}
func {{$name}}Const(n *node) {
v0 := n.child[0].rval
isConst := v0.IsValid() && isConstantValue(v0.Type())
t := n.typ.rtype
v := n.child[0].rval
if isConst {
t = constVal
}
n.rval = reflect.New(t).Elem()
{{- if $op.Bool}}
n.rval.SetBool({{$op.Name}} v.Bool())
if isConst {
v := constant.UnaryOp(token.{{tokenFromName $name}}, vConstantValue(v0), 0)
n.rval.Set(reflect.ValueOf(v))
} else {
n.rval.SetBool({{$op.Name}} v0.Bool())
}
{{- else}}
switch t.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n.rval.SetInt({{$op.Name}} v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
n.rval.SetUint({{$op.Name}} v.Uint())
switch {
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())
{{- if $op.Float}}
case reflect.Float32, reflect.Float64:
n.rval.SetFloat({{$op.Name}} v.Float())
case reflect.Complex64, reflect.Complex128:
n.rval.SetComplex({{$op.Name}} v.Complex())
case isFloat(t):
n.rval.SetFloat({{$op.Name}} v0.Float())
case isComplex(t):
n.rval.SetComplex({{$op.Name}} v0.Complex())
{{- end}}
}
{{- end}}
@@ -382,6 +412,83 @@ func {{$name}}(n *node) {
dest := genValueOutput(n, reflect.TypeOf(true))
c0, c1 := n.child[0], n.child[1]
{{- if or (eq $op.Name "==") (eq $op.Name "!=") }}
if c0.typ.cat == aliasT || c1.typ.cat == aliasT {
switch {
case c0.rval.IsValid():
i0 := c0.rval.Interface()
v1 := genValue(c1)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
i1 := v1(f).Interface()
if i0 != i1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
i1 := v1(f).Interface()
dest(f).SetBool(i0 {{$op.Name}} i1)
return tnext
}
}
case c1.rval.IsValid():
i1 := c1.rval.Interface()
v0 := genValue(c0)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
if i0 != i1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
dest(f).SetBool(i0 {{$op.Name}} i1)
return tnext
}
}
default:
v0 := genValue(c0)
v1 := genValue(c1)
if n.fnext != nil {
fnext := getExec(n.fnext)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
i1 := v1(f).Interface()
if i0 != i1 {
dest(f).SetBool(true)
return tnext
}
dest(f).SetBool(false)
return fnext
}
} else {
dest := genValue(n)
n.exec = func(f *frame) bltn {
i0 := v0(f).Interface()
i1 := v1(f).Interface()
dest(f).SetBool(i0 {{$op.Name}} i1)
return tnext
}
}
}
return
}
{{- end}}
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
case isString(t0) || isString(t1):
switch {
@@ -810,7 +917,7 @@ func {{$name}}(n *node) {
{{end}}
`
// Op define operator name and properties
// Op define operator name and properties.
type Op struct {
Name string // +, -, ...
Str bool // true if operator applies to string
@@ -822,6 +929,22 @@ type Op struct {
func main() {
base := template.New("goexports")
base.Funcs(template.FuncMap{
"tokenFromName": func(name string) string {
switch name {
case "andNot":
return "AND_NOT"
case "neg":
return "SUB"
case "pos":
return "ADD"
case "bitNot":
return "XOR"
default:
return strings.ToUpper(name)
}
},
})
parse, err := base.Parse(model)
if err != nil {
log.Fatal(err)

View File

@@ -3,6 +3,7 @@ package interp
import (
"fmt"
"go/ast"
"go/constant"
"go/parser"
"go/scanner"
"go/token"
@@ -11,10 +12,10 @@ import (
"sync/atomic"
)
// nkind defines the kind of AST, i.e. the grammar category
// nkind defines the kind of AST, i.e. the grammar category.
type nkind uint
// Node kinds for the go language
// Node kinds for the go language.
const (
undefNode nkind = iota
addressExpr
@@ -30,7 +31,10 @@ const (
caseBody
caseClause
chanType
chanTypeSend
chanTypeRecv
commClause
commClauseDefault
compositeLitExpr
constDecl
continueStmt
@@ -93,80 +97,83 @@ const (
)
var kinds = [...]string{
undefNode: "undefNode",
addressExpr: "addressExpr",
arrayType: "arrayType",
assignStmt: "assignStmt",
assignXStmt: "assignXStmt",
basicLit: "basicLit",
binaryExpr: "binaryExpr",
blockStmt: "blockStmt",
branchStmt: "branchStmt",
breakStmt: "breakStmt",
callExpr: "callExpr",
caseBody: "caseBody",
caseClause: "caseClause",
chanType: "chanType",
commClause: "commClause",
compositeLitExpr: "compositeLitExpr",
constDecl: "constDecl",
continueStmt: "continueStmt",
declStmt: "declStmt",
deferStmt: "deferStmt",
defineStmt: "defineStmt",
defineXStmt: "defineXStmt",
ellipsisExpr: "ellipsisExpr",
exprStmt: "exprStmt",
fallthroughtStmt: "fallthroughStmt",
fieldExpr: "fieldExpr",
fieldList: "fieldList",
fileStmt: "fileStmt",
forStmt0: "forStmt0",
forStmt1: "forStmt1",
forStmt2: "forStmt2",
forStmt3: "forStmt3",
forStmt3a: "forStmt3a",
forStmt4: "forStmt4",
forRangeStmt: "forRangeStmt",
funcDecl: "funcDecl",
funcType: "funcType",
funcLit: "funcLit",
goStmt: "goStmt",
gotoStmt: "gotoStmt",
identExpr: "identExpr",
ifStmt0: "ifStmt0",
ifStmt1: "ifStmt1",
ifStmt2: "ifStmt2",
ifStmt3: "ifStmt3",
importDecl: "importDecl",
importSpec: "importSpec",
incDecStmt: "incDecStmt",
indexExpr: "indexExpr",
interfaceType: "interfaceType",
keyValueExpr: "keyValueExpr",
labeledStmt: "labeledStmt",
landExpr: "landExpr",
lorExpr: "lorExpr",
mapType: "mapType",
parenExpr: "parenExpr",
rangeStmt: "rangeStmt",
returnStmt: "returnStmt",
selectStmt: "selectStmt",
selectorExpr: "selectorExpr",
selectorImport: "selectorImport",
sendStmt: "sendStmt",
sliceExpr: "sliceExpr",
starExpr: "starExpr",
structType: "structType",
switchStmt: "switchStmt",
switchIfStmt: "switchIfStmt",
typeAssertExpr: "typeAssertExpr",
typeDecl: "typeDecl",
typeSpec: "typeSpec",
typeSwitch: "typeSwitch",
unaryExpr: "unaryExpr",
valueSpec: "valueSpec",
varDecl: "varDecl",
undefNode: "undefNode",
addressExpr: "addressExpr",
arrayType: "arrayType",
assignStmt: "assignStmt",
assignXStmt: "assignXStmt",
basicLit: "basicLit",
binaryExpr: "binaryExpr",
blockStmt: "blockStmt",
branchStmt: "branchStmt",
breakStmt: "breakStmt",
callExpr: "callExpr",
caseBody: "caseBody",
caseClause: "caseClause",
chanType: "chanType",
chanTypeSend: "chanTypeSend",
chanTypeRecv: "chanTypeRecv",
commClause: "commClause",
commClauseDefault: "commClauseDefault",
compositeLitExpr: "compositeLitExpr",
constDecl: "constDecl",
continueStmt: "continueStmt",
declStmt: "declStmt",
deferStmt: "deferStmt",
defineStmt: "defineStmt",
defineXStmt: "defineXStmt",
ellipsisExpr: "ellipsisExpr",
exprStmt: "exprStmt",
fallthroughtStmt: "fallthroughStmt",
fieldExpr: "fieldExpr",
fieldList: "fieldList",
fileStmt: "fileStmt",
forStmt0: "forStmt0",
forStmt1: "forStmt1",
forStmt2: "forStmt2",
forStmt3: "forStmt3",
forStmt3a: "forStmt3a",
forStmt4: "forStmt4",
forRangeStmt: "forRangeStmt",
funcDecl: "funcDecl",
funcType: "funcType",
funcLit: "funcLit",
goStmt: "goStmt",
gotoStmt: "gotoStmt",
identExpr: "identExpr",
ifStmt0: "ifStmt0",
ifStmt1: "ifStmt1",
ifStmt2: "ifStmt2",
ifStmt3: "ifStmt3",
importDecl: "importDecl",
importSpec: "importSpec",
incDecStmt: "incDecStmt",
indexExpr: "indexExpr",
interfaceType: "interfaceType",
keyValueExpr: "keyValueExpr",
labeledStmt: "labeledStmt",
landExpr: "landExpr",
lorExpr: "lorExpr",
mapType: "mapType",
parenExpr: "parenExpr",
rangeStmt: "rangeStmt",
returnStmt: "returnStmt",
selectStmt: "selectStmt",
selectorExpr: "selectorExpr",
selectorImport: "selectorImport",
sendStmt: "sendStmt",
sliceExpr: "sliceExpr",
starExpr: "starExpr",
structType: "structType",
switchStmt: "switchStmt",
switchIfStmt: "switchIfStmt",
typeAssertExpr: "typeAssertExpr",
typeDecl: "typeDecl",
typeSpec: "typeSpec",
typeSwitch: "typeSwitch",
unaryExpr: "unaryExpr",
valueSpec: "valueSpec",
varDecl: "varDecl",
}
func (k nkind) String() string {
@@ -176,13 +183,13 @@ func (k nkind) String() string {
return "nKind(" + strconv.Itoa(int(k)) + ")"
}
// astError represents an error during AST build stage
// astError represents an error during AST build stage.
type astError error
// action defines the node action to perform at execution
// action defines the node action to perform at execution.
type action uint
// Node actions for the go language
// Node actions for the go language.
const (
aNop action = iota
aAddr
@@ -197,6 +204,7 @@ const (
aBitNot
aBranch
aCall
aCallSlice
aCase
aCompositeLit
aConvert
@@ -242,6 +250,7 @@ const (
aTypeAssert
aXor
aXorAssign
aMax
)
var actions = [...]string{
@@ -258,6 +267,7 @@ var actions = [...]string{
aBitNot: "^",
aBranch: "branch",
aCall: "call",
aCallSlice: "callSlice",
aCase: "case",
aCompositeLit: "compositeLit",
aConvert: "convert",
@@ -470,14 +480,14 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
v, _, _, _ := strconv.UnquoteChar(a.Value[1:len(a.Value)-1], '\'')
n.rval = reflect.ValueOf(v)
case token.FLOAT:
v, _ := strconv.ParseFloat(a.Value, 64)
v := constant.MakeFromLiteral(a.Value, a.Kind, 0)
n.rval = reflect.ValueOf(v)
case token.IMAG:
v, _ := strconv.ParseFloat(a.Value[:len(a.Value)-1], 64)
n.rval = reflect.ValueOf(complex(0, v))
v := constant.MakeFromLiteral(a.Value, a.Kind, 0)
n.rval = reflect.ValueOf(v)
case token.INT:
v, _ := strconv.ParseInt(a.Value, 0, 0)
n.rval = reflect.ValueOf(int(v))
v := constant.MakeFromLiteral(a.Value, a.Kind, 0)
n.rval = reflect.ValueOf(v)
case token.STRING:
v, _ := strconv.Unquote(a.Value)
n.rval = reflect.ValueOf(v)
@@ -549,16 +559,32 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
st.push(addChild(&root, anc, pos, kind, aNop), nod)
case *ast.CallExpr:
st.push(addChild(&root, anc, pos, callExpr, aCall), nod)
action := aCall
if a.Ellipsis != token.NoPos {
action = aCallSlice
}
st.push(addChild(&root, anc, pos, callExpr, action), nod)
case *ast.CaseClause:
st.push(addChild(&root, anc, pos, caseClause, aCase), nod)
case *ast.ChanType:
st.push(addChild(&root, anc, pos, chanType, aNop), nod)
switch a.Dir {
case ast.SEND | ast.RECV:
st.push(addChild(&root, anc, pos, chanType, aNop), nod)
case ast.SEND:
st.push(addChild(&root, anc, pos, chanTypeSend, aNop), nod)
case ast.RECV:
st.push(addChild(&root, anc, pos, chanTypeRecv, aNop), nod)
}
case *ast.CommClause:
st.push(addChild(&root, anc, pos, commClause, aNop), nod)
kind := commClause
if a.Comm == nil {
kind = commClauseDefault
}
st.push(addChild(&root, anc, pos, kind, aNop), nod)
case *ast.CommentGroup:
return false
@@ -856,7 +882,7 @@ func (s *nodestack) top() astNode {
return astNode{}
}
// dup returns a duplicated node subtree
// dup returns a duplicated node subtree.
func (interp *Interpreter) dup(nod, anc *node) *node {
nindex := atomic.AddInt64(&interp.nindex, 1)
n := *nod

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