Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d64563edee | ||
|
|
07039262a0 | ||
|
|
4ed9ccb5c4 | ||
|
|
25edcfee7a | ||
|
|
d183f4205e | ||
|
|
821e9ee006 | ||
|
|
00e3f924c1 | ||
|
|
2248851d77 | ||
|
|
f74d1ea6d8 | ||
|
|
606b4c3a37 | ||
|
|
4e77fc9436 | ||
|
|
ad9db379e7 | ||
|
|
7be17d393f | ||
|
|
5665c9a410 | ||
|
|
1cf9d345aa | ||
|
|
f07f25f1ba | ||
|
|
c93b836c77 | ||
|
|
371103f0d1 | ||
|
|
8bd7afbe62 | ||
|
|
8ea3a493f4 | ||
|
|
f2abd346c0 | ||
|
|
c784713aca | ||
|
|
14acf618af | ||
|
|
fbee2baf9d | ||
|
|
2819b4167b | ||
|
|
2af660cb1f | ||
|
|
8323068414 | ||
|
|
5b62f9fdb6 | ||
|
|
4f66e3fe6c | ||
|
|
1335b4c64f | ||
|
|
da03c922ca | ||
|
|
9620116c30 | ||
|
|
dd7197f2a2 | ||
|
|
9214806342 | ||
|
|
348e713a8e | ||
|
|
cb81fe41ab | ||
|
|
a876bb3673 | ||
|
|
afa46daccd | ||
|
|
3c00da291e | ||
|
|
c847481184 | ||
|
|
f46ef67180 | ||
|
|
229ddfdae1 | ||
|
|
aa7f0849e3 | ||
|
|
7617b8a090 | ||
|
|
4e06abe002 | ||
|
|
b1a758dd5a | ||
|
|
5bf4daef2d | ||
|
|
e56db3b82e | ||
|
|
d3bbe01d5c | ||
|
|
286d6c6359 | ||
|
|
84424b52bc | ||
|
|
98c2dcd3e5 | ||
|
|
808f0bde9d | ||
|
|
c5c6012947 | ||
|
|
836060c8ad | ||
|
|
7a54353c7b |
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@@ -8,7 +8,7 @@ on:
|
||||
|
||||
env:
|
||||
GO_VERSION: 1.17
|
||||
GOLANGCI_LINT_VERSION: v1.41.1
|
||||
GOLANGCI_LINT_VERSION: v1.42.1
|
||||
|
||||
jobs:
|
||||
|
||||
|
||||
@@ -75,6 +75,9 @@
|
||||
[[issues.exclude-rules]]
|
||||
path = "interp/interp.go"
|
||||
text = "`out` can be `io.Writer`"
|
||||
[[issues.exclude-rules]]
|
||||
path = "interp/interp.go"
|
||||
text = "`Panic` should conform to the `XxxError` format"
|
||||
[[issues.exclude-rules]]
|
||||
path = "interp/interp_eval_test.go"
|
||||
linters = ["thelper"]
|
||||
|
||||
10
_test/a29.go
10
_test/a29.go
@@ -6,16 +6,18 @@ const (
|
||||
zero = iota
|
||||
one
|
||||
two
|
||||
three
|
||||
)
|
||||
|
||||
func main() {
|
||||
a := [...]string{
|
||||
zero: "zero",
|
||||
one: "one",
|
||||
two: "two",
|
||||
zero: "zero",
|
||||
one: "one",
|
||||
three: "three",
|
||||
three + 2: "five",
|
||||
}
|
||||
fmt.Printf("%v %T\n", a, a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// [zero one two] [3]string
|
||||
// [zero one three five] [6]string
|
||||
|
||||
27
_test/alias4.go
Normal file
27
_test/alias4.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type A http.Header
|
||||
|
||||
func (a A) Test1() {
|
||||
fmt.Println("test1")
|
||||
}
|
||||
|
||||
type B A
|
||||
|
||||
func (b B) Test2() {
|
||||
fmt.Println("test2")
|
||||
}
|
||||
|
||||
func main() {
|
||||
b := B{}
|
||||
|
||||
b.Test2()
|
||||
}
|
||||
|
||||
// Output:
|
||||
// test2
|
||||
24
_test/break0.go
Normal file
24
_test/break0.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
n := 2
|
||||
m := 2
|
||||
foo := true
|
||||
OuterLoop:
|
||||
println("Boo")
|
||||
for i := 0; i < n; i++ {
|
||||
println("I: ", i)
|
||||
for j := 0; j < m; j++ {
|
||||
switch foo {
|
||||
case true:
|
||||
println(foo)
|
||||
break OuterLoop
|
||||
case false:
|
||||
println(foo)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 15:5: invalid break label OuterLoop
|
||||
24
_test/break1.go
Normal file
24
_test/break1.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
n := 2
|
||||
m := 2
|
||||
foo := true
|
||||
OuterLoop:
|
||||
for i := 0; i < n; i++ {
|
||||
println("I: ", i)
|
||||
for j := 0; j < m; j++ {
|
||||
switch foo {
|
||||
case true:
|
||||
println(foo)
|
||||
break OuterLoop
|
||||
case false:
|
||||
println(foo)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// I: 0
|
||||
// true
|
||||
25
_test/break2.go
Normal file
25
_test/break2.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
n := 2
|
||||
m := 2
|
||||
foo := true
|
||||
OuterLoop:
|
||||
for i := 0; i < n; i++ {
|
||||
println("I: ", i)
|
||||
for j := 0; j < m; j++ {
|
||||
switch foo {
|
||||
case true:
|
||||
println(foo)
|
||||
break OuterLoop
|
||||
case false:
|
||||
println(foo)
|
||||
continue OuterLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// I: 0
|
||||
// true
|
||||
27
_test/break3.go
Normal file
27
_test/break3.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
n := 2
|
||||
m := 2
|
||||
foo := true
|
||||
goto OuterLoop
|
||||
println("Boo")
|
||||
OuterLoop:
|
||||
for i := 0; i < n; i++ {
|
||||
println("I: ", i)
|
||||
for j := 0; j < m; j++ {
|
||||
switch foo {
|
||||
case true:
|
||||
println(foo)
|
||||
break OuterLoop
|
||||
case false:
|
||||
println(foo)
|
||||
goto OuterLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// I: 0
|
||||
// true
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -13,7 +13,7 @@ func client(uri string) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -21,7 +21,7 @@ func client(uri string) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -13,7 +13,7 @@ func client(uri string) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -40,7 +40,7 @@ func client(uri string) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -40,7 +40,7 @@ func client(uri string) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -41,7 +41,7 @@ func client(uri string) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
26
_test/cont2.go
Normal file
26
_test/cont2.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
n := 2
|
||||
m := 2
|
||||
foo := true
|
||||
OuterLoop:
|
||||
for i := 0; i < n; i++ {
|
||||
println("I: ", i)
|
||||
for j := 0; j < m; j++ {
|
||||
switch foo {
|
||||
case true:
|
||||
println(foo)
|
||||
continue OuterLoop
|
||||
case false:
|
||||
println(foo)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// I: 0
|
||||
// true
|
||||
// I: 1
|
||||
// true
|
||||
24
_test/cont3.go
Normal file
24
_test/cont3.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
n := 2
|
||||
m := 2
|
||||
foo := true
|
||||
OuterLoop:
|
||||
println("boo")
|
||||
for i := 0; i < n; i++ {
|
||||
println("I: ", i)
|
||||
for j := 0; j < m; j++ {
|
||||
switch foo {
|
||||
case true:
|
||||
println(foo)
|
||||
continue OuterLoop
|
||||
case false:
|
||||
println(foo)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 15:5: invalid continue label OuterLoop
|
||||
@@ -2,12 +2,11 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
file, err := ioutil.TempFile("", "yeagibench")
|
||||
file, err := os.CreateTemp("", "yeagibench")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -23,7 +22,7 @@ func main() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(file.Name())
|
||||
b, err := os.ReadFile(file.Name())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
func main() {
|
||||
r := strings.NewReader("Go is a general-purpose language designed with systems programming in mind.")
|
||||
|
||||
b, err := ioutil.ReadAll(r)
|
||||
b, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
@@ -13,7 +12,7 @@ type sink interface {
|
||||
|
||||
func newSink() sink {
|
||||
// return os.Stdout // Stdout is special in yaegi tests
|
||||
file, err := ioutil.TempFile("", "yaegi-test.*")
|
||||
file, err := os.CreateTemp("", "yaegi-test.*")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
67
_test/issue-1260.go
Normal file
67
_test/issue-1260.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
type WriteSyncer interface {
|
||||
io.Writer
|
||||
Sync() error
|
||||
}
|
||||
|
||||
type Sink interface {
|
||||
WriteSyncer
|
||||
io.Closer
|
||||
}
|
||||
|
||||
func newFileSink(path string) (Sink, error) {
|
||||
return os.OpenFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
|
||||
}
|
||||
|
||||
type Sink1 struct{ name string }
|
||||
|
||||
func (s Sink1) Write(b []byte) (int, error) { println("in Write"); return 0, nil }
|
||||
func (s Sink1) Sync() error { println("in Sync"); return nil }
|
||||
func (s Sink1) Close() error { println("in Close", s.name); return nil }
|
||||
func newS1(name string) Sink { return Sink1{name} }
|
||||
func newS1p(name string) Sink { return &Sink1{name} }
|
||||
|
||||
type Sink2 struct{ name string }
|
||||
|
||||
func (s *Sink2) Write(b []byte) (int, error) { println("in Write"); return 0, nil }
|
||||
func (s *Sink2) Sync() error { println("in Sync"); return nil }
|
||||
func (s *Sink2) Close() error { println("in Close", s.name); return nil }
|
||||
func newS2(name string) Sink { return Sink1{name} }
|
||||
|
||||
func main() {
|
||||
tmpfile, err := os.CreateTemp("", "xxx")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer os.Remove(tmpfile.Name())
|
||||
closers := []io.Closer{}
|
||||
sink, err := newFileSink(tmpfile.Name())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
closers = append(closers, sink)
|
||||
|
||||
s1p := newS1p("ptr")
|
||||
s1 := newS1("struct")
|
||||
s2 := newS2("ptr2")
|
||||
closers = append(closers, s1p, s1, s2)
|
||||
for _, closer := range closers {
|
||||
fmt.Println(closer.Close())
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// <nil>
|
||||
// in Close ptr
|
||||
// <nil>
|
||||
// in Close struct
|
||||
// <nil>
|
||||
// in Close ptr2
|
||||
// <nil>
|
||||
24
_test/issue-1276.go
Normal file
24
_test/issue-1276.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
)
|
||||
|
||||
type customFlag struct{}
|
||||
|
||||
func (cf customFlag) String() string {
|
||||
return "custom flag"
|
||||
}
|
||||
|
||||
func (cf customFlag) Set(string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Var(customFlag{}, "cf", "custom flag")
|
||||
flag.Parse()
|
||||
println("Hello, playground")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Hello, playground
|
||||
30
_test/issue-1280.go
Normal file
30
_test/issue-1280.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
type DBReader interface {
|
||||
io.ReadCloser
|
||||
io.ReaderAt
|
||||
}
|
||||
|
||||
type DB struct {
|
||||
f DBReader
|
||||
}
|
||||
|
||||
func main() {
|
||||
f, err := os.Open("/dev/null")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
d := &DB{f}
|
||||
data := make([]byte, 1)
|
||||
_, _ = d.f.ReadAt(data, 0)
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bye
|
||||
25
_test/issue-1285.go
Normal file
25
_test/issue-1285.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
type (
|
||||
T1 struct{ Path [12]int8 }
|
||||
T2 struct{ Path *[12]int8 }
|
||||
)
|
||||
|
||||
var (
|
||||
t11 = &T1{}
|
||||
t21 = &T2{}
|
||||
)
|
||||
|
||||
func main() {
|
||||
b := [12]byte{}
|
||||
t12 := &T1{}
|
||||
t22 := &T2{}
|
||||
b11 := (*[len(t11.Path)]byte)(&b)
|
||||
b12 := (*[len(t12.Path)]byte)(&b)
|
||||
b21 := (*[len(t21.Path)]byte)(&b)
|
||||
b22 := (*[len(t22.Path)]byte)(&b)
|
||||
println(len(b11), len(b12), len(b21), len(b22))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 12 12 12 12
|
||||
10
_test/issue-1288.go
Normal file
10
_test/issue-1288.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
println("Hi")
|
||||
goto done
|
||||
done:
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Hi
|
||||
20
_test/issue-1300.go
Normal file
20
_test/issue-1300.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
const buflen = 512
|
||||
|
||||
type T struct {
|
||||
buf []byte
|
||||
}
|
||||
|
||||
func f(t *T) { *t = T{buf: make([]byte, 0, buflen)} }
|
||||
|
||||
func main() {
|
||||
s := T{}
|
||||
println(cap(s.buf))
|
||||
f(&s)
|
||||
println(cap(s.buf))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 0
|
||||
// 512
|
||||
16
_test/issue-1304.go
Normal file
16
_test/issue-1304.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
type Node struct {
|
||||
Name string
|
||||
Alias *Node
|
||||
Child []*Node
|
||||
}
|
||||
|
||||
func main() {
|
||||
n := &Node{Name: "parent"}
|
||||
n.Child = append(n.Child, &Node{Name: "child"})
|
||||
println(n.Name, n.Child[0].Name)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// parent child
|
||||
15
_test/issue-1306.go
Normal file
15
_test/issue-1306.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func check() (result bool, err error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
result, error := check()
|
||||
fmt.Println(result, error)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true <nil>
|
||||
27
_test/issue-1308.go
Normal file
27
_test/issue-1308.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type test struct {
|
||||
v interface{}
|
||||
s string
|
||||
}
|
||||
|
||||
type T struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := []test{
|
||||
{
|
||||
v: []interface{}{
|
||||
T{"hello"},
|
||||
},
|
||||
s: "world",
|
||||
},
|
||||
}
|
||||
fmt.Println(t)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// [{[{hello}] world}]
|
||||
19
_test/issue-1311.go
Normal file
19
_test/issue-1311.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
v interface{}
|
||||
}
|
||||
|
||||
func f() (ret int64, err error) {
|
||||
ret += 2
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := &T{}
|
||||
t.v, _ = f()
|
||||
println(t.v.(int64))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
32
_test/issue-1315.go
Normal file
32
_test/issue-1315.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
type Intf interface {
|
||||
M()
|
||||
}
|
||||
|
||||
type T struct {
|
||||
s string
|
||||
}
|
||||
|
||||
func (t *T) M() { println("in M") }
|
||||
|
||||
func f(i interface{}) {
|
||||
switch j := i.(type) {
|
||||
case Intf:
|
||||
j.M()
|
||||
default:
|
||||
println("default")
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
var i Intf
|
||||
var k interface{} = 1
|
||||
i = &T{"hello"}
|
||||
f(i)
|
||||
f(k)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// in M
|
||||
// default
|
||||
29
_test/issue-1320.go
Normal file
29
_test/issue-1320.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
type Pooler interface {
|
||||
Get() string
|
||||
}
|
||||
|
||||
type baseClient struct {
|
||||
connPool Pooler
|
||||
}
|
||||
|
||||
type connPool struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (c *connPool) Get() string { return c.name }
|
||||
|
||||
func newBaseClient(i int, p Pooler) *baseClient {
|
||||
return &baseClient{connPool: p}
|
||||
}
|
||||
|
||||
func newConnPool() *connPool { return &connPool{name: "connPool"} }
|
||||
|
||||
func main() {
|
||||
b := newBaseClient(0, newConnPool())
|
||||
println(b.connPool.(*connPool).name)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// connPool
|
||||
42
_test/issue-1326.go
Normal file
42
_test/issue-1326.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
type Option interface {
|
||||
apply(*T)
|
||||
}
|
||||
|
||||
type T struct {
|
||||
s string
|
||||
}
|
||||
|
||||
type opt struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (o *opt) apply(t *T) {
|
||||
println(o.name)
|
||||
}
|
||||
|
||||
func BuildOptions() []Option {
|
||||
return []Option{
|
||||
&opt{"opt1"},
|
||||
&opt{"opt2"},
|
||||
}
|
||||
}
|
||||
|
||||
func NewT(name string, options ...Option) *T {
|
||||
t := &T{name}
|
||||
for _, opt := range options {
|
||||
opt.apply(t)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := NewT("hello", BuildOptions()...)
|
||||
println(t.s)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// opt1
|
||||
// opt2
|
||||
// hello
|
||||
16
_test/issue-1328.go
Normal file
16
_test/issue-1328.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
func main() {
|
||||
script := "hello"
|
||||
sumRaw := sha1.Sum([]byte(script))
|
||||
sum := hex.EncodeToString(sumRaw[:])
|
||||
println(sum)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d
|
||||
42
_test/issue-1330.go
Normal file
42
_test/issue-1330.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
)
|
||||
|
||||
type wrappedConn struct {
|
||||
net.Conn
|
||||
}
|
||||
|
||||
func main() {
|
||||
_, err := net.Listen("tcp", "127.0.0.1:49153")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
dialer := &net.Dialer{
|
||||
LocalAddr: &net.TCPAddr{
|
||||
IP: net.ParseIP("127.0.0.1"),
|
||||
Port: 0,
|
||||
},
|
||||
}
|
||||
|
||||
conn, err := dialer.Dial("tcp", "127.0.0.1:49153")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
t := &wrappedConn{conn}
|
||||
var w io.Writer = t
|
||||
if n, err := w.Write([]byte("hello")); err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
fmt.Println(n)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
17
_test/issue-1332.go
Normal file
17
_test/issue-1332.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
func run(fn func(name string)) { fn("test") }
|
||||
|
||||
type T2 struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (t *T2) f(s string) { println(s, t.name) }
|
||||
|
||||
func main() {
|
||||
t2 := &T2{"foo"}
|
||||
run(t2.f)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// test foo
|
||||
37
_test/issue-1333.go
Normal file
37
_test/issue-1333.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
)
|
||||
|
||||
func mock(name string) http.HandlerFunc {
|
||||
return func(rw http.ResponseWriter, req *http.Request) {
|
||||
fmt.Fprint(rw, "Hello ", name)
|
||||
}
|
||||
}
|
||||
|
||||
func client(uri string) {
|
||||
resp, err := http.Get(uri)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(body))
|
||||
}
|
||||
|
||||
func main() {
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
defer server.Close()
|
||||
mux.Handle("/", mock("foo"))
|
||||
client(server.URL)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Hello foo
|
||||
26
_test/issue-1337.go
Normal file
26
_test/issue-1337.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
func f(i interface{}) {
|
||||
switch at := i.(type) {
|
||||
case int, int8:
|
||||
println("integer", at)
|
||||
case io.Reader:
|
||||
println("reader")
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
func main() {
|
||||
var fd *os.File
|
||||
var r io.Reader = fd
|
||||
f(r)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// reader
|
||||
// bye
|
||||
12
_test/issue-1342.go
Normal file
12
_test/issue-1342.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
var a interface{}
|
||||
a = "a"
|
||||
fmt.Println(a, a == "a")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// a true
|
||||
13
_test/issue-1344.go
Normal file
13
_test/issue-1344.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
var m = map[string]interface{}{"a": "a"}
|
||||
a, _ := m["a"]
|
||||
b, ok := a.(string)
|
||||
fmt.Println("a:", a, ", b:", b, ", ok:", ok)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// a: a , b: a , ok: true
|
||||
21
_test/issue-1354.go
Normal file
21
_test/issue-1354.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
println(test()) // Go prints true, Yaegi false
|
||||
}
|
||||
|
||||
func test() bool {
|
||||
if true {
|
||||
goto label
|
||||
}
|
||||
goto label
|
||||
label:
|
||||
println("Go continues here")
|
||||
return true
|
||||
println("Yaegi goes straight to this return (this line is never printed)")
|
||||
return false
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Go continues here
|
||||
// true
|
||||
22
_test/issue-1355.go
Normal file
22
_test/issue-1355.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import "github.com/traefik/yaegi/_test/p2"
|
||||
|
||||
func f(i interface{}) {
|
||||
_, ok := i.(p2.I)
|
||||
println("ok:", ok)
|
||||
}
|
||||
|
||||
func main() {
|
||||
var v *p2.T
|
||||
var i interface{}
|
||||
|
||||
i = v
|
||||
_, ok := i.(p2.I)
|
||||
println("ok:", ok)
|
||||
f(v)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ok: true
|
||||
// ok: true
|
||||
14
_test/issue-1360.go
Normal file
14
_test/issue-1360.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
. "net"
|
||||
)
|
||||
|
||||
func main() {
|
||||
v := IP{}
|
||||
fmt.Println(v)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// <nil>
|
||||
27
_test/issue-1361.go
Normal file
27
_test/issue-1361.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
type obj struct {
|
||||
num float64
|
||||
}
|
||||
|
||||
type Fun func(o *obj) (r *obj, err error)
|
||||
|
||||
func numFun(fn func(f float64) float64) Fun {
|
||||
return func(o *obj) (*obj, error) {
|
||||
return &obj{fn(o.num)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
f := numFun(math.Cos)
|
||||
r, err := f(&obj{})
|
||||
fmt.Println(r, err)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// &{1} <nil>
|
||||
16
_test/issue-1364.go
Normal file
16
_test/issue-1364.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var value interface{}
|
||||
var err error
|
||||
value, err = strconv.ParseFloat("123", 64)
|
||||
fmt.Println(value, err)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 123 <nil>
|
||||
18
_test/issue-1365.go
Normal file
18
_test/issue-1365.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
func genInt() (int, error) { return 3, nil }
|
||||
|
||||
func getInt() (value int) {
|
||||
value, err := genInt()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
println(getInt())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 3
|
||||
16
_test/issue-1368.go
Normal file
16
_test/issue-1368.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
const dollar byte = 36
|
||||
|
||||
func main() {
|
||||
var c byte = 36
|
||||
switch true {
|
||||
case c == dollar:
|
||||
println("ok")
|
||||
default:
|
||||
println("not ok")
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ok
|
||||
18
_test/issue-1371.go
Normal file
18
_test/issue-1371.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type node struct {
|
||||
parent *node
|
||||
child []*node
|
||||
key string
|
||||
}
|
||||
|
||||
func main() {
|
||||
root := &node{key: "root"}
|
||||
root.child = nil
|
||||
fmt.Println("root:", root)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// root: &{<nil> [] root}
|
||||
17
_test/issue-1373.go
Normal file
17
_test/issue-1373.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
)
|
||||
|
||||
func NewBadExpr() ast.Expr {
|
||||
return &ast.BadExpr{}
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Printf("%T\n", NewBadExpr().(*ast.BadExpr))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// *ast.BadExpr
|
||||
38
_test/issue-1375.go
Normal file
38
_test/issue-1375.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Option func(*Struct)
|
||||
|
||||
func WithOption(opt string) Option {
|
||||
return func(s *Struct) {
|
||||
s.opt = opt
|
||||
}
|
||||
}
|
||||
|
||||
type Struct struct {
|
||||
opt string
|
||||
}
|
||||
|
||||
func New(opts ...Option) *Struct {
|
||||
s := new(Struct)
|
||||
for _, opt := range opts {
|
||||
opt(s)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Struct) ShowOption() {
|
||||
fmt.Println(s.opt)
|
||||
}
|
||||
|
||||
func main() {
|
||||
opts := []Option{
|
||||
WithOption("test"),
|
||||
}
|
||||
s := New(opts...)
|
||||
s.ShowOption()
|
||||
}
|
||||
|
||||
// Output:
|
||||
// test
|
||||
21
_test/issue-1378.go
Normal file
21
_test/issue-1378.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
t, err := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fn := func() error {
|
||||
_, err := t.GobEncode()
|
||||
return err
|
||||
}
|
||||
fmt.Println(fn())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// <nil>
|
||||
58
_test/issue-1381.go
Normal file
58
_test/issue-1381.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var bufPtrOne *bytes.Buffer
|
||||
var bufPtrTwo *bytes.Buffer
|
||||
var bufPtrThree *bytes.Buffer
|
||||
var bufPtrFour *bytes.Buffer
|
||||
|
||||
for i := 0; i < 2; i++ {
|
||||
bufOne := bytes.Buffer{}
|
||||
bufTwo := &bytes.Buffer{}
|
||||
var bufThree bytes.Buffer
|
||||
bufFour := new(bytes.Buffer)
|
||||
|
||||
if bufPtrOne == nil {
|
||||
bufPtrOne = &bufOne
|
||||
} else if bufPtrOne == &bufOne {
|
||||
fmt.Println("bufOne was not properly redeclared")
|
||||
} else {
|
||||
fmt.Println("bufOne is properly redeclared")
|
||||
}
|
||||
|
||||
if bufPtrTwo == nil {
|
||||
bufPtrTwo = bufTwo
|
||||
} else if bufPtrTwo == bufTwo {
|
||||
fmt.Println("bufTwo was not properly redeclared")
|
||||
} else {
|
||||
fmt.Println("bufTwo is properly redeclared")
|
||||
}
|
||||
|
||||
if bufPtrThree == nil {
|
||||
bufPtrThree = &bufThree
|
||||
} else if bufPtrThree == &bufThree {
|
||||
fmt.Println("bufThree was not properly redeclared")
|
||||
} else {
|
||||
fmt.Println("bufThree is properly redeclared")
|
||||
}
|
||||
|
||||
if bufPtrFour == nil {
|
||||
bufPtrFour = bufFour
|
||||
} else if bufPtrFour == bufFour {
|
||||
fmt.Println("bufFour was not properly redeclared")
|
||||
} else {
|
||||
fmt.Println("bufFour is properly redeclared")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bufOne is properly redeclared
|
||||
// bufTwo is properly redeclared
|
||||
// bufThree is properly redeclared
|
||||
// bufFour is properly redeclared
|
||||
@@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
@@ -36,7 +35,7 @@ type pipe struct {
|
||||
|
||||
func newReadAutoCloser(r io.Reader) readAutoCloser {
|
||||
if _, ok := r.(io.Closer); !ok {
|
||||
return readAutoCloser{ioutil.NopCloser(r)}
|
||||
return readAutoCloser{io.NopCloser(r)}
|
||||
}
|
||||
return readAutoCloser{r.(io.ReadCloser)}
|
||||
}
|
||||
@@ -44,7 +43,7 @@ func newReadAutoCloser(r io.Reader) readAutoCloser {
|
||||
func main() {
|
||||
p := &pipe{}
|
||||
p.Reader = newReadAutoCloser(strings.NewReader("test"))
|
||||
b, err := ioutil.ReadAll(p.Reader)
|
||||
b, err := io.ReadAll(p.Reader)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
14
_test/method38.go
Normal file
14
_test/method38.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/traefik/yaegi/_test/method38"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(method38.Get())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// &{[] {<nil>}}
|
||||
19
_test/method38/a.go
Normal file
19
_test/method38/a.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package method38
|
||||
|
||||
import "sync"
|
||||
|
||||
func NewPool() Pool { return Pool{} }
|
||||
|
||||
type Buffer struct {
|
||||
bs []byte
|
||||
pool Pool
|
||||
}
|
||||
|
||||
type Pool struct {
|
||||
p *sync.Pool
|
||||
}
|
||||
|
||||
var (
|
||||
_pool = NewPool()
|
||||
Get = _pool.Get
|
||||
)
|
||||
3
_test/method38/b.go
Normal file
3
_test/method38/b.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package method38
|
||||
|
||||
func (p Pool) Get() *Buffer { return &Buffer{} }
|
||||
32
_test/method39.go
Normal file
32
_test/method39.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/traefik/yaegi/_test/method38"
|
||||
)
|
||||
|
||||
func NewPool() Pool { return Pool{} }
|
||||
|
||||
type Buffer struct {
|
||||
bs []byte
|
||||
pool Pool
|
||||
}
|
||||
|
||||
type Pool struct {
|
||||
p *sync.Pool
|
||||
}
|
||||
|
||||
var (
|
||||
_pool = NewPool()
|
||||
Get = _pool.Get
|
||||
)
|
||||
|
||||
|
||||
func main() {
|
||||
fmt.Println(Get())
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 17:11: undefined selector Get
|
||||
@@ -2,11 +2,11 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
_, err := ioutil.ReadFile("__NotExisting__")
|
||||
_, err := os.ReadFile("__NotExisting__")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
9
_test/p2/p2.go
Normal file
9
_test/p2/p2.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package p2
|
||||
|
||||
type I interface {
|
||||
isI()
|
||||
}
|
||||
|
||||
type T struct{}
|
||||
|
||||
func (t *T) isI() {}
|
||||
0
_test/p3/empty
Normal file
0
_test/p3/empty
Normal file
@@ -8,6 +8,7 @@ type A struct {
|
||||
}
|
||||
|
||||
type D struct {
|
||||
F *A
|
||||
E *A
|
||||
}
|
||||
|
||||
|
||||
22
_test/struct61.go
Normal file
22
_test/struct61.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type A struct {
|
||||
B string
|
||||
D
|
||||
}
|
||||
|
||||
type D struct {
|
||||
*A
|
||||
E *A
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := &A{B: "b"}
|
||||
a.D = D{E: a}
|
||||
fmt.Println(a.D.E.B)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// b
|
||||
11
_test/struct62.go
Normal file
11
_test/struct62.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
type A struct{ *A }
|
||||
v := &A{}
|
||||
v.A = v
|
||||
println("v.A.A = v", v.A.A == v)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// v.A.A = v true
|
||||
17
_test/type27.go
Normal file
17
_test/type27.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Foo = int
|
||||
|
||||
func (f Foo) Bar() int {
|
||||
return f * f
|
||||
}
|
||||
|
||||
func main() {
|
||||
x := Foo(1)
|
||||
fmt.Println(x.Bar())
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 7:1: cannot define new methods on non-local type int
|
||||
17
_test/type28.go
Normal file
17
_test/type28.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Foo = int
|
||||
|
||||
func (f *Foo) Bar() int {
|
||||
return *f * *f
|
||||
}
|
||||
|
||||
func main() {
|
||||
x := Foo(1)
|
||||
fmt.Println(x.Bar())
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 7:1: cannot define new methods on non-local type int
|
||||
12
_test/type29.go
Normal file
12
_test/type29.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
var Foo int
|
||||
|
||||
func (f Foo) Bar() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func main() {}
|
||||
|
||||
// Error:
|
||||
// 5:1: cannot define new methods on non-local type int
|
||||
12
_test/type30.go
Normal file
12
_test/type30.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
var Foo *int
|
||||
|
||||
func (f Foo) Bar() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func main() {}
|
||||
|
||||
// Error:
|
||||
// 5:1: cannot define new methods on non-local type int
|
||||
17
_test/type31.go
Normal file
17
_test/type31.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func (f Foo) Bar() int {
|
||||
return f * f
|
||||
}
|
||||
|
||||
type Foo = int
|
||||
|
||||
func main() {
|
||||
x := Foo(1)
|
||||
fmt.Println(x.Bar())
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 5:1: cannot define new methods on non-local type int
|
||||
17
_test/type32.go
Normal file
17
_test/type32.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func (f *Foo) Bar() int {
|
||||
return *f * *f
|
||||
}
|
||||
|
||||
type Foo = int
|
||||
|
||||
func main() {
|
||||
x := Foo(1)
|
||||
fmt.Println(x.Bar())
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 5:1: cannot define new methods on non-local type int
|
||||
11
_test/type33.go
Normal file
11
_test/type33.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
func (f *Foo) Bar() int {
|
||||
return *f * *f
|
||||
}
|
||||
|
||||
func main() {
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 3:1: undefined: Foo
|
||||
19
_test/var16.go
Normal file
19
_test/var16.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
func getArray() ([]int, error) { println("getArray"); return []int{1, 2}, nil }
|
||||
|
||||
func getNum() (int, error) { println("getNum"); return 3, nil }
|
||||
|
||||
func main() {
|
||||
if a, err := getNum(); err != nil {
|
||||
println("#1", a)
|
||||
} else if a, err := getArray(); err != nil {
|
||||
println("#2", a)
|
||||
}
|
||||
println("#3")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// getNum
|
||||
// getArray
|
||||
// #3
|
||||
@@ -18,12 +18,14 @@ func extractCmd(arg []string) error {
|
||||
var name string
|
||||
var exclude string
|
||||
var include string
|
||||
var tag string
|
||||
|
||||
eflag := flag.NewFlagSet("run", flag.ContinueOnError)
|
||||
eflag.StringVar(&licensePath, "license", "", "path to a LICENSE file")
|
||||
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.StringVar(&tag, "tag", "", "comma separated list of build tags to be added to the created package")
|
||||
eflag.Usage = func() {
|
||||
fmt.Println("Usage: yaegi extract [options] packages...")
|
||||
fmt.Println("Options:")
|
||||
@@ -56,6 +58,9 @@ func extractCmd(arg []string) error {
|
||||
Dest: name,
|
||||
License: license,
|
||||
}
|
||||
if tag != "" {
|
||||
ext.Tag = strings.Split(tag, ",")
|
||||
}
|
||||
|
||||
if exclude != "" {
|
||||
ext.Exclude = strings.Split(exclude, ",")
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
@@ -47,7 +46,12 @@ func run(arg []string) error {
|
||||
}
|
||||
args := rflag.Args()
|
||||
|
||||
i := interp.New(interp.Options{GoPath: build.Default.GOPATH, BuildTags: strings.Split(tags, ",")})
|
||||
i := interp.New(interp.Options{
|
||||
GoPath: build.Default.GOPATH,
|
||||
BuildTags: strings.Split(tags, ","),
|
||||
Env: os.Environ(),
|
||||
Unrestricted: useUnrestricted,
|
||||
})
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -130,7 +134,7 @@ func isFile(path string) bool {
|
||||
}
|
||||
|
||||
func runFile(i *interp.Interpreter, path string, noAutoImport bool) error {
|
||||
b, err := ioutil.ReadFile(path)
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -116,7 +116,12 @@ func test(arg []string) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
i := interp.New(interp.Options{GoPath: build.Default.GOPATH, BuildTags: strings.Split(tags, ",")})
|
||||
i := interp.New(interp.Options{
|
||||
GoPath: build.Default.GOPATH,
|
||||
BuildTags: strings.Split(tags, ","),
|
||||
Env: os.Environ(),
|
||||
Unrestricted: useUnrestricted,
|
||||
})
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -38,7 +37,7 @@ func applyCIMultiplier(timeout time.Duration) time.Duration {
|
||||
}
|
||||
|
||||
func TestYaegiCmdCancel(t *testing.T) {
|
||||
tmp, err := ioutil.TempDir("", "yaegi-")
|
||||
tmp, err := os.MkdirTemp("", "yaegi-")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tmp directory: %v", err)
|
||||
}
|
||||
|
||||
@@ -80,7 +80,14 @@ func init() {
|
||||
{{end}}
|
||||
}
|
||||
{{range $m := $value.Method -}}
|
||||
func (W {{$value.Name}}) {{$m.Name}}{{$m.Param}} {{$m.Result}} { {{$m.Ret}} W.W{{$m.Name}}{{$m.Arg}} }
|
||||
func (W {{$value.Name}}) {{$m.Name}}{{$m.Param}} {{$m.Result}} {
|
||||
{{- if eq $m.Name "String"}}
|
||||
if W.WString == nil {
|
||||
return ""
|
||||
}
|
||||
{{end -}}
|
||||
{{$m.Ret}} W.W{{$m.Name}}{{$m.Arg}}
|
||||
}
|
||||
{{end}}
|
||||
{{end}}
|
||||
`
|
||||
@@ -270,6 +277,15 @@ func (e *Extractor) genContent(importPath string, p *types.Package) ([]byte, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, t := range e.Tag {
|
||||
if len(t) != 0 {
|
||||
buildTags += "," + t
|
||||
}
|
||||
}
|
||||
if len(buildTags) != 0 && buildTags[0] == ',' {
|
||||
buildTags = buildTags[1:]
|
||||
}
|
||||
|
||||
b := new(bytes.Buffer)
|
||||
data := map[string]interface{}{
|
||||
"Dest": e.Dest,
|
||||
@@ -336,6 +352,7 @@ type Extractor struct {
|
||||
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.
|
||||
Tag []string // Comma separated of build tags to be added to the created package.
|
||||
}
|
||||
|
||||
// importPath checks whether pkgIdent is an existing directory relative to
|
||||
|
||||
@@ -3,8 +3,8 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"go/format"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
@@ -535,6 +535,7 @@ func {{$name}}(n *node) {
|
||||
typ := n.typ.concrete().TypeOf()
|
||||
isInterface := n.typ.TypeOf().Kind() == reflect.Interface
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
|
||||
|
||||
{{- if or (eq $op.Name "==") (eq $op.Name "!=") }}
|
||||
|
||||
@@ -557,7 +558,7 @@ func {{$name}}(n *node) {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
if i0 {{$op.Name}} i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
@@ -579,7 +580,7 @@ func {{$name}}(n *node) {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
if i0 != i1 {
|
||||
if i0 {{$op.Name}} i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
@@ -602,7 +603,7 @@ func {{$name}}(n *node) {
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
if i0 {{$op.Name}} i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
@@ -621,9 +622,39 @@ func {{$name}}(n *node) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Do not attempt to optimize '==' or '!=' if an operand is an interface.
|
||||
// This will preserve proper dynamic type checking at runtime. For static types,
|
||||
// type checks are already performed, so bypass them if possible.
|
||||
if t0.Kind() == reflect.Interface || t1.Kind() == reflect.Interface {
|
||||
v0 := genValue(c0)
|
||||
v1 := genValue(c1)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
if i0 {{$op.Name}} i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 {{$op.Name}} i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
{{- end}}
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
switch {
|
||||
case isString(t0) || isString(t1):
|
||||
switch {
|
||||
case isInterface:
|
||||
@@ -1183,7 +1214,7 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err = ioutil.WriteFile("op.go", source, 0666); err != nil {
|
||||
if err = os.WriteFile("op.go", source, 0o666); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,17 @@ type dummy struct{}
|
||||
// DummyType represents a stand-in for a recursive type.
|
||||
var DummyType = reflect.TypeOf(dummy{})
|
||||
|
||||
// the following type sizes must match their original definition in Go src/reflect/type.go.
|
||||
|
||||
type rtype struct {
|
||||
_ [48]byte
|
||||
_ uintptr
|
||||
_ uintptr
|
||||
_ uint32
|
||||
_ uint32
|
||||
_ uintptr
|
||||
_ uintptr
|
||||
_ uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
type emptyInterface struct {
|
||||
@@ -20,21 +29,21 @@ type emptyInterface struct {
|
||||
}
|
||||
|
||||
type structField struct {
|
||||
_ int64
|
||||
_ uintptr
|
||||
typ *rtype
|
||||
_ uintptr
|
||||
}
|
||||
|
||||
type structType struct {
|
||||
rtype
|
||||
_ int64
|
||||
_ uintptr
|
||||
fields []structField
|
||||
}
|
||||
|
||||
// SwapFieldType swaps the type of the struct field with the given type.
|
||||
// SetFieldType sets the type of the struct field at the given index, to the given type.
|
||||
//
|
||||
// The struct type must have been created at runtime. This is very unsafe.
|
||||
func SwapFieldType(s reflect.Type, idx int, t reflect.Type) {
|
||||
func SetFieldType(s reflect.Type, idx int, t reflect.Type) {
|
||||
if s.Kind() != reflect.Struct || idx >= s.NumField() {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ func TestSwapFieldType(t *testing.T) {
|
||||
typ := reflect.StructOf(f)
|
||||
ntyp := reflect.PtrTo(typ)
|
||||
|
||||
unsafe2.SwapFieldType(typ, 1, ntyp)
|
||||
unsafe2.SetFieldType(typ, 1, ntyp)
|
||||
|
||||
if typ.Field(1).Type != ntyp {
|
||||
t.Fatalf("unexpected field type: want %s; got %s", ntyp, typ.Field(1).Type)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
@@ -93,7 +92,8 @@ const (
|
||||
switchIfStmt
|
||||
typeAssertExpr
|
||||
typeDecl
|
||||
typeSpec
|
||||
typeSpec // type A int
|
||||
typeSpecAssign // type A = int
|
||||
typeSwitch
|
||||
unaryExpr
|
||||
valueSpec
|
||||
@@ -176,6 +176,7 @@ var kinds = [...]string{
|
||||
typeAssertExpr: "typeAssertExpr",
|
||||
typeDecl: "typeDecl",
|
||||
typeSpec: "typeSpec",
|
||||
typeSpecAssign: "typeSpecAssign",
|
||||
typeSwitch: "typeSwitch",
|
||||
unaryExpr: "unaryExpr",
|
||||
valueSpec: "valueSpec",
|
||||
@@ -281,6 +282,7 @@ var actions = [...]string{
|
||||
aDec: "--",
|
||||
aEqual: "==",
|
||||
aGreater: ">",
|
||||
aGreaterEqual: ">=",
|
||||
aGetFunc: "getFunc",
|
||||
aGetIndex: "getIndex",
|
||||
aGetMethod: "getMethod",
|
||||
@@ -289,6 +291,7 @@ var actions = [...]string{
|
||||
aLand: "&&",
|
||||
aLor: "||",
|
||||
aLower: "<",
|
||||
aLowerEqual: "<=",
|
||||
aMethod: "Method",
|
||||
aMul: "*",
|
||||
aMulAssign: "*=",
|
||||
@@ -360,21 +363,14 @@ func wrapInMain(src string) string {
|
||||
return fmt.Sprintf("package main; func main() {%s\n}", src)
|
||||
}
|
||||
|
||||
// Note: no type analysis is performed at this stage, it is done in pre-order
|
||||
// processing of CFG, in order to accommodate forward type declarations.
|
||||
|
||||
// ast parses src string containing Go code and generates the corresponding AST.
|
||||
// The package name and the AST root node are returned.
|
||||
// The given name is used to set the filename of the relevant source file in the
|
||||
// interpreter's FileSet.
|
||||
func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error) {
|
||||
var inFunc bool
|
||||
func (interp *Interpreter) parse(src, name string, inc bool) (node ast.Node, err error) {
|
||||
mode := parser.DeclarationErrors
|
||||
|
||||
// Allow incremental parsing of declarations or statements, by inserting
|
||||
// them in a pseudo file package or function. Those statements or
|
||||
// declarations will be always evaluated in the global scope.
|
||||
var tok token.Token
|
||||
var inFunc bool
|
||||
if inc {
|
||||
tok = interp.firstToken(src)
|
||||
switch tok {
|
||||
@@ -391,18 +387,18 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
|
||||
}
|
||||
|
||||
if ok, err := interp.buildOk(&interp.context, name, src); !ok || err != nil {
|
||||
return "", nil, err // skip source not matching build constraints
|
||||
return nil, err // skip source not matching build constraints
|
||||
}
|
||||
|
||||
f, err := parser.ParseFile(interp.fset, name, src, mode)
|
||||
if err != nil {
|
||||
// only retry if we're on an expression/statement about a func
|
||||
if !inc || tok != token.FUNC {
|
||||
return "", nil, err
|
||||
return nil, err
|
||||
}
|
||||
// do not bother retrying if we know it's an error we're going to ignore later on.
|
||||
if ignoreError(err, src) {
|
||||
return "", nil, err
|
||||
return nil, err
|
||||
}
|
||||
// do not lose initial error, in case retrying fails.
|
||||
initialError := err
|
||||
@@ -410,16 +406,32 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
|
||||
src := wrapInMain(strings.TrimPrefix(src, "package main;"))
|
||||
f, err = parser.ParseFile(interp.fset, name, src, mode)
|
||||
if err != nil {
|
||||
return "", nil, initialError
|
||||
return nil, initialError
|
||||
}
|
||||
}
|
||||
|
||||
setYaegiTags(&interp.context, f.Comments)
|
||||
if inFunc {
|
||||
// return the body of the wrapper main function
|
||||
return f.Decls[0].(*ast.FuncDecl).Body, nil
|
||||
}
|
||||
|
||||
setYaegiTags(&interp.context, f.Comments)
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// Note: no type analysis is performed at this stage, it is done in pre-order
|
||||
// processing of CFG, in order to accommodate forward type declarations.
|
||||
|
||||
// ast parses src string containing Go code and generates the corresponding AST.
|
||||
// The package name and the AST root node are returned.
|
||||
// The given name is used to set the filename of the relevant source file in the
|
||||
// interpreter's FileSet.
|
||||
func (interp *Interpreter) ast(f ast.Node) (string, *node, error) {
|
||||
var err error
|
||||
var root *node
|
||||
var anc astNode
|
||||
var st nodestack
|
||||
var pkgName string
|
||||
pkgName := "main"
|
||||
|
||||
addChild := func(root **node, anc astNode, pos token.Pos, kind nkind, act action) *node {
|
||||
var i interface{}
|
||||
@@ -627,7 +639,7 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
|
||||
}
|
||||
st.push(addChild(&root, anc, pos, kind, aNop), nod)
|
||||
|
||||
case *ast.CommentGroup:
|
||||
case *ast.CommentGroup, *ast.EmptyStmt:
|
||||
return false
|
||||
|
||||
case *ast.CompositeLit:
|
||||
@@ -826,6 +838,10 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
|
||||
st.push(addChild(&root, anc, pos, typeAssertExpr, aTypeAssert), nod)
|
||||
|
||||
case *ast.TypeSpec:
|
||||
if a.Assign.IsValid() {
|
||||
st.push(addChild(&root, anc, pos, typeSpecAssign, aNop), nod)
|
||||
break
|
||||
}
|
||||
st.push(addChild(&root, anc, pos, typeSpec, aNop), nod)
|
||||
|
||||
case *ast.TypeSwitchStmt:
|
||||
@@ -892,15 +908,7 @@ func (interp *Interpreter) ast(src, name string, inc bool) (string, *node, error
|
||||
}
|
||||
return true
|
||||
})
|
||||
if inFunc {
|
||||
// Incremental parsing: statements were inserted in a pseudo function.
|
||||
// Set root to function body so its statements are evaluated in global scope.
|
||||
root = root.child[1].child[3]
|
||||
root.anc = nil
|
||||
}
|
||||
if pkgName == "" {
|
||||
return "", root, errors.New("no package name found")
|
||||
}
|
||||
|
||||
interp.roots = append(interp.roots, root)
|
||||
return pkgName, root, err
|
||||
}
|
||||
|
||||
304
interp/cfg.go
304
interp/cfg.go
@@ -49,8 +49,10 @@ const nilIdent = "nil"
|
||||
// and pre-compute frame sizes and indexes for all un-named (temporary) and named
|
||||
// variables. A list of nodes of init functions is returned.
|
||||
// Following this pass, the CFG is ready to run.
|
||||
func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node, error) {
|
||||
sc := interp.initScopePkg(importPath, pkgName)
|
||||
func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string) ([]*node, error) {
|
||||
if sc == nil {
|
||||
sc = interp.initScopePkg(importPath, pkgName)
|
||||
}
|
||||
check := typecheck{scope: sc}
|
||||
var initNodes []*node
|
||||
var err error
|
||||
@@ -62,6 +64,9 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if n.scope == nil {
|
||||
n.scope = sc
|
||||
}
|
||||
switch n.kind {
|
||||
case binaryExpr, unaryExpr, parenExpr:
|
||||
if isBoolAction(n) {
|
||||
@@ -197,38 +202,42 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
n.findex = -1
|
||||
n.val = nil
|
||||
sc = sc.pushBloc()
|
||||
// Pre-define symbols for labels defined in this block, so we are sure that
|
||||
// they are already defined when met.
|
||||
// TODO(marc): labels must be stored outside of symbols to avoid collisions.
|
||||
for _, c := range n.child {
|
||||
if c.kind != labeledStmt {
|
||||
continue
|
||||
}
|
||||
label := c.child[0].ident
|
||||
sym := &symbol{kind: labelSym, node: c, index: -1}
|
||||
sc.sym[label] = sym
|
||||
c.sym = sym
|
||||
}
|
||||
|
||||
case breakStmt, continueStmt, gotoStmt:
|
||||
if len(n.child) > 0 {
|
||||
// Handle labeled statements.
|
||||
label := n.child[0].ident
|
||||
if sym, _, ok := sc.lookup(label); ok {
|
||||
if sym.kind != labelSym {
|
||||
err = n.child[0].cfgErrorf("label %s not defined", label)
|
||||
break
|
||||
}
|
||||
sym.from = append(sym.from, n)
|
||||
n.sym = sym
|
||||
} else {
|
||||
n.sym = &symbol{kind: labelSym, from: []*node{n}, index: -1}
|
||||
sc.sym[label] = n.sym
|
||||
}
|
||||
if len(n.child) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
case labeledStmt:
|
||||
// Handle labeled statements.
|
||||
label := n.child[0].ident
|
||||
// TODO(marc): labels must be stored outside of symbols to avoid collisions
|
||||
// Used labels are searched in current and sub scopes, not upper ones.
|
||||
if sym, ok := sc.lookdown(label); ok {
|
||||
sym.node = n
|
||||
if sym, _, ok := sc.lookup(label); ok {
|
||||
if sym.kind != labelSym {
|
||||
err = n.child[0].cfgErrorf("label %s not defined", label)
|
||||
break
|
||||
}
|
||||
n.sym = sym
|
||||
} else {
|
||||
n.sym = &symbol{kind: labelSym, node: n, index: -1}
|
||||
n.sym = &symbol{kind: labelSym, index: -1}
|
||||
sc.sym[label] = n.sym
|
||||
}
|
||||
if n.kind == gotoStmt {
|
||||
n.sym.from = append(n.sym.from, n) // To allow forward goto statements.
|
||||
}
|
||||
sc.sym[label] = n.sym
|
||||
|
||||
case caseClause:
|
||||
sc = sc.pushBloc()
|
||||
@@ -284,11 +293,11 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
|
||||
case compositeLitExpr:
|
||||
if len(n.child) > 0 && n.child[0].isType(sc) {
|
||||
// Get type from 1st child
|
||||
// Get type from 1st child.
|
||||
if n.typ, err = nodeType(interp, sc, n.child[0]); err != nil {
|
||||
return false
|
||||
}
|
||||
// Indicate that the first child is the type
|
||||
// Indicate that the first child is the type.
|
||||
n.nleft = 1
|
||||
} else {
|
||||
// Get type from ancestor (implicit type)
|
||||
@@ -314,6 +323,10 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
}
|
||||
// Propagate type to children, to handle implicit types
|
||||
for _, c := range child {
|
||||
if isBlank(c) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
return false
|
||||
}
|
||||
switch c.kind {
|
||||
case binaryExpr, unaryExpr, compositeLitExpr:
|
||||
// Do not attempt to propagate composite type to operator expressions,
|
||||
@@ -410,11 +423,11 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
sc.loop = n
|
||||
|
||||
case importSpec:
|
||||
// already all done in gta
|
||||
// Already all done in GTA.
|
||||
return false
|
||||
|
||||
case typeSpec:
|
||||
// processing already done in GTA pass for global types, only parses inlined types
|
||||
// Processing already done in GTA pass for global types, only parses inlined types.
|
||||
if sc.def == nil {
|
||||
return false
|
||||
}
|
||||
@@ -424,8 +437,11 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
return false
|
||||
}
|
||||
if typ.incomplete {
|
||||
err = n.cfgErrorf("invalid type declaration")
|
||||
return false
|
||||
// Type may still be incomplete in case of a local recursive struct declaration.
|
||||
if typ, err = typ.finalize(); err != nil {
|
||||
err = n.cfgErrorf("invalid type declaration")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
switch n.child[1].kind {
|
||||
@@ -443,7 +459,7 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
// values which may be used in further declarations.
|
||||
if !sc.global {
|
||||
for _, c := range n.child {
|
||||
if _, err = interp.cfg(c, importPath, pkgName); err != nil {
|
||||
if _, err = interp.cfg(c, sc, importPath, pkgName); err != nil {
|
||||
// No error processing here, to allow recovery in subtree nodes.
|
||||
err = nil
|
||||
}
|
||||
@@ -470,6 +486,10 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
|
||||
switch n.kind {
|
||||
case addressExpr:
|
||||
if isBlank(n.child[0]) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
break
|
||||
}
|
||||
wireChild(n)
|
||||
|
||||
err = check.addressExpr(n)
|
||||
@@ -505,6 +525,11 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
updateSym := false
|
||||
var sym *symbol
|
||||
var level int
|
||||
|
||||
if isBlank(src) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
break
|
||||
}
|
||||
if n.kind == defineStmt || (n.kind == assignStmt && dest.ident == "_") {
|
||||
if atyp != nil {
|
||||
dest.typ = atyp
|
||||
@@ -594,8 +619,8 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
// Skip optimisation for assigned interface.
|
||||
break
|
||||
}
|
||||
if dest.action == aGetIndex {
|
||||
// Skip optimization, as it does not work when assigning to a struct field.
|
||||
if dest.action == aGetIndex || dest.action == aStar {
|
||||
// Skip optimization, as it does not work when assigning to a struct field or a dereferenced pointer.
|
||||
break
|
||||
}
|
||||
n.gen = nop
|
||||
@@ -637,6 +662,10 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
}
|
||||
|
||||
case incDecStmt:
|
||||
err = check.unaryExpr(n)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
wireChild(n)
|
||||
n.findex = n.child[0].findex
|
||||
n.level = n.child[0].level
|
||||
@@ -657,7 +686,12 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
if r := lc.child[0].typ.numOut(); r != l {
|
||||
err = n.cfgErrorf("assignment mismatch: %d variables but %s returns %d values", l, lc.child[0].name(), r)
|
||||
}
|
||||
n.gen = nop
|
||||
if isBinCall(lc, sc) {
|
||||
n.gen = nop
|
||||
} else {
|
||||
// TODO (marc): skip if no conversion or wrapping is needed.
|
||||
n.gen = assignFromCall
|
||||
}
|
||||
case indexExpr:
|
||||
lc.gen = getIndexMap2
|
||||
n.gen = nop
|
||||
@@ -750,6 +784,10 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
}
|
||||
|
||||
case indexExpr:
|
||||
if isBlank(n.child[0]) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
break
|
||||
}
|
||||
wireChild(n)
|
||||
t := n.child[0].typ
|
||||
switch t.cat {
|
||||
@@ -840,28 +878,51 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
n.rval = l.rval
|
||||
|
||||
case breakStmt:
|
||||
if len(n.child) > 0 {
|
||||
gotoLabel(n.sym)
|
||||
} else {
|
||||
if len(n.child) == 0 {
|
||||
n.tnext = sc.loop
|
||||
break
|
||||
}
|
||||
if !n.hasAnc(n.sym.node) {
|
||||
err = n.cfgErrorf("invalid break label %s", n.child[0].ident)
|
||||
break
|
||||
}
|
||||
n.tnext = n.sym.node
|
||||
|
||||
case continueStmt:
|
||||
if len(n.child) > 0 {
|
||||
gotoLabel(n.sym)
|
||||
} else {
|
||||
if len(n.child) == 0 {
|
||||
n.tnext = sc.loopRestart
|
||||
break
|
||||
}
|
||||
if !n.hasAnc(n.sym.node) {
|
||||
err = n.cfgErrorf("invalid continue label %s", n.child[0].ident)
|
||||
break
|
||||
}
|
||||
n.tnext = n.sym.node.child[1].lastChild().start
|
||||
|
||||
case gotoStmt:
|
||||
gotoLabel(n.sym)
|
||||
if n.sym.node == nil {
|
||||
// It can be only due to a forward goto, to be resolved at labeledStmt.
|
||||
// Invalid goto labels are catched at AST parsing.
|
||||
break
|
||||
}
|
||||
n.tnext = n.sym.node.start
|
||||
|
||||
case labeledStmt:
|
||||
wireChild(n)
|
||||
n.start = n.child[1].start
|
||||
gotoLabel(n.sym)
|
||||
if len(n.child) > 1 {
|
||||
n.start = n.child[1].start
|
||||
}
|
||||
for _, c := range n.sym.from {
|
||||
c.tnext = n.start // Resolve forward goto.
|
||||
}
|
||||
|
||||
case callExpr:
|
||||
for _, c := range n.child {
|
||||
if isBlank(c) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
return
|
||||
}
|
||||
}
|
||||
wireChild(n)
|
||||
switch {
|
||||
case isBuiltinCall(n, sc):
|
||||
@@ -885,7 +946,11 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
// Store result directly to frame output location, to avoid a frame copy.
|
||||
n.findex = 0
|
||||
case bname == "cap" && isInConstOrTypeDecl(n):
|
||||
switch n.child[1].typ.TypeOf().Kind() {
|
||||
t := n.child[1].typ.TypeOf()
|
||||
for t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
switch t.Kind() {
|
||||
case reflect.Array, reflect.Chan:
|
||||
capConst(n)
|
||||
default:
|
||||
@@ -894,7 +959,11 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
n.findex = notInFrame
|
||||
n.gen = nop
|
||||
case bname == "len" && isInConstOrTypeDecl(n):
|
||||
switch n.child[1].typ.TypeOf().Kind() {
|
||||
t := n.child[1].typ.TypeOf()
|
||||
for t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
switch t.Kind() {
|
||||
case reflect.Array, reflect.Chan, reflect.String:
|
||||
lenConst(n)
|
||||
default:
|
||||
@@ -955,7 +1024,7 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
n.typ = c0.typ
|
||||
n.findex = sc.add(n.typ)
|
||||
}
|
||||
case isBinCall(n):
|
||||
case isBinCall(n, sc):
|
||||
err = check.arguments(n, n.child[1:], n.child[0], n.action == aCallSlice)
|
||||
if err != nil {
|
||||
break
|
||||
@@ -1255,7 +1324,7 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
// retry with the filename, in case ident is a package name.
|
||||
sym, level, found = sc.lookup(filepath.Join(n.ident, baseName))
|
||||
if !found {
|
||||
err = n.cfgErrorf("undefined: %s", n.ident)
|
||||
err = n.cfgErrorf("undefined: %s %d", n.ident, n.index)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -1369,9 +1438,17 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
sc = sc.pop()
|
||||
|
||||
case keyValueExpr:
|
||||
if isBlank(n.child[1]) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
break
|
||||
}
|
||||
wireChild(n)
|
||||
|
||||
case landExpr:
|
||||
if isBlank(n.child[0]) || isBlank(n.child[1]) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
break
|
||||
}
|
||||
n.start = n.child[0].start
|
||||
n.child[0].tnext = n.child[1].start
|
||||
setFNext(n.child[0], n)
|
||||
@@ -1383,6 +1460,10 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
}
|
||||
|
||||
case lorExpr:
|
||||
if isBlank(n.child[0]) || isBlank(n.child[1]) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
break
|
||||
}
|
||||
n.start = n.child[0].start
|
||||
n.child[0].tnext = n
|
||||
setFNext(n.child[0], n.child[1].start)
|
||||
@@ -1428,6 +1509,12 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
err = n.cfgErrorf("too many arguments to return")
|
||||
break
|
||||
}
|
||||
for _, c := range n.child {
|
||||
if isBlank(c) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
return
|
||||
}
|
||||
}
|
||||
returnSig := sc.def.child[2]
|
||||
if mustReturnValue(returnSig) {
|
||||
nret := len(n.child)
|
||||
@@ -1719,6 +1806,10 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
}
|
||||
|
||||
case starExpr:
|
||||
if isBlank(n.child[0]) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
break
|
||||
}
|
||||
switch {
|
||||
case n.anc.kind == defineStmt && len(n.anc.child) == 3 && n.anc.child[1] == n:
|
||||
// pointer type expression in a var definition
|
||||
@@ -1809,6 +1900,7 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
setFNext(c, clauses[i+1])
|
||||
}
|
||||
}
|
||||
sbn.start = clauses[0].start
|
||||
n.start = n.child[0].start
|
||||
n.child[0].tnext = sbn.start
|
||||
|
||||
@@ -1863,6 +1955,10 @@ func (interp *Interpreter) cfg(root *node, importPath, pkgName string) ([]*node,
|
||||
|
||||
wireChild(n)
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
if isBlank(c0) || isBlank(c1) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
break
|
||||
}
|
||||
if c1.typ == nil {
|
||||
if c1.typ, err = nodeType(interp, sc, c1); err != nil {
|
||||
return
|
||||
@@ -1998,13 +2094,18 @@ func compDefineX(sc *scope, n *node) error {
|
||||
} else {
|
||||
types = funtype.ret
|
||||
}
|
||||
if n.child[l-1].isType(sc) {
|
||||
if n.anc.kind == varDecl && n.child[l-1].isType(sc) {
|
||||
l--
|
||||
}
|
||||
if len(types) != l {
|
||||
return n.cfgErrorf("assignment mismatch: %d variables but %s returns %d values", l, src.child[0].name(), len(types))
|
||||
}
|
||||
n.gen = nop
|
||||
if isBinCall(src, sc) {
|
||||
n.gen = nop
|
||||
} else {
|
||||
// TODO (marc): skip if no conversion or wrapping is needed.
|
||||
n.gen = assignFromCall
|
||||
}
|
||||
|
||||
case indexExpr:
|
||||
types = append(types, src.typ, sc.getType("bool"))
|
||||
@@ -2032,12 +2133,18 @@ func compDefineX(sc *scope, n *node) error {
|
||||
}
|
||||
|
||||
for i, t := range types {
|
||||
index := sc.add(t)
|
||||
sc.sym[n.child[i].ident] = &symbol{index: index, kind: varSym, typ: t}
|
||||
var index int
|
||||
id := n.child[i].ident
|
||||
if sym, level, ok := sc.lookup(id); ok && level == n.child[i].level && sym.kind == varSym && sym.typ.equals(t) {
|
||||
// Reuse symbol in case of a variable redeclaration with the same type.
|
||||
index = sym.index
|
||||
} else {
|
||||
index = sc.add(t)
|
||||
sc.sym[id] = &symbol{index: index, kind: varSym, typ: t}
|
||||
}
|
||||
n.child[i].typ = t
|
||||
n.child[i].findex = index
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2391,6 +2498,15 @@ func (n *node) fieldType(m int) *node {
|
||||
// lastChild returns the last child of a node.
|
||||
func (n *node) lastChild() *node { return n.child[len(n.child)-1] }
|
||||
|
||||
func (n *node) hasAnc(nod *node) bool {
|
||||
for a := n.anc; a != nil; a = a.anc {
|
||||
if a == nod {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isKey(n *node) bool {
|
||||
return n.anc.kind == fileStmt ||
|
||||
(n.anc.kind == selectorExpr && n.anc.child[0] != n) ||
|
||||
@@ -2407,7 +2523,7 @@ func isInConstOrTypeDecl(n *node) bool {
|
||||
anc := n.anc
|
||||
for anc != nil {
|
||||
switch anc.kind {
|
||||
case constDecl, typeDecl:
|
||||
case constDecl, typeDecl, arrayType, chanType:
|
||||
return true
|
||||
case varDecl, funcDecl:
|
||||
return false
|
||||
@@ -2453,8 +2569,19 @@ func isCall(n *node) bool {
|
||||
return n.action == aCall || n.action == aCallSlice
|
||||
}
|
||||
|
||||
func isBinCall(n *node) bool {
|
||||
return isCall(n) && n.child[0].typ.cat == valueT && n.child[0].typ.rtype.Kind() == reflect.Func
|
||||
func isBinCall(n *node, sc *scope) bool {
|
||||
if !isCall(n) || len(n.child) == 0 {
|
||||
return false
|
||||
}
|
||||
c0 := n.child[0]
|
||||
if c0.typ == nil {
|
||||
// If called early in parsing, child type may not be known yet.
|
||||
c0.typ, _ = nodeType(n.interp, sc, c0)
|
||||
if c0.typ == nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return c0.typ.cat == valueT && c0.typ.rtype.Kind() == reflect.Func
|
||||
}
|
||||
|
||||
func isOffsetof(n *node) bool {
|
||||
@@ -2546,21 +2673,10 @@ func typeSwichAssign(n *node) bool {
|
||||
return ts.kind == typeSwitch && ts.child[1].action == aAssign
|
||||
}
|
||||
|
||||
func gotoLabel(s *symbol) {
|
||||
if s.node == nil {
|
||||
return
|
||||
}
|
||||
for _, c := range s.from {
|
||||
if c.tnext == nil {
|
||||
c.tnext = s.node.start
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func compositeGenerator(n *node, typ *itype, rtyp reflect.Type) (gen bltnGenerator) {
|
||||
switch typ.cat {
|
||||
case aliasT, ptrT:
|
||||
gen = compositeGenerator(n, n.typ.val, rtyp)
|
||||
gen = compositeGenerator(n, typ.val, rtyp)
|
||||
case arrayT, sliceT:
|
||||
gen = arrayLit
|
||||
case mapT:
|
||||
@@ -2614,23 +2730,54 @@ func compositeGenerator(n *node, typ *itype, rtyp reflect.Type) (gen bltnGenerat
|
||||
// arrayTypeLen returns the node's array length. If the expression is an
|
||||
// array variable it is determined from the value's type, otherwise it is
|
||||
// computed from the source definition.
|
||||
func arrayTypeLen(n *node) int {
|
||||
func arrayTypeLen(n *node, sc *scope) (int, error) {
|
||||
if n.typ != nil && n.typ.cat == arrayT {
|
||||
return n.typ.length
|
||||
return n.typ.length, nil
|
||||
}
|
||||
max := -1
|
||||
for i, c := range n.child[1:] {
|
||||
r := i
|
||||
if c.kind == keyValueExpr {
|
||||
if v := c.child[0].rval; v.IsValid() {
|
||||
r = int(c.child[0].rval.Int())
|
||||
for _, c := range n.child[1:] {
|
||||
var r int
|
||||
|
||||
if c.kind != keyValueExpr {
|
||||
r = max + 1
|
||||
max = r
|
||||
continue
|
||||
}
|
||||
|
||||
c0 := c.child[0]
|
||||
v := c0.rval
|
||||
if v.IsValid() {
|
||||
r = int(v.Int())
|
||||
} else {
|
||||
// Resolve array key value as a constant.
|
||||
if c0.kind == identExpr {
|
||||
// Key is defined by a symbol which must be a constant integer.
|
||||
sym, _, ok := sc.lookup(c0.ident)
|
||||
if !ok {
|
||||
return 0, c0.cfgErrorf("undefined: %s", c0.ident)
|
||||
}
|
||||
if sym.kind != constSym {
|
||||
return 0, c0.cfgErrorf("non-constant array bound %q", c0.ident)
|
||||
}
|
||||
r = int(vInt(sym.rval))
|
||||
} else {
|
||||
// Key is defined by a numeric constant expression.
|
||||
if _, err := c0.interp.cfg(c0, sc, sc.pkgID, sc.pkgName); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cv, ok := c0.rval.Interface().(constant.Value)
|
||||
if !ok {
|
||||
return 0, c0.cfgErrorf("non-constant expression")
|
||||
}
|
||||
r = constToInt(cv)
|
||||
}
|
||||
}
|
||||
|
||||
if r > max {
|
||||
max = r
|
||||
}
|
||||
}
|
||||
return max + 1
|
||||
return max + 1, nil
|
||||
}
|
||||
|
||||
// isValueUntyped returns true if value is untyped.
|
||||
@@ -2658,3 +2805,10 @@ func isBoolAction(n *node) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isBlank(n *node) bool {
|
||||
if n.kind == parenExpr && len(n.child) > 0 {
|
||||
return isBlank(n.child[0])
|
||||
}
|
||||
return n.ident == "_"
|
||||
}
|
||||
|
||||
84
interp/compile_test.go
Normal file
84
interp/compile_test.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"testing"
|
||||
|
||||
"github.com/traefik/yaegi/stdlib"
|
||||
)
|
||||
|
||||
func TestCompileAST(t *testing.T) {
|
||||
i := New(Options{})
|
||||
file, err := parser.ParseFile(i.FileSet(), "_.go", `
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Foo struct{}
|
||||
|
||||
var foo Foo
|
||||
const bar = "asdf"
|
||||
|
||||
func main() {
|
||||
fmt.Println(1)
|
||||
}
|
||||
`, 0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if len(file.Imports) != 1 || len(file.Decls) != 5 {
|
||||
panic("wrong number of imports or decls")
|
||||
}
|
||||
|
||||
dType := file.Decls[1].(*ast.GenDecl)
|
||||
dVar := file.Decls[2].(*ast.GenDecl)
|
||||
dConst := file.Decls[3].(*ast.GenDecl)
|
||||
dFunc := file.Decls[4].(*ast.FuncDecl)
|
||||
|
||||
if dType.Tok != token.TYPE {
|
||||
panic("decl[1] is not a type")
|
||||
}
|
||||
if dVar.Tok != token.VAR {
|
||||
panic("decl[2] is not a var")
|
||||
}
|
||||
if dConst.Tok != token.CONST {
|
||||
panic("decl[3] is not a const")
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
node ast.Node
|
||||
skip string
|
||||
}{
|
||||
{desc: "file", node: file},
|
||||
{desc: "import", node: file.Imports[0]},
|
||||
{desc: "type", node: dType},
|
||||
{desc: "var", node: dVar, skip: "not supported"},
|
||||
{desc: "const", node: dConst},
|
||||
{desc: "func", node: dFunc},
|
||||
{desc: "block", node: dFunc.Body},
|
||||
{desc: "expr", node: dFunc.Body.List[0]},
|
||||
}
|
||||
|
||||
_ = i.Use(stdlib.Symbols)
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.desc, func(t *testing.T) {
|
||||
if c.skip != "" {
|
||||
t.Skip(c.skip)
|
||||
}
|
||||
|
||||
i := i
|
||||
if _, ok := c.node.(*ast.File); ok {
|
||||
i = New(Options{})
|
||||
_ = i.Use(stdlib.Symbols)
|
||||
}
|
||||
_, err := i.CompileAST(c.node)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to compile %s: %v", c.desc, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package interp
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -69,7 +68,7 @@ func (nopCloser) Close() error { return nil }
|
||||
// dotWriter returns an output stream to a dot(1) co-process where to write data in .dot format.
|
||||
func dotWriter(dotCmd string) io.WriteCloser {
|
||||
if dotCmd == "" {
|
||||
return nopCloser{ioutil.Discard}
|
||||
return nopCloser{io.Discard}
|
||||
}
|
||||
fields := strings.Fields(dotCmd)
|
||||
cmd := exec.Command(fields[0], fields[1:]...)
|
||||
|
||||
100
interp/gta.go
100
interp/gta.go
@@ -25,7 +25,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
case constDecl:
|
||||
// Early parse of constDecl subtree, to compute all constant
|
||||
// values which may be used in further declarations.
|
||||
if _, err = interp.cfg(n, importPath, pkgName); err != nil {
|
||||
if _, err = interp.cfg(n, sc, importPath, pkgName); err != nil {
|
||||
// No error processing here, to allow recovery in subtree nodes.
|
||||
// TODO(marc): check for a non recoverable error and return it for better diagnostic.
|
||||
err = nil
|
||||
@@ -37,10 +37,17 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
}
|
||||
|
||||
case defineStmt:
|
||||
var atyp *itype
|
||||
var (
|
||||
atyp *itype
|
||||
err2 error
|
||||
)
|
||||
if n.nleft+n.nright < len(n.child) {
|
||||
// Type is declared explicitly in the assign expression.
|
||||
if atyp, err = nodeType(interp, sc, n.child[n.nleft]); err != nil {
|
||||
if atyp, err2 = nodeType(interp, sc, n.child[n.nleft]); err2 != nil {
|
||||
// The type does not exist yet, stash the error and come back
|
||||
// when the type is known.
|
||||
n.meta = err2
|
||||
revisit = append(revisit, n)
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -52,9 +59,12 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
|
||||
for i := 0; i < n.nleft; i++ {
|
||||
dest, src := n.child[i], n.child[sbase+i]
|
||||
if isBlank(src) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
}
|
||||
val := src.rval
|
||||
if n.anc.kind == constDecl {
|
||||
if _, err2 := interp.cfg(n, importPath, pkgName); err2 != nil {
|
||||
if _, err2 := interp.cfg(n, sc, importPath, pkgName); err2 != nil {
|
||||
// Constant value can not be computed yet.
|
||||
// Come back when child dependencies are known.
|
||||
revisit = append(revisit, n)
|
||||
@@ -63,7 +73,11 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
}
|
||||
typ := atyp
|
||||
if typ == nil {
|
||||
if typ, err = nodeType(interp, sc, src); err != nil || typ == nil {
|
||||
if typ, err2 = nodeType(interp, sc, src); err2 != nil || typ == nil {
|
||||
// The type does is not known yet, stash the error and come back
|
||||
// when the type is known.
|
||||
n.meta = err2
|
||||
revisit = append(revisit, n)
|
||||
return false
|
||||
}
|
||||
val = src.rval
|
||||
@@ -138,26 +152,26 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
n.ident = ident
|
||||
rcvr := n.child[0].child[0]
|
||||
rtn := rcvr.lastChild()
|
||||
typeName := rtn.ident
|
||||
if typeName == "" {
|
||||
// The receiver is a pointer, retrieve typeName from indirection
|
||||
typeName = rtn.child[0].ident
|
||||
elementType := sc.getType(typeName)
|
||||
if elementType == nil {
|
||||
// Add type if necessary, so method can be registered
|
||||
sc.sym[typeName] = &symbol{kind: typeSym, typ: &itype{name: typeName, path: pkgName, incomplete: true, node: rtn.child[0], scope: sc}}
|
||||
elementType = sc.sym[typeName].typ
|
||||
}
|
||||
typName, typPtr := rtn.ident, false
|
||||
if typName == "" {
|
||||
typName, typPtr = rtn.child[0].ident, true
|
||||
}
|
||||
sym, _, found := sc.lookup(typName)
|
||||
if !found {
|
||||
n.meta = n.cfgErrorf("undefined: %s", typName)
|
||||
revisit = append(revisit, n)
|
||||
return false
|
||||
}
|
||||
if sym.kind != typeSym || (sym.node != nil && sym.node.kind == typeSpecAssign) {
|
||||
err = n.cfgErrorf("cannot define new methods on non-local type %s", baseType(sym.typ).id())
|
||||
return false
|
||||
}
|
||||
rcvrtype = sym.typ
|
||||
if typPtr {
|
||||
elementType := sym.typ
|
||||
rcvrtype = ptrOf(elementType, withNode(rtn), withScope(sc))
|
||||
rcvrtype.incomplete = elementType.incomplete
|
||||
elementType.addMethod(n)
|
||||
} else {
|
||||
rcvrtype = sc.getType(typeName)
|
||||
if rcvrtype == nil {
|
||||
// Add type if necessary, so method can be registered
|
||||
sc.sym[typeName] = &symbol{kind: typeSym, typ: &itype{name: typeName, path: pkgName, incomplete: true, node: rtn, scope: sc}}
|
||||
rcvrtype = sc.sym[typeName].typ
|
||||
}
|
||||
}
|
||||
rcvrtype.addMethod(n)
|
||||
n.child[0].child[0].lastChild().typ = rcvrtype
|
||||
@@ -197,15 +211,23 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
case ".": // import symbols in current scope
|
||||
for n, v := range pkg {
|
||||
typ := v.Type()
|
||||
kind := binSym
|
||||
if isBinType(v) {
|
||||
typ = typ.Elem()
|
||||
kind = typeSym
|
||||
}
|
||||
sc.sym[n] = &symbol{kind: binSym, typ: valueTOf(typ, withScope(sc)), rval: v}
|
||||
sc.sym[n] = &symbol{kind: kind, typ: valueTOf(typ, withScope(sc)), rval: v}
|
||||
}
|
||||
default: // import symbols in package namespace
|
||||
if name == "" {
|
||||
name = interp.pkgNames[ipath]
|
||||
}
|
||||
|
||||
// If an incomplete type exists, delete it
|
||||
if sym, exists := sc.sym[name]; exists && sym.kind == typeSym && sym.typ.incomplete {
|
||||
delete(sc.sym, name)
|
||||
}
|
||||
|
||||
// Imports of a same package are all mapped in the same scope, so we cannot just
|
||||
// map them by their names, otherwise we could have collisions from same-name
|
||||
// imports in different source files of the same package. Therefore, we suffix
|
||||
@@ -254,7 +276,11 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
err = n.cfgErrorf("import %q error: %v", ipath, err)
|
||||
}
|
||||
|
||||
case typeSpec:
|
||||
case typeSpec, typeSpecAssign:
|
||||
if isBlank(n.child[0]) {
|
||||
err = n.cfgErrorf("cannot use _ as value")
|
||||
return false
|
||||
}
|
||||
typeName := n.child[0].ident
|
||||
var typ *itype
|
||||
if typ, err = nodeType(interp, sc, n.child[1]); err != nil {
|
||||
@@ -284,9 +310,13 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
}
|
||||
sym, exists := sc.sym[typeName]
|
||||
if !exists {
|
||||
sc.sym[typeName] = &symbol{kind: typeSym}
|
||||
sc.sym[typeName] = &symbol{kind: typeSym, node: n}
|
||||
} else {
|
||||
if sym.typ != nil && (len(sym.typ.method) > 0) {
|
||||
if n.kind == typeSpecAssign {
|
||||
err = n.cfgErrorf("cannot define new methods on non-local type %s", baseType(typ).id())
|
||||
return false
|
||||
}
|
||||
// Type has already been seen as a receiver in a method function
|
||||
for _, m := range sym.typ.method {
|
||||
n.typ.addMethod(m)
|
||||
@@ -294,7 +324,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
} else {
|
||||
// TODO(mpl): figure out how to detect redeclarations without breaking type aliases.
|
||||
// Allow redeclarations for now.
|
||||
sc.sym[typeName] = &symbol{kind: typeSym}
|
||||
sc.sym[typeName] = &symbol{kind: typeSym, node: n}
|
||||
}
|
||||
}
|
||||
sc.sym[typeName].typ = n.typ
|
||||
@@ -312,6 +342,17 @@ func (interp *Interpreter) gta(root *node, rpath, importPath, pkgName string) ([
|
||||
return revisit, err
|
||||
}
|
||||
|
||||
func baseType(t *itype) *itype {
|
||||
for {
|
||||
switch t.cat {
|
||||
case ptrT, aliasT:
|
||||
t = t.val
|
||||
default:
|
||||
return t
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// gtaRetry (re)applies gta until all global constants and types are defined.
|
||||
func (interp *Interpreter) gtaRetry(nodes []*node, importPath, pkgName string) error {
|
||||
revisit := []*node{}
|
||||
@@ -334,10 +375,15 @@ func (interp *Interpreter) gtaRetry(nodes []*node, importPath, pkgName string) e
|
||||
|
||||
if len(revisit) > 0 {
|
||||
n := revisit[0]
|
||||
if n.kind == typeSpec {
|
||||
switch n.kind {
|
||||
case typeSpec, typeSpecAssign:
|
||||
if err := definedType(n.typ); err != nil {
|
||||
return err
|
||||
}
|
||||
case defineStmt, funcDecl:
|
||||
if err, ok := n.meta.(error); ok {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return n.cfgErrorf("constant definition loop")
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -53,6 +55,7 @@ type node struct {
|
||||
val interface{} // static generic value (CFG execution)
|
||||
rval reflect.Value // reflection value to let runtime access interpreter (CFG)
|
||||
ident string // set if node is a var or func
|
||||
meta interface{} // meta stores meta information between gta runs, like errors
|
||||
}
|
||||
|
||||
func (n *node) shouldBreak() bool {
|
||||
@@ -168,19 +171,22 @@ type imports map[string]map[string]*symbol
|
||||
|
||||
// opt stores interpreter options.
|
||||
type opt struct {
|
||||
astDot bool // display AST graph (debug)
|
||||
cfgDot bool // display CFG graph (debug)
|
||||
// dotCmd is the command to process the dot graph produced when astDot and/or
|
||||
// cfgDot is enabled. It defaults to 'dot -Tdot -o <filename>.dot'.
|
||||
dotCmd string
|
||||
noRun bool // compile, but do not run
|
||||
fastChan bool // disable cancellable chan operations
|
||||
context build.Context // build context: GOPATH, build constraints
|
||||
specialStdio bool // Allows os.Stdin, os.Stdout, os.Stderr to not be file descriptors
|
||||
stdin io.Reader // standard input
|
||||
stdout io.Writer // standard output
|
||||
stderr io.Writer // standard error
|
||||
filesystem fs.FS
|
||||
context build.Context // build context: GOPATH, build constraints
|
||||
stdin io.Reader // standard input
|
||||
stdout io.Writer // standard output
|
||||
stderr io.Writer // standard error
|
||||
args []string // cmdline args
|
||||
env map[string]string // environment of interpreter, entries in form of "key=value"
|
||||
filesystem fs.FS // filesystem containing sources
|
||||
astDot bool // display AST graph (debug)
|
||||
cfgDot bool // display CFG graph (debug)
|
||||
noRun bool // compile, but do not run
|
||||
fastChan bool // disable cancellable chan operations
|
||||
specialStdio bool // allows os.Stdin, os.Stdout, os.Stderr to not be file descriptors
|
||||
unrestricted bool // allow use of non sandboxed symbols
|
||||
}
|
||||
|
||||
// Interpreter contains global resources and state.
|
||||
@@ -302,17 +308,26 @@ type Options struct {
|
||||
Stdin io.Reader
|
||||
Stdout, Stderr io.Writer
|
||||
|
||||
// Cmdline args, defaults to os.Args.
|
||||
Args []string
|
||||
|
||||
// Environment of interpreter. Entries are in the form "key=values".
|
||||
Env []string
|
||||
|
||||
// SourcecodeFilesystem is where the _sourcecode_ is loaded from and does
|
||||
// NOT affect the filesystem of scripts when they run.
|
||||
// It can be any fs.FS compliant filesystem (e.g. embed.FS, or fstest.MapFS for testing)
|
||||
// See example/fs/fs_test.go for an example.
|
||||
SourcecodeFilesystem fs.FS
|
||||
|
||||
// Unrestricted allows to run non sandboxed stdlib symbols such as os/exec and environment
|
||||
Unrestricted bool
|
||||
}
|
||||
|
||||
// New returns a new interpreter.
|
||||
func New(options Options) *Interpreter {
|
||||
i := Interpreter{
|
||||
opt: opt{context: build.Default, filesystem: &realFS{}},
|
||||
opt: opt{context: build.Default, filesystem: &realFS{}, env: map[string]string{}},
|
||||
frame: newFrame(nil, 0, 0),
|
||||
fset: token.NewFileSet(),
|
||||
universe: initUniverse(),
|
||||
@@ -336,6 +351,24 @@ func New(options Options) *Interpreter {
|
||||
i.opt.stderr = os.Stderr
|
||||
}
|
||||
|
||||
if i.opt.args = options.Args; i.opt.args == nil {
|
||||
i.opt.args = os.Args
|
||||
}
|
||||
|
||||
// unrestricted allows to use non sandboxed stdlib symbols and env.
|
||||
if options.Unrestricted {
|
||||
i.opt.unrestricted = true
|
||||
} else {
|
||||
for _, e := range options.Env {
|
||||
a := strings.SplitN(e, "=", 2)
|
||||
if len(a) == 2 {
|
||||
i.opt.env[a[0]] = a[1]
|
||||
} else {
|
||||
i.opt.env[a[0]] = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if options.SourcecodeFilesystem != nil {
|
||||
i.opt.filesystem = options.SourcecodeFilesystem
|
||||
}
|
||||
@@ -572,7 +605,7 @@ func isFile(filesystem fs.FS, path string) bool {
|
||||
}
|
||||
|
||||
func (interp *Interpreter) eval(src, name string, inc bool) (res reflect.Value, err error) {
|
||||
prog, err := interp.compile(src, name, inc)
|
||||
prog, err := interp.compileSrc(src, name, inc)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
@@ -597,7 +630,14 @@ func (interp *Interpreter) EvalWithContext(ctx context.Context, src string) (ref
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer close(done)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
var pc [64]uintptr
|
||||
n := runtime.Callers(1, pc[:])
|
||||
err = Panic{Value: r, Callers: pc[:n], Stack: debug.Stack()}
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
v, err = interp.Eval(src)
|
||||
}()
|
||||
|
||||
@@ -715,6 +755,7 @@ func fixStdlib(interp *Interpreter) {
|
||||
}
|
||||
|
||||
if p = interp.binPkg["os"]; p != nil {
|
||||
p["Args"] = reflect.ValueOf(&interp.args).Elem()
|
||||
if interp.specialStdio {
|
||||
// Inherit streams from interpreter even if they do not have a file descriptor.
|
||||
p["Stdin"] = reflect.ValueOf(&stdin).Elem()
|
||||
@@ -732,6 +773,22 @@ func fixStdlib(interp *Interpreter) {
|
||||
p["Stderr"] = reflect.ValueOf(&s).Elem()
|
||||
}
|
||||
}
|
||||
if !interp.unrestricted {
|
||||
// In restricted mode, scripts can only access to a passed virtualized env, and can not write the real one.
|
||||
getenv := func(key string) string { return interp.env[key] }
|
||||
p["Clearenv"] = reflect.ValueOf(func() { interp.env = map[string]string{} })
|
||||
p["ExpandEnv"] = reflect.ValueOf(func(s string) string { return os.Expand(s, getenv) })
|
||||
p["Getenv"] = reflect.ValueOf(getenv)
|
||||
p["LookupEnv"] = reflect.ValueOf(func(key string) (s string, ok bool) { s, ok = interp.env[key]; return })
|
||||
p["Setenv"] = reflect.ValueOf(func(key, value string) error { interp.env[key] = value; return nil })
|
||||
p["Unsetenv"] = reflect.ValueOf(func(key string) error { delete(interp.env, key); return nil })
|
||||
p["Environ"] = reflect.ValueOf(func() (a []string) {
|
||||
for k, v := range interp.env {
|
||||
a = append(a, k+"="+v)
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if p = interp.binPkg["math/bits"]; p != nil {
|
||||
|
||||
@@ -2,7 +2,7 @@ package interp_test
|
||||
|
||||
import (
|
||||
"go/build"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -20,13 +20,13 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
}
|
||||
dir := filepath.Join("..", "_test", "tmp")
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
if err := os.Mkdir(dir, 0700); err != nil {
|
||||
if err := os.Mkdir(dir, 0o700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
baseDir := filepath.Join("..", "_test")
|
||||
files, err := ioutil.ReadDir(baseDir)
|
||||
files, err := os.ReadDir(baseDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -37,6 +37,8 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
file.Name() == "assign12.go" || // expect error
|
||||
file.Name() == "assign15.go" || // expect error
|
||||
file.Name() == "bad0.go" || // expect error
|
||||
file.Name() == "break0.go" || // expect error
|
||||
file.Name() == "cont3.go" || // expect error
|
||||
file.Name() == "const9.go" || // expect error
|
||||
file.Name() == "export1.go" || // non-main package
|
||||
file.Name() == "export0.go" || // non-main package
|
||||
@@ -51,11 +53,14 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
file.Name() == "init1.go" || // expect error
|
||||
file.Name() == "io0.go" || // use random number
|
||||
file.Name() == "issue-1093.go" || // expect error
|
||||
file.Name() == "issue-1276.go" || // expect error
|
||||
file.Name() == "issue-1330.go" || // expect error
|
||||
file.Name() == "op1.go" || // expect error
|
||||
file.Name() == "op7.go" || // expect error
|
||||
file.Name() == "op9.go" || // expect error
|
||||
file.Name() == "bltn0.go" || // expect error
|
||||
file.Name() == "method16.go" || // private struct field
|
||||
file.Name() == "method39.go" || // expect error
|
||||
file.Name() == "switch8.go" || // expect error
|
||||
file.Name() == "switch9.go" || // expect error
|
||||
file.Name() == "switch13.go" || // expect error
|
||||
@@ -100,7 +105,14 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
file.Name() == "server.go" || // syntax parsing
|
||||
file.Name() == "range9.go" || // expect error
|
||||
file.Name() == "unsafe6.go" || // needs go.mod to be 1.17
|
||||
file.Name() == "unsafe7.go" { // needs go.mod to be 1.17
|
||||
file.Name() == "unsafe7.go" || // needs go.mod to be 1.17
|
||||
file.Name() == "type27.go" || // expect error
|
||||
file.Name() == "type28.go" || // expect error
|
||||
file.Name() == "type29.go" || // expect error
|
||||
file.Name() == "type30.go" || // expect error
|
||||
file.Name() == "type31.go" || // expect error
|
||||
file.Name() == "type32.go" || // expect error
|
||||
file.Name() == "type33.go" { // expect error
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -136,7 +148,7 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
if err = w.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
outInterp, err := ioutil.ReadAll(r)
|
||||
outInterp, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -188,6 +200,16 @@ func TestInterpErrorConsistency(t *testing.T) {
|
||||
expectedInterp: "1:1: expected 'package', found println",
|
||||
expectedExec: "1:1: expected 'package', found println",
|
||||
},
|
||||
{
|
||||
fileName: "break0.go",
|
||||
expectedInterp: "15:5: invalid break label OuterLoop",
|
||||
expectedExec: "15:11: invalid break label OuterLoop",
|
||||
},
|
||||
{
|
||||
fileName: "cont3.go",
|
||||
expectedInterp: "15:5: invalid continue label OuterLoop",
|
||||
expectedExec: "15:14: invalid continue label OuterLoop",
|
||||
},
|
||||
{
|
||||
fileName: "const9.go",
|
||||
expectedInterp: "5:2: constant definition loop",
|
||||
|
||||
@@ -5,8 +5,9 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"go/parser"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -128,6 +129,10 @@ func TestEvalAssign(t *testing.T) {
|
||||
{src: "i := 1; j := &i; (*j) = 2", res: "2"},
|
||||
{src: "i64 := testpkg.val; i64 == 11", res: "true"},
|
||||
{pre: func() { eval(t, i, "k := 1") }, src: `k := "Hello world"`, res: "Hello world"}, // allow reassignment in subsequent evaluations
|
||||
{src: "_ = _", err: "1:28: cannot use _ as value"},
|
||||
{src: "j := true || _", err: "1:33: cannot use _ as value"},
|
||||
{src: "j := true && _", err: "1:33: cannot use _ as value"},
|
||||
{src: "j := interface{}(int(1)); j.(_)", err: "1:54: cannot use _ as value"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -178,6 +183,7 @@ func TestEvalBuiltin(t *testing.T) {
|
||||
{src: `t := map[int]int{}; t[123]--; t`, res: "map[123:-1]"},
|
||||
{src: `t := map[int]int{}; t[123] += 1; t`, res: "map[123:1]"},
|
||||
{src: `t := map[int]int{}; t[123] -= 1; t`, res: "map[123:-1]"},
|
||||
{src: `println("hello", _)`, err: "1:28: cannot use _ as value"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -202,6 +208,14 @@ func TestEvalDeclWithExpr(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestEvalTypeSpec(t *testing.T) {
|
||||
i := interp.New(interp.Options{})
|
||||
runTests(t, i, []testCase{
|
||||
{src: `type _ struct{}`, err: "1:19: cannot use _ as value"},
|
||||
{src: `a := struct{a, _ int}{32, 0}`, res: "{32 0}"},
|
||||
})
|
||||
}
|
||||
|
||||
func TestEvalFunc(t *testing.T) {
|
||||
i := interp.New(interp.Options{})
|
||||
runTests(t, i, []testCase{
|
||||
@@ -210,6 +224,8 @@ func TestEvalFunc(t *testing.T) {
|
||||
{src: `(func () int {f := func() (a, b int) {a, b = 3, 4; return}; x, y := f(); return x+y})()`, res: "7"},
|
||||
{src: `(func () int {f := func() (a int, b, c int) {a, b, c = 3, 4, 5; return}; x, y, z := f(); return x+y+z})()`, res: "12"},
|
||||
{src: `(func () int {f := func() (a, b, c int) {a, b, c = 3, 4, 5; return}; x, y, z := f(); return x+y+z})()`, res: "12"},
|
||||
{src: `func f() int { return _ }`, err: "1:29: cannot use _ as value"},
|
||||
{src: `(func (x int) {})(_)`, err: "1:28: cannot use _ as value"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -418,6 +434,8 @@ func TestEvalComparison(t *testing.T) {
|
||||
{src: `2 > 1`, res: "true"},
|
||||
{src: `1.2 > 1.1`, res: "true"},
|
||||
{src: `"hhh" > "ggg"`, res: "true"},
|
||||
{src: `a, b, c := 1, 1, false; if a == b { c = true }; c`, res: "true"},
|
||||
{src: `a, b, c := 1, 2, false; if a != b { c = true }; c`, res: "true"},
|
||||
{
|
||||
desc: "mismatched types",
|
||||
src: `
|
||||
@@ -430,6 +448,16 @@ func TestEvalComparison(t *testing.T) {
|
||||
`,
|
||||
err: "7:13: invalid operation: mismatched types main.Foo and main.Bar",
|
||||
},
|
||||
{src: `1 > _`, err: "1:28: cannot use _ as value"},
|
||||
{src: `(_) > 1`, err: "1:28: cannot use _ as value"},
|
||||
{src: `v := interface{}(2); v == 2`, res: "true"},
|
||||
{src: `v := interface{}(2); v > 1`, err: "1:49: invalid operation: operator > not defined on interface{}"},
|
||||
{src: `v := interface{}(int64(2)); v == 2`, res: "false"},
|
||||
{src: `v := interface{}(int64(2)); v != 2`, res: "true"},
|
||||
{src: `v := interface{}(2.3); v == 2.3`, res: "true"},
|
||||
{src: `v := interface{}(float32(2.3)); v != 2.3`, res: "true"},
|
||||
{src: `v := interface{}("hello"); v == "hello"`, res: "true"},
|
||||
{src: `v := interface{}("hello"); v < "hellp"`, err: "1:55: invalid operation: operator < not defined on interface{}"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -445,6 +473,7 @@ func TestEvalCompositeArray(t *testing.T) {
|
||||
{src: `a := [1]int{1, 2}`, err: "1:43: index 1 is out of bounds (>= 1)"},
|
||||
{src: `b := [l]int{1, 2}`, res: "[1 2 0 0 0 0 0 0 0 0]"},
|
||||
{src: `i := 10; a := [i]int{1, 2}`, err: "1:43: non-constant array bound \"i\""},
|
||||
{src: `c := [...]float64{1, 3: 3.4, 5}`, res: "[1 0 0 3.4 5]"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -474,6 +503,8 @@ func TestEvalCompositeStruct(t *testing.T) {
|
||||
{src: `a := struct{A,B,C int}{A:1,A:2,C:3}`, err: "1:55: duplicate field name A in struct literal"},
|
||||
{src: `a := struct{A,B,C int}{A:1,B:2.2,C:3}`, err: "1:57: 11/5 truncated to int"},
|
||||
{src: `a := struct{A,B,C int}{A:1,2,C:3}`, err: "1:55: mixture of field:value and value elements in struct literal"},
|
||||
{src: `a := struct{A,B,C int}{1,2,_}`, err: "1:33: cannot use _ as value"},
|
||||
{src: `a := struct{A,B,C int}{B: _}`, err: "1:51: cannot use _ as value"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -498,6 +529,8 @@ func TestEvalSliceExpression(t *testing.T) {
|
||||
{src: `a := []int{0,1,2,3}[1:3:]`, err: "1:51: 3rd index required in 3-index slice"},
|
||||
{src: `a := []int{0,1,2}[3:1]`, err: "invalid index values, must be low <= high <= max"},
|
||||
{pre: func() { eval(t, i, `type Str = string; var r Str = "truc"`) }, src: `r[1]`, res: "114"},
|
||||
{src: `_[12]`, err: "1:28: cannot use _ as value"},
|
||||
{src: `b := []int{0,1,2}[_:4]`, err: "1:33: cannot use _ as value"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -508,6 +541,7 @@ func TestEvalConversion(t *testing.T) {
|
||||
{src: `i := 1.1; a := uint64(i)`, res: "1"},
|
||||
{src: `b := string(49)`, res: "1"},
|
||||
{src: `c := uint64(1.1)`, err: "1:40: cannot convert expression of type untyped float to type uint64"},
|
||||
{src: `int(_)`, err: "1:28: cannot use _ as value"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -517,6 +551,9 @@ func TestEvalUnary(t *testing.T) {
|
||||
{src: "a := -1", res: "-1"},
|
||||
{src: "b := +1", res: "1", skip: "BUG"},
|
||||
{src: "c := !false", res: "true"},
|
||||
{src: "_ = 2; _++", err: "1:35: cannot use _ as value"},
|
||||
{src: "_ = false; !_ == true", err: "1:39: cannot use _ as value"},
|
||||
{src: "!((((_))))", err: "1:28: cannot use _ as value"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -882,7 +919,7 @@ func TestMultiEval(t *testing.T) {
|
||||
if err = w.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
outInterp, err := ioutil.ReadAll(r)
|
||||
outInterp, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -913,7 +950,7 @@ func TestMultiEvalNoName(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for k, v := range names {
|
||||
data, err := ioutil.ReadFile(filepath.Join(f.Name(), v))
|
||||
data, err := os.ReadFile(filepath.Join(f.Name(), v))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -1623,6 +1660,16 @@ func TestStdio(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoGoFiles(t *testing.T) {
|
||||
i := interp.New(interp.Options{GoPath: build.Default.GOPATH})
|
||||
_, err := i.Eval(`import "github.com/traefik/yaegi/_test/p3"`)
|
||||
if strings.Contains(err.Error(), "no Go files in") {
|
||||
return
|
||||
}
|
||||
|
||||
t.Fatalf("failed to detect no Go files: %v", err)
|
||||
}
|
||||
|
||||
func TestIssue1142(t *testing.T) {
|
||||
i := interp.New(interp.Options{})
|
||||
runTests(t, i, []testCase{
|
||||
@@ -1712,3 +1759,94 @@ func TestIssue1151(t *testing.T) {
|
||||
{src: "x := pkg.Array{1}", res: "[1]"},
|
||||
})
|
||||
}
|
||||
|
||||
func TestPassArgs(t *testing.T) {
|
||||
i := interp.New(interp.Options{Args: []string{"arg0", "arg1"}})
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
i.ImportUsed()
|
||||
runTests(t, i, []testCase{
|
||||
{src: "os.Args", res: "[arg0 arg1]"},
|
||||
})
|
||||
}
|
||||
|
||||
func TestRestrictedEnv(t *testing.T) {
|
||||
i := interp.New(interp.Options{Env: []string{"foo=bar"}})
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
i.ImportUsed()
|
||||
runTests(t, i, []testCase{
|
||||
{src: `os.Getenv("foo")`, res: "bar"},
|
||||
{src: `s, ok := os.LookupEnv("foo"); s`, res: "bar"},
|
||||
{src: `s, ok := os.LookupEnv("foo"); ok`, res: "true"},
|
||||
{src: `s, ok := os.LookupEnv("PATH"); s`, res: ""},
|
||||
{src: `s, ok := os.LookupEnv("PATH"); ok`, res: "false"},
|
||||
{src: `os.Setenv("foo", "baz"); os.Environ()`, res: "[foo=baz]"},
|
||||
{src: `os.ExpandEnv("foo is ${foo}")`, res: "foo is baz"},
|
||||
{src: `os.Unsetenv("foo"); os.Environ()`, res: "[]"},
|
||||
{src: `os.Setenv("foo", "baz"); os.Environ()`, res: "[foo=baz]"},
|
||||
{src: `os.Clearenv(); os.Environ()`, res: "[]"},
|
||||
{src: `os.Setenv("foo", "baz"); os.Environ()`, res: "[foo=baz]"},
|
||||
})
|
||||
if s, ok := os.LookupEnv("foo"); ok {
|
||||
t.Fatal("expected \"\", got " + s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue1388(t *testing.T) {
|
||||
i := interp.New(interp.Options{Env: []string{"foo=bar"}})
|
||||
err := i.Use(stdlib.Symbols)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = i.Eval(`x := errors.New("")`)
|
||||
if err == nil {
|
||||
t.Fatal("Expected an error")
|
||||
}
|
||||
|
||||
_, err = i.Eval(`import "errors"`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = i.Eval(`x := errors.New("")`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue1383(t *testing.T) {
|
||||
const src = `
|
||||
package main
|
||||
|
||||
func main() {
|
||||
fmt.Println("Hello")
|
||||
}
|
||||
`
|
||||
|
||||
interp := interp.New(interp.Options{})
|
||||
err := interp.Use(stdlib.Symbols)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = interp.Eval(`import "fmt"`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ast, err := parser.ParseFile(interp.FileSet(), "_.go", src, parser.DeclarationErrors)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
prog, err := interp.CompileAST(ast)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = interp.Execute(prog)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"go/build"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -26,7 +25,7 @@ func TestFile(t *testing.T) {
|
||||
_ = os.Setenv("YAEGI_SPECIAL_STDIO", "1")
|
||||
|
||||
baseDir := filepath.Join("..", "_test")
|
||||
files, err := ioutil.ReadDir(baseDir)
|
||||
files, err := os.ReadDir(baseDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"go/constant"
|
||||
"log"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/traefik/yaegi/stdlib"
|
||||
)
|
||||
|
||||
func init() { log.SetFlags(log.Lshortfile) }
|
||||
@@ -19,13 +22,13 @@ func TestIsNatural(t *testing.T) {
|
||||
n: &node{
|
||||
typ: &itype{
|
||||
rtype: func() reflect.Type {
|
||||
var x uint = 3
|
||||
return reflect.TypeOf(x)
|
||||
var a uint = 3
|
||||
return reflect.TypeOf(a)
|
||||
}(),
|
||||
},
|
||||
rval: func() reflect.Value {
|
||||
var x uint = 3
|
||||
return reflect.ValueOf(x)
|
||||
var a uint = 3
|
||||
return reflect.ValueOf(a)
|
||||
}(),
|
||||
},
|
||||
expected: true,
|
||||
@@ -35,13 +38,13 @@ func TestIsNatural(t *testing.T) {
|
||||
n: &node{
|
||||
typ: &itype{
|
||||
rtype: func() reflect.Type {
|
||||
x := 3
|
||||
return reflect.TypeOf(x)
|
||||
a := 3
|
||||
return reflect.TypeOf(a)
|
||||
}(),
|
||||
},
|
||||
rval: func() reflect.Value {
|
||||
x := 3
|
||||
return reflect.ValueOf(x)
|
||||
a := 3
|
||||
return reflect.ValueOf(a)
|
||||
}(),
|
||||
},
|
||||
expected: true,
|
||||
@@ -51,13 +54,13 @@ func TestIsNatural(t *testing.T) {
|
||||
n: &node{
|
||||
typ: &itype{
|
||||
rtype: func() reflect.Type {
|
||||
var x int = 3
|
||||
return reflect.TypeOf(x)
|
||||
var a int = 3
|
||||
return reflect.TypeOf(a)
|
||||
}(),
|
||||
},
|
||||
rval: func() reflect.Value {
|
||||
var x int = 3
|
||||
return reflect.ValueOf(x)
|
||||
var a int = 3
|
||||
return reflect.ValueOf(a)
|
||||
}(),
|
||||
},
|
||||
expected: true,
|
||||
@@ -67,13 +70,13 @@ func TestIsNatural(t *testing.T) {
|
||||
n: &node{
|
||||
typ: &itype{
|
||||
rtype: func() reflect.Type {
|
||||
var x float64 = 3.0
|
||||
return reflect.TypeOf(x)
|
||||
var a float64 = 3.0
|
||||
return reflect.TypeOf(a)
|
||||
}(),
|
||||
},
|
||||
rval: func() reflect.Value {
|
||||
var x float64 = 3.0
|
||||
return reflect.ValueOf(x)
|
||||
var a float64 = 3.0
|
||||
return reflect.ValueOf(a)
|
||||
}(),
|
||||
},
|
||||
expected: true,
|
||||
@@ -83,13 +86,13 @@ func TestIsNatural(t *testing.T) {
|
||||
n: &node{
|
||||
typ: &itype{
|
||||
rtype: func() reflect.Type {
|
||||
var x float64 = 3.14
|
||||
return reflect.TypeOf(x)
|
||||
var a float64 = 3.14
|
||||
return reflect.TypeOf(a)
|
||||
}(),
|
||||
},
|
||||
rval: func() reflect.Value {
|
||||
var x float64 = 3.14
|
||||
return reflect.ValueOf(x)
|
||||
var a float64 = 3.14
|
||||
return reflect.ValueOf(a)
|
||||
}(),
|
||||
},
|
||||
expected: false,
|
||||
@@ -99,13 +102,13 @@ func TestIsNatural(t *testing.T) {
|
||||
n: &node{
|
||||
typ: &itype{
|
||||
rtype: func() reflect.Type {
|
||||
var x int = -3
|
||||
return reflect.TypeOf(x)
|
||||
var a int = -3
|
||||
return reflect.TypeOf(a)
|
||||
}(),
|
||||
},
|
||||
rval: func() reflect.Value {
|
||||
var x int = -3
|
||||
return reflect.ValueOf(x)
|
||||
var a int = -3
|
||||
return reflect.ValueOf(a)
|
||||
}(),
|
||||
},
|
||||
expected: false,
|
||||
@@ -188,3 +191,42 @@ func TestIsNatural(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGlobals(t *testing.T) {
|
||||
i := New(Options{})
|
||||
if err := i.Use(stdlib.Symbols); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := i.Eval("var a = 1"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := i.Eval("b := 2"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := i.Eval("const c = 3"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
g := i.Globals()
|
||||
a := g["a"]
|
||||
if !a.IsValid() {
|
||||
t.Fatal("a not found")
|
||||
}
|
||||
if a := a.Interface(); a != 1 {
|
||||
t.Fatalf("wrong a: want (%[1]T) %[1]v, have (%[2]T) %[2]v", 1, a)
|
||||
}
|
||||
b := g["b"]
|
||||
if !b.IsValid() {
|
||||
t.Fatal("b not found")
|
||||
}
|
||||
if b := b.Interface(); b != 2 {
|
||||
t.Fatalf("wrong b: want (%[1]T) %[1]v, have (%[2]T) %[2]v", 2, b)
|
||||
}
|
||||
c := g["c"]
|
||||
if !c.IsValid() {
|
||||
t.Fatal("c not found")
|
||||
}
|
||||
if cc, ok := c.Interface().(constant.Value); ok && constant.MakeInt64(3) != cc {
|
||||
t.Fatalf("wrong c: want (%[1]T) %[1]v, have (%[2]T) %[2]v", constant.MakeInt64(3), cc)
|
||||
}
|
||||
}
|
||||
|
||||
84
interp/op.go
84
interp/op.go
@@ -2624,6 +2624,7 @@ func equal(n *node) {
|
||||
typ := n.typ.concrete().TypeOf()
|
||||
isInterface := n.typ.TypeOf().Kind() == reflect.Interface
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
|
||||
|
||||
if c0.typ.cat == aliasT || c1.typ.cat == aliasT {
|
||||
switch {
|
||||
@@ -2644,7 +2645,7 @@ func equal(n *node) {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
if i0 == i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
@@ -2666,7 +2667,7 @@ func equal(n *node) {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
if i0 != i1 {
|
||||
if i0 == i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
@@ -2689,7 +2690,7 @@ func equal(n *node) {
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
if i0 == i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
@@ -2709,7 +2710,37 @@ func equal(n *node) {
|
||||
return
|
||||
}
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
// Do not attempt to optimize '==' or '!=' if an operand is an interface.
|
||||
// This will preserve proper dynamic type checking at runtime. For static types,
|
||||
// type checks are already performed, so bypass them if possible.
|
||||
if t0.Kind() == reflect.Interface || t1.Kind() == reflect.Interface {
|
||||
v0 := genValue(c0)
|
||||
v1 := genValue(c1)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
if i0 == i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 == i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch {
|
||||
case isString(t0) || isString(t1):
|
||||
switch {
|
||||
case isInterface:
|
||||
@@ -3193,8 +3224,9 @@ func greater(n *node) {
|
||||
typ := n.typ.concrete().TypeOf()
|
||||
isInterface := n.typ.TypeOf().Kind() == reflect.Interface
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
switch {
|
||||
case isString(t0) || isString(t1):
|
||||
switch {
|
||||
case isInterface:
|
||||
@@ -3520,8 +3552,9 @@ func greaterEqual(n *node) {
|
||||
typ := n.typ.concrete().TypeOf()
|
||||
isInterface := n.typ.TypeOf().Kind() == reflect.Interface
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
switch {
|
||||
case isString(t0) || isString(t1):
|
||||
switch {
|
||||
case isInterface:
|
||||
@@ -3847,8 +3880,9 @@ func lower(n *node) {
|
||||
typ := n.typ.concrete().TypeOf()
|
||||
isInterface := n.typ.TypeOf().Kind() == reflect.Interface
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
switch {
|
||||
case isString(t0) || isString(t1):
|
||||
switch {
|
||||
case isInterface:
|
||||
@@ -4174,8 +4208,9 @@ func lowerEqual(n *node) {
|
||||
typ := n.typ.concrete().TypeOf()
|
||||
isInterface := n.typ.TypeOf().Kind() == reflect.Interface
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
switch {
|
||||
case isString(t0) || isString(t1):
|
||||
switch {
|
||||
case isInterface:
|
||||
@@ -4501,6 +4536,7 @@ func notEqual(n *node) {
|
||||
typ := n.typ.concrete().TypeOf()
|
||||
isInterface := n.typ.TypeOf().Kind() == reflect.Interface
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
|
||||
|
||||
if c0.typ.cat == aliasT || c1.typ.cat == aliasT {
|
||||
switch {
|
||||
@@ -4586,7 +4622,37 @@ func notEqual(n *node) {
|
||||
return
|
||||
}
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
// Do not attempt to optimize '==' or '!=' if an operand is an interface.
|
||||
// This will preserve proper dynamic type checking at runtime. For static types,
|
||||
// type checks are already performed, so bypass them if possible.
|
||||
if t0.Kind() == reflect.Interface || t1.Kind() == reflect.Interface {
|
||||
v0 := genValue(c0)
|
||||
v1 := genValue(c1)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch {
|
||||
case isString(t0) || isString(t1):
|
||||
switch {
|
||||
case isInterface:
|
||||
|
||||
@@ -2,7 +2,9 @@ package interp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
@@ -15,9 +17,15 @@ type Program struct {
|
||||
init []*node
|
||||
}
|
||||
|
||||
// FileSet is the fileset that must be used for parsing Go that will be passed
|
||||
// to interp.CompileAST().
|
||||
func (interp *Interpreter) FileSet() *token.FileSet {
|
||||
return interp.fset
|
||||
}
|
||||
|
||||
// Compile parses and compiles a Go code represented as a string.
|
||||
func (interp *Interpreter) Compile(src string) (*Program, error) {
|
||||
return interp.compile(src, "", true)
|
||||
return interp.compileSrc(src, "", true)
|
||||
}
|
||||
|
||||
// CompilePath parses and compiles a Go code located at the given path.
|
||||
@@ -27,14 +35,14 @@ func (interp *Interpreter) CompilePath(path string) (*Program, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(path)
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return interp.compile(string(b), path, false)
|
||||
return interp.compileSrc(string(b), path, false)
|
||||
}
|
||||
|
||||
func (interp *Interpreter) compile(src, name string, inc bool) (*Program, error) {
|
||||
func (interp *Interpreter) compileSrc(src, name string, inc bool) (*Program, error) {
|
||||
if name != "" {
|
||||
interp.name = name
|
||||
}
|
||||
@@ -43,7 +51,23 @@ func (interp *Interpreter) compile(src, name string, inc bool) (*Program, error)
|
||||
}
|
||||
|
||||
// Parse source to AST.
|
||||
pkgName, root, err := interp.ast(src, interp.name, inc)
|
||||
n, err := interp.parse(src, interp.name, inc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return interp.CompileAST(n)
|
||||
}
|
||||
|
||||
// CompileAST builds a Program for the given Go code AST. Files and block
|
||||
// statements can be compiled, as can most expressions. Var declaration nodes
|
||||
// cannot be compiled.
|
||||
//
|
||||
// WARNING: The node must have been parsed using interp.FileSet(). Results are
|
||||
// unpredictable otherwise.
|
||||
func (interp *Interpreter) CompileAST(n ast.Node) (*Program, error) {
|
||||
// Convert AST.
|
||||
pkgName, root, err := interp.ast(n)
|
||||
if err != nil || root == nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -65,7 +89,7 @@ func (interp *Interpreter) compile(src, name string, inc bool) (*Program, error)
|
||||
}
|
||||
|
||||
// Annotate AST with CFG informations.
|
||||
initNodes, err := interp.cfg(root, pkgName, pkgName)
|
||||
initNodes, err := interp.cfg(root, nil, pkgName, pkgName)
|
||||
if err != nil {
|
||||
if interp.cfgDot {
|
||||
dotCmd := interp.dotCmd
|
||||
|
||||
420
interp/run.go
420
interp/run.go
@@ -9,7 +9,6 @@ import (
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// bltn type defines functions which run at CFG execution.
|
||||
@@ -616,6 +615,34 @@ func convert(n *node) {
|
||||
}
|
||||
}
|
||||
|
||||
// assignFromCall assigns values from a function call.
|
||||
func assignFromCall(n *node) {
|
||||
ncall := n.lastChild()
|
||||
l := len(n.child) - 1
|
||||
if n.anc.kind == varDecl && n.child[l-1].isType(n.scope) {
|
||||
// Ignore the type in the assignment if it is part of a variable declaration.
|
||||
l--
|
||||
}
|
||||
dvalue := make([]func(*frame) reflect.Value, l)
|
||||
for i := range dvalue {
|
||||
if n.child[i].ident == "_" {
|
||||
continue
|
||||
}
|
||||
dvalue[i] = genValue(n.child[i])
|
||||
}
|
||||
next := getExec(n.tnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
for i, v := range dvalue {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
s := f.data[ncall.findex+i]
|
||||
v(f).Set(s)
|
||||
}
|
||||
return next
|
||||
}
|
||||
}
|
||||
|
||||
func assign(n *node) {
|
||||
next := getExec(n.tnext)
|
||||
dvalue := make([]func(*frame) reflect.Value, n.nleft)
|
||||
@@ -952,24 +979,32 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
d[i] = reflect.New(t).Elem()
|
||||
}
|
||||
|
||||
// Copy method receiver as first argument, if defined.
|
||||
if rcvr != nil {
|
||||
if rcvr == nil {
|
||||
d = d[numRet:]
|
||||
} else {
|
||||
// Copy method receiver as first argument.
|
||||
src, dest := rcvr(f), d[numRet]
|
||||
if src.Type().Kind() != dest.Type().Kind() {
|
||||
sk, dk := src.Kind(), dest.Kind()
|
||||
switch {
|
||||
case sk == reflect.Ptr && dk != reflect.Ptr:
|
||||
dest.Set(src.Elem())
|
||||
case sk != reflect.Ptr && dk == reflect.Ptr:
|
||||
dest.Set(src.Addr())
|
||||
} else {
|
||||
default:
|
||||
if wrappedSrc, ok := src.Interface().(valueInterface); ok {
|
||||
src = wrappedSrc.value
|
||||
}
|
||||
dest.Set(src)
|
||||
}
|
||||
d = d[numRet+1:]
|
||||
} else {
|
||||
d = d[numRet:]
|
||||
}
|
||||
|
||||
// Copy function input arguments in local frame.
|
||||
for i, arg := range in {
|
||||
if i >= len(d) {
|
||||
// In case of unused arg, there may be not even a frame entry allocated, just skip.
|
||||
break
|
||||
}
|
||||
typ := def.typ.arg[i]
|
||||
switch {
|
||||
case isEmptyInterface(typ):
|
||||
@@ -1006,9 +1041,14 @@ func genInterfaceWrapper(n *node, typ reflect.Type) func(*frame) reflect.Value {
|
||||
if typ == nil || typ.Kind() != reflect.Interface || typ.NumMethod() == 0 || n.typ.cat == valueT {
|
||||
return value
|
||||
}
|
||||
nt := n.typ.frameType()
|
||||
if nt != nil && nt.Implements(typ) {
|
||||
return value
|
||||
tc := n.typ.cat
|
||||
if tc != structT {
|
||||
// Always force wrapper generation for struct types, as they may contain
|
||||
// embedded interface fields which require wrapping, even if reported as
|
||||
// implementing typ by reflect.
|
||||
if nt := n.typ.frameType(); nt != nil && nt.Implements(typ) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
mn := typ.NumMethod()
|
||||
names := make([]string, mn)
|
||||
@@ -1026,35 +1066,41 @@ func genInterfaceWrapper(n *node, typ reflect.Type) func(*frame) reflect.Value {
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
v := value(f)
|
||||
if v.Type().Implements(typ) {
|
||||
if tc != structT && v.Type().Implements(typ) {
|
||||
return v
|
||||
}
|
||||
vv := v
|
||||
switch v.Kind() {
|
||||
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
||||
if v.IsNil() {
|
||||
return reflect.New(typ).Elem()
|
||||
}
|
||||
if v.Kind() == reflect.Ptr {
|
||||
vv = v.Elem()
|
||||
}
|
||||
}
|
||||
var n2 *node
|
||||
if vi, ok := v.Interface().(valueInterface); ok {
|
||||
n2 = vi.node
|
||||
}
|
||||
v = getConcreteValue(v)
|
||||
w := reflect.New(wrap).Elem()
|
||||
w.Field(0).Set(v)
|
||||
for i, m := range methods {
|
||||
if m == nil {
|
||||
if r := v.MethodByName(names[i]); r.IsValid() {
|
||||
// First direct method lookup on field.
|
||||
if r := methodByName(v, names[i], indexes[i]); r.IsValid() {
|
||||
w.Field(i + 1).Set(r)
|
||||
continue
|
||||
}
|
||||
o := vv.FieldByIndex(indexes[i])
|
||||
if r := o.MethodByName(names[i]); r.IsValid() {
|
||||
w.Field(i + 1).Set(r)
|
||||
} else {
|
||||
if n2 == nil {
|
||||
panic(n.cfgErrorf("method not found: %s", names[i]))
|
||||
}
|
||||
continue
|
||||
// Method lookup in embedded valueInterface.
|
||||
m2, i2 := n2.typ.lookupMethod(names[i])
|
||||
if m2 != nil {
|
||||
nod := *m2
|
||||
nod.recv = &receiver{n, v, i2}
|
||||
w.Field(i + 1).Set(genFunctionWrapper(&nod)(f))
|
||||
continue
|
||||
}
|
||||
panic(n.cfgErrorf("method not found: %s", names[i]))
|
||||
}
|
||||
nod := *m
|
||||
nod.recv = &receiver{n, v, indexes[i]}
|
||||
@@ -1064,70 +1110,132 @@ func genInterfaceWrapper(n *node, typ reflect.Type) func(*frame) reflect.Value {
|
||||
}
|
||||
}
|
||||
|
||||
// methodByName returns the method corresponding to name on value, or nil if not found.
|
||||
// The search is extended on valueInterface wrapper if present.
|
||||
// If valid, the returned value is a method function with the receiver already set
|
||||
// (no need to pass it at call).
|
||||
func methodByName(value reflect.Value, name string, index []int) (v reflect.Value) {
|
||||
if vi, ok := value.Interface().(valueInterface); ok {
|
||||
if v = getConcreteValue(vi.value).MethodByName(name); v.IsValid() {
|
||||
return
|
||||
}
|
||||
}
|
||||
if v = value.MethodByName(name); v.IsValid() {
|
||||
return
|
||||
}
|
||||
for value.Kind() == reflect.Ptr {
|
||||
value = value.Elem()
|
||||
if checkFieldIndex(value.Type(), index) {
|
||||
value = value.FieldByIndex(index)
|
||||
}
|
||||
if v = value.MethodByName(name); v.IsValid() {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func checkFieldIndex(typ reflect.Type, index []int) bool {
|
||||
if len(index) == 0 {
|
||||
return false
|
||||
}
|
||||
t := typ
|
||||
for t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
if t.Kind() != reflect.Struct {
|
||||
return false
|
||||
}
|
||||
i := index[0]
|
||||
if i >= t.NumField() {
|
||||
return false
|
||||
}
|
||||
if len(index) > 1 {
|
||||
return checkFieldIndex(t.Field(i).Type, index[1:])
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func call(n *node) {
|
||||
goroutine := n.anc.kind == goStmt
|
||||
var method bool
|
||||
value := genValue(n.child[0])
|
||||
c0 := n.child[0]
|
||||
value := genValue(c0)
|
||||
var values []func(*frame) reflect.Value
|
||||
|
||||
recvIndexLater := false
|
||||
switch {
|
||||
case n.child[0].recv != nil:
|
||||
case c0.recv != nil:
|
||||
// Compute method receiver value.
|
||||
values = append(values, genValueRecv(n.child[0]))
|
||||
values = append(values, genValueRecv(c0))
|
||||
method = true
|
||||
case len(n.child[0].child) > 0 && n.child[0].child[0].typ != nil && isInterfaceSrc(n.child[0].child[0].typ):
|
||||
case len(c0.child) > 0 && c0.child[0].typ != nil && isInterfaceSrc(c0.child[0].typ):
|
||||
recvIndexLater = true
|
||||
values = append(values, genValueBinRecv(n.child[0], &receiver{node: n.child[0].child[0]}))
|
||||
values = append(values, genValueBinRecv(c0, &receiver{node: c0.child[0]}))
|
||||
value = genValueBinMethodOnInterface(n, value)
|
||||
method = true
|
||||
case n.child[0].action == aMethod:
|
||||
case c0.action == aMethod:
|
||||
// Add a place holder for interface method receiver.
|
||||
values = append(values, nil)
|
||||
method = true
|
||||
}
|
||||
|
||||
numRet := len(n.child[0].typ.ret)
|
||||
numRet := len(c0.typ.ret)
|
||||
variadic := variadicPos(n)
|
||||
child := n.child[1:]
|
||||
tnext := getExec(n.tnext)
|
||||
fnext := getExec(n.fnext)
|
||||
hasVariadicArgs := n.action == aCallSlice // callSlice implies variadic call with ellipsis.
|
||||
|
||||
// Compute input argument value functions.
|
||||
for i, c := range child {
|
||||
var arg *itype
|
||||
if variadic >= 0 && i >= variadic {
|
||||
arg = c0.typ.arg[variadic].val
|
||||
} else {
|
||||
arg = c0.typ.arg[i]
|
||||
}
|
||||
switch {
|
||||
case isBinCall(c):
|
||||
case isBinCall(c, c.scope):
|
||||
// Handle nested function calls: pass returned values as arguments.
|
||||
numOut := c.child[0].typ.rtype.NumOut()
|
||||
for j := 0; j < numOut; j++ {
|
||||
ind := c.findex + j
|
||||
values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
|
||||
if hasVariadicArgs || !isInterfaceSrc(arg) || isEmptyInterface(arg) {
|
||||
values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
|
||||
continue
|
||||
}
|
||||
values = append(values, func(f *frame) reflect.Value {
|
||||
return reflect.ValueOf(valueInterface{value: f.data[ind]})
|
||||
})
|
||||
}
|
||||
case isRegularCall(c):
|
||||
// Arguments are return values of a nested function call.
|
||||
for j := range c.child[0].typ.ret {
|
||||
cc0 := c.child[0]
|
||||
for j := range cc0.typ.ret {
|
||||
ind := c.findex + j
|
||||
values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
|
||||
if hasVariadicArgs || !isInterfaceSrc(arg) || isEmptyInterface(arg) {
|
||||
values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
|
||||
continue
|
||||
}
|
||||
values = append(values, func(f *frame) reflect.Value {
|
||||
return reflect.ValueOf(valueInterface{node: cc0.typ.ret[j].node, value: f.data[ind]})
|
||||
})
|
||||
}
|
||||
default:
|
||||
var arg *itype
|
||||
if variadic >= 0 && i >= variadic {
|
||||
arg = n.child[0].typ.arg[variadic].val
|
||||
} else {
|
||||
arg = n.child[0].typ.arg[i]
|
||||
}
|
||||
if c.kind == basicLit || c.rval.IsValid() {
|
||||
argType := arg.TypeOf()
|
||||
convertLiteralValue(c, argType)
|
||||
}
|
||||
switch {
|
||||
case isEmptyInterface(arg):
|
||||
case hasVariadicArgs:
|
||||
values = append(values, genValue(c))
|
||||
case isInterfaceSrc(arg) && n.action != aCallSlice:
|
||||
// callSlice implies variadic call with ellipsis, do not wrap in valueInterface.
|
||||
case isInterfaceSrc(arg) && (!isEmptyInterface(arg) || len(c.typ.method) > 0):
|
||||
values = append(values, genValueInterface(c))
|
||||
case isInterfaceBin(arg):
|
||||
values = append(values, genInterfaceWrapper(c, arg.rtype))
|
||||
case isFuncSrc(arg):
|
||||
values = append(values, genValueNode(c))
|
||||
default:
|
||||
values = append(values, genValue(c))
|
||||
}
|
||||
@@ -1135,10 +1243,11 @@ func call(n *node) {
|
||||
}
|
||||
|
||||
// Compute output argument value functions.
|
||||
rtypes := n.child[0].typ.ret
|
||||
rtypes := c0.typ.ret
|
||||
rvalues := make([]func(*frame) reflect.Value, len(rtypes))
|
||||
switch n.anc.kind {
|
||||
case defineXStmt, assignXStmt:
|
||||
l := n.level
|
||||
for i := range rvalues {
|
||||
c := n.anc.child[i]
|
||||
switch {
|
||||
@@ -1147,7 +1256,8 @@ func call(n *node) {
|
||||
case isInterfaceSrc(c.typ) && !isEmptyInterface(c.typ) && !isInterfaceSrc(rtypes[i]):
|
||||
rvalues[i] = genValueInterfaceValue(c)
|
||||
default:
|
||||
rvalues[i] = genValue(c)
|
||||
j := n.findex + i
|
||||
rvalues[i] = func(f *frame) reflect.Value { return getFrame(f, l).data[j] }
|
||||
}
|
||||
}
|
||||
case returnStmt:
|
||||
@@ -1168,7 +1278,7 @@ func call(n *node) {
|
||||
|
||||
if n.anc.kind == deferStmt {
|
||||
// Store function call in frame for deferred execution.
|
||||
value = genFunctionWrapper(n.child[0])
|
||||
value = genFunctionWrapper(c0)
|
||||
if method {
|
||||
// The receiver is already passed in the function wrapper, skip it.
|
||||
values = values[1:]
|
||||
@@ -1295,12 +1405,22 @@ func call(n *node) {
|
||||
}
|
||||
default:
|
||||
val := v(f)
|
||||
// The !val.IsZero is to work around a recursive struct zero interface
|
||||
// issue. Once there is a better way to handle this case, the dest
|
||||
// can just be set.
|
||||
if !val.IsZero() || dest[i].Type().Kind() == reflect.Interface {
|
||||
dest[i].Set(val)
|
||||
if val.IsZero() && dest[i].Kind() != reflect.Interface {
|
||||
// Work around a recursive struct zero interface issue.
|
||||
// Once there is a better way to handle this case, the dest can just be set.
|
||||
continue
|
||||
}
|
||||
if nod, ok := val.Interface().(*node); ok && nod.recv != nil {
|
||||
// An interpreted method is passed as value in a function call.
|
||||
// It must be wrapped now, otherwise the receiver will be missing
|
||||
// at the method call (#1332).
|
||||
// TODO (marc): wrapping interpreted functions should be always done
|
||||
// everywhere at runtime to simplify the whole code,
|
||||
// but it requires deeper refactoring.
|
||||
dest[i] = genFunctionWrapper(nod)(f)
|
||||
continue
|
||||
}
|
||||
dest[i].Set(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1374,7 +1494,7 @@ func callBin(n *node) {
|
||||
}
|
||||
|
||||
switch {
|
||||
case isBinCall(c):
|
||||
case isBinCall(c, c.scope):
|
||||
// Handle nested function calls: pass returned values as arguments
|
||||
numOut := c.child[0].typ.rtype.NumOut()
|
||||
for j := 0; j < numOut; j++ {
|
||||
@@ -1487,12 +1607,13 @@ func callBin(n *node) {
|
||||
rvalues := make([]func(*frame) reflect.Value, funcType.NumOut())
|
||||
for i := range rvalues {
|
||||
c := n.anc.child[i]
|
||||
if c.ident != "_" {
|
||||
if isInterfaceSrc(c.typ) {
|
||||
rvalues[i] = genValueInterfaceValue(c)
|
||||
} else {
|
||||
rvalues[i] = genValue(c)
|
||||
}
|
||||
if c.ident == "_" {
|
||||
continue
|
||||
}
|
||||
if isInterfaceSrc(c.typ) {
|
||||
rvalues[i] = genValueInterfaceValue(c)
|
||||
} else {
|
||||
rvalues[i] = genValue(c)
|
||||
}
|
||||
}
|
||||
n.exec = func(f *frame) bltn {
|
||||
@@ -1519,7 +1640,11 @@ func callBin(n *node) {
|
||||
}
|
||||
out := callFn(value(f), in)
|
||||
for i, v := range out {
|
||||
f.data[b+i].Set(v)
|
||||
dest := f.data[b+i]
|
||||
if _, ok := dest.Interface().(valueInterface); ok {
|
||||
v = reflect.ValueOf(valueInterface{value: v})
|
||||
}
|
||||
dest.Set(v)
|
||||
}
|
||||
return tnext
|
||||
}
|
||||
@@ -1531,11 +1656,16 @@ func callBin(n *node) {
|
||||
}
|
||||
out := callFn(value(f), in)
|
||||
for i := 0; i < len(out); i++ {
|
||||
if out[i].Type().Kind() == reflect.Func {
|
||||
getFrame(f, n.level).data[n.findex+i] = out[i]
|
||||
} else {
|
||||
getFrame(f, n.level).data[n.findex+i].Set(out[i])
|
||||
r := out[i]
|
||||
if r.Kind() == reflect.Func {
|
||||
getFrame(f, n.level).data[n.findex+i] = r
|
||||
continue
|
||||
}
|
||||
dest := getFrame(f, n.level).data[n.findex+i]
|
||||
if _, ok := dest.Interface().(valueInterface); ok {
|
||||
r = reflect.ValueOf(valueInterface{value: r})
|
||||
}
|
||||
dest.Set(r)
|
||||
}
|
||||
return tnext
|
||||
}
|
||||
@@ -1636,9 +1766,6 @@ func getIndexArray(n *node) {
|
||||
}
|
||||
}
|
||||
|
||||
// valueInterfaceType is the reflection type of valueInterface.
|
||||
var valueInterfaceType = reflect.TypeOf((*valueInterface)(nil)).Elem()
|
||||
|
||||
// getIndexMap retrieves map value from index.
|
||||
func getIndexMap(n *node) {
|
||||
dest := genValue(n)
|
||||
@@ -1703,7 +1830,6 @@ func getIndexMap2(n *node) {
|
||||
value0 := genValue(n.child[0]) // map
|
||||
value2 := genValue(n.anc.child[1]) // status
|
||||
next := getExec(n.tnext)
|
||||
typ := n.anc.child[0].typ
|
||||
doValue := n.anc.child[0].ident != "_"
|
||||
doStatus := n.anc.child[1].ident != "_"
|
||||
|
||||
@@ -1720,21 +1846,6 @@ func getIndexMap2(n *node) {
|
||||
value2(f).SetBool(v.IsValid())
|
||||
return next
|
||||
}
|
||||
case isInterfaceSrc(typ):
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value0(f).MapIndex(mi)
|
||||
if v.IsValid() {
|
||||
if e := v.Elem(); e.Type().AssignableTo(valueInterfaceType) {
|
||||
dest(f).Set(e)
|
||||
} else {
|
||||
dest(f).Set(reflect.ValueOf(valueInterface{n, e}))
|
||||
}
|
||||
}
|
||||
if doStatus {
|
||||
value2(f).SetBool(v.IsValid())
|
||||
}
|
||||
return next
|
||||
}
|
||||
default:
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value0(f).MapIndex(mi)
|
||||
@@ -1756,21 +1867,6 @@ func getIndexMap2(n *node) {
|
||||
value2(f).SetBool(v.IsValid())
|
||||
return next
|
||||
}
|
||||
case isInterfaceSrc(typ):
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value0(f).MapIndex(value1(f))
|
||||
if v.IsValid() {
|
||||
if e := v.Elem(); e.Type().AssignableTo(valueInterfaceType) {
|
||||
dest(f).Set(e)
|
||||
} else {
|
||||
dest(f).Set(reflect.ValueOf(valueInterface{n, e}))
|
||||
}
|
||||
}
|
||||
if doStatus {
|
||||
value2(f).SetBool(v.IsValid())
|
||||
}
|
||||
return next
|
||||
}
|
||||
default:
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value0(f).MapIndex(value1(f))
|
||||
@@ -1847,7 +1943,7 @@ func getMethodByName(n *node) {
|
||||
}
|
||||
return next
|
||||
}
|
||||
m, li := val.node.typ.lookupMethod(name)
|
||||
m, li := typ.lookupMethod(name)
|
||||
if m == nil {
|
||||
panic(n.cfgErrorf("method not found: %s", name))
|
||||
}
|
||||
@@ -2256,9 +2352,13 @@ func _return(n *node) {
|
||||
}
|
||||
values[i] = genValueInterface(c)
|
||||
case valueT:
|
||||
if t.rtype.Kind() == reflect.Interface {
|
||||
switch t.rtype.Kind() {
|
||||
case reflect.Interface:
|
||||
values[i] = genInterfaceWrapper(c, t.rtype)
|
||||
break
|
||||
continue
|
||||
case reflect.Func:
|
||||
values[i] = genFunctionWrapper(c)
|
||||
continue
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
@@ -2491,6 +2591,9 @@ func doCompositeBinStruct(n *node, hasType bool) {
|
||||
}
|
||||
}
|
||||
|
||||
frameIndex := n.findex
|
||||
l := n.level
|
||||
|
||||
n.exec = func(f *frame) bltn {
|
||||
s := reflect.New(typ).Elem()
|
||||
for i, v := range values {
|
||||
@@ -2498,10 +2601,10 @@ func doCompositeBinStruct(n *node, hasType bool) {
|
||||
}
|
||||
d := value(f)
|
||||
switch {
|
||||
case d.Type().Kind() == reflect.Ptr:
|
||||
case d.Kind() == reflect.Ptr:
|
||||
d.Set(s.Addr())
|
||||
default:
|
||||
d.Set(s)
|
||||
getFrame(f, l).data[frameIndex] = s
|
||||
}
|
||||
return next
|
||||
}
|
||||
@@ -2526,8 +2629,6 @@ func doComposite(n *node, hasType bool, keyed bool) {
|
||||
if typ.cat == ptrT || typ.cat == aliasT {
|
||||
typ = typ.val
|
||||
}
|
||||
var mu sync.Mutex
|
||||
typ.mu = &mu
|
||||
child := n.child
|
||||
if hasType {
|
||||
child = n.child[1:]
|
||||
@@ -2553,9 +2654,9 @@ func doComposite(n *node, hasType bool, keyed bool) {
|
||||
values[fieldIndex] = func(*frame) reflect.Value { return reflect.New(rft).Elem() }
|
||||
case isFuncSrc(val.typ):
|
||||
values[fieldIndex] = genValueAsFunctionWrapper(val)
|
||||
case isArray(val.typ) && val.typ.val != nil && isInterfaceSrc(val.typ.val):
|
||||
case isArray(val.typ) && val.typ.val != nil && isInterfaceSrc(val.typ.val) && !isEmptyInterface(val.typ.val):
|
||||
values[fieldIndex] = genValueInterfaceArray(val)
|
||||
case isInterfaceSrc(ft) && !isEmptyInterface(ft):
|
||||
case isInterfaceSrc(ft) && (!isEmptyInterface(ft) || len(val.typ.method) > 0):
|
||||
values[fieldIndex] = genValueInterface(val)
|
||||
case isInterface(ft):
|
||||
values[fieldIndex] = genInterfaceWrapper(val, rft)
|
||||
@@ -2566,17 +2667,16 @@ func doComposite(n *node, hasType bool, keyed bool) {
|
||||
|
||||
frameIndex := n.findex
|
||||
l := n.level
|
||||
rt := typ.TypeOf()
|
||||
|
||||
n.exec = func(f *frame) bltn {
|
||||
typ.mu.Lock()
|
||||
// No need to call zero() as doComposite is only called for a structT.
|
||||
a := reflect.New(typ.TypeOf()).Elem()
|
||||
typ.mu.Unlock()
|
||||
a := reflect.New(rt).Elem()
|
||||
for i, v := range values {
|
||||
a.Field(i).Set(v(f))
|
||||
}
|
||||
d := value(f)
|
||||
switch {
|
||||
case d.Type().Kind() == reflect.Ptr:
|
||||
case d.Kind() == reflect.Ptr:
|
||||
d.Set(a.Addr())
|
||||
case destInterface:
|
||||
if len(destType(n).field) > 0 {
|
||||
@@ -2832,13 +2932,22 @@ func _case(n *node) {
|
||||
if typ.cat == nilT && v.IsNil() {
|
||||
return tnext
|
||||
}
|
||||
if typ.TypeOf().String() == t.String() {
|
||||
destValue(f).Set(v.Elem())
|
||||
rtyp := typ.TypeOf()
|
||||
if rtyp == nil {
|
||||
return fnext
|
||||
}
|
||||
elem := v.Elem()
|
||||
if rtyp.String() == t.String() && implementsInterface(v, typ) {
|
||||
destValue(f).Set(elem)
|
||||
return tnext
|
||||
}
|
||||
ival := v.Interface()
|
||||
if ival != nil && typ.TypeOf().String() == reflect.TypeOf(ival).String() {
|
||||
destValue(f).Set(v.Elem())
|
||||
if ival != nil && rtyp.String() == reflect.TypeOf(ival).String() {
|
||||
destValue(f).Set(elem)
|
||||
return tnext
|
||||
}
|
||||
if typ.cat == valueT && rtyp.Kind() == reflect.Interface && elem.IsValid() && elem.Type().Implements(rtyp) {
|
||||
destValue(f).Set(elem)
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
@@ -2856,12 +2965,37 @@ func _case(n *node) {
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
|
||||
default:
|
||||
// TODO(mpl): probably needs to be fixed for empty interfaces, like above.
|
||||
// match against multiple types: assign var to interface value
|
||||
n.exec = func(f *frame) bltn {
|
||||
val := srcValue(f)
|
||||
if v := srcValue(f).Interface().(valueInterface).node; v != nil {
|
||||
if t := val.Type(); t.Kind() == reflect.Interface {
|
||||
for _, typ := range types {
|
||||
if typ.cat == nilT && val.IsNil() {
|
||||
return tnext
|
||||
}
|
||||
rtyp := typ.TypeOf()
|
||||
if rtyp == nil {
|
||||
continue
|
||||
}
|
||||
elem := val.Elem()
|
||||
if rtyp.String() == t.String() && implementsInterface(val, typ) {
|
||||
destValue(f).Set(elem)
|
||||
return tnext
|
||||
}
|
||||
ival := val.Interface()
|
||||
if ival != nil && rtyp.String() == reflect.TypeOf(ival).String() {
|
||||
destValue(f).Set(elem)
|
||||
return tnext
|
||||
}
|
||||
if typ.cat == valueT && rtyp.Kind() == reflect.Interface && elem.IsValid() && elem.Type().Implements(rtyp) {
|
||||
destValue(f).Set(elem)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
if v := val.Interface().(valueInterface).node; v != nil {
|
||||
for _, typ := range types {
|
||||
if v.typ.id() == typ.id() {
|
||||
destValue(f).Set(val)
|
||||
@@ -2900,6 +3034,22 @@ func _case(n *node) {
|
||||
}
|
||||
}
|
||||
|
||||
func implementsInterface(v reflect.Value, t *itype) bool {
|
||||
rt := v.Type()
|
||||
if t.cat == valueT {
|
||||
return rt.Implements(t.rtype)
|
||||
}
|
||||
vt := &itype{cat: valueT, rtype: rt}
|
||||
if vt.methods().contains(t.methods()) {
|
||||
return true
|
||||
}
|
||||
vi, ok := v.Interface().(valueInterface)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return vi.node != nil && vi.node.typ.methods().contains(t.methods())
|
||||
}
|
||||
|
||||
func appendSlice(n *node) {
|
||||
dest := genValueOutput(n, n.typ.rtype)
|
||||
next := getExec(n.tnext)
|
||||
@@ -2946,13 +3096,11 @@ func _append(n *node) {
|
||||
l := len(args)
|
||||
values := make([]func(*frame) reflect.Value, l)
|
||||
for i, arg := range args {
|
||||
switch {
|
||||
case isEmptyInterface(n.typ.val):
|
||||
values[i] = genValue(arg)
|
||||
case isInterfaceSrc(n.typ.val):
|
||||
switch elem := n.typ.elem(); {
|
||||
case isInterfaceSrc(elem) && (!isEmptyInterface(elem) || len(arg.typ.method) > 0):
|
||||
values[i] = genValueInterface(arg)
|
||||
case isInterfaceBin(n.typ.val):
|
||||
values[i] = genInterfaceWrapper(arg, n.typ.val.rtype)
|
||||
case isInterfaceBin(elem):
|
||||
values[i] = genInterfaceWrapper(arg, elem.rtype)
|
||||
case arg.typ.untyped:
|
||||
values[i] = genValueAs(arg, n.child[1].typ.TypeOf().Elem())
|
||||
default:
|
||||
@@ -2971,9 +3119,7 @@ func _append(n *node) {
|
||||
default:
|
||||
var value0 func(*frame) reflect.Value
|
||||
switch elem := n.typ.elem(); {
|
||||
case isEmptyInterface(elem):
|
||||
value0 = genValue(n.child[2])
|
||||
case isInterfaceSrc(elem):
|
||||
case isInterfaceSrc(elem) && (!isEmptyInterface(elem) || len(n.child[2].typ.method) > 0):
|
||||
value0 = genValueInterface(n.child[2])
|
||||
case isInterfaceBin(elem):
|
||||
value0 = genInterfaceWrapper(n.child[2], elem.rtype)
|
||||
@@ -3109,23 +3255,37 @@ func _delete(n *node) {
|
||||
}
|
||||
|
||||
func capConst(n *node) {
|
||||
n.rval = reflect.New(reflect.TypeOf(int(0))).Elem()
|
||||
// There is no Cap() method for reflect.Type, just return Len() instead.
|
||||
n.rval.SetInt(int64(n.child[1].typ.TypeOf().Len()))
|
||||
lenConst(n)
|
||||
}
|
||||
|
||||
func lenConst(n *node) {
|
||||
n.rval = reflect.New(reflect.TypeOf(int(0))).Elem()
|
||||
if c1 := n.child[1]; c1.rval.IsValid() {
|
||||
c1 := n.child[1]
|
||||
if c1.rval.IsValid() {
|
||||
n.rval.SetInt(int64(len(vString(c1.rval))))
|
||||
} else {
|
||||
n.rval.SetInt(int64(c1.typ.TypeOf().Len()))
|
||||
return
|
||||
}
|
||||
t := c1.typ.TypeOf()
|
||||
for t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
n.rval.SetInt(int64(t.Len()))
|
||||
}
|
||||
|
||||
func _len(n *node) {
|
||||
dest := genValueOutput(n, reflect.TypeOf(int(0)))
|
||||
value := genValue(n.child[1])
|
||||
if isPtr(n.child[1].typ) {
|
||||
val := value
|
||||
value = func(f *frame) reflect.Value {
|
||||
v := val(f).Elem()
|
||||
for v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
return v
|
||||
}
|
||||
}
|
||||
next := getExec(n.tnext)
|
||||
|
||||
if wantEmptyInterface(n) {
|
||||
|
||||
@@ -47,7 +47,7 @@ type symbol struct {
|
||||
kind sKind
|
||||
typ *itype // Type of value
|
||||
node *node // Node value if index is negative
|
||||
from []*node // list of nodes jumping to node if kind is label, or nil
|
||||
from []*node // list of goto nodes jumping to this label node, or nil
|
||||
recv *receiver // receiver node value, if sym refers to a method
|
||||
index int // index of value in frame or -1
|
||||
rval reflect.Value // default value (used for constants)
|
||||
@@ -144,20 +144,6 @@ func (s *scope) lookup(ident string) (*symbol, int, bool) {
|
||||
return nil, 0, false
|
||||
}
|
||||
|
||||
// lookdown searches for a symbol in the current scope and included ones, recursively.
|
||||
// It returns the first found symbol and true, or nil and false.
|
||||
func (s *scope) lookdown(ident string) (*symbol, bool) {
|
||||
if sym, ok := s.sym[ident]; ok {
|
||||
return sym, true
|
||||
}
|
||||
for _, c := range s.child {
|
||||
if sym, ok := c.lookdown(ident); ok {
|
||||
return sym, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (s *scope) rangeChanType(n *node) *itype {
|
||||
if sym, _, found := s.lookup(n.child[1].ident); found {
|
||||
if t := sym.typ; len(n.child) == 3 && t != nil && (t.cat == chanT || t.cat == chanRecvT) {
|
||||
@@ -241,3 +227,26 @@ func (interp *Interpreter) initScopePkg(pkgID, pkgName string) *scope {
|
||||
interp.mutex.Unlock()
|
||||
return sc
|
||||
}
|
||||
|
||||
// Globals returns a map of global variables and constants in the main package.
|
||||
func (interp *Interpreter) Globals() map[string]reflect.Value {
|
||||
syms := map[string]reflect.Value{}
|
||||
interp.mutex.RLock()
|
||||
defer interp.mutex.RUnlock()
|
||||
|
||||
v, ok := interp.srcPkg["main"]
|
||||
if !ok {
|
||||
return syms
|
||||
}
|
||||
|
||||
for n, s := range v {
|
||||
switch s.kind {
|
||||
case constSym:
|
||||
syms[n] = s.rval
|
||||
case varSym:
|
||||
syms[n] = interp.frame.data[s.index]
|
||||
}
|
||||
}
|
||||
|
||||
return syms
|
||||
}
|
||||
|
||||
@@ -73,8 +73,16 @@ func (interp *Interpreter) importSrc(rPath, importPath string, skipTest bool) (s
|
||||
return "", err
|
||||
}
|
||||
|
||||
n, err := interp.parse(string(buf), name, false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if n == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var pname string
|
||||
if pname, root, err = interp.ast(string(buf), name, false); err != nil {
|
||||
if pname, root, err = interp.ast(n); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if root == nil {
|
||||
@@ -114,7 +122,7 @@ func (interp *Interpreter) importSrc(rPath, importPath string, skipTest bool) (s
|
||||
// Generate control flow graphs.
|
||||
for _, root := range rootNodes {
|
||||
var nodes []*node
|
||||
if nodes, err = interp.cfg(root, importPath, pkgName); err != nil {
|
||||
if nodes, err = interp.cfg(root, nil, importPath, pkgName); err != nil {
|
||||
return "", err
|
||||
}
|
||||
initNodes = append(initNodes, nodes...)
|
||||
@@ -124,6 +132,10 @@ func (interp *Interpreter) importSrc(rPath, importPath string, skipTest bool) (s
|
||||
// the global symbols in the package scope.
|
||||
interp.mutex.Lock()
|
||||
gs := interp.scopes[importPath]
|
||||
if gs == nil {
|
||||
// A nil scope means that no even an empty package is created from source.
|
||||
return "", fmt.Errorf("no Go files in %s", dir)
|
||||
}
|
||||
interp.srcPkg[importPath] = gs.sym
|
||||
interp.pkgNames[importPath] = pkgName
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@@ -50,7 +49,7 @@ func Test_effectivePkg(t *testing.T) {
|
||||
|
||||
func Test_pkgDir(t *testing.T) {
|
||||
// create GOPATH
|
||||
goPath, err := ioutil.TempDir("", "pkdir")
|
||||
goPath, err := os.MkdirTemp("", "pkdir")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -60,7 +59,7 @@ func Test_pkgDir(t *testing.T) {
|
||||
|
||||
// Create project
|
||||
project := filepath.Join(goPath, "src", "guthib.com", "foo", "root")
|
||||
if err := os.MkdirAll(project, 0700); err != nil {
|
||||
if err := os.MkdirAll(project, 0o700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -81,7 +80,7 @@ func Test_pkgDir(t *testing.T) {
|
||||
path: "guthib.com/foo/bar",
|
||||
root: "",
|
||||
setup: func() error {
|
||||
return os.MkdirAll(filepath.Join(goPath, "src", "guthib.com", "foo", "bar"), 0700)
|
||||
return os.MkdirAll(filepath.Join(goPath, "src", "guthib.com", "foo", "bar"), 0o700)
|
||||
},
|
||||
expected: expected{
|
||||
dir: filepath.Join(goPath, "src", "guthib.com", "foo", "bar"),
|
||||
@@ -93,7 +92,7 @@ func Test_pkgDir(t *testing.T) {
|
||||
path: "guthib.com/foo/bar",
|
||||
root: filepath.Join("guthib.com", "foo", "root"),
|
||||
setup: func() error {
|
||||
return os.MkdirAll(filepath.Join(project, "vendor", "guthib.com", "foo", "bar"), 0700)
|
||||
return os.MkdirAll(filepath.Join(project, "vendor", "guthib.com", "foo", "bar"), 0o700)
|
||||
},
|
||||
expected: expected{
|
||||
dir: filepath.Join(goPath, "src", "guthib.com", "foo", "root", "vendor", "guthib.com", "foo", "bar"),
|
||||
@@ -105,7 +104,7 @@ func Test_pkgDir(t *testing.T) {
|
||||
path: "guthib.com/foo/bar",
|
||||
root: filepath.Join("guthib.com", "foo", "root"),
|
||||
setup: func() error {
|
||||
return os.MkdirAll(filepath.Join(goPath, "src", "guthib.com", "foo", "bar"), 0700)
|
||||
return os.MkdirAll(filepath.Join(goPath, "src", "guthib.com", "foo", "bar"), 0o700)
|
||||
},
|
||||
expected: expected{
|
||||
dir: filepath.Join(goPath, "src", "guthib.com", "foo", "bar"),
|
||||
@@ -117,10 +116,10 @@ func Test_pkgDir(t *testing.T) {
|
||||
path: "guthib.com/foo/bar",
|
||||
root: filepath.Join("guthib.com", "foo", "root", "vendor", "guthib.com", "foo", "bir"),
|
||||
setup: func() error {
|
||||
if err := os.MkdirAll(filepath.Join(project, "vendor", "guthib.com", "foo", "bar"), 0700); err != nil {
|
||||
if err := os.MkdirAll(filepath.Join(project, "vendor", "guthib.com", "foo", "bar"), 0o700); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.MkdirAll(filepath.Join(project, "vendor", "guthib.com", "foo", "bir"), 0700)
|
||||
return os.MkdirAll(filepath.Join(project, "vendor", "guthib.com", "foo", "bir"), 0o700)
|
||||
},
|
||||
expected: expected{
|
||||
dir: filepath.Join(goPath, "src", "guthib.com", "foo", "root", "vendor", "guthib.com", "foo", "bar"),
|
||||
@@ -132,10 +131,10 @@ func Test_pkgDir(t *testing.T) {
|
||||
path: "guthib.com/foo/bar",
|
||||
root: filepath.Join("guthib.com", "foo", "root", "vendor", "guthib.com", "foo", "bir"),
|
||||
setup: func() error {
|
||||
if err := os.MkdirAll(filepath.Join(goPath, "src", "guthib.com", "foo", "bar"), 0700); err != nil {
|
||||
if err := os.MkdirAll(filepath.Join(goPath, "src", "guthib.com", "foo", "bar"), 0o700); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.MkdirAll(filepath.Join(project, "vendor", "guthib.com", "foo", "bir"), 0700)
|
||||
return os.MkdirAll(filepath.Join(project, "vendor", "guthib.com", "foo", "bir"), 0o700)
|
||||
},
|
||||
expected: expected{
|
||||
dir: filepath.Join(goPath, "src", "guthib.com", "foo", "bar"),
|
||||
@@ -149,10 +148,10 @@ func Test_pkgDir(t *testing.T) {
|
||||
setup: func() error {
|
||||
if err := os.MkdirAll(
|
||||
filepath.Join(goPath, "src", "guthib.com", "foo", "root", "vendor", "guthib.com", "foo", "bir", "vendor", "guthib.com", "foo", "bur"),
|
||||
0700); err != nil {
|
||||
0o700); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.MkdirAll(filepath.Join(project, "vendor", "guthib.com", "foo", "bar"), 0700)
|
||||
return os.MkdirAll(filepath.Join(project, "vendor", "guthib.com", "foo", "bar"), 0o700)
|
||||
},
|
||||
expected: expected{
|
||||
dir: filepath.Join(project, "vendor", "guthib.com", "foo", "bar"),
|
||||
@@ -173,7 +172,7 @@ func Test_pkgDir(t *testing.T) {
|
||||
if err := os.RemoveAll(goPath); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.MkdirAll(goPath, 0700); err != nil {
|
||||
if err := os.MkdirAll(goPath, 0o700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
||||
189
interp/type.go
189
interp/type.go
@@ -6,7 +6,7 @@ import (
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"sync"
|
||||
"strings"
|
||||
|
||||
"github.com/traefik/yaegi/internal/unsafe2"
|
||||
)
|
||||
@@ -109,7 +109,6 @@ type structField struct {
|
||||
|
||||
// itype defines the internal representation of types in the interpreter.
|
||||
type itype struct {
|
||||
mu *sync.Mutex
|
||||
cat tcat // Type category
|
||||
field []structField // Array of struct fields if structT or interfaceT
|
||||
key *itype // Type of key element if MapT or nil
|
||||
@@ -239,6 +238,9 @@ func namedOf(val *itype, path, name string, opts ...itypeOption) *itype {
|
||||
if path != "" {
|
||||
str = path + "." + name
|
||||
}
|
||||
for val.cat == aliasT {
|
||||
val = val.val
|
||||
}
|
||||
t := &itype{cat: aliasT, val: val, path: path, name: name, str: str}
|
||||
for _, opt := range opts {
|
||||
opt(t)
|
||||
@@ -361,12 +363,25 @@ func structOf(t *itype, fields []structField, opts ...itypeOption) *itype {
|
||||
return t
|
||||
}
|
||||
|
||||
// nodeType returns a type definition for the corresponding AST subtree.
|
||||
func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
return nodeType2(interp, sc, n, map[*node]bool{})
|
||||
// seenNode determines if a node has been seen.
|
||||
//
|
||||
// seenNode treats the slice of nodes as the path traveled down a node
|
||||
// tree.
|
||||
func seenNode(ns []*node, n *node) bool {
|
||||
for _, nn := range ns {
|
||||
if nn == n {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func nodeType2(interp *Interpreter, sc *scope, n *node, seen map[*node]bool) (t *itype, err error) {
|
||||
// nodeType returns a type definition for the corresponding AST subtree.
|
||||
func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
return nodeType2(interp, sc, n, nil)
|
||||
}
|
||||
|
||||
func nodeType2(interp *Interpreter, sc *scope, n *node, seen []*node) (t *itype, err error) {
|
||||
if n.typ != nil && !n.typ.incomplete {
|
||||
return n.typ, nil
|
||||
}
|
||||
@@ -376,14 +391,15 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen map[*node]bool) (t
|
||||
if sym.typ.isComplete() {
|
||||
return sym.typ, nil
|
||||
}
|
||||
if seen[n] {
|
||||
// TODO (marc): find a better way to distinguish recursive vs incomplete types.
|
||||
if seenNode(seen, n) {
|
||||
// We have seen this node in our tree, so it must be recursive.
|
||||
sym.typ.incomplete = false
|
||||
return sym.typ, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
seen[n] = true
|
||||
seen = append(seen, n)
|
||||
defer func() { seen = seen[:len(seen)-1] }()
|
||||
|
||||
switch n.kind {
|
||||
case addressExpr, starExpr:
|
||||
@@ -421,7 +437,10 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen map[*node]bool) (t
|
||||
}
|
||||
case c0.kind == ellipsisExpr:
|
||||
// [...]T expression, get size from the length of composite array.
|
||||
length = arrayTypeLen(n.anc)
|
||||
length, err = arrayTypeLen(n.anc, sc)
|
||||
if err != nil {
|
||||
incomplete = true
|
||||
}
|
||||
case c0.kind == identExpr:
|
||||
sym, _, ok := sc.lookup(c0.ident)
|
||||
if !ok {
|
||||
@@ -439,7 +458,7 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen map[*node]bool) (t
|
||||
length = int(vInt(sym.rval))
|
||||
default:
|
||||
// Size is defined by a numeric constant expression.
|
||||
if _, err = interp.cfg(c0, sc.pkgID, sc.pkgName); err != nil {
|
||||
if _, err = interp.cfg(c0, sc, sc.pkgID, sc.pkgName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v, ok := c0.rval.Interface().(constant.Value)
|
||||
@@ -833,6 +852,14 @@ func nodeType2(interp *Interpreter, sc *scope, n *node, seen map[*node]bool) (t
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if t.cat == valueT {
|
||||
switch t.rtype.Kind() {
|
||||
case reflect.Array, reflect.Ptr:
|
||||
t = valueTOf(reflect.SliceOf(t.rtype.Elem()), withScope(sc))
|
||||
}
|
||||
break
|
||||
}
|
||||
if t.cat == ptrT {
|
||||
t = t.val
|
||||
}
|
||||
@@ -1054,7 +1081,7 @@ func (t *itype) in(i int) *itype {
|
||||
return t.arg[i]
|
||||
case valueT:
|
||||
if t.rtype.Kind() == reflect.Func {
|
||||
if t.recv != nil {
|
||||
if t.recv != nil && !isInterface(t.recv) {
|
||||
i++
|
||||
}
|
||||
if t.rtype.IsVariadic() && i == t.rtype.NumIn()-1 {
|
||||
@@ -1508,22 +1535,33 @@ func (t *itype) getMethod(name string) *node {
|
||||
// LookupMethod returns a pointer to method definition associated to type t
|
||||
// and the list of indices to access the right struct field, in case of an embedded method.
|
||||
func (t *itype) lookupMethod(name string) (*node, []int) {
|
||||
return t.lookupMethod2(name, nil)
|
||||
}
|
||||
|
||||
func (t *itype) lookupMethod2(name string, seen map[*itype]bool) (*node, []int) {
|
||||
if seen == nil {
|
||||
seen = map[*itype]bool{}
|
||||
}
|
||||
if seen[t] {
|
||||
return nil, nil
|
||||
}
|
||||
seen[t] = true
|
||||
if t.cat == ptrT {
|
||||
return t.val.lookupMethod(name)
|
||||
return t.val.lookupMethod2(name, seen)
|
||||
}
|
||||
var index []int
|
||||
m := t.getMethod(name)
|
||||
if m == nil {
|
||||
for i, f := range t.field {
|
||||
if f.embed {
|
||||
if n, index2 := f.typ.lookupMethod(name); n != nil {
|
||||
if n, index2 := f.typ.lookupMethod2(name, seen); n != nil {
|
||||
index = append([]int{i}, index2...)
|
||||
return n, index
|
||||
}
|
||||
}
|
||||
}
|
||||
if t.cat == aliasT || isInterfaceSrc(t) && t.val != nil {
|
||||
return t.val.lookupMethod(name)
|
||||
return t.val.lookupMethod2(name, seen)
|
||||
}
|
||||
}
|
||||
return m, index
|
||||
@@ -1542,12 +1580,23 @@ func (t *itype) methodDepth(name string) int {
|
||||
|
||||
// LookupBinMethod returns a method and a path to access a field in a struct object (the receiver).
|
||||
func (t *itype) lookupBinMethod(name string) (m reflect.Method, index []int, isPtr, ok bool) {
|
||||
return t.lookupBinMethod2(name, nil)
|
||||
}
|
||||
|
||||
func (t *itype) lookupBinMethod2(name string, seen map[*itype]bool) (m reflect.Method, index []int, isPtr, ok bool) {
|
||||
if seen == nil {
|
||||
seen = map[*itype]bool{}
|
||||
}
|
||||
if seen[t] {
|
||||
return
|
||||
}
|
||||
seen[t] = true
|
||||
if t.cat == ptrT {
|
||||
return t.val.lookupBinMethod(name)
|
||||
return t.val.lookupBinMethod2(name, seen)
|
||||
}
|
||||
for i, f := range t.field {
|
||||
if f.embed {
|
||||
if m2, index2, isPtr2, ok2 := f.typ.lookupBinMethod(name); ok2 {
|
||||
if m2, index2, isPtr2, ok2 := f.typ.lookupBinMethod2(name, seen); ok2 {
|
||||
index = append([]int{i}, index2...)
|
||||
return m2, index, isPtr2, ok2
|
||||
}
|
||||
@@ -1610,8 +1659,19 @@ type fieldRebuild struct {
|
||||
}
|
||||
|
||||
type refTypeContext struct {
|
||||
defined map[string]*itype
|
||||
refs map[string][]fieldRebuild
|
||||
defined map[string]*itype
|
||||
|
||||
// refs keeps track of all the places (in the same type recursion) where the
|
||||
// type name (as key) is used as a field of another (or possibly the same) struct
|
||||
// type. Each of these fields will then live as an unsafe2.dummy type until the
|
||||
// whole recursion is fully resolved, and the type is fixed.
|
||||
refs map[string][]fieldRebuild
|
||||
|
||||
// When we detect for the first time that we are in a recursive type (thanks to
|
||||
// defined), we keep track of the first occurrence of the type where the recursion
|
||||
// started, so we can restart the last step that fixes all the types from the same
|
||||
// "top-level" point.
|
||||
rect *itype
|
||||
rebuilding bool
|
||||
}
|
||||
|
||||
@@ -1620,12 +1680,57 @@ func (c *refTypeContext) Clone() *refTypeContext {
|
||||
return &refTypeContext{defined: c.defined, refs: c.refs, rebuilding: c.rebuilding}
|
||||
}
|
||||
|
||||
func (c *refTypeContext) isComplete() bool {
|
||||
for _, t := range c.defined {
|
||||
if t.rtype == nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *itype) fixDummy(typ reflect.Type) reflect.Type {
|
||||
if typ == unsafe2.DummyType {
|
||||
return t.rtype
|
||||
}
|
||||
switch typ.Kind() {
|
||||
case reflect.Array:
|
||||
return reflect.ArrayOf(typ.Len(), t.fixDummy(typ.Elem()))
|
||||
case reflect.Chan:
|
||||
return reflect.ChanOf(typ.ChanDir(), t.fixDummy(typ.Elem()))
|
||||
case reflect.Func:
|
||||
in := make([]reflect.Type, typ.NumIn())
|
||||
for i := range in {
|
||||
in[i] = t.fixDummy(typ.In(i))
|
||||
}
|
||||
out := make([]reflect.Type, typ.NumOut())
|
||||
for i := range out {
|
||||
out[i] = t.fixDummy(typ.Out(i))
|
||||
}
|
||||
return reflect.FuncOf(in, out, typ.IsVariadic())
|
||||
case reflect.Map:
|
||||
return reflect.MapOf(t.fixDummy(typ.Key()), t.fixDummy(typ.Elem()))
|
||||
case reflect.Ptr:
|
||||
return reflect.PtrTo(t.fixDummy(typ.Elem()))
|
||||
case reflect.Slice:
|
||||
return reflect.SliceOf(t.fixDummy(typ.Elem()))
|
||||
case reflect.Struct:
|
||||
fields := make([]reflect.StructField, typ.NumField())
|
||||
for i := range fields {
|
||||
fields[i] = typ.Field(i)
|
||||
fields[i].Type = t.fixDummy(fields[i].Type)
|
||||
}
|
||||
return reflect.StructOf(fields)
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
// RefType returns a reflect.Type representation from an interpreter type.
|
||||
// In simple cases, reflect types are directly mapped from the interpreter
|
||||
// counterpart.
|
||||
// For recursive named struct or interfaces, as reflect does not permit to
|
||||
// create a recursive named struct, a nil type is set temporarily for each recursive
|
||||
// field. When done, the nil type fields are updated with the original reflect type
|
||||
// create a recursive named struct, a dummy type is set temporarily for each recursive
|
||||
// field. When done, the dummy type fields are updated with the original reflect type
|
||||
// pointer using unsafe. We thus obtain a usable recursive type definition, except
|
||||
// for string representation, as created reflect types are still unnamed.
|
||||
func (t *itype) refType(ctx *refTypeContext) reflect.Type {
|
||||
@@ -1647,14 +1752,23 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
|
||||
return t.rtype
|
||||
}
|
||||
if dt := ctx.defined[name]; dt != nil {
|
||||
// We get here when we are a struct field, and our type name has already been
|
||||
// seen at least once in one of our englobing structs. i.e. there's at least one
|
||||
// level of type recursion.
|
||||
if dt.rtype != nil {
|
||||
t.rtype = dt.rtype
|
||||
return dt.rtype
|
||||
}
|
||||
|
||||
// To indicate that a rebuild is needed on the nearest struct
|
||||
// field, create an entry with a nil type.
|
||||
// The recursion has not been fully resolved yet.
|
||||
// To indicate that a rebuild is needed on the englobing struct,
|
||||
// return a dummy field type and create an entry with an empty fieldRebuild.
|
||||
flds := ctx.refs[name]
|
||||
ctx.rect = dt
|
||||
|
||||
// We know we are used as a field by someone, but we don't know by who
|
||||
// at this point in the code, so we just mark it as an empty fieldRebuild for now.
|
||||
// We'll complete the fieldRebuild in the caller.
|
||||
ctx.refs[name] = append(flds, fieldRebuild{})
|
||||
return unsafe2.DummyType
|
||||
}
|
||||
@@ -1697,9 +1811,8 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
|
||||
}
|
||||
var fields []reflect.StructField
|
||||
for i, f := range t.field {
|
||||
fctx := ctx.Clone()
|
||||
field := reflect.StructField{
|
||||
Name: exportName(f.name), Type: f.typ.refType(fctx),
|
||||
Name: exportName(f.name), Type: f.typ.refType(ctx),
|
||||
Tag: reflect.StructTag(f.tag), Anonymous: f.embed,
|
||||
}
|
||||
fields = append(fields, field)
|
||||
@@ -1712,13 +1825,35 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
|
||||
}
|
||||
}
|
||||
}
|
||||
fieldFix := []int{} // Slice of field indices to fix for recursivity.
|
||||
t.rtype = reflect.StructOf(fields)
|
||||
if ctx.isComplete() {
|
||||
for _, s := range ctx.defined {
|
||||
for i := 0; i < s.rtype.NumField(); i++ {
|
||||
f := s.rtype.Field(i)
|
||||
if strings.HasSuffix(f.Type.String(), "unsafe2.dummy") {
|
||||
unsafe2.SetFieldType(s.rtype, i, ctx.rect.fixDummy(s.rtype.Field(i).Type))
|
||||
if name == s.path+"/"+s.name {
|
||||
fieldFix = append(fieldFix, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The rtype has now been built, we can go back and rebuild
|
||||
// all the recursive types that relied on this type.
|
||||
// However, as we are keyed by type name, if two or more (recursive) fields at
|
||||
// the same depth level are of the same type, or a "variation" of the same type
|
||||
// (slice of, map of, etc), they "mask" each other, and only one
|
||||
// of them is in ctx.refs. That is why the code around here is a bit convoluted,
|
||||
// and we need both the loop above, around all the struct fields, and the loop
|
||||
// below, around the ctx.refs.
|
||||
for _, f := range ctx.refs[name] {
|
||||
ftyp := f.typ.field[f.idx].typ.refType(&refTypeContext{defined: ctx.defined, rebuilding: true})
|
||||
unsafe2.SwapFieldType(f.typ.rtype, f.idx, ftyp)
|
||||
for _, index := range fieldFix {
|
||||
ftyp := f.typ.field[index].typ.refType(&refTypeContext{defined: ctx.defined, rebuilding: true})
|
||||
unsafe2.SetFieldType(f.typ.rtype, index, ftyp)
|
||||
}
|
||||
}
|
||||
default:
|
||||
if z, _ := t.zero(); z.IsValid() {
|
||||
|
||||
@@ -3,6 +3,7 @@ package interp
|
||||
import (
|
||||
"errors"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"math"
|
||||
"reflect"
|
||||
)
|
||||
@@ -53,7 +54,7 @@ func (check typecheck) assignment(n *node, typ *itype, context string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !n.typ.assignableTo(typ) {
|
||||
if !n.typ.assignableTo(typ) && typ.str != "*unsafe2.dummy" {
|
||||
if context == "" {
|
||||
return n.cfgErrorf("cannot use type %s as type %s", n.typ.id(), typ.id())
|
||||
}
|
||||
@@ -124,6 +125,8 @@ func (check typecheck) starExpr(n *node) error {
|
||||
}
|
||||
|
||||
var unaryOpPredicates = opPredicates{
|
||||
aInc: isNumber,
|
||||
aDec: isNumber,
|
||||
aPos: isNumber,
|
||||
aNeg: isNumber,
|
||||
aBitNot: isInt,
|
||||
@@ -133,6 +136,9 @@ var unaryOpPredicates = opPredicates{
|
||||
// unaryExpr type checks a unary expression.
|
||||
func (check typecheck) unaryExpr(n *node) error {
|
||||
c0 := n.child[0]
|
||||
if isBlank(c0) {
|
||||
return n.cfgErrorf("cannot use _ as value")
|
||||
}
|
||||
t0 := c0.typ.TypeOf()
|
||||
|
||||
if n.action == aRecv {
|
||||
@@ -196,7 +202,7 @@ func (check typecheck) comparison(n *node) error {
|
||||
if typ.isNil() {
|
||||
typ = c1.typ
|
||||
}
|
||||
return n.cfgErrorf("invalid operation: operator %v not defined on %s", n.action, typ.id(), ".")
|
||||
return n.cfgErrorf("invalid operation: operator %v not defined on %s", n.action, typ.id())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -221,6 +227,10 @@ var binaryOpPredicates = opPredicates{
|
||||
func (check typecheck) binaryExpr(n *node) error {
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
|
||||
if isBlank(c0) || isBlank(c1) {
|
||||
return n.cfgErrorf("cannot use _ as value")
|
||||
}
|
||||
|
||||
a := n.action
|
||||
if isAssignAction(a) {
|
||||
a--
|
||||
@@ -476,6 +486,12 @@ func (check typecheck) structBinLitExpr(child []*node, typ reflect.Type) error {
|
||||
|
||||
// sliceExpr type checks a slice expression.
|
||||
func (check typecheck) sliceExpr(n *node) error {
|
||||
for _, c := range n.child {
|
||||
if isBlank(c) {
|
||||
return n.cfgErrorf("cannot use _ as value")
|
||||
}
|
||||
}
|
||||
|
||||
c, child := n.child[0], n.child[1:]
|
||||
|
||||
t := c.typ.TypeOf()
|
||||
@@ -591,6 +607,12 @@ func (check typecheck) typeAssertionExpr(n *node, typ *itype) error {
|
||||
continue
|
||||
}
|
||||
if tm == nil {
|
||||
// Lookup for non-exported methods is impossible
|
||||
// for bin types, ignore them as they can't be used
|
||||
// directly by the interpreted programs.
|
||||
if !token.IsExported(name) && isBin(typ) {
|
||||
continue
|
||||
}
|
||||
return n.cfgErrorf("impossible type assertion: %s does not implement %s (missing %v method)", typ.id(), n.typ.id(), name)
|
||||
}
|
||||
if tm.recv != nil && tm.recv.TypeOf().Kind() == reflect.Ptr && typ.TypeOf().Kind() != reflect.Ptr {
|
||||
@@ -850,7 +872,7 @@ func (check typecheck) builtin(name string, n *node, child []*node, ellipsis boo
|
||||
return params[0].nod.cfgErrorf("first argument to delete must be map; have %s", typ.id())
|
||||
}
|
||||
ktyp := params[1].Type()
|
||||
if !ktyp.assignableTo(typ.key) {
|
||||
if typ.key != nil && !ktyp.assignableTo(typ.key) {
|
||||
return params[1].nod.cfgErrorf("cannot use %s as type %s in delete", ktyp.id(), typ.key.id())
|
||||
}
|
||||
case bltnMake:
|
||||
|
||||
@@ -81,8 +81,14 @@ func genValueBinMethodOnInterface(n *node, defaultGen func(*frame) reflect.Value
|
||||
}
|
||||
|
||||
func genValueRecvIndirect(n *node) func(*frame) reflect.Value {
|
||||
v := genValueRecv(n)
|
||||
return func(f *frame) reflect.Value { return v(f).Elem() }
|
||||
vr := genValueRecv(n)
|
||||
return func(f *frame) reflect.Value {
|
||||
v := vr(f)
|
||||
if vi, ok := v.Interface().(valueInterface); ok {
|
||||
return vi.value
|
||||
}
|
||||
return v.Elem()
|
||||
}
|
||||
}
|
||||
|
||||
func genValueRecv(n *node) func(*frame) reflect.Value {
|
||||
@@ -141,7 +147,7 @@ func genValueAsFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
return reflect.New(typ).Elem()
|
||||
}
|
||||
vn, ok := v.Interface().(*node)
|
||||
if ok && vn.rval.IsValid() && vn.rval.Type().Kind() == reflect.Func {
|
||||
if ok && vn.rval.Kind() == reflect.Func {
|
||||
// The node value is already a callable func, no need to wrap it.
|
||||
return vn.rval
|
||||
}
|
||||
@@ -154,7 +160,7 @@ func genValueAs(n *node, t reflect.Type) func(*frame) reflect.Value {
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
v := value(f)
|
||||
switch v.Type().Kind() {
|
||||
switch v.Kind() {
|
||||
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice, reflect.UnsafePointer:
|
||||
if v.IsNil() {
|
||||
return reflect.New(t).Elem()
|
||||
@@ -213,7 +219,7 @@ func genValue(n *node) func(*frame) reflect.Value {
|
||||
func genDestValue(typ *itype, n *node) func(*frame) reflect.Value {
|
||||
convertLiteralValue(n, typ.TypeOf())
|
||||
switch {
|
||||
case isInterfaceSrc(typ) && !isEmptyInterface(typ):
|
||||
case isInterfaceSrc(typ) && (!isEmptyInterface(typ) || len(n.typ.method) > 0):
|
||||
return genValueInterface(n)
|
||||
case isFuncSrc(typ) && (n.typ.cat == valueT || n.typ.cat == nilT):
|
||||
return genValueNode(n)
|
||||
@@ -312,7 +318,7 @@ func genValueInterface(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
|
||||
// empty interface, do not wrap.
|
||||
if nod.typ.cat == interfaceT && len(nod.typ.field) == 0 {
|
||||
if nod != nil && isEmptyInterface(nod.typ) {
|
||||
return v
|
||||
}
|
||||
|
||||
@@ -332,13 +338,13 @@ func getConcreteValue(val reflect.Value) reflect.Value {
|
||||
if v.NumMethod() > 0 {
|
||||
return v
|
||||
}
|
||||
if v.Type().Kind() != reflect.Struct {
|
||||
if v.Kind() != reflect.Struct {
|
||||
return v
|
||||
}
|
||||
// Search a concrete value in fields of an emulated interface.
|
||||
for i := v.NumField() - 1; i >= 0; i-- {
|
||||
vv := v.Field(i)
|
||||
if vv.Type().Kind() == reflect.Interface {
|
||||
if vv.Kind() == reflect.Interface {
|
||||
vv = vv.Elem()
|
||||
}
|
||||
if vv.IsValid() {
|
||||
@@ -402,7 +408,7 @@ func genValueInterfaceValue(n *node) func(*frame) reflect.Value {
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
v := value(f)
|
||||
if v.Interface().(valueInterface).node == nil {
|
||||
if vi, ok := v.Interface().(valueInterface); ok && vi.node == nil {
|
||||
// Uninitialized interface value, set it to a correct zero value.
|
||||
v.Set(zeroInterfaceValue())
|
||||
v = value(f)
|
||||
@@ -415,7 +421,11 @@ func genValueNode(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
return reflect.ValueOf(&node{rval: value(f)})
|
||||
v := value(f)
|
||||
if _, ok := v.Interface().(*node); ok {
|
||||
return v
|
||||
}
|
||||
return reflect.ValueOf(&node{rval: v})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,7 +434,7 @@ func vInt(v reflect.Value) (i int64) {
|
||||
i, _ = constant.Int64Val(constant.ToInt(c))
|
||||
return i
|
||||
}
|
||||
switch v.Type().Kind() {
|
||||
switch v.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
i = v.Int()
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
@@ -442,7 +452,7 @@ func vUint(v reflect.Value) (i uint64) {
|
||||
i, _ = constant.Uint64Val(constant.ToInt(c))
|
||||
return i
|
||||
}
|
||||
switch v.Type().Kind() {
|
||||
switch v.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
i = uint64(v.Int())
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
@@ -462,7 +472,7 @@ func vComplex(v reflect.Value) (c complex128) {
|
||||
img, _ := constant.Float64Val(constant.Imag(c))
|
||||
return complex(rel, img)
|
||||
}
|
||||
switch v.Type().Kind() {
|
||||
switch v.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
c = complex(float64(v.Int()), 0)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
@@ -480,7 +490,7 @@ func vFloat(v reflect.Value) (i float64) {
|
||||
i, _ = constant.Float64Val(constant.ToFloat(c))
|
||||
return i
|
||||
}
|
||||
switch v.Type().Kind() {
|
||||
switch v.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
i = float64(v.Int())
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
@@ -585,5 +595,6 @@ func genComplex(n *node) func(*frame) complex128 {
|
||||
|
||||
func genValueString(n *node) func(*frame) (reflect.Value, string) {
|
||||
value := genValue(n)
|
||||
|
||||
return func(f *frame) (reflect.Value, string) { v := value(f); return v, v.String() }
|
||||
}
|
||||
|
||||
@@ -47,8 +47,12 @@ type _compress_flate_Reader struct {
|
||||
WReadByte func() (byte, error)
|
||||
}
|
||||
|
||||
func (W _compress_flate_Reader) Read(p []byte) (n int, err error) { return W.WRead(p) }
|
||||
func (W _compress_flate_Reader) ReadByte() (byte, error) { return W.WReadByte() }
|
||||
func (W _compress_flate_Reader) Read(p []byte) (n int, err error) {
|
||||
return W.WRead(p)
|
||||
}
|
||||
func (W _compress_flate_Reader) ReadByte() (byte, error) {
|
||||
return W.WReadByte()
|
||||
}
|
||||
|
||||
// _compress_flate_Resetter is an interface wrapper for Resetter type
|
||||
type _compress_flate_Resetter struct {
|
||||
@@ -56,4 +60,6 @@ type _compress_flate_Resetter struct {
|
||||
WReset func(r io.Reader, dict []byte) error
|
||||
}
|
||||
|
||||
func (W _compress_flate_Resetter) Reset(r io.Reader, dict []byte) error { return W.WReset(r, dict) }
|
||||
func (W _compress_flate_Resetter) Reset(r io.Reader, dict []byte) error {
|
||||
return W.WReset(r, dict)
|
||||
}
|
||||
|
||||
@@ -44,4 +44,6 @@ type _compress_zlib_Resetter struct {
|
||||
WReset func(r io.Reader, dict []byte) error
|
||||
}
|
||||
|
||||
func (W _compress_zlib_Resetter) Reset(r io.Reader, dict []byte) error { return W.WReset(r, dict) }
|
||||
func (W _compress_zlib_Resetter) Reset(r io.Reader, dict []byte) error {
|
||||
return W.WReset(r, dict)
|
||||
}
|
||||
|
||||
@@ -36,8 +36,18 @@ type _container_heap_Interface struct {
|
||||
WSwap func(i int, j int)
|
||||
}
|
||||
|
||||
func (W _container_heap_Interface) Len() int { return W.WLen() }
|
||||
func (W _container_heap_Interface) Less(i int, j int) bool { return W.WLess(i, j) }
|
||||
func (W _container_heap_Interface) Pop() interface{} { return W.WPop() }
|
||||
func (W _container_heap_Interface) Push(x interface{}) { W.WPush(x) }
|
||||
func (W _container_heap_Interface) Swap(i int, j int) { W.WSwap(i, j) }
|
||||
func (W _container_heap_Interface) Len() int {
|
||||
return W.WLen()
|
||||
}
|
||||
func (W _container_heap_Interface) Less(i int, j int) bool {
|
||||
return W.WLess(i, j)
|
||||
}
|
||||
func (W _container_heap_Interface) Pop() interface{} {
|
||||
return W.WPop()
|
||||
}
|
||||
func (W _container_heap_Interface) Push(x interface{}) {
|
||||
W.WPush(x)
|
||||
}
|
||||
func (W _container_heap_Interface) Swap(i int, j int) {
|
||||
W.WSwap(i, j)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user