Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c580dfdbc8 | ||
|
|
29e1777d82 | ||
|
|
50a34fd2a7 | ||
|
|
465cb578e7 | ||
|
|
12942b59a0 | ||
|
|
3e76267f8e | ||
|
|
988f0c9672 | ||
|
|
b0053c874f | ||
|
|
b20ad3a01d | ||
|
|
e78650d359 | ||
|
|
7327ff2811 | ||
|
|
ebde09b47d | ||
|
|
4995654e04 | ||
|
|
0a99eb48c3 | ||
|
|
4a22635585 | ||
|
|
b52dd8cc08 | ||
|
|
daaeac6e2c | ||
|
|
ca68c6cd95 | ||
|
|
953b122e67 | ||
|
|
9b07e73b5e | ||
|
|
78bbcda1f8 | ||
|
|
6e33f89146 | ||
|
|
d29b0a48ff | ||
|
|
c7c1bea7ef | ||
|
|
1ae2649655 | ||
|
|
0ace9244c4 | ||
|
|
2edd18a0c0 | ||
|
|
cfb73445a2 | ||
|
|
94e0b582ea | ||
|
|
3548c8744e | ||
|
|
d8bdc6670b | ||
|
|
27520f6dae |
@@ -37,6 +37,7 @@
|
||||
"funlen",
|
||||
"gocognit",
|
||||
"stylecheck",
|
||||
"gomnd",
|
||||
]
|
||||
|
||||
[issues]
|
||||
|
||||
@@ -19,8 +19,8 @@ cache:
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- go: 1.12.x
|
||||
- go: 1.13.x
|
||||
- go: 1.14.x
|
||||
env: STABLE=true
|
||||
|
||||
env:
|
||||
|
||||
@@ -18,7 +18,7 @@ It powers executable Go scripts and plugins, in embedded interpreters or interac
|
||||
* Works everywhere Go works
|
||||
* All Go & runtime resources accessible from script (with control)
|
||||
* Security: `unsafe` and `syscall` packages neither used nor exported by default
|
||||
* Support Go 1.12 and Go 1.13 (the latest 2 major releases)
|
||||
* Support Go 1.13 and Go 1.14 (the latest 2 major releases)
|
||||
|
||||
## Install
|
||||
|
||||
|
||||
@@ -9,5 +9,5 @@ func main() {
|
||||
fmt.Println(buf)
|
||||
}
|
||||
|
||||
// Output
|
||||
// Output:
|
||||
// []
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
println("Hello")
|
||||
|
||||
// Error:
|
||||
// _test/bad0.go:1:1: expected 'package', found println
|
||||
// 1:1: expected 'package', found println
|
||||
|
||||
12
_test/bin3.go
Normal file
12
_test/bin3.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
str := "part1"
|
||||
str += fmt.Sprintf("%s", "part2")
|
||||
fmt.Println(str)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// part1part2
|
||||
20
_test/chan9.go
Normal file
20
_test/chan9.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
type Channel chan string
|
||||
|
||||
type T struct {
|
||||
Channel
|
||||
}
|
||||
|
||||
func send(c Channel) { c <- "ping" }
|
||||
|
||||
func main() {
|
||||
t := &T{}
|
||||
t.Channel = make(Channel)
|
||||
go send(t.Channel)
|
||||
msg := <-t.Channel
|
||||
println(msg)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ping
|
||||
59
_test/cli2.go
Normal file
59
_test/cli2.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type T struct {
|
||||
ln net.Listener
|
||||
}
|
||||
|
||||
func (t *T) Close() {
|
||||
t.ln.Close()
|
||||
}
|
||||
|
||||
func client(uri string) {
|
||||
resp, err := http.Get(uri)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(string(body))
|
||||
}
|
||||
|
||||
func server(ln net.Listener, ready chan bool) {
|
||||
http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
|
||||
var r1 *http.Request = r
|
||||
fmt.Fprintln(w, "Welcome to my website!", r1.RequestURI)
|
||||
})
|
||||
|
||||
go http.Serve(ln, nil)
|
||||
ready <- true
|
||||
}
|
||||
|
||||
func main() {
|
||||
ln, err := net.Listen("tcp", "localhost:0")
|
||||
t := &T{ln}
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer t.Close()
|
||||
// defer ln.Close()
|
||||
|
||||
ready := make(chan bool)
|
||||
go server(ln, ready)
|
||||
<-ready
|
||||
|
||||
client(fmt.Sprintf("http://%s/hello", ln.Addr().String()))
|
||||
http.DefaultServeMux = &http.ServeMux{}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Welcome to my website! /hello
|
||||
@@ -7,5 +7,5 @@ func main() {
|
||||
fmt.Printf("%T %v\n", s, s)
|
||||
}
|
||||
|
||||
// Output
|
||||
// Output:
|
||||
// int 2
|
||||
|
||||
20
_test/composite7.go
Normal file
20
_test/composite7.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
name string
|
||||
}
|
||||
|
||||
var tab = []*T{{
|
||||
name: "foo",
|
||||
}, {
|
||||
name: "bar",
|
||||
}}
|
||||
|
||||
func main() {
|
||||
println(len(tab))
|
||||
println(tab[0].name)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
// foo
|
||||
16
_test/composite8.go
Normal file
16
_test/composite8.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
type T struct{ I int }
|
||||
|
||||
func main() {
|
||||
t := []*T{}
|
||||
s := []int{1, 2}
|
||||
for _, e := range s {
|
||||
x := &T{e}
|
||||
t = append(t, x)
|
||||
}
|
||||
println(t[0].I, t[1].I)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1 2
|
||||
17
_test/const10.go
Normal file
17
_test/const10.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
const (
|
||||
a = 2
|
||||
b = c + d
|
||||
c = a + d
|
||||
d = e + f
|
||||
e = 3
|
||||
f = 4
|
||||
)
|
||||
|
||||
func main() {
|
||||
println(b)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 16
|
||||
15
_test/const8.go
Normal file
15
_test/const8.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
const (
|
||||
a = 2
|
||||
b = c + d
|
||||
c = 4
|
||||
d = 5
|
||||
)
|
||||
|
||||
func main() {
|
||||
println(a, b, c, d)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2 9 4 5
|
||||
17
_test/const9.go
Normal file
17
_test/const9.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
const (
|
||||
a = 2
|
||||
b = c + d
|
||||
c = a + d
|
||||
d = e + f
|
||||
e = b + 2
|
||||
f = 4
|
||||
)
|
||||
|
||||
func main() {
|
||||
println(b)
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 5:2: constant definition loop
|
||||
23
_test/defer4.go
Normal file
23
_test/defer4.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import "sync"
|
||||
|
||||
type T struct {
|
||||
mu sync.RWMutex
|
||||
name string
|
||||
}
|
||||
|
||||
func (t *T) get() string {
|
||||
t.mu.RLock()
|
||||
defer t.mu.RUnlock()
|
||||
return t.name
|
||||
}
|
||||
|
||||
var d = T{name: "test"}
|
||||
|
||||
func main() {
|
||||
println(d.get())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// test
|
||||
13
_test/for10.go
Normal file
13
_test/for10.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
for a := 0; false; {
|
||||
println("nok", a)
|
||||
a++
|
||||
break
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bye
|
||||
14
_test/for11.go
Normal file
14
_test/for11.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := 0
|
||||
for ; true; a++ {
|
||||
println("nok", a)
|
||||
break
|
||||
}
|
||||
println("bye", a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// nok 0
|
||||
// bye 0
|
||||
12
_test/for12.go
Normal file
12
_test/for12.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
for a := 0; false; a++ {
|
||||
println("nok", a)
|
||||
break
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bye
|
||||
13
_test/for13.go
Normal file
13
_test/for13.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := 0
|
||||
for ; false; a++ {
|
||||
println("nok", a)
|
||||
break
|
||||
}
|
||||
println("bye", a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bye 0
|
||||
16
_test/for14.go
Normal file
16
_test/for14.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
for a := 0; true; a++ {
|
||||
println(a)
|
||||
if a > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 0
|
||||
// 1
|
||||
// bye
|
||||
12
_test/for9.go
Normal file
12
_test/for9.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
for false {
|
||||
println("nok")
|
||||
break
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bye
|
||||
17
_test/if3.go
Normal file
17
_test/if3.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := 0
|
||||
if false {
|
||||
println("false")
|
||||
a = 1
|
||||
} else {
|
||||
println("true")
|
||||
a = -1
|
||||
}
|
||||
println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// -1
|
||||
19
_test/if4.go
Normal file
19
_test/if4.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
const bad = false
|
||||
|
||||
func main() {
|
||||
a := 0
|
||||
if bad {
|
||||
println("false")
|
||||
a = 1
|
||||
} else {
|
||||
println("true")
|
||||
a = -1
|
||||
}
|
||||
println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// -1
|
||||
12
_test/if5.go
Normal file
12
_test/if5.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
if true {
|
||||
println("ok")
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ok
|
||||
// bye
|
||||
11
_test/if6.go
Normal file
11
_test/if6.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
if false {
|
||||
println("nok")
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bye
|
||||
15
_test/if7.go
Normal file
15
_test/if7.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := 0
|
||||
b := false
|
||||
if (b) {
|
||||
a = 1
|
||||
} else {
|
||||
a = -1
|
||||
}
|
||||
println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// -1
|
||||
12
_test/interface20.go
Normal file
12
_test/interface20.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
var a interface{}
|
||||
a = string("A")
|
||||
fmt.Println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// A
|
||||
12
_test/interface21.go
Normal file
12
_test/interface21.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
s := make([]interface{}, 1)
|
||||
s[0] = 1
|
||||
fmt.Println(s[0])
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
12
_test/interface22.go
Normal file
12
_test/interface22.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
s := make([]interface{}, 0)
|
||||
s = append(s, 1)
|
||||
fmt.Println(s[0])
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
12
_test/interface23.go
Normal file
12
_test/interface23.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
m := make(map[string]interface{})
|
||||
m["A"] = string("A")
|
||||
fmt.Println(m["A"])
|
||||
}
|
||||
|
||||
// Output:
|
||||
// A
|
||||
11
_test/interface24.go
Normal file
11
_test/interface24.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
m := make(map[string]interface{})
|
||||
fmt.Println(m["B"])
|
||||
}
|
||||
|
||||
// Output:
|
||||
// <nil>
|
||||
14
_test/interface25.go
Normal file
14
_test/interface25.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
m := make(map[string]interface{})
|
||||
m["A"] = 1
|
||||
for _, v := range m {
|
||||
fmt.Println(v)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
14
_test/interface26.go
Normal file
14
_test/interface26.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
s := make([]interface{}, 0)
|
||||
s = append(s, 1)
|
||||
for _, v := range s {
|
||||
fmt.Println(v)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
12
_test/interface27.go
Normal file
12
_test/interface27.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
var errs = map[int]error{0: nil}
|
||||
|
||||
func main() {
|
||||
fmt.Println(errs)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// map[0:<nil>]
|
||||
12
_test/interface28.go
Normal file
12
_test/interface28.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
var errs = []error{nil}
|
||||
|
||||
func main() {
|
||||
fmt.Println(errs)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// [<nil>]
|
||||
9
_test/interface29.go
Normal file
9
_test/interface29.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var a interface{}
|
||||
println(a == nil)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
9
_test/interface30.go
Normal file
9
_test/interface30.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var a interface{}
|
||||
println(a != nil)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// false
|
||||
11
_test/interface31.go
Normal file
11
_test/interface31.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
s := []interface{}{"test", 2}
|
||||
fmt.Println(s[0], s[1])
|
||||
}
|
||||
|
||||
// Output:
|
||||
// test 2
|
||||
11
_test/interface32.go
Normal file
11
_test/interface32.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
s := [2]interface{}{"test", 2}
|
||||
fmt.Println(s[0], s[1])
|
||||
}
|
||||
|
||||
// Output:
|
||||
// test 2
|
||||
11
_test/interface33.go
Normal file
11
_test/interface33.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
var a = map[string]interface{}{"test": "test"}
|
||||
fmt.Println(a["test"])
|
||||
}
|
||||
|
||||
// Output:
|
||||
// test
|
||||
11
_test/interface34.go
Normal file
11
_test/interface34.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
s := [2]interface{}{1: "test", 0: 2}
|
||||
fmt.Println(s[0], s[1])
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2 test
|
||||
15
_test/interface35.go
Normal file
15
_test/interface35.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type T struct {
|
||||
I interface{}
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := T{"test"}
|
||||
fmt.Println(t)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {test}
|
||||
55
_test/issue-558.go
Normal file
55
_test/issue-558.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type readAutoCloser struct {
|
||||
r io.ReadCloser
|
||||
}
|
||||
|
||||
func (a readAutoCloser) Read(b []byte) (n int, err error) {
|
||||
if a.r == nil {
|
||||
return 0, io.EOF
|
||||
}
|
||||
n, err = a.r.Read(b)
|
||||
if err == io.EOF {
|
||||
a.Close()
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (a readAutoCloser) Close() error {
|
||||
if a.r == nil {
|
||||
return nil
|
||||
}
|
||||
return a.r.(io.Closer).Close()
|
||||
}
|
||||
|
||||
type pipe struct {
|
||||
Reader readAutoCloser
|
||||
}
|
||||
|
||||
func newReadAutoCloser(r io.Reader) readAutoCloser {
|
||||
if _, ok := r.(io.Closer); !ok {
|
||||
return readAutoCloser{ioutil.NopCloser(r)}
|
||||
}
|
||||
return readAutoCloser{r.(io.ReadCloser)}
|
||||
}
|
||||
|
||||
func main() {
|
||||
p := &pipe{}
|
||||
p.Reader = newReadAutoCloser(strings.NewReader("test"))
|
||||
b, err := ioutil.ReadAll(p.Reader)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(string(b))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// test
|
||||
@@ -14,3 +14,6 @@ func main() {
|
||||
m := cmap{}
|
||||
fmt.Println(m)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {map[]}
|
||||
|
||||
15
_test/map21.go
Normal file
15
_test/map21.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
var m = map[int]string{
|
||||
1: "foo",
|
||||
}
|
||||
|
||||
func main() {
|
||||
var ok bool
|
||||
if _, ok = m[1]; ok {
|
||||
println("ok")
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ok
|
||||
14
_test/map22.go
Normal file
14
_test/map22.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
var m = map[int]string{
|
||||
1: "foo",
|
||||
}
|
||||
|
||||
func main() {
|
||||
var s string
|
||||
s, _ = m[1]
|
||||
println(s)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// foo
|
||||
13
_test/map23.go
Normal file
13
_test/map23.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
var m = map[int]string{
|
||||
1: "foo",
|
||||
}
|
||||
|
||||
func main() {
|
||||
_, _ = m[1]
|
||||
println("ok")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ok
|
||||
17
_test/select4.go
Normal file
17
_test/select4.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
c1 := make(chan string)
|
||||
|
||||
go func() { c1 <- "done" }()
|
||||
|
||||
select {
|
||||
case msg1 := <-c1:
|
||||
println("received from c1:", msg1)
|
||||
}
|
||||
println("Bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// received from c1: done
|
||||
// Bye
|
||||
22
_test/select5.go
Normal file
22
_test/select5.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
c1 chan string
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := &T{}
|
||||
t.c1 = make(chan string)
|
||||
|
||||
go func(c chan string) { c <- "done" }(t.c1)
|
||||
|
||||
select {
|
||||
case msg1 := <-t.c1:
|
||||
println("received from c1:", msg1)
|
||||
}
|
||||
println("Bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// received from c1: done
|
||||
// Bye
|
||||
22
_test/select6.go
Normal file
22
_test/select6.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
c1 chan string
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := &T{}
|
||||
t.c1 = make(chan string)
|
||||
|
||||
go func(c chan string) { c <- "done" }(t.c1)
|
||||
|
||||
select {
|
||||
case <-t.c1:
|
||||
println("received from c1")
|
||||
}
|
||||
println("Bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// received from c1
|
||||
// Bye
|
||||
24
_test/select7.go
Normal file
24
_test/select7.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
c1 chan string
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := &T{}
|
||||
t.c1 = make(chan string)
|
||||
a := 0
|
||||
|
||||
go func() {
|
||||
select {
|
||||
case t.c1 <- "done":
|
||||
a++
|
||||
}
|
||||
}()
|
||||
|
||||
msg1 := <-t.c1
|
||||
println("received from c1:", msg1)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// received from c1: done
|
||||
24
_test/select8.go
Normal file
24
_test/select8.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
c1 chan string
|
||||
c2 chan string
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := &T{}
|
||||
t.c1 = make(chan string)
|
||||
|
||||
go func(c chan string) { c <- "done" }(t.c1)
|
||||
|
||||
select {
|
||||
case msg := <-t.c1:
|
||||
println("received from c1:", msg)
|
||||
case <-t.c2:
|
||||
}
|
||||
println("Bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// received from c1: done
|
||||
// Bye
|
||||
22
_test/select9.go
Normal file
22
_test/select9.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
c1 chan string
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := &T{}
|
||||
t.c1 = make(chan string)
|
||||
|
||||
go func() {
|
||||
select {
|
||||
case t.c1 <- "done":
|
||||
}
|
||||
}()
|
||||
|
||||
msg1 := <-t.c1
|
||||
println("received from c1:", msg1)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// received from c1: done
|
||||
39
_test/struct32.go
Normal file
39
_test/struct32.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
type T0 struct {
|
||||
name string
|
||||
}
|
||||
|
||||
type lookupFunc func(s string) T0
|
||||
|
||||
type T1 struct {
|
||||
name string
|
||||
info lookupFunc
|
||||
}
|
||||
|
||||
func (t T0) F1() bool { println("in F1"); return true }
|
||||
|
||||
type T2 struct {
|
||||
t1 T1
|
||||
}
|
||||
|
||||
func (t2 *T2) f() {
|
||||
info := t2.t1.info("foo")
|
||||
println(info.F1())
|
||||
}
|
||||
|
||||
var t0 = T0{"t0"}
|
||||
|
||||
func main() {
|
||||
t := &T2{T1{
|
||||
"bar", func(s string) T0 { return t0 },
|
||||
}}
|
||||
|
||||
println("hello")
|
||||
println(t.t1.info("foo").F1())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
// in F1
|
||||
// true
|
||||
34
_test/struct33.go
Normal file
34
_test/struct33.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
type T0 struct {
|
||||
name string
|
||||
}
|
||||
|
||||
type lookupFunc func(s string) T0
|
||||
|
||||
type T1 struct {
|
||||
name string
|
||||
info lookupFunc
|
||||
}
|
||||
|
||||
func (t T0) F1() bool { println("in F1"); return true }
|
||||
|
||||
var t0 = T0{"t0"}
|
||||
|
||||
func look(s string) T0 { println("in look"); return t0 }
|
||||
|
||||
var table = []*T1{{
|
||||
name: "bar",
|
||||
info: look,
|
||||
},
|
||||
}
|
||||
|
||||
func main() {
|
||||
info := table[0].info
|
||||
println(info("foo").F1())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// in look
|
||||
// in F1
|
||||
// true
|
||||
16
_test/struct34.go
Normal file
16
_test/struct34.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
f func(*T)
|
||||
}
|
||||
|
||||
func f1(t *T) { t.f = f2 }
|
||||
|
||||
func f2(t *T) { t.f = f1 }
|
||||
|
||||
func main() {
|
||||
println("ok")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ok
|
||||
16
_test/struct35.go
Normal file
16
_test/struct35.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
f func(*T)
|
||||
}
|
||||
|
||||
func f1(t *T) { t.f = f1 }
|
||||
|
||||
func main() {
|
||||
t := &T{}
|
||||
f1(t)
|
||||
println(t.f != nil)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
21
_test/struct36.go
Normal file
21
_test/struct36.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type S struct {
|
||||
http.Client
|
||||
}
|
||||
|
||||
func main() {
|
||||
var s S
|
||||
if _, err := s.Get("url"); err != nil {
|
||||
println(strings.Contains(err.Error(), "unsupported protocol scheme"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
17
_test/struct37.go
Normal file
17
_test/struct37.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type MyHttpClient struct {
|
||||
*http.Client
|
||||
}
|
||||
|
||||
func main() {
|
||||
c := new(MyHttpClient)
|
||||
c.Client = new(http.Client)
|
||||
_, err := c.Get("url")
|
||||
println(strings.Contains(err.Error(), "unsupported protocol scheme"))
|
||||
}
|
||||
24
_test/struct38.go
Normal file
24
_test/struct38.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
f func(t *T1)
|
||||
y *xxx
|
||||
}
|
||||
|
||||
type T1 struct {
|
||||
T
|
||||
}
|
||||
|
||||
type xxx struct{}
|
||||
|
||||
var (
|
||||
x1 *T1 = x
|
||||
x = &T1{}
|
||||
)
|
||||
|
||||
func main() {
|
||||
println("ok")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ok
|
||||
22
_test/struct39.go
Normal file
22
_test/struct39.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
t *T1
|
||||
y *xxx
|
||||
}
|
||||
|
||||
type T1 struct {
|
||||
T
|
||||
}
|
||||
|
||||
var x = &T1{}
|
||||
var t = &T{}
|
||||
|
||||
type xxx struct{}
|
||||
|
||||
func main() {
|
||||
println("ok")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ok
|
||||
23
_test/struct40.go
Normal file
23
_test/struct40.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
t *T1
|
||||
y *xxx
|
||||
}
|
||||
|
||||
type T1 struct {
|
||||
T
|
||||
}
|
||||
|
||||
func f(t *T) { println("in f") }
|
||||
|
||||
var x = &T1{}
|
||||
|
||||
type xxx struct{}
|
||||
|
||||
func main() {
|
||||
println("ok")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ok
|
||||
26
_test/struct41.go
Normal file
26
_test/struct41.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
type Ti func(*T)
|
||||
|
||||
type T1 struct {
|
||||
t Ti
|
||||
}
|
||||
|
||||
type T struct {
|
||||
t Ti
|
||||
y *xxx
|
||||
}
|
||||
|
||||
func f(t *T) { println("in f") }
|
||||
|
||||
type xxx struct{}
|
||||
|
||||
var x = &T1{t: f}
|
||||
|
||||
func main() {
|
||||
x.t = f
|
||||
println("ok")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ok
|
||||
19
_test/struct42.go
Normal file
19
_test/struct42.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
t func(*T)
|
||||
y *xxx
|
||||
}
|
||||
|
||||
func f(t *T) { println("in f") }
|
||||
|
||||
var x = &T{t: f}
|
||||
|
||||
type xxx struct{}
|
||||
|
||||
func main() {
|
||||
println("ok")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ok
|
||||
19
_test/struct43.go
Normal file
19
_test/struct43.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
t func(*T)
|
||||
y *xxx
|
||||
}
|
||||
|
||||
func f(t *T) { println("in f") }
|
||||
|
||||
type xxx struct{}
|
||||
|
||||
func main() {
|
||||
x := &T{}
|
||||
x.t = f
|
||||
println("ok")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ok
|
||||
27
_test/struct44.go
Normal file
27
_test/struct44.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
type Ti func(*T) X
|
||||
|
||||
type T1 struct {
|
||||
t Ti
|
||||
}
|
||||
|
||||
type T struct {
|
||||
t Ti
|
||||
y *xxx
|
||||
}
|
||||
|
||||
func f(t *T) X { println("in f"); return X{} }
|
||||
|
||||
type X struct{ Name string }
|
||||
|
||||
type xxx struct{}
|
||||
|
||||
var x = &T1{t: f}
|
||||
|
||||
func main() {
|
||||
println("ok")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ok
|
||||
17
_test/struct45.go
Normal file
17
_test/struct45.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
b bool
|
||||
}
|
||||
|
||||
type T1 struct {
|
||||
T
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := &T1{}
|
||||
t.b = true
|
||||
println(t.b)
|
||||
}
|
||||
|
||||
// Output: true
|
||||
15
_test/switch23.go
Normal file
15
_test/switch23.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
func getType() string { return "T1" }
|
||||
|
||||
func main() {
|
||||
switch getType() {
|
||||
case "T1":
|
||||
println("T1")
|
||||
default:
|
||||
println("default")
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// T1
|
||||
16
_test/switch24.go
Normal file
16
_test/switch24.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := 3
|
||||
switch a + 2 {
|
||||
case 5:
|
||||
println(5)
|
||||
default:
|
||||
println("default")
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
// bye
|
||||
18
_test/switch25.go
Normal file
18
_test/switch25.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := 2
|
||||
switch {
|
||||
case a == 1:
|
||||
println(1)
|
||||
case a == 2:
|
||||
println(2)
|
||||
default:
|
||||
println("default")
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
// bye
|
||||
18
_test/switch26.go
Normal file
18
_test/switch26.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := 1
|
||||
switch a := 2; {
|
||||
case a == 1:
|
||||
println(1)
|
||||
case a == 2:
|
||||
println(2)
|
||||
default:
|
||||
println("default")
|
||||
}
|
||||
println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
// 1
|
||||
16
_test/switch27.go
Normal file
16
_test/switch27.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
//a := false
|
||||
switch false {
|
||||
case true:
|
||||
println("true")
|
||||
case false:
|
||||
println("false")
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// false
|
||||
// bye
|
||||
15
_test/switch28.go
Normal file
15
_test/switch28.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
switch {
|
||||
case true:
|
||||
println("true")
|
||||
case false:
|
||||
println("false")
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// bye
|
||||
14
_test/switch29.go
Normal file
14
_test/switch29.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := 3
|
||||
switch a {
|
||||
case 3:
|
||||
println("three")
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// three
|
||||
// bye
|
||||
16
_test/switch30.go
Normal file
16
_test/switch30.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := 3
|
||||
switch a {
|
||||
default:
|
||||
//println("default")
|
||||
case 3:
|
||||
println("three")
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// three
|
||||
// bye
|
||||
10
_test/switch31.go
Normal file
10
_test/switch31.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
switch {
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bye
|
||||
11
_test/switch32.go
Normal file
11
_test/switch32.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := 1
|
||||
switch a {
|
||||
}
|
||||
println("bye", a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bye 1
|
||||
11
_test/switch33.go
Normal file
11
_test/switch33.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var a interface{}
|
||||
switch a.(type) {
|
||||
}
|
||||
println("bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// bye
|
||||
20
_test/type18.go
Normal file
20
_test/type18.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
name string
|
||||
size int
|
||||
}
|
||||
|
||||
var table = []*T{{
|
||||
name: "foo",
|
||||
size: 2,
|
||||
}}
|
||||
|
||||
var s = table[0].size
|
||||
|
||||
func main() {
|
||||
println(s)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
21
_test/type19.go
Normal file
21
_test/type19.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
name string
|
||||
size int
|
||||
}
|
||||
|
||||
var table = map[int]*T{
|
||||
0: {
|
||||
name: "foo",
|
||||
size: 2,
|
||||
}}
|
||||
|
||||
var s = table[0].size
|
||||
|
||||
func main() {
|
||||
println(s)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
18
_test/type20.go
Normal file
18
_test/type20.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func isCloser(r io.Reader) bool {
|
||||
_, ok := r.(io.Closer)
|
||||
return ok
|
||||
}
|
||||
|
||||
func main() {
|
||||
println(isCloser(strings.NewReader("test")))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// false
|
||||
@@ -47,8 +47,10 @@ import (
|
||||
func main() {
|
||||
var interactive bool
|
||||
var tags string
|
||||
var cmd string
|
||||
flag.BoolVar(&interactive, "i", false, "start an interactive REPL")
|
||||
flag.StringVar(&tags, "tags", "", "set a list of build tags")
|
||||
flag.StringVar(&cmd, "e", "", "set the command to be executed (instead of script or/and shell)")
|
||||
flag.Usage = func() {
|
||||
fmt.Println("Usage:", os.Args[0], "[options] [script] [args]")
|
||||
fmt.Println("Options:")
|
||||
@@ -62,31 +64,41 @@ func main() {
|
||||
i.Use(stdlib.Symbols)
|
||||
i.Use(interp.Symbols)
|
||||
|
||||
if len(args) > 0 {
|
||||
// Skip first os arg to set command line as expected by interpreted main
|
||||
os.Args = os.Args[1:]
|
||||
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
|
||||
if cmd != `` {
|
||||
i.REPL(strings.NewReader(cmd), os.Stderr)
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(args[0])
|
||||
if err != nil {
|
||||
log.Fatal("Could not read file: ", args[0])
|
||||
}
|
||||
|
||||
s := string(b)
|
||||
if s[:2] == "#!" {
|
||||
// Allow executable go scripts, but fix them prior to parse
|
||||
s = strings.Replace(s, "#!", "//", 1)
|
||||
}
|
||||
|
||||
i.Name = args[0]
|
||||
if _, err := i.Eval(s); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
if interactive {
|
||||
if len(args) == 0 {
|
||||
if interactive || cmd == `` {
|
||||
i.REPL(os.Stdin, os.Stdout)
|
||||
}
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
// Skip first os arg to set command line as expected by interpreted main
|
||||
os.Args = os.Args[1:]
|
||||
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
|
||||
|
||||
b, err := ioutil.ReadFile(args[0])
|
||||
if err != nil {
|
||||
log.Fatal("Could not read file: ", args[0])
|
||||
}
|
||||
|
||||
s := string(b)
|
||||
if s[:2] == "#!" {
|
||||
// Allow executable go scripts, but fix them prior to parse
|
||||
s = strings.Replace(s, "#!", "//", 1)
|
||||
}
|
||||
|
||||
i.Name = args[0]
|
||||
if _, err := i.Eval(s); err != nil {
|
||||
fmt.Println(err)
|
||||
if p, ok := err.(interp.Panic); ok {
|
||||
fmt.Println(string(p.Stack))
|
||||
}
|
||||
}
|
||||
|
||||
if interactive {
|
||||
i.REPL(os.Stdin, os.Stdout)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,7 +200,6 @@ const (
|
||||
aCase
|
||||
aCompositeLit
|
||||
aDec
|
||||
aDefer
|
||||
aEqual
|
||||
aGreater
|
||||
aGreaterEqual
|
||||
@@ -258,7 +257,6 @@ var actions = [...]string{
|
||||
aCase: "case",
|
||||
aCompositeLit: "compositeLit",
|
||||
aDec: "--",
|
||||
aDefer: "defer",
|
||||
aEqual: "==",
|
||||
aGreater: ">",
|
||||
aGetFunc: "getFunc",
|
||||
@@ -569,7 +567,7 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
|
||||
st.push(addChild(&root, anc, pos, declStmt, aNop), nod)
|
||||
|
||||
case *ast.DeferStmt:
|
||||
st.push(addChild(&root, anc, pos, deferStmt, aDefer), nod)
|
||||
st.push(addChild(&root, anc, pos, deferStmt, aNop), nod)
|
||||
|
||||
case *ast.Ellipsis:
|
||||
st.push(addChild(&root, anc, pos, ellipsisExpr, aNop), nod)
|
||||
|
||||
346
interp/cfg.go
346
interp/cfg.go
@@ -10,7 +10,12 @@ import (
|
||||
)
|
||||
|
||||
// A cfgError represents an error during CFG build stage
|
||||
type cfgError error
|
||||
type cfgError struct {
|
||||
*node
|
||||
error
|
||||
}
|
||||
|
||||
func (c *cfgError) Error() string { return c.error.Error() }
|
||||
|
||||
var constOp = map[action]func(*node){
|
||||
aAdd: addConst,
|
||||
@@ -201,13 +206,20 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
sc = sc.pushBloc()
|
||||
if n.child[0].action == aAssign {
|
||||
ch := n.child[0].child[1].child[0]
|
||||
if sym, _, ok := sc.lookup(ch.ident); ok {
|
||||
assigned := n.child[0].child[0]
|
||||
index := sc.add(sym.typ.val)
|
||||
sc.sym[assigned.ident] = &symbol{index: index, kind: varSym, typ: sym.typ.val}
|
||||
assigned.findex = index
|
||||
assigned.typ = sym.typ.val
|
||||
var typ *itype
|
||||
if typ, err = nodeType(interp, sc, ch); err != nil {
|
||||
return false
|
||||
}
|
||||
if !isChan(typ) {
|
||||
err = n.cfgErrorf("invalid operation: receive from non-chan type")
|
||||
return false
|
||||
}
|
||||
elem := chanElement(typ)
|
||||
assigned := n.child[0].child[0]
|
||||
index := sc.add(elem)
|
||||
sc.sym[assigned.ident] = &symbol{index: index, kind: varSym, typ: elem}
|
||||
assigned.findex = index
|
||||
assigned.typ = elem
|
||||
}
|
||||
|
||||
case compositeLitExpr:
|
||||
@@ -312,7 +324,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
sc = sc.pushBloc()
|
||||
|
||||
case switchStmt, switchIfStmt, typeSwitch:
|
||||
// Make sure default clause is in last position
|
||||
// Make sure default clause is in last position.
|
||||
c := n.lastChild().child
|
||||
if i, l := getDefault(n), len(c)-1; i >= 0 && i != l {
|
||||
c[i], c[l] = c[l], c[i]
|
||||
@@ -459,7 +471,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
err = n.cfgErrorf("illegal operand types for '%v' operator", n.action)
|
||||
}
|
||||
default:
|
||||
// Detect invalid float truncate
|
||||
// Detect invalid float truncate.
|
||||
if isInt(t0) && isFloat(t1) {
|
||||
err = src.cfgErrorf("invalid float truncate")
|
||||
return
|
||||
@@ -470,7 +482,8 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
// Propagate type
|
||||
// TODO: Check that existing destination type matches source type
|
||||
switch {
|
||||
case n.action == aAssign && src.action == aCall:
|
||||
case n.action == aAssign && src.action == aCall && dest.typ.cat != interfaceT:
|
||||
// Call action may perform the assignment directly.
|
||||
n.gen = nop
|
||||
src.level = level
|
||||
src.findex = dest.findex
|
||||
@@ -478,7 +491,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
src.typ = dest.typ
|
||||
}
|
||||
case n.action == aAssign && src.action == aRecv:
|
||||
// Assign by reading from a receiving channel
|
||||
// Assign by reading from a receiving channel.
|
||||
n.gen = nop
|
||||
src.findex = dest.findex // Set recv address to LHS
|
||||
dest.typ = src.typ
|
||||
@@ -487,16 +500,16 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
src.findex = dest.findex
|
||||
src.level = level
|
||||
case src.kind == basicLit:
|
||||
// TODO: perform constant folding and propagation here
|
||||
// TODO: perform constant folding and propagation here.
|
||||
switch {
|
||||
case dest.typ.cat == interfaceT:
|
||||
case isComplex(dest.typ.TypeOf()):
|
||||
// value set in genValue
|
||||
// Value set in genValue.
|
||||
case !src.rval.IsValid():
|
||||
// Assign to nil
|
||||
// Assign to nil.
|
||||
src.rval = reflect.New(dest.typ.TypeOf()).Elem()
|
||||
default:
|
||||
// Convert literal value to destination type
|
||||
// Convert literal value to destination type.
|
||||
src.rval = src.rval.Convert(dest.typ.TypeOf())
|
||||
src.typ = dest.typ
|
||||
}
|
||||
@@ -771,10 +784,20 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
case isBinCall(n):
|
||||
n.gen = callBin
|
||||
if typ := n.child[0].typ.rtype; typ.NumOut() > 0 {
|
||||
n.typ = &itype{cat: valueT, rtype: typ.Out(0)}
|
||||
n.findex = sc.add(n.typ)
|
||||
for i := 1; i < typ.NumOut(); i++ {
|
||||
sc.add(&itype{cat: valueT, rtype: typ.Out(i)})
|
||||
if funcType := n.child[0].typ.val; funcType != nil {
|
||||
// Use the original unwrapped function type, to allow future field and
|
||||
// methods resolutions, otherwise impossible on the opaque bin type.
|
||||
n.typ = funcType.ret[0]
|
||||
n.findex = sc.add(n.typ)
|
||||
for i := 1; i < len(funcType.ret); i++ {
|
||||
sc.add(funcType.ret[i])
|
||||
}
|
||||
} else {
|
||||
n.typ = &itype{cat: valueT, rtype: typ.Out(0)}
|
||||
n.findex = sc.add(n.typ)
|
||||
for i := 1; i < typ.NumOut(); i++ {
|
||||
sc.add(&itype{cat: valueT, rtype: typ.Out(i)})
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
@@ -815,7 +838,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
} else {
|
||||
n.start = n.child[0].start // default clause
|
||||
}
|
||||
n.lastChild().tnext = n.anc.anc // exit node is SelectStmt
|
||||
n.lastChild().tnext = n.anc.anc // exit node is selectStmt
|
||||
sc = sc.pop()
|
||||
|
||||
case compositeLitExpr:
|
||||
@@ -824,7 +847,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
n.findex = sc.add(n.typ)
|
||||
}
|
||||
// TODO: Check that composite literal expr matches corresponding type
|
||||
n.gen = compositeGenerator(n)
|
||||
n.gen = compositeGenerator(n, sc)
|
||||
|
||||
case fallthroughtStmt:
|
||||
if n.anc.kind != caseBody {
|
||||
@@ -847,10 +870,18 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
if !isBool(cond.typ) {
|
||||
err = cond.cfgErrorf("non-bool used as for condition")
|
||||
}
|
||||
n.start = cond.start
|
||||
cond.tnext = body.start
|
||||
if cond.rval.IsValid() {
|
||||
// Condition is known at compile time, bypass test.
|
||||
if cond.rval.Bool() {
|
||||
n.start = body.start
|
||||
body.tnext = body.start
|
||||
}
|
||||
} else {
|
||||
n.start = cond.start
|
||||
cond.tnext = body.start
|
||||
body.tnext = cond.start
|
||||
}
|
||||
cond.fnext = n
|
||||
body.tnext = cond.start
|
||||
sc = sc.pop()
|
||||
|
||||
case forStmt2: // for init; cond; {}
|
||||
@@ -859,10 +890,20 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
err = cond.cfgErrorf("non-bool used as for condition")
|
||||
}
|
||||
n.start = init.start
|
||||
init.tnext = cond.start
|
||||
if cond.rval.IsValid() {
|
||||
// Condition is known at compile time, bypass test.
|
||||
if cond.rval.Bool() {
|
||||
init.tnext = body.start
|
||||
body.tnext = body.start
|
||||
} else {
|
||||
init.tnext = n
|
||||
}
|
||||
} else {
|
||||
init.tnext = cond.start
|
||||
body.tnext = cond.start
|
||||
}
|
||||
cond.tnext = body.start
|
||||
cond.fnext = n
|
||||
body.tnext = cond.start
|
||||
sc = sc.pop()
|
||||
|
||||
case forStmt3: // for ; cond; post {}
|
||||
@@ -870,14 +911,22 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
if !isBool(cond.typ) {
|
||||
err = cond.cfgErrorf("non-bool used as for condition")
|
||||
}
|
||||
n.start = cond.start
|
||||
if cond.rval.IsValid() {
|
||||
// Condition is known at compile time, bypass test.
|
||||
if cond.rval.Bool() {
|
||||
n.start = body.start
|
||||
post.tnext = body.start
|
||||
}
|
||||
} else {
|
||||
n.start = cond.start
|
||||
post.tnext = cond.start
|
||||
}
|
||||
cond.tnext = body.start
|
||||
cond.fnext = n
|
||||
body.tnext = post.start
|
||||
post.tnext = cond.start
|
||||
sc = sc.pop()
|
||||
|
||||
case forStmt3a: // for int; ; post {}
|
||||
case forStmt3a: // for init; ; post {}
|
||||
init, post, body := n.child[0], n.child[1], n.child[2]
|
||||
n.start = init.start
|
||||
init.tnext = body.start
|
||||
@@ -891,11 +940,21 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
err = cond.cfgErrorf("non-bool used as for condition")
|
||||
}
|
||||
n.start = init.start
|
||||
init.tnext = cond.start
|
||||
if cond.rval.IsValid() {
|
||||
// Condition is known at compile time, bypass test.
|
||||
if cond.rval.Bool() {
|
||||
init.tnext = body.start
|
||||
post.tnext = body.start
|
||||
} else {
|
||||
init.tnext = n
|
||||
}
|
||||
} else {
|
||||
init.tnext = cond.start
|
||||
post.tnext = cond.start
|
||||
}
|
||||
cond.tnext = body.start
|
||||
cond.fnext = n
|
||||
body.tnext = post.start
|
||||
post.tnext = cond.start
|
||||
sc = sc.pop()
|
||||
|
||||
case forRangeStmt:
|
||||
@@ -920,7 +979,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
sc = sc.pop()
|
||||
err = genRun(n)
|
||||
|
||||
case goStmt:
|
||||
case deferStmt, goStmt:
|
||||
wireChild(n)
|
||||
|
||||
case identExpr:
|
||||
@@ -952,10 +1011,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
}
|
||||
}
|
||||
if sym.kind == varSym && sym.typ != nil && sym.typ.TypeOf().Kind() == reflect.Bool {
|
||||
switch n.anc.kind {
|
||||
case ifStmt0, ifStmt1, ifStmt2, ifStmt3, forStmt1, forStmt2, forStmt3, forStmt4:
|
||||
n.gen = branch
|
||||
}
|
||||
fixBranch(n)
|
||||
}
|
||||
}
|
||||
if n.sym != nil {
|
||||
@@ -970,8 +1026,15 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
if !isBool(cond.typ) {
|
||||
err = cond.cfgErrorf("non-bool used as if condition")
|
||||
}
|
||||
n.start = cond.start
|
||||
cond.tnext = tbody.start
|
||||
if cond.rval.IsValid() {
|
||||
// Condition is known at compile time, bypass test.
|
||||
if cond.rval.Bool() {
|
||||
n.start = tbody.start
|
||||
}
|
||||
} else {
|
||||
n.start = cond.start
|
||||
cond.tnext = tbody.start
|
||||
}
|
||||
cond.fnext = n
|
||||
tbody.tnext = n
|
||||
sc = sc.pop()
|
||||
@@ -981,9 +1044,18 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
if !isBool(cond.typ) {
|
||||
err = cond.cfgErrorf("non-bool used as if condition")
|
||||
}
|
||||
n.start = cond.start
|
||||
cond.tnext = tbody.start
|
||||
cond.fnext = fbody.start
|
||||
if cond.rval.IsValid() {
|
||||
// Condition is known at compile time, bypass test and the useless branch.
|
||||
if cond.rval.Bool() {
|
||||
n.start = tbody.start
|
||||
} else {
|
||||
n.start = fbody.start
|
||||
}
|
||||
} else {
|
||||
n.start = cond.start
|
||||
cond.tnext = tbody.start
|
||||
cond.fnext = fbody.start
|
||||
}
|
||||
tbody.tnext = n
|
||||
fbody.tnext = n
|
||||
sc = sc.pop()
|
||||
@@ -994,9 +1066,18 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
err = cond.cfgErrorf("non-bool used as if condition")
|
||||
}
|
||||
n.start = init.start
|
||||
if cond.rval.IsValid() {
|
||||
// Condition is known at compile time, bypass test.
|
||||
if cond.rval.Bool() {
|
||||
init.tnext = tbody.start
|
||||
} else {
|
||||
init.tnext = n
|
||||
}
|
||||
} else {
|
||||
init.tnext = cond.start
|
||||
cond.tnext = tbody.start
|
||||
}
|
||||
tbody.tnext = n
|
||||
init.tnext = cond.start
|
||||
cond.tnext = tbody.start
|
||||
cond.fnext = n
|
||||
sc = sc.pop()
|
||||
|
||||
@@ -1006,9 +1087,18 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
err = cond.cfgErrorf("non-bool used as if condition")
|
||||
}
|
||||
n.start = init.start
|
||||
init.tnext = cond.start
|
||||
cond.tnext = tbody.start
|
||||
cond.fnext = fbody.start
|
||||
if cond.rval.IsValid() {
|
||||
// Condition is known at compile time, bypass test.
|
||||
if cond.rval.Bool() {
|
||||
init.tnext = tbody.start
|
||||
} else {
|
||||
init.tnext = fbody.start
|
||||
}
|
||||
} else {
|
||||
init.tnext = cond.start
|
||||
cond.tnext = tbody.start
|
||||
cond.fnext = fbody.start
|
||||
}
|
||||
tbody.tnext = n
|
||||
fbody.tnext = n
|
||||
sc = sc.pop()
|
||||
@@ -1198,11 +1288,12 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
n.recv = &receiver{node: n.child[0], index: lind}
|
||||
}
|
||||
} else if m, lind, isPtr, ok := n.typ.lookupBinMethod(n.child[1].ident); ok {
|
||||
if isPtr {
|
||||
if isPtr && n.typ.fieldSeq(lind).cat != ptrT {
|
||||
n.gen = getIndexSeqPtrMethod
|
||||
} else {
|
||||
n.gen = getIndexSeqMethod
|
||||
}
|
||||
n.recv = &receiver{node: n.child[0], index: lind}
|
||||
n.val = append([]int{m.Index}, lind...)
|
||||
n.typ = &itype{cat: valueT, rtype: m.Type}
|
||||
} else if ti := n.typ.lookupField(n.child[1].ident); len(ti) > 0 {
|
||||
@@ -1219,7 +1310,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
if n.typ.cat == funcT {
|
||||
// function in a struct field is always wrapped in reflect.Value
|
||||
rtype := n.typ.TypeOf()
|
||||
n.typ = &itype{cat: valueT, rtype: rtype}
|
||||
n.typ = &itype{cat: valueT, rtype: rtype, val: n.typ}
|
||||
}
|
||||
default:
|
||||
n.gen = getIndexSeq
|
||||
@@ -1227,7 +1318,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
if n.typ.cat == funcT {
|
||||
// function in a struct field is always wrapped in reflect.Value
|
||||
rtype := n.typ.TypeOf()
|
||||
n.typ = &itype{cat: valueT, rtype: rtype}
|
||||
n.typ = &itype{cat: valueT, rtype: rtype, val: n.typ}
|
||||
}
|
||||
}
|
||||
} else if s, lind, ok := n.typ.lookupBinField(n.child[1].ident); ok {
|
||||
@@ -1245,9 +1336,40 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
|
||||
case selectStmt:
|
||||
wireChild(n)
|
||||
// Move action to block statement, so select node can be an exit point
|
||||
// Move action to block statement, so select node can be an exit point.
|
||||
n.child[0].gen = _select
|
||||
n.start = n.child[0]
|
||||
// Chain channel init actions in commClauses prior to invoke select.
|
||||
var cur *node
|
||||
for _, c := range n.child[0].child {
|
||||
var an *node // channel init action node
|
||||
c0 := c.child[0]
|
||||
switch {
|
||||
case c0.kind == exprStmt && len(c0.child) == 1 && c0.child[0].action == aRecv:
|
||||
an = c0.child[0].child[0]
|
||||
case c0.action == aAssign:
|
||||
an = c0.lastChild().child[0]
|
||||
case c0.kind == sendStmt:
|
||||
an = c0.child[0]
|
||||
}
|
||||
if an != nil {
|
||||
if cur == nil {
|
||||
// First channel init action, the entry point for the select block.
|
||||
n.start = an.start
|
||||
} else {
|
||||
// Chain channel init action to the previous one.
|
||||
cur.tnext = an.start
|
||||
}
|
||||
}
|
||||
cur = an
|
||||
}
|
||||
// Invoke select action
|
||||
if cur == nil {
|
||||
// There is no channel init action, call select directly.
|
||||
n.start = n.child[0]
|
||||
} else {
|
||||
// Select is called after the last channel init action.
|
||||
cur.tnext = n.child[0]
|
||||
}
|
||||
|
||||
case starExpr:
|
||||
switch {
|
||||
@@ -1291,39 +1413,56 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
fallthrough
|
||||
|
||||
case switchStmt:
|
||||
sc = sc.pop()
|
||||
sbn := n.lastChild() // switch block node
|
||||
clauses := sbn.child
|
||||
l := len(clauses)
|
||||
// Chain case clauses
|
||||
if l == 0 {
|
||||
// Switch is empty
|
||||
break
|
||||
}
|
||||
// Chain case clauses.
|
||||
for i, c := range clauses[:l-1] {
|
||||
c.fnext = clauses[i+1] // chain to next clause
|
||||
body := c.lastChild()
|
||||
c.tnext = body.start
|
||||
if len(body.child) > 0 && body.lastChild().kind == fallthroughtStmt {
|
||||
if n.kind == typeSwitch {
|
||||
err = body.lastChild().cfgErrorf("cannot fallthrough in type switch")
|
||||
}
|
||||
body.tnext = clauses[i+1].lastChild().start
|
||||
c.fnext = clauses[i+1] // Chain to next clause.
|
||||
if len(c.child) == 0 {
|
||||
c.tnext = n // Clause body is empty, exit.
|
||||
} else {
|
||||
body.tnext = n
|
||||
body := c.lastChild()
|
||||
c.tnext = body.start
|
||||
if len(body.child) > 0 && body.lastChild().kind == fallthroughtStmt {
|
||||
if n.kind == typeSwitch {
|
||||
err = body.lastChild().cfgErrorf("cannot fallthrough in type switch")
|
||||
}
|
||||
if len(clauses[i+1].child) == 0 {
|
||||
body.tnext = n // Fallthrough to next with empty body, just exit.
|
||||
} else {
|
||||
body.tnext = clauses[i+1].lastChild().start
|
||||
}
|
||||
} else {
|
||||
body.tnext = n // Exit switch at end of clause body.
|
||||
}
|
||||
}
|
||||
}
|
||||
c := clauses[l-1]
|
||||
c.tnext = c.lastChild().start
|
||||
if n.child[0].action == aAssign &&
|
||||
(n.child[0].child[0].kind != typeAssertExpr || len(n.child[0].child[0].child) > 1) {
|
||||
// switch init statement is defined
|
||||
n.start = n.child[0].start
|
||||
n.child[0].tnext = sbn.start
|
||||
c := clauses[l-1] // Last clause.
|
||||
if len(c.child) == 0 {
|
||||
c.tnext = n // Clause body is empty, exit.
|
||||
} else {
|
||||
n.start = sbn.start
|
||||
body := c.lastChild()
|
||||
c.tnext = body.start
|
||||
body.tnext = n
|
||||
}
|
||||
sc = sc.pop()
|
||||
n.start = n.child[0].start
|
||||
n.child[0].tnext = sbn.start
|
||||
|
||||
case switchIfStmt: // like an if-else chain
|
||||
sc = sc.pop()
|
||||
sbn := n.lastChild() // switch block node
|
||||
clauses := sbn.child
|
||||
l := len(clauses)
|
||||
if l == 0 {
|
||||
// Switch is empty
|
||||
break
|
||||
}
|
||||
// Wire case clauses in reverse order so the next start node is already resolved when used.
|
||||
for i := l - 1; i >= 0; i-- {
|
||||
c := clauses[i]
|
||||
@@ -1344,17 +1483,13 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
// If last case body statement is a fallthrough, then jump to next case body
|
||||
if i < l-1 && len(body.child) > 0 && body.lastChild().kind == fallthroughtStmt {
|
||||
body.tnext = clauses[i+1].lastChild().start
|
||||
} else {
|
||||
body.tnext = n
|
||||
}
|
||||
}
|
||||
sbn.start = clauses[0].start
|
||||
if n.child[0].action == aAssign {
|
||||
// switch init statement is defined
|
||||
n.start = n.child[0].start
|
||||
n.child[0].tnext = sbn.start
|
||||
} else {
|
||||
n.start = sbn.start
|
||||
}
|
||||
sc = sc.pop()
|
||||
n.start = n.child[0].start
|
||||
n.child[0].tnext = sbn.start
|
||||
|
||||
case typeAssertExpr:
|
||||
if len(n.child) > 1 {
|
||||
@@ -1539,13 +1674,13 @@ func childPos(n *node) int {
|
||||
return -1
|
||||
}
|
||||
|
||||
func (n *node) cfgErrorf(format string, a ...interface{}) cfgError {
|
||||
func (n *node) cfgErrorf(format string, a ...interface{}) *cfgError {
|
||||
a = append([]interface{}{n.interp.fset.Position(n.pos)}, a...)
|
||||
return cfgError(fmt.Errorf("%s: "+format, a...))
|
||||
return &cfgError{n, fmt.Errorf("%s: "+format, a...)}
|
||||
}
|
||||
|
||||
func genRun(nod *node) error {
|
||||
var err cfgError
|
||||
var err error
|
||||
|
||||
nod.Walk(func(n *node) bool {
|
||||
if err != nil {
|
||||
@@ -1568,11 +1703,27 @@ func genRun(nod *node) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Find default case clause index of a switch statement, if any
|
||||
// FixBranch sets the branch action to the identExpr node if it is a bool
|
||||
// used in a conditional expression.
|
||||
func fixBranch(n *node) {
|
||||
switch n.anc.kind {
|
||||
case ifStmt0, ifStmt1, ifStmt2, ifStmt3, forStmt1, forStmt2, forStmt3, forStmt4:
|
||||
n.gen = branch
|
||||
case parenExpr:
|
||||
fixBranch(n.anc)
|
||||
}
|
||||
}
|
||||
|
||||
// GetDefault return the index of default case clause in a switch statement, or -1.
|
||||
func getDefault(n *node) int {
|
||||
for i, c := range n.lastChild().child {
|
||||
if len(c.child) == 1 {
|
||||
switch len(c.child) {
|
||||
case 0:
|
||||
return i
|
||||
case 1:
|
||||
if c.child[0].kind == caseBody {
|
||||
return i
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1
|
||||
@@ -1735,6 +1886,10 @@ func isKey(n *node) bool {
|
||||
(n.anc.kind == fieldExpr && len(n.anc.child) > 1 && n.anc.child[0] == n)
|
||||
}
|
||||
|
||||
func isField(n *node) bool {
|
||||
return n.kind == selectorExpr && len(n.child) > 0 && n.child[0].typ != nil && isStruct(n.child[0].typ)
|
||||
}
|
||||
|
||||
// isNewDefine returns true if node refers to a new definition
|
||||
func isNewDefine(n *node, sc *scope) bool {
|
||||
if n.ident == "_" {
|
||||
@@ -1849,21 +2004,32 @@ func gotoLabel(s *symbol) {
|
||||
}
|
||||
}
|
||||
|
||||
func compositeGenerator(n *node) (gen bltnGenerator) {
|
||||
func compositeGenerator(n *node, sc *scope) (gen bltnGenerator) {
|
||||
switch n.typ.cat {
|
||||
case aliasT, ptrT:
|
||||
n.typ.val.untyped = n.typ.untyped
|
||||
n.typ = n.typ.val
|
||||
gen = compositeGenerator(n)
|
||||
gen = compositeGenerator(n, sc)
|
||||
case arrayT:
|
||||
gen = arrayLit
|
||||
case mapT:
|
||||
gen = mapLit
|
||||
case structT:
|
||||
if len(n.child) > 0 && n.lastChild().kind == keyValueExpr {
|
||||
gen = compositeSparse
|
||||
} else {
|
||||
gen = compositeLit
|
||||
switch {
|
||||
case len(n.child) == 0:
|
||||
gen = compositeLitNotype
|
||||
case n.lastChild().kind == keyValueExpr:
|
||||
if n.child[0].isType(sc) {
|
||||
gen = compositeSparse
|
||||
} else {
|
||||
gen = compositeSparseNotype
|
||||
}
|
||||
default:
|
||||
if n.child[0].isType(sc) {
|
||||
gen = compositeLit
|
||||
} else {
|
||||
gen = compositeLitNotype
|
||||
}
|
||||
}
|
||||
case valueT:
|
||||
switch k := n.typ.rtype.Kind(); k {
|
||||
@@ -1875,7 +2041,7 @@ func compositeGenerator(n *node) (gen bltnGenerator) {
|
||||
log.Panic(n.cfgErrorf("compositeGenerator not implemented for type kind: %s", k))
|
||||
}
|
||||
}
|
||||
return
|
||||
return gen
|
||||
}
|
||||
|
||||
// arrayTypeLen returns the node's array length. If the expression is an
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
import "reflect"
|
||||
|
||||
// gta performs a global types analysis on the AST, registering types,
|
||||
// variables and functions symbols at package level, prior to CFG.
|
||||
@@ -23,7 +21,10 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
iotaValue = 0
|
||||
// Early parse of constDecl subtree, to compute all constant
|
||||
// values which may be necessary in further declarations.
|
||||
_, err = interp.cfg(n, pkgID)
|
||||
if _, err = interp.cfg(n, pkgID); err != nil {
|
||||
// No error processing here, to allow recovery in subtree nodes.
|
||||
err = nil
|
||||
}
|
||||
|
||||
case blockStmt:
|
||||
if n != root {
|
||||
@@ -47,6 +48,14 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
for i := 0; i < n.nleft; i++ {
|
||||
dest, src := n.child[i], n.child[sbase+i]
|
||||
val := reflect.ValueOf(iotaValue)
|
||||
if n.anc.kind == constDecl {
|
||||
if _, err2 := interp.cfg(n, pkgID); err2 != nil {
|
||||
// Constant value can not be computed yet.
|
||||
// Come back when child dependencies are known.
|
||||
revisit = append(revisit, n)
|
||||
return false
|
||||
}
|
||||
}
|
||||
typ := atyp
|
||||
if typ == nil {
|
||||
if typ, err = nodeType(interp, sc, src); err != nil {
|
||||
@@ -54,7 +63,7 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
}
|
||||
val = src.rval
|
||||
}
|
||||
if typ.incomplete {
|
||||
if !typ.isComplete() {
|
||||
// Come back when type is known.
|
||||
revisit = append(revisit, n)
|
||||
return false
|
||||
@@ -64,7 +73,7 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
return false
|
||||
}
|
||||
if typ.isBinMethod {
|
||||
typ = &itype{cat: valueT, rtype: typ.methodCallType(), isBinMethod: true}
|
||||
typ = &itype{cat: valueT, rtype: typ.methodCallType(), isBinMethod: true, scope: sc}
|
||||
}
|
||||
if sc.sym[dest.ident] == nil {
|
||||
sc.sym[dest.ident] = &symbol{kind: varSym, global: true, index: sc.add(typ), typ: typ, rval: val}
|
||||
@@ -85,7 +94,7 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
if n.typ, err = nodeType(interp, sc, n.child[l]); err != nil {
|
||||
return false
|
||||
}
|
||||
if n.typ.incomplete {
|
||||
if !n.typ.isComplete() {
|
||||
// Come back when type is known.
|
||||
revisit = append(revisit, n)
|
||||
return false
|
||||
@@ -130,7 +139,7 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
// Add a function symbol in the package name space
|
||||
sc.sym[n.child[1].ident] = &symbol{kind: funcSym, typ: n.typ, node: n, index: -1}
|
||||
}
|
||||
if n.typ.incomplete {
|
||||
if !n.typ.isComplete() {
|
||||
revisit = append(revisit, n)
|
||||
}
|
||||
return false
|
||||
@@ -154,10 +163,10 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
if isBinType(v) {
|
||||
typ = typ.Elem()
|
||||
}
|
||||
sc.sym[n] = &symbol{kind: binSym, typ: &itype{cat: valueT, rtype: typ}, rval: v}
|
||||
sc.sym[n] = &symbol{kind: binSym, typ: &itype{cat: valueT, rtype: typ, scope: sc}, rval: v}
|
||||
}
|
||||
default: // import symbols in package namespace
|
||||
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT, path: ipath}}
|
||||
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT, path: ipath, scope: sc}}
|
||||
}
|
||||
} else if err = interp.importSrc(rpath, ipath); err == nil {
|
||||
sc.types = interp.universe.types
|
||||
@@ -170,7 +179,7 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
}
|
||||
}
|
||||
default: // import symbols in package namespace
|
||||
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT, path: ipath}}
|
||||
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT, path: ipath, scope: sc}}
|
||||
}
|
||||
} else {
|
||||
err = n.cfgErrorf("import %q error: %v", ipath, err)
|
||||
@@ -183,7 +192,7 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
return false
|
||||
}
|
||||
if n.child[1].kind == identExpr {
|
||||
n.typ = &itype{cat: aliasT, val: typ, name: typeName, path: rpath, field: typ.field, incomplete: typ.incomplete}
|
||||
n.typ = &itype{cat: aliasT, val: typ, name: typeName, path: rpath, field: typ.field, incomplete: typ.incomplete, scope: sc}
|
||||
copy(n.typ.method, typ.method)
|
||||
} else {
|
||||
n.typ = typ
|
||||
@@ -197,7 +206,7 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
n.typ.method = append(n.typ.method, sc.sym[typeName].typ.method...)
|
||||
}
|
||||
sc.sym[typeName].typ = n.typ
|
||||
if n.typ.incomplete {
|
||||
if !n.typ.isComplete() {
|
||||
revisit = append(revisit, n)
|
||||
}
|
||||
return false
|
||||
@@ -210,3 +219,42 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
}
|
||||
return revisit, err
|
||||
}
|
||||
|
||||
// gtaRetry (re)applies gta until all global constants and types are defined.
|
||||
func (interp *Interpreter) gtaRetry(nodes []*node, rpath, pkgID string) error {
|
||||
revisit := []*node{}
|
||||
for {
|
||||
for _, n := range nodes {
|
||||
list, err := interp.gta(n, rpath, pkgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
revisit = append(revisit, list...)
|
||||
}
|
||||
|
||||
if len(revisit) == 0 || equalNodes(nodes, revisit) {
|
||||
break
|
||||
}
|
||||
|
||||
nodes = revisit
|
||||
revisit = []*node{}
|
||||
}
|
||||
|
||||
if len(revisit) > 0 {
|
||||
return revisit[0].cfgErrorf("constant definition loop")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// equalNodes returns true if two slices of nodes are identical.
|
||||
func equalNodes(a, b []*node) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i, n := range a {
|
||||
if n != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
@@ -158,6 +160,24 @@ type _error struct {
|
||||
|
||||
func (w _error) Error() string { return w.WError() }
|
||||
|
||||
// Panic is an error recovered from a panic call in interpreted code.
|
||||
type Panic struct {
|
||||
// Value is the recovered value of a call to panic.
|
||||
Value interface{}
|
||||
|
||||
// Callers is the call stack obtained from the recover call.
|
||||
// It may be used as the parameter to runtime.CallersFrames.
|
||||
Callers []uintptr
|
||||
|
||||
// Stack is the call stack buffer for debug.
|
||||
Stack []byte
|
||||
}
|
||||
|
||||
// TODO: Capture interpreter stack frames also and remove
|
||||
// fmt.Println(n.cfgErrorf("panic")) in runCfg.
|
||||
|
||||
func (e Panic) Error() string { return fmt.Sprint(e.Value) }
|
||||
|
||||
// Walk traverses AST n in depth first order, call cbin function
|
||||
// at node entry and cbout function at node exit.
|
||||
func (n *node) Walk(in func(n *node) bool, out func(n *node)) {
|
||||
@@ -291,10 +311,17 @@ func (interp *Interpreter) main() *node {
|
||||
|
||||
// Eval evaluates Go code represented as a string. It returns a map on
|
||||
// current interpreted package exported symbols
|
||||
func (interp *Interpreter) Eval(src string) (reflect.Value, error) {
|
||||
var res reflect.Value
|
||||
func (interp *Interpreter) Eval(src string) (res reflect.Value, err error) {
|
||||
defer func() {
|
||||
r := recover()
|
||||
if r != nil {
|
||||
var pc [64]uintptr // 64 frames should be enough.
|
||||
n := runtime.Callers(1, pc[:])
|
||||
err = Panic{Value: r, Callers: pc[:n], Stack: debug.Stack()}
|
||||
}
|
||||
}()
|
||||
|
||||
// Parse source to AST
|
||||
// Parse source to AST.
|
||||
pkgName, root, err := interp.ast(src, interp.Name)
|
||||
if err != nil || root == nil {
|
||||
return res, err
|
||||
@@ -307,16 +334,10 @@ func (interp *Interpreter) Eval(src string) (reflect.Value, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Global type analysis
|
||||
revisit, err := interp.gta(root, pkgName, interp.Name)
|
||||
if err != nil {
|
||||
// Perform global types analysis.
|
||||
if err = interp.gtaRetry([]*node{root}, pkgName, interp.Name); err != nil {
|
||||
return res, err
|
||||
}
|
||||
for _, n := range revisit {
|
||||
if _, err = interp.gta(n, pkgName, interp.Name); err != nil {
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
|
||||
// Annotate AST with CFG infos
|
||||
initNodes, err := interp.cfg(root, interp.Name)
|
||||
@@ -446,12 +467,15 @@ func (interp *Interpreter) REPL(in io.Reader, out io.Writer) {
|
||||
v, err := interp.EvalWithContext(ctx, src)
|
||||
signal.Reset()
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
switch e := err.(type) {
|
||||
case scanner.ErrorList:
|
||||
// Early failure in the scanner: the source is incomplete
|
||||
// and no AST could be produced, neither compiled / run.
|
||||
// Get one more line, and retry
|
||||
continue
|
||||
case Panic:
|
||||
fmt.Fprintln(out, e.Value)
|
||||
fmt.Fprintln(out, string(e.Stack))
|
||||
default:
|
||||
fmt.Fprintln(out, err)
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
for _, file := range files {
|
||||
if filepath.Ext(file.Name()) != ".go" ||
|
||||
file.Name() == "bad0.go" || // expect error
|
||||
file.Name() == "const9.go" || // expect error
|
||||
file.Name() == "export1.go" || // non-main package
|
||||
file.Name() == "export0.go" || // non-main package
|
||||
file.Name() == "for7.go" || // expect error
|
||||
@@ -138,6 +139,11 @@ func TestInterpErrorConsistency(t *testing.T) {
|
||||
expectedInterp: "1:1: expected 'package', found println",
|
||||
expectedExec: "1:1: expected 'package', found println",
|
||||
},
|
||||
{
|
||||
fileName: "const9.go",
|
||||
expectedInterp: "5:2: constant definition loop",
|
||||
expectedExec: "5:2: constant definition loop",
|
||||
},
|
||||
{
|
||||
fileName: "if2.go",
|
||||
expectedInterp: "7:5: non-bool used as if condition",
|
||||
|
||||
@@ -525,7 +525,11 @@ func eval(t *testing.T, i *interp.Interpreter, src string) reflect.Value {
|
||||
t.Helper()
|
||||
res, err := i.Eval(src)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
t.Logf("Error: %v", err)
|
||||
if e, ok := err.(interp.Panic); ok {
|
||||
t.Logf(string(e.Stack))
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
return res
|
||||
}
|
||||
@@ -541,7 +545,11 @@ func assertEval(t *testing.T, i *interp.Interpreter, src, expectedError, expecte
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("got an error %v", err)
|
||||
t.Logf("got an error: %v", err)
|
||||
if e, ok := err.(interp.Panic); ok {
|
||||
t.Logf(string(e.Stack))
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if fmt.Sprintf("%v", res) != expectedRes {
|
||||
|
||||
467
interp/run.go
467
interp/run.go
@@ -29,7 +29,6 @@ var builtin = [...]bltnGenerator{
|
||||
aCase: _case,
|
||||
aCompositeLit: arrayLit,
|
||||
aDec: dec,
|
||||
aDefer: _defer,
|
||||
aEqual: equal,
|
||||
aGetFunc: getFunc,
|
||||
aGreater: greater,
|
||||
@@ -122,30 +121,33 @@ func runCfg(n *node, f *frame) {
|
||||
}
|
||||
|
||||
func typeAssertStatus(n *node) {
|
||||
value := genValue(n.child[0]) // input value
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
value := genValue(c0) // input value
|
||||
value1 := genValue(n.anc.child[1]) // returned status
|
||||
typ := c1.typ.rtype // type to assert
|
||||
next := getExec(n.tnext)
|
||||
|
||||
switch {
|
||||
case n.child[0].typ.cat == valueT:
|
||||
case c0.typ.cat == valueT:
|
||||
n.exec = func(f *frame) bltn {
|
||||
if !value(f).IsValid() || value(f).IsNil() {
|
||||
v := value(f)
|
||||
if !v.IsValid() || v.IsNil() {
|
||||
value1(f).SetBool(false)
|
||||
}
|
||||
value1(f).SetBool(true)
|
||||
value1(f).SetBool(v.Type().Implements(typ))
|
||||
return next
|
||||
}
|
||||
case n.child[1].typ.cat == interfaceT:
|
||||
case c1.typ.cat == interfaceT:
|
||||
n.exec = func(f *frame) bltn {
|
||||
_, ok := value(f).Interface().(valueInterface)
|
||||
//value0(f).Set(reflect.ValueOf(valueInterface{v.node, v.value}))
|
||||
// TODO: verify that value(f) implements asserted type.
|
||||
value1(f).SetBool(ok)
|
||||
return next
|
||||
}
|
||||
default:
|
||||
n.exec = func(f *frame) bltn {
|
||||
_, ok := value(f).Interface().(valueInterface)
|
||||
//value0(f).Set(v.value)
|
||||
// TODO: verify that value(f) implements asserted type.
|
||||
value1(f).SetBool(ok)
|
||||
return next
|
||||
}
|
||||
@@ -153,25 +155,29 @@ func typeAssertStatus(n *node) {
|
||||
}
|
||||
|
||||
func typeAssert(n *node) {
|
||||
value := genValue(n.child[0]) // input value
|
||||
dest := genValue(n) // returned result
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
value := genValue(c0) // input value
|
||||
dest := genValue(n) // returned result
|
||||
next := getExec(n.tnext)
|
||||
|
||||
switch {
|
||||
case n.child[0].typ.cat == valueT:
|
||||
case c0.typ.cat == valueT:
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).Set(value(f).Elem())
|
||||
v := value(f)
|
||||
dest(f).Set(v.Elem())
|
||||
return next
|
||||
}
|
||||
case n.child[1].typ.cat == interfaceT:
|
||||
case c1.typ.cat == interfaceT:
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value(f).Interface().(valueInterface)
|
||||
// TODO: verify that value(f) implements asserted type.
|
||||
dest(f).Set(reflect.ValueOf(valueInterface{v.node, v.value}))
|
||||
return next
|
||||
}
|
||||
default:
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value(f).Interface().(valueInterface)
|
||||
// TODO: verify that value(f) implements asserted type.
|
||||
dest(f).Set(v.value)
|
||||
return next
|
||||
}
|
||||
@@ -196,6 +202,7 @@ func typeAssert2(n *node) {
|
||||
case n.child[1].typ.cat == interfaceT:
|
||||
n.exec = func(f *frame) bltn {
|
||||
v, ok := value(f).Interface().(valueInterface)
|
||||
// TODO: verify that value(f) implements asserted type.
|
||||
value0(f).Set(reflect.ValueOf(valueInterface{v.node, v.value}))
|
||||
value1(f).SetBool(ok)
|
||||
return next
|
||||
@@ -203,6 +210,7 @@ func typeAssert2(n *node) {
|
||||
default:
|
||||
n.exec = func(f *frame) bltn {
|
||||
v, ok := value(f).Interface().(valueInterface)
|
||||
// TODO: verify that value(f) implements asserted type.
|
||||
value0(f).Set(v.value)
|
||||
value1(f).SetBool(ok)
|
||||
return next
|
||||
@@ -264,7 +272,9 @@ func assign(n *node) {
|
||||
svalue[i] = genValueInterface(src)
|
||||
case (dest.typ.cat == valueT || dest.typ.cat == errorT) && dest.typ.rtype.Kind() == reflect.Interface:
|
||||
svalue[i] = genInterfaceWrapper(src, dest.typ.rtype)
|
||||
case dest.typ.cat == valueT && src.typ.cat == funcT:
|
||||
case src.typ.cat == funcT && dest.typ.cat == valueT:
|
||||
svalue[i] = genFunctionWrapper(src)
|
||||
case src.typ.cat == funcT && isField(dest):
|
||||
svalue[i] = genFunctionWrapper(src)
|
||||
case dest.typ.cat == funcT && src.typ.cat == valueT:
|
||||
svalue[i] = genValueNode(src)
|
||||
@@ -481,7 +491,6 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
if def, ok = n.val.(*node); !ok {
|
||||
return genValueAsFunctionWrapper(n)
|
||||
}
|
||||
setExec(def.child[3].start)
|
||||
start := def.child[3].start
|
||||
numRet := len(def.typ.ret)
|
||||
var rcvr func(*frame) reflect.Value
|
||||
@@ -493,12 +502,13 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
rcvr = genValueRecv(n)
|
||||
}
|
||||
}
|
||||
funcType := n.typ.TypeOf()
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
if n.frame != nil { // Use closure context if defined
|
||||
f = n.frame
|
||||
}
|
||||
return reflect.MakeFunc(n.typ.TypeOf(), func(in []reflect.Value) []reflect.Value {
|
||||
return reflect.MakeFunc(funcType, func(in []reflect.Value) []reflect.Value {
|
||||
// Allocate and init local frame. All values to be settable and addressable.
|
||||
fr := newFrame(f, len(def.types), f.runid())
|
||||
d := fr.data
|
||||
@@ -571,17 +581,22 @@ func genInterfaceWrapper(n *node, typ reflect.Type) func(*frame) reflect.Value {
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
v := value(f)
|
||||
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()
|
||||
}
|
||||
}
|
||||
w := reflect.New(wrap).Elem()
|
||||
for i, m := range methods {
|
||||
if m == nil {
|
||||
if r := v.FieldByIndex(indexes[i]).MethodByName(names[i]); r.IsValid() {
|
||||
w.Field(i).Set(v.FieldByIndex(indexes[i]).MethodByName(names[i]))
|
||||
o := vv.FieldByIndex(indexes[i])
|
||||
if r := o.MethodByName(names[i]); r.IsValid() {
|
||||
w.Field(i).Set(o.MethodByName(names[i]))
|
||||
} else {
|
||||
log.Println(n.cfgErrorf("genInterfaceWrapper error, no method %s", names[i]))
|
||||
}
|
||||
@@ -595,47 +610,6 @@ func genInterfaceWrapper(n *node, typ reflect.Type) func(*frame) reflect.Value {
|
||||
}
|
||||
}
|
||||
|
||||
func _defer(n *node) {
|
||||
tnext := getExec(n.tnext)
|
||||
values := make([]func(*frame) reflect.Value, len(n.child[0].child))
|
||||
var method func(*frame) reflect.Value
|
||||
|
||||
for i, c := range n.child[0].child {
|
||||
if c.typ.cat == funcT {
|
||||
values[i] = genFunctionWrapper(c)
|
||||
} else {
|
||||
if c.recv != nil {
|
||||
// defer a method on a binary obj
|
||||
mi := c.val.(int)
|
||||
m := genValue(c.child[0])
|
||||
method = func(f *frame) reflect.Value { return m(f).Method(mi) }
|
||||
}
|
||||
values[i] = genValue(c)
|
||||
}
|
||||
}
|
||||
|
||||
if method != nil {
|
||||
n.exec = func(f *frame) bltn {
|
||||
val := make([]reflect.Value, len(values))
|
||||
val[0] = method(f)
|
||||
for i, v := range values[1:] {
|
||||
val[i+1] = v(f)
|
||||
}
|
||||
f.deferred = append([][]reflect.Value{val}, f.deferred...)
|
||||
return tnext
|
||||
}
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
val := make([]reflect.Value, len(values))
|
||||
for i, v := range values {
|
||||
val[i] = v(f)
|
||||
}
|
||||
f.deferred = append([][]reflect.Value{val}, f.deferred...)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func call(n *node) {
|
||||
goroutine := n.anc.kind == goStmt
|
||||
var method bool
|
||||
@@ -707,20 +681,44 @@ func call(n *node) {
|
||||
}
|
||||
}
|
||||
|
||||
if n.anc.kind == deferStmt {
|
||||
// Store function call in frame for deferred execution.
|
||||
value = genFunctionWrapper(n.child[0])
|
||||
if method {
|
||||
// The receiver is already passed in the function wrapper, skip it.
|
||||
values = values[1:]
|
||||
}
|
||||
n.exec = func(f *frame) bltn {
|
||||
val := make([]reflect.Value, len(values)+1)
|
||||
val[0] = value(f)
|
||||
for i, v := range values {
|
||||
val[i+1] = v(f)
|
||||
}
|
||||
f.deferred = append([][]reflect.Value{val}, f.deferred...)
|
||||
return tnext
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
n.exec = func(f *frame) bltn {
|
||||
def := value(f).Interface().(*node)
|
||||
var def *node
|
||||
var ok bool
|
||||
bf := value(f)
|
||||
if def, ok = bf.Interface().(*node); ok {
|
||||
bf = def.rval
|
||||
}
|
||||
|
||||
// Call bin func if defined
|
||||
if def.rval.IsValid() {
|
||||
if bf.IsValid() {
|
||||
in := make([]reflect.Value, len(values))
|
||||
for i, v := range values {
|
||||
in[i] = v(f)
|
||||
}
|
||||
if goroutine {
|
||||
go def.rval.Call(in)
|
||||
go bf.Call(in)
|
||||
return tnext
|
||||
}
|
||||
out := def.rval.Call(in)
|
||||
out := bf.Call(in)
|
||||
for i, v := range rvalues {
|
||||
if v != nil {
|
||||
v(f).Set(out[i])
|
||||
@@ -823,7 +821,7 @@ func pindex(i, variadic int) int {
|
||||
return variadic
|
||||
}
|
||||
|
||||
// Call a function from a bin import, accessible through reflect
|
||||
// Callbin calls a function from a bin import, accessible through reflect.
|
||||
func callBin(n *node) {
|
||||
tnext := getExec(n.tnext)
|
||||
fnext := getExec(n.fnext)
|
||||
@@ -835,9 +833,9 @@ func callBin(n *node) {
|
||||
if funcType.IsVariadic() {
|
||||
variadic = funcType.NumIn() - 1
|
||||
}
|
||||
// method signature obtained from reflect.Type include receiver as 1st arg, except for interface types
|
||||
// A method signature obtained from reflect.Type includes receiver as 1st arg, except for interface types.
|
||||
rcvrOffset := 0
|
||||
if recv := n.child[0].recv; recv != nil && recv.node.typ.TypeOf().Kind() != reflect.Interface {
|
||||
if recv := n.child[0].recv; recv != nil && !isInterface(recv.node.typ) {
|
||||
if funcType.NumIn() > len(child) {
|
||||
rcvrOffset = 1
|
||||
}
|
||||
@@ -886,8 +884,19 @@ func callBin(n *node) {
|
||||
l := len(values)
|
||||
|
||||
switch {
|
||||
case n.anc.kind == deferStmt:
|
||||
// Store function call in frame for deferred execution.
|
||||
n.exec = func(f *frame) bltn {
|
||||
val := make([]reflect.Value, l+1)
|
||||
val[0] = value(f)
|
||||
for i, v := range values {
|
||||
val[i+1] = v(f)
|
||||
}
|
||||
f.deferred = append([][]reflect.Value{val}, f.deferred...)
|
||||
return tnext
|
||||
}
|
||||
case n.anc.kind == goStmt:
|
||||
// Execute function in a goroutine, discard results
|
||||
// Execute function in a goroutine, discard results.
|
||||
n.exec = func(f *frame) bltn {
|
||||
in := make([]reflect.Value, l)
|
||||
for i, v := range values {
|
||||
@@ -897,7 +906,7 @@ func callBin(n *node) {
|
||||
return tnext
|
||||
}
|
||||
case fnext != nil:
|
||||
// Handle branching according to boolean result
|
||||
// Handle branching according to boolean result.
|
||||
n.exec = func(f *frame) bltn {
|
||||
in := make([]reflect.Value, l)
|
||||
for i, v := range values {
|
||||
@@ -910,8 +919,10 @@ func callBin(n *node) {
|
||||
return fnext
|
||||
}
|
||||
default:
|
||||
switch n.anc.kind {
|
||||
case defineStmt, assignStmt, defineXStmt, assignXStmt:
|
||||
switch n.anc.action {
|
||||
case aAssign, aAssignX:
|
||||
// The function call is part of an assign expression, we write results direcly
|
||||
// to assigned location, to avoid an additional assign operation.
|
||||
rvalues := make([]func(*frame) reflect.Value, funcType.NumOut())
|
||||
for i := range rvalues {
|
||||
c := n.anc.child[i]
|
||||
@@ -1025,12 +1036,13 @@ func getIndexMap(n *node) {
|
||||
dest := genValue(n)
|
||||
value0 := genValue(n.child[0]) // map
|
||||
tnext := getExec(n.tnext)
|
||||
z := reflect.New(n.child[0].typ.TypeOf().Elem()).Elem()
|
||||
z := reflect.New(n.child[0].typ.frameType().Elem()).Elem()
|
||||
|
||||
if n.child[1].rval.IsValid() { // constant map index
|
||||
mi := n.child[1].rval
|
||||
|
||||
if n.fnext != nil {
|
||||
switch {
|
||||
case n.fnext != nil:
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
if v := value0(f).MapIndex(mi); v.IsValid() && v.Bool() {
|
||||
@@ -1040,7 +1052,17 @@ func getIndexMap(n *node) {
|
||||
dest(f).Set(z)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
case n.typ.cat == interfaceT:
|
||||
z = reflect.New(n.child[0].typ.val.frameType()).Elem()
|
||||
n.exec = func(f *frame) bltn {
|
||||
if v := value0(f).MapIndex(mi); v.IsValid() {
|
||||
dest(f).Set(v.Elem())
|
||||
} else {
|
||||
dest(f).Set(z)
|
||||
}
|
||||
return tnext
|
||||
}
|
||||
default:
|
||||
n.exec = func(f *frame) bltn {
|
||||
if v := value0(f).MapIndex(mi); v.IsValid() {
|
||||
dest(f).Set(v)
|
||||
@@ -1053,7 +1075,8 @@ func getIndexMap(n *node) {
|
||||
} else {
|
||||
value1 := genValue(n.child[1]) // map index
|
||||
|
||||
if n.fnext != nil {
|
||||
switch {
|
||||
case n.fnext != nil:
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
if v := value0(f).MapIndex(value1(f)); v.IsValid() && v.Bool() {
|
||||
@@ -1063,7 +1086,17 @@ func getIndexMap(n *node) {
|
||||
dest(f).Set(z)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
case n.typ.cat == interfaceT:
|
||||
z = reflect.New(n.child[0].typ.val.frameType()).Elem()
|
||||
n.exec = func(f *frame) bltn {
|
||||
if v := value0(f).MapIndex(value1(f)); v.IsValid() {
|
||||
dest(f).Set(v.Elem())
|
||||
} else {
|
||||
dest(f).Set(z)
|
||||
}
|
||||
return tnext
|
||||
}
|
||||
default:
|
||||
n.exec = func(f *frame) bltn {
|
||||
if v := value0(f).MapIndex(value1(f)); v.IsValid() {
|
||||
dest(f).Set(v)
|
||||
@@ -1082,26 +1115,77 @@ 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 != "_"
|
||||
|
||||
if !doValue && !doStatus {
|
||||
nop(n)
|
||||
return
|
||||
}
|
||||
if n.child[1].rval.IsValid() { // constant map index
|
||||
mi := n.child[1].rval
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value0(f).MapIndex(mi)
|
||||
if v.IsValid() {
|
||||
dest(f).Set(v)
|
||||
switch {
|
||||
case !doValue:
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value0(f).MapIndex(mi)
|
||||
value2(f).SetBool(v.IsValid())
|
||||
return next
|
||||
}
|
||||
case typ.cat == interfaceT:
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value0(f).MapIndex(mi)
|
||||
if v.IsValid() {
|
||||
dest(f).Set(v.Elem())
|
||||
}
|
||||
if doStatus {
|
||||
value2(f).SetBool(v.IsValid())
|
||||
}
|
||||
return next
|
||||
}
|
||||
default:
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value0(f).MapIndex(mi)
|
||||
if v.IsValid() {
|
||||
dest(f).Set(v)
|
||||
}
|
||||
if doStatus {
|
||||
value2(f).SetBool(v.IsValid())
|
||||
}
|
||||
return next
|
||||
}
|
||||
value2(f).SetBool(v.IsValid())
|
||||
return next
|
||||
}
|
||||
} else {
|
||||
value1 := genValue(n.child[1]) // map index
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value0(f).MapIndex(value1(f))
|
||||
if v.IsValid() {
|
||||
dest(f).Set(v)
|
||||
switch {
|
||||
case !doValue:
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value0(f).MapIndex(value1(f))
|
||||
value2(f).SetBool(v.IsValid())
|
||||
return next
|
||||
}
|
||||
case typ.cat == interfaceT:
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value0(f).MapIndex(value1(f))
|
||||
if v.IsValid() {
|
||||
dest(f).Set(v.Elem())
|
||||
}
|
||||
if doStatus {
|
||||
value2(f).SetBool(v.IsValid())
|
||||
}
|
||||
return next
|
||||
}
|
||||
default:
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value0(f).MapIndex(value1(f))
|
||||
if v.IsValid() {
|
||||
dest(f).Set(v)
|
||||
}
|
||||
if doStatus {
|
||||
value2(f).SetBool(v.IsValid())
|
||||
}
|
||||
return next
|
||||
}
|
||||
value2(f).SetBool(v.IsValid())
|
||||
return next
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1496,11 +1580,19 @@ func arrayLit(n *node) {
|
||||
for i, c := range child {
|
||||
if c.kind == keyValueExpr {
|
||||
convertLiteralValue(c.child[1], rtype)
|
||||
values[i] = genValue(c.child[1])
|
||||
if n.typ.val.cat == interfaceT {
|
||||
values[i] = genValueInterface(c.child[1])
|
||||
} else {
|
||||
values[i] = genValue(c.child[1])
|
||||
}
|
||||
index[i] = int(vInt(c.child[0].rval))
|
||||
} else {
|
||||
convertLiteralValue(c, rtype)
|
||||
values[i] = genValue(c)
|
||||
if n.typ.val.cat == interfaceT {
|
||||
values[i] = genValueInterface(c)
|
||||
} else {
|
||||
values[i] = genValue(c)
|
||||
}
|
||||
index[i] = prev
|
||||
}
|
||||
prev = index[i] + 1
|
||||
@@ -1509,14 +1601,14 @@ func arrayLit(n *node) {
|
||||
}
|
||||
}
|
||||
|
||||
var a reflect.Value
|
||||
if n.typ.sizedef {
|
||||
a, _ = n.typ.zero()
|
||||
} else {
|
||||
a = reflect.MakeSlice(n.typ.TypeOf(), max, max)
|
||||
}
|
||||
|
||||
typ := n.typ.frameType()
|
||||
n.exec = func(f *frame) bltn {
|
||||
var a reflect.Value
|
||||
if n.typ.sizedef {
|
||||
a, _ = n.typ.zero()
|
||||
} else {
|
||||
a = reflect.MakeSlice(typ, max, max)
|
||||
}
|
||||
for i, v := range values {
|
||||
a.Index(index[i]).Set(v(f))
|
||||
}
|
||||
@@ -1538,8 +1630,16 @@ func mapLit(n *node) {
|
||||
for i, c := range child {
|
||||
convertLiteralValue(c.child[0], n.typ.key.TypeOf())
|
||||
convertLiteralValue(c.child[1], n.typ.val.TypeOf())
|
||||
keys[i] = genValue(c.child[0])
|
||||
values[i] = genValue(c.child[1])
|
||||
if n.typ.key.cat == interfaceT {
|
||||
keys[i] = genValueInterface(c.child[0])
|
||||
} else {
|
||||
keys[i] = genValue(c.child[0])
|
||||
}
|
||||
if n.typ.val.cat == interfaceT {
|
||||
values[i] = genValueInterface(c.child[1])
|
||||
} else {
|
||||
values[i] = genValue(c.child[1])
|
||||
}
|
||||
}
|
||||
|
||||
n.exec = func(f *frame) bltn {
|
||||
@@ -1628,12 +1728,12 @@ func destType(n *node) *itype {
|
||||
}
|
||||
}
|
||||
|
||||
// compositeLit creates and populates a struct object
|
||||
func compositeLit(n *node) {
|
||||
// doCompositeLit creates and populates a struct object
|
||||
func doCompositeLit(n *node, hasType bool) {
|
||||
value := valueGenerator(n, n.findex)
|
||||
next := getExec(n.tnext)
|
||||
child := n.child
|
||||
if !n.typ.untyped {
|
||||
if hasType {
|
||||
child = n.child[1:]
|
||||
}
|
||||
destInterface := destType(n).cat == interfaceT
|
||||
@@ -1648,6 +1748,7 @@ func compositeLit(n *node) {
|
||||
}
|
||||
}
|
||||
|
||||
i := n.findex
|
||||
n.exec = func(f *frame) bltn {
|
||||
a := reflect.New(n.typ.TypeOf()).Elem()
|
||||
for i, v := range values {
|
||||
@@ -1659,18 +1760,21 @@ func compositeLit(n *node) {
|
||||
case destInterface:
|
||||
d.Set(reflect.ValueOf(valueInterface{n, a}))
|
||||
default:
|
||||
d.Set(a)
|
||||
f.data[i] = a
|
||||
}
|
||||
return next
|
||||
}
|
||||
}
|
||||
|
||||
// compositeSparse creates a struct Object, filling fields from sparse key-values
|
||||
func compositeSparse(n *node) {
|
||||
func compositeLit(n *node) { doCompositeLit(n, true) }
|
||||
func compositeLitNotype(n *node) { doCompositeLit(n, false) }
|
||||
|
||||
// doCompositeSparse creates a struct Object, filling fields from sparse key-values
|
||||
func doCompositeSparse(n *node, hasType bool) {
|
||||
value := valueGenerator(n, n.findex)
|
||||
next := getExec(n.tnext)
|
||||
child := n.child
|
||||
if !n.typ.untyped {
|
||||
if hasType {
|
||||
child = n.child[1:]
|
||||
}
|
||||
|
||||
@@ -1700,6 +1804,9 @@ func compositeSparse(n *node) {
|
||||
}
|
||||
}
|
||||
|
||||
func compositeSparse(n *node) { doCompositeSparse(n, true) }
|
||||
func compositeSparseNotype(n *node) { doCompositeSparse(n, false) }
|
||||
|
||||
func empty(n *node) {}
|
||||
|
||||
var rat = reflect.ValueOf((*[]rune)(nil)).Type().Elem() // runes array type
|
||||
@@ -1785,14 +1892,26 @@ func rangeMap(n *node) {
|
||||
if len(n.child) == 4 {
|
||||
index1 := n.child[1].findex // map value location in frame
|
||||
value = genValue(n.child[2]) // map
|
||||
n.exec = func(f *frame) bltn {
|
||||
iter := f.data[index2].Interface().(*reflect.MapIter)
|
||||
if !iter.Next() {
|
||||
return fnext
|
||||
if n.child[1].typ.cat == interfaceT {
|
||||
n.exec = func(f *frame) bltn {
|
||||
iter := f.data[index2].Interface().(*reflect.MapIter)
|
||||
if !iter.Next() {
|
||||
return fnext
|
||||
}
|
||||
f.data[index0].Set(iter.Key())
|
||||
f.data[index1].Set(iter.Value().Elem())
|
||||
return tnext
|
||||
}
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
iter := f.data[index2].Interface().(*reflect.MapIter)
|
||||
if !iter.Next() {
|
||||
return fnext
|
||||
}
|
||||
f.data[index0].Set(iter.Key())
|
||||
f.data[index1].Set(iter.Value())
|
||||
return tnext
|
||||
}
|
||||
f.data[index0].Set(iter.Key())
|
||||
f.data[index1].Set(iter.Value())
|
||||
return tnext
|
||||
}
|
||||
} else {
|
||||
value = genValue(n.child[1]) // map
|
||||
@@ -1960,6 +2079,8 @@ func _append(n *node) {
|
||||
values := make([]func(*frame) reflect.Value, l)
|
||||
for i, arg := range args {
|
||||
switch {
|
||||
case n.typ.val.cat == interfaceT:
|
||||
values[i] = genValueInterface(arg)
|
||||
case isRecursiveStruct(n.typ.val, n.typ.val.rtype):
|
||||
values[i] = genValueInterfacePtr(arg)
|
||||
case arg.typ.untyped:
|
||||
@@ -1980,6 +2101,8 @@ func _append(n *node) {
|
||||
} else {
|
||||
var value0 func(*frame) reflect.Value
|
||||
switch {
|
||||
case n.typ.val.cat == interfaceT:
|
||||
value0 = genValueInterface(n.child[2])
|
||||
case isRecursiveStruct(n.typ.val, n.typ.val.rtype):
|
||||
value0 = genValueInterfacePtr(n.child[2])
|
||||
case n.child[2].typ.untyped:
|
||||
@@ -2113,7 +2236,7 @@ func _new(n *node) {
|
||||
func _make(n *node) {
|
||||
dest := genValue(n)
|
||||
next := getExec(n.tnext)
|
||||
typ := n.child[1].typ.TypeOf()
|
||||
typ := n.child[1].typ.frameType()
|
||||
|
||||
switch typ.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
@@ -2306,14 +2429,16 @@ func recv2(n *node) {
|
||||
}
|
||||
|
||||
func convertLiteralValue(n *node, t reflect.Type) {
|
||||
// Skip non-constant values, undefined target type or interface target type.
|
||||
if !(n.kind == basicLit || n.rval.IsValid()) || t == nil || t.Kind() == reflect.Interface {
|
||||
return
|
||||
}
|
||||
if n.rval.IsValid() {
|
||||
switch {
|
||||
case n.typ.cat == nilT:
|
||||
// Create a zero value of target type.
|
||||
n.rval = reflect.New(t).Elem()
|
||||
case !(n.kind == basicLit || n.rval.IsValid()) || t == nil || t.Kind() == reflect.Interface:
|
||||
// Skip non-constant values, undefined target type or interface target type.
|
||||
case n.rval.IsValid():
|
||||
// Convert constant value to target type.
|
||||
n.rval = n.rval.Convert(t)
|
||||
} else {
|
||||
default:
|
||||
// Create a zero value of target type.
|
||||
n.rval = reflect.New(t).Elem()
|
||||
}
|
||||
@@ -2391,7 +2516,9 @@ func _select(n *node) {
|
||||
cases := make([]reflect.SelectCase, nbClause+1)
|
||||
|
||||
for i := 0; i < nbClause; i++ {
|
||||
if len(n.child[i].child) > 1 {
|
||||
switch c0 := n.child[i].child[0]; {
|
||||
case len(n.child[i].child) > 1:
|
||||
// The comm clause contains a channel operation and a clause body.
|
||||
clause[i] = getExec(n.child[i].child[1].start)
|
||||
chans[i], assigned[i], ok[i], cases[i].Dir = clauseChanDir(n.child[i])
|
||||
chanValues[i] = genValue(chans[i])
|
||||
@@ -2401,8 +2528,18 @@ func _select(n *node) {
|
||||
if ok[i] != nil {
|
||||
okValues[i] = genValue(ok[i])
|
||||
}
|
||||
} else {
|
||||
clause[i] = getExec(n.child[i].child[0].start)
|
||||
case c0.kind == exprStmt && len(c0.child) == 1 && c0.child[0].action == aRecv:
|
||||
// The comm clause has an empty body clause after channel receive.
|
||||
chanValues[i] = genValue(c0.child[0].child[0])
|
||||
cases[i].Dir = reflect.SelectRecv
|
||||
case c0.kind == sendStmt:
|
||||
// The comm clause as an empty body clause after channel send.
|
||||
chanValues[i] = genValue(c0.child[0])
|
||||
cases[i].Dir = reflect.SelectSend
|
||||
assignedValues[i] = genValue(c0.child[1])
|
||||
default:
|
||||
// The comm clause has a default clause.
|
||||
clause[i] = getExec(c0.start)
|
||||
cases[i].Dir = reflect.SelectDefault
|
||||
}
|
||||
}
|
||||
@@ -2501,56 +2638,94 @@ func slice0(n *node) {
|
||||
|
||||
func isNil(n *node) {
|
||||
var value func(*frame) reflect.Value
|
||||
if n.child[0].typ.cat == funcT {
|
||||
value = genValueAsFunctionWrapper(n.child[0])
|
||||
c0 := n.child[0]
|
||||
if c0.typ.cat == funcT {
|
||||
value = genValueAsFunctionWrapper(c0)
|
||||
} else {
|
||||
value = genValue(n.child[0])
|
||||
value = genValue(c0)
|
||||
}
|
||||
tnext := getExec(n.tnext)
|
||||
dest := genValue(n)
|
||||
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
if value(f).IsNil() {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
if c0.typ.cat == interfaceT {
|
||||
n.exec = func(f *frame) bltn {
|
||||
if (value(f).Interface().(valueInterface) == valueInterface{}) {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
if value(f).IsNil() {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).SetBool(value(f).IsNil())
|
||||
return tnext
|
||||
if c0.typ.cat == interfaceT {
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).SetBool(value(f).Interface().(valueInterface) == valueInterface{})
|
||||
return tnext
|
||||
}
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).SetBool(value(f).IsNil())
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func isNotNil(n *node) {
|
||||
var value func(*frame) reflect.Value
|
||||
if n.child[0].typ.cat == funcT {
|
||||
value = genValueAsFunctionWrapper(n.child[0])
|
||||
c0 := n.child[0]
|
||||
if c0.typ.cat == funcT {
|
||||
value = genValueAsFunctionWrapper(c0)
|
||||
} else {
|
||||
value = genValue(n.child[0])
|
||||
value = genValue(c0)
|
||||
}
|
||||
tnext := getExec(n.tnext)
|
||||
dest := genValue(n)
|
||||
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
if value(f).IsNil() {
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
if c0.typ.cat == interfaceT {
|
||||
n.exec = func(f *frame) bltn {
|
||||
if (value(f).Interface().(valueInterface) == valueInterface{}) {
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
if value(f).IsNil() {
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).SetBool(!value(f).IsNil())
|
||||
return tnext
|
||||
if c0.typ.cat == interfaceT {
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).SetBool(!(value(f).Interface().(valueInterface) == valueInterface{}))
|
||||
return tnext
|
||||
}
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).SetBool(!value(f).IsNil())
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ func (interp *Interpreter) importSrc(rPath, path string) error {
|
||||
var root *node
|
||||
var pkgName string
|
||||
|
||||
// Parse source files
|
||||
// Parse source files.
|
||||
for _, file := range files {
|
||||
name := file.Name()
|
||||
if skipFile(&interp.context, name) {
|
||||
@@ -86,12 +86,10 @@ func (interp *Interpreter) importSrc(rPath, path string) error {
|
||||
revisit[subRPath] = append(revisit[subRPath], list...)
|
||||
}
|
||||
|
||||
// revisit incomplete nodes where GTA could not complete
|
||||
// Revisit incomplete nodes where GTA could not complete.
|
||||
for pkg, nodes := range revisit {
|
||||
for _, n := range nodes {
|
||||
if _, err = interp.gta(n, pkg, path); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = interp.gtaRetry(nodes, pkg, path); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
259
interp/type.go
259
interp/type.go
@@ -111,6 +111,7 @@ type itype struct {
|
||||
size int // Size of array if ArrayT
|
||||
rtype reflect.Type // Reflection type if ValueT, or nil
|
||||
incomplete bool // true if type must be parsed again (out of order declarations)
|
||||
recursive bool // true if the type has an element which refer to itself
|
||||
untyped bool // true for a literal value (string or number)
|
||||
sizedef bool // true if array size is computed from type definition
|
||||
isBinMethod bool // true if the type refers to a bin method function
|
||||
@@ -139,7 +140,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
}
|
||||
}
|
||||
|
||||
var err cfgError
|
||||
var err error
|
||||
switch n.kind {
|
||||
case addressExpr, starExpr:
|
||||
t.cat = ptrT
|
||||
@@ -277,7 +278,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
case isFloat64(t0) && isFloat64(t1):
|
||||
t = sc.getType("complex128")
|
||||
case nt0.untyped && isNumber(t0) && nt1.untyped && isNumber(t1):
|
||||
t = &itype{cat: valueT, rtype: complexType}
|
||||
t = &itype{cat: valueT, rtype: complexType, scope: sc}
|
||||
case nt0.untyped && isFloat32(t1) || nt1.untyped && isFloat32(t0):
|
||||
t = sc.getType("complex64")
|
||||
case nt0.untyped && isFloat64(t1) || nt1.untyped && isFloat64(t0):
|
||||
@@ -300,7 +301,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
case k == reflect.Complex128:
|
||||
t = sc.getType("float64")
|
||||
case t.untyped && isNumber(t.TypeOf()):
|
||||
t = &itype{cat: valueT, rtype: floatType, untyped: true}
|
||||
t = &itype{cat: valueT, rtype: floatType, untyped: true, scope: sc}
|
||||
default:
|
||||
err = n.cfgErrorf("invalid complex type %s", k)
|
||||
}
|
||||
@@ -311,7 +312,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
t, err = nodeType(interp, sc, n.child[1])
|
||||
case "new":
|
||||
t, err = nodeType(interp, sc, n.child[1])
|
||||
t = &itype{cat: ptrT, val: t, incomplete: t.incomplete}
|
||||
t = &itype{cat: ptrT, val: t, incomplete: t.incomplete, scope: sc}
|
||||
case "recover":
|
||||
t = sc.getType("interface{}")
|
||||
}
|
||||
@@ -325,7 +326,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
switch t.cat {
|
||||
case valueT:
|
||||
if t.rtype.NumOut() == 1 {
|
||||
t = &itype{cat: valueT, rtype: t.rtype.Out(0)}
|
||||
t = &itype{cat: valueT, rtype: t.rtype.Out(0), scope: sc}
|
||||
}
|
||||
default:
|
||||
if len(t.ret) == 1 {
|
||||
@@ -406,6 +407,20 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
sc.sym[n.ident] = &symbol{kind: typeSym, typ: t}
|
||||
}
|
||||
|
||||
case indexExpr:
|
||||
var lt *itype
|
||||
if lt, err = nodeType(interp, sc, n.child[0]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if lt.incomplete {
|
||||
t.incomplete = true
|
||||
break
|
||||
}
|
||||
switch lt.cat {
|
||||
case arrayT, mapT:
|
||||
t = lt.val
|
||||
}
|
||||
|
||||
case interfaceType:
|
||||
t.cat = interfaceT
|
||||
if sname := typeName(n); sname != "" {
|
||||
@@ -482,11 +497,11 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
if m, _ := lt.lookupMethod(name); m != nil {
|
||||
t, err = nodeType(interp, sc, m.child[2])
|
||||
} else if bm, _, _, ok := lt.lookupBinMethod(name); ok {
|
||||
t = &itype{cat: valueT, rtype: bm.Type, isBinMethod: true}
|
||||
t = &itype{cat: valueT, rtype: bm.Type, isBinMethod: true, scope: sc}
|
||||
} else if ti := lt.lookupField(name); len(ti) > 0 {
|
||||
t = lt.fieldSeq(ti)
|
||||
} else if bs, _, ok := lt.lookupBinField(name); ok {
|
||||
t = &itype{cat: valueT, rtype: bs.Type}
|
||||
t = &itype{cat: valueT, rtype: bs.Type, scope: sc}
|
||||
} else {
|
||||
err = lt.node.cfgErrorf("undefined selector %s", name)
|
||||
}
|
||||
@@ -612,13 +627,18 @@ func init() {
|
||||
zeroValues[uintptrT] = reflect.ValueOf(uintptr(0))
|
||||
}
|
||||
|
||||
// if type is incomplete, re-parse it.
|
||||
// Finalize returns a type pointer and error. It reparses a type from the
|
||||
// partial AST if necessary (after missing dependecy data is available).
|
||||
// If error is nil, the type is guarranteed to be completely defined and
|
||||
// usable for CFG.
|
||||
func (t *itype) finalize() (*itype, error) {
|
||||
var err cfgError
|
||||
var err error
|
||||
if t.incomplete {
|
||||
sym, _, found := t.scope.lookup(t.name)
|
||||
if found && !sym.typ.incomplete {
|
||||
sym.typ.method = append(sym.typ.method, t.method...)
|
||||
t.method = sym.typ.method
|
||||
t.incomplete = false
|
||||
return sym.typ, nil
|
||||
}
|
||||
m := t.method
|
||||
@@ -637,6 +657,99 @@ func (t *itype) finalize() (*itype, error) {
|
||||
return t, err
|
||||
}
|
||||
|
||||
// ReferTo returns true if the type contains a reference to a
|
||||
// full type name. It allows to asses a type recursive status.
|
||||
func (t *itype) referTo(name string, seen map[*itype]bool) bool {
|
||||
if t.path+"/"+t.name == name {
|
||||
return true
|
||||
}
|
||||
if seen[t] {
|
||||
return false
|
||||
}
|
||||
seen[t] = true
|
||||
switch t.cat {
|
||||
case aliasT, arrayT, chanT, ptrT:
|
||||
return t.val.referTo(name, seen)
|
||||
case funcT:
|
||||
for _, a := range t.arg {
|
||||
if a.referTo(name, seen) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, a := range t.ret {
|
||||
if a.referTo(name, seen) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
case mapT:
|
||||
return t.key.referTo(name, seen) || t.val.referTo(name, seen)
|
||||
case structT, interfaceT:
|
||||
for _, f := range t.field {
|
||||
if f.typ.referTo(name, seen) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsRecursive returns true if type is recursive.
|
||||
// Only a named struct or interface can be recursive.
|
||||
func (t *itype) isRecursive() bool {
|
||||
if t.name == "" {
|
||||
return false
|
||||
}
|
||||
switch t.cat {
|
||||
case structT, interfaceT:
|
||||
for _, f := range t.field {
|
||||
if f.typ.referTo(t.path+"/"+t.name, map[*itype]bool{}) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isComplete returns true if type definition is complete.
|
||||
func (t *itype) isComplete() bool { return isComplete(t, map[string]bool{}) }
|
||||
|
||||
func isComplete(t *itype, visited map[string]bool) bool {
|
||||
if t.incomplete {
|
||||
return false
|
||||
}
|
||||
name := t.path + "/" + t.name
|
||||
if visited[name] {
|
||||
return !t.incomplete
|
||||
}
|
||||
if t.name != "" {
|
||||
visited[name] = true
|
||||
}
|
||||
switch t.cat {
|
||||
case aliasT, arrayT, chanT, ptrT:
|
||||
return isComplete(t.val, visited)
|
||||
case funcT:
|
||||
complete := true
|
||||
for _, a := range t.arg {
|
||||
complete = complete && isComplete(a, visited)
|
||||
}
|
||||
for _, a := range t.ret {
|
||||
complete = complete && isComplete(a, visited)
|
||||
}
|
||||
return complete
|
||||
case interfaceT, structT:
|
||||
complete := true
|
||||
for _, f := range t.field {
|
||||
complete = complete && isComplete(f.typ, visited)
|
||||
}
|
||||
return complete
|
||||
case mapT:
|
||||
return isComplete(t.key, visited) && isComplete(t.val, visited)
|
||||
case nilT:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Equals returns true if the given type is identical to the receiver one.
|
||||
func (t *itype) equals(o *itype) bool {
|
||||
switch ti, oi := isInterface(t), isInterface(o); {
|
||||
@@ -731,7 +844,7 @@ func (t *itype) zero() (v reflect.Value, err error) {
|
||||
v, err = t.val.zero()
|
||||
|
||||
case arrayT, ptrT, structT:
|
||||
v = reflect.New(t.TypeOf()).Elem()
|
||||
v = reflect.New(t.frameType()).Elem()
|
||||
|
||||
case valueT:
|
||||
v = reflect.New(t.rtype).Elem()
|
||||
@@ -744,7 +857,8 @@ func (t *itype) zero() (v reflect.Value, err error) {
|
||||
|
||||
// fieldIndex returns the field index from name in a struct, or -1 if not found
|
||||
func (t *itype) fieldIndex(name string) int {
|
||||
if t.cat == ptrT {
|
||||
switch t.cat {
|
||||
case aliasT, ptrT:
|
||||
return t.val.fieldIndex(name)
|
||||
}
|
||||
for i, field := range t.field {
|
||||
@@ -769,6 +883,10 @@ func (t *itype) fieldSeq(seq []int) *itype {
|
||||
|
||||
// lookupField returns a list of indices, i.e. a path to access a field in a struct object
|
||||
func (t *itype) lookupField(name string) []int {
|
||||
switch t.cat {
|
||||
case aliasT, ptrT:
|
||||
return t.val.lookupField(name)
|
||||
}
|
||||
if fi := t.fieldIndex(name); fi >= 0 {
|
||||
return []int{fi}
|
||||
}
|
||||
@@ -807,7 +925,7 @@ func (t *itype) lookupBinField(name string) (s reflect.StructField, index []int,
|
||||
return s, index, ok
|
||||
}
|
||||
|
||||
// methodCallType returns a method function type without the receiver defined.
|
||||
// MethodCallType returns a method function type without the receiver defined.
|
||||
// The input type must be a method function type with the receiver as the first input argument.
|
||||
func (t *itype) methodCallType() reflect.Type {
|
||||
it := []reflect.Type{}
|
||||
@@ -823,7 +941,7 @@ func (t *itype) methodCallType() reflect.Type {
|
||||
return reflect.FuncOf(it, ot, t.rtype.IsVariadic())
|
||||
}
|
||||
|
||||
// getMethod returns a pointer to the method definition
|
||||
// GetMethod returns a pointer to the method definition.
|
||||
func (t *itype) getMethod(name string) *node {
|
||||
for _, m := range t.method {
|
||||
if name == m.ident {
|
||||
@@ -833,8 +951,8 @@ func (t *itype) getMethod(name string) *node {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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) {
|
||||
if t.cat == ptrT {
|
||||
return t.val.lookupMethod(name)
|
||||
@@ -854,14 +972,12 @@ func (t *itype) lookupMethod(name string) (*node, []int) {
|
||||
return m, index
|
||||
}
|
||||
|
||||
// lookupBinMethod returns a method and a path to access a field in a struct object (the receiver)
|
||||
func (t *itype) lookupBinMethod(name string) (reflect.Method, []int, bool, bool) {
|
||||
var isPtr bool
|
||||
// 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 bool, ok bool) {
|
||||
if t.cat == ptrT {
|
||||
return t.val.lookupBinMethod(name)
|
||||
}
|
||||
var index []int
|
||||
m, ok := t.TypeOf().MethodByName(name)
|
||||
m, ok = t.TypeOf().MethodByName(name)
|
||||
if !ok {
|
||||
m, ok = reflect.PtrTo(t.TypeOf()).MethodByName(name)
|
||||
isPtr = ok
|
||||
@@ -886,63 +1002,98 @@ func exportName(s string) string {
|
||||
return "X" + s
|
||||
}
|
||||
|
||||
var interf = reflect.TypeOf(new(interface{})).Elem()
|
||||
|
||||
func (t *itype) refType(defined map[string]bool) reflect.Type {
|
||||
if t.rtype != nil {
|
||||
return t.rtype
|
||||
}
|
||||
var interf = reflect.TypeOf((*interface{})(nil)).Elem()
|
||||
|
||||
// RefType returns a reflect.Type representation from an interpereter 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, an interface{} is returned in place to
|
||||
// avoid infinitely nested structs.
|
||||
func (t *itype) refType(defined map[string]*itype, wrapRecursive bool) reflect.Type {
|
||||
if t.incomplete || t.cat == nilT {
|
||||
var err error
|
||||
if t, err = t.finalize(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
if t.val != nil && defined[t.val.name] && !t.val.incomplete && t.val.rtype == nil {
|
||||
recursive := false
|
||||
name := t.path + "/" + t.name
|
||||
// Predefined types from universe or runtime may have a nil scope.
|
||||
if t.scope != nil {
|
||||
if st := t.scope.sym[t.name]; st != nil {
|
||||
// Update the type recursive status. Several copies of type
|
||||
// may exist per symbol, as a new type is created at each GTA
|
||||
// pass (several needed due to out of order declarations), and
|
||||
// a node can still point to a previous copy.
|
||||
st.typ.recursive = st.typ.recursive || st.typ.isRecursive()
|
||||
recursive = st.typ.isRecursive()
|
||||
}
|
||||
}
|
||||
if wrapRecursive && t.recursive {
|
||||
return interf
|
||||
}
|
||||
if t.rtype != nil {
|
||||
return t.rtype
|
||||
}
|
||||
if defined[name] != nil && defined[name].rtype != nil {
|
||||
return defined[name].rtype
|
||||
}
|
||||
if t.val != nil && defined[t.val.path+"/"+t.val.name] != nil && t.val.rtype == nil {
|
||||
// Replace reference to self (direct or indirect) by an interface{} to handle
|
||||
// recursive types with reflect.
|
||||
t.val.rtype = interf
|
||||
recursive = true
|
||||
}
|
||||
switch t.cat {
|
||||
case aliasT:
|
||||
t.rtype = t.val.refType(defined)
|
||||
t.rtype = t.val.refType(defined, wrapRecursive)
|
||||
case arrayT, variadicT:
|
||||
if t.sizedef {
|
||||
t.rtype = reflect.ArrayOf(t.size, t.val.refType(defined))
|
||||
t.rtype = reflect.ArrayOf(t.size, t.val.refType(defined, wrapRecursive))
|
||||
} else {
|
||||
t.rtype = reflect.SliceOf(t.val.refType(defined))
|
||||
t.rtype = reflect.SliceOf(t.val.refType(defined, wrapRecursive))
|
||||
}
|
||||
case chanT:
|
||||
t.rtype = reflect.ChanOf(reflect.BothDir, t.val.refType(defined))
|
||||
t.rtype = reflect.ChanOf(reflect.BothDir, t.val.refType(defined, wrapRecursive))
|
||||
case errorT:
|
||||
t.rtype = reflect.TypeOf(new(error)).Elem()
|
||||
case funcT:
|
||||
if t.name != "" {
|
||||
defined[name] = t
|
||||
}
|
||||
in := make([]reflect.Type, len(t.arg))
|
||||
out := make([]reflect.Type, len(t.ret))
|
||||
for i, v := range t.arg {
|
||||
in[i] = v.refType(defined)
|
||||
in[i] = v.refType(defined, true)
|
||||
}
|
||||
for i, v := range t.ret {
|
||||
out[i] = v.refType(defined)
|
||||
out[i] = v.refType(defined, true)
|
||||
}
|
||||
t.rtype = reflect.FuncOf(in, out, false)
|
||||
case interfaceT:
|
||||
t.rtype = interf
|
||||
case mapT:
|
||||
t.rtype = reflect.MapOf(t.key.refType(defined), t.val.refType(defined))
|
||||
t.rtype = reflect.MapOf(t.key.refType(defined, wrapRecursive), t.val.refType(defined, wrapRecursive))
|
||||
case ptrT:
|
||||
t.rtype = reflect.PtrTo(t.val.refType(defined))
|
||||
t.rtype = reflect.PtrTo(t.val.refType(defined, wrapRecursive))
|
||||
case structT:
|
||||
if t.name != "" {
|
||||
defined[t.name] = true
|
||||
if defined[name] != nil {
|
||||
recursive = true
|
||||
}
|
||||
defined[name] = t
|
||||
}
|
||||
var fields []reflect.StructField
|
||||
for _, f := range t.field {
|
||||
field := reflect.StructField{Name: exportName(f.name), Type: f.typ.refType(defined), Tag: reflect.StructTag(f.tag)}
|
||||
field := reflect.StructField{Name: exportName(f.name), Type: f.typ.refType(defined, wrapRecursive), Tag: reflect.StructTag(f.tag)}
|
||||
fields = append(fields, field)
|
||||
}
|
||||
t.rtype = reflect.StructOf(fields)
|
||||
if recursive && wrapRecursive {
|
||||
t.rtype = interf
|
||||
} else {
|
||||
t.rtype = reflect.StructOf(fields)
|
||||
}
|
||||
default:
|
||||
if z, _ := t.zero(); z.IsValid() {
|
||||
t.rtype = z.Type()
|
||||
@@ -953,7 +1104,7 @@ func (t *itype) refType(defined map[string]bool) reflect.Type {
|
||||
|
||||
// TypeOf returns the reflection type of dynamic interpreter type t.
|
||||
func (t *itype) TypeOf() reflect.Type {
|
||||
return t.refType(map[string]bool{})
|
||||
return t.refType(map[string]*itype{}, false)
|
||||
}
|
||||
|
||||
func (t *itype) frameType() (r reflect.Type) {
|
||||
@@ -1006,6 +1157,24 @@ func isShiftNode(n *node) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// chanElement returns the channel element type.
|
||||
func chanElement(t *itype) *itype {
|
||||
switch t.cat {
|
||||
case aliasT:
|
||||
return chanElement(t.val)
|
||||
case chanT:
|
||||
return t.val
|
||||
case valueT:
|
||||
return &itype{cat: valueT, rtype: t.rtype.Elem(), node: t.node, scope: t.scope}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// isChan returns true if type is of channel kind.
|
||||
func isChan(t *itype) bool {
|
||||
return t.TypeOf().Kind() == reflect.Chan
|
||||
}
|
||||
|
||||
func isInterfaceSrc(t *itype) bool {
|
||||
return t.cat == interfaceT || (t.cat == aliasT && isInterfaceSrc(t.val))
|
||||
}
|
||||
@@ -1017,7 +1186,17 @@ func isInterface(t *itype) bool {
|
||||
func isStruct(t *itype) bool {
|
||||
// Test first for a struct category, because a recursive interpreter struct may be
|
||||
// represented by an interface{} at reflect level.
|
||||
return t.cat == structT || t.TypeOf().Kind() == reflect.Struct
|
||||
switch t.cat {
|
||||
case structT:
|
||||
return true
|
||||
case aliasT, ptrT:
|
||||
return isStruct(t.val)
|
||||
case valueT:
|
||||
k := t.rtype.Kind()
|
||||
return k == reflect.Struct || (k == reflect.Ptr && t.rtype.Elem().Kind() == reflect.Struct)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func isBool(t *itype) bool { return t.TypeOf().Kind() == reflect.Bool }
|
||||
|
||||
@@ -164,7 +164,7 @@ func genValueInterface(n *node) func(*frame) reflect.Value {
|
||||
return func(f *frame) reflect.Value {
|
||||
v := value(f)
|
||||
nod := n
|
||||
for {
|
||||
for v.IsValid() {
|
||||
// traverse interface indirections to find out concrete type
|
||||
vi, ok := v.Interface().(valueInterface)
|
||||
if !ok {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by 'goexports archive/tar'. DO NOT EDIT.
|
||||
|
||||
// +build go1.12,!go1.13
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by 'goexports archive/zip'. DO NOT EDIT.
|
||||
|
||||
// +build go1.12,!go1.13
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by 'goexports bufio'. DO NOT EDIT.
|
||||
|
||||
// +build go1.12,!go1.13
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by 'goexports bytes'. DO NOT EDIT.
|
||||
|
||||
// +build go1.12,!go1.13
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -54,6 +54,7 @@ func init() {
|
||||
"ToTitleSpecial": reflect.ValueOf(bytes.ToTitleSpecial),
|
||||
"ToUpper": reflect.ValueOf(bytes.ToUpper),
|
||||
"ToUpperSpecial": reflect.ValueOf(bytes.ToUpperSpecial),
|
||||
"ToValidUTF8": reflect.ValueOf(bytes.ToValidUTF8),
|
||||
"Trim": reflect.ValueOf(bytes.Trim),
|
||||
"TrimFunc": reflect.ValueOf(bytes.TrimFunc),
|
||||
"TrimLeft": reflect.ValueOf(bytes.TrimLeft),
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by 'goexports compress/bzip2'. DO NOT EDIT.
|
||||
|
||||
// +build go1.12,!go1.13
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by 'goexports compress/flate'. DO NOT EDIT.
|
||||
|
||||
// +build go1.12,!go1.13
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by 'goexports compress/gzip'. DO NOT EDIT.
|
||||
|
||||
// +build go1.12,!go1.13
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
package stdlib
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by 'goexports compress/lzw'. DO NOT EDIT.
|
||||
|
||||
// +build go1.12,!go1.13
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
package stdlib
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user