Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0cd93a936 | ||
|
|
611a8c37fa | ||
|
|
e71ddc7edd | ||
|
|
1fe75f149d | ||
|
|
cdc352cee2 | ||
|
|
88569f5df7 | ||
|
|
2ac0c6f70b | ||
|
|
bd4ce37baa | ||
|
|
9c4d3d1e5a | ||
|
|
25c681c1e6 | ||
|
|
9c51f6bb69 | ||
|
|
589b2a0cd2 | ||
|
|
68911f8b4e | ||
|
|
e5a7b0de11 | ||
|
|
563270ca02 | ||
|
|
5eecbe515b | ||
|
|
0a79069dfc | ||
|
|
0c8f538cd9 | ||
|
|
ca80ada849 | ||
|
|
3c6df504df | ||
|
|
98eacf3610 | ||
|
|
16ff52a949 | ||
|
|
640d1429e5 | ||
|
|
659913eebe | ||
|
|
b3766509cc | ||
|
|
bc2b224bae | ||
|
|
9d4685deea | ||
|
|
2a70a71dc2 | ||
|
|
851444453c | ||
|
|
a8b1c2a017 | ||
|
|
d229c2a2c7 | ||
|
|
2f2df7a0f8 | ||
|
|
4058fd8c44 | ||
|
|
097a745e72 | ||
|
|
1f514e63a8 | ||
|
|
a15ecb7176 | ||
|
|
d4aa84f729 |
@@ -41,6 +41,8 @@
|
||||
"testpackage",
|
||||
"goerr113",
|
||||
"nestif",
|
||||
"exhaustive",
|
||||
"nlreturn",
|
||||
]
|
||||
|
||||
[issues]
|
||||
|
||||
@@ -19,8 +19,8 @@ cache:
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- go: 1.13.x
|
||||
- go: 1.14.x
|
||||
- go: 1.15.x
|
||||
env: STABLE=true
|
||||
|
||||
env:
|
||||
|
||||
25
_test/addr0.go
Normal file
25
_test/addr0.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type extendedRequest struct {
|
||||
http.Request
|
||||
|
||||
Data string
|
||||
}
|
||||
|
||||
func main() {
|
||||
r := extendedRequest{}
|
||||
req := &r.Request
|
||||
|
||||
|
||||
fmt.Println(r)
|
||||
fmt.Println(req)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {{ <nil> 0 0 map[] <nil> <nil> 0 [] false map[] map[] <nil> map[] <nil> <nil> <nil> <nil>} }
|
||||
// &{ <nil> 0 0 map[] <nil> <nil> 0 [] false map[] map[] <nil> map[] <nil> <nil> <nil> <nil>}
|
||||
16
_test/assign14.go
Normal file
16
_test/assign14.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
var optionsG map[string]string = nil
|
||||
|
||||
var roundG = 30
|
||||
|
||||
func main() {
|
||||
dummy := roundG
|
||||
roundG = dummy + 1
|
||||
println(roundG)
|
||||
println(optionsG == nil)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 31
|
||||
// true
|
||||
11
_test/assign15.go
Normal file
11
_test/assign15.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var c chan<- struct{} = make(chan struct{})
|
||||
var d <-chan struct{} = c
|
||||
|
||||
_ = d
|
||||
}
|
||||
|
||||
// Error:
|
||||
// _test/assign15.go:5:26: cannot use type chan<- struct{} as type <-chan struct{} in assignment
|
||||
14
_test/comp2.go
Normal file
14
_test/comp2.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
type delta int32
|
||||
|
||||
func main() {
|
||||
a := delta(-1)
|
||||
|
||||
println(a != -1)
|
||||
println(a == -1)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// false
|
||||
// true
|
||||
15
_test/composite11.go
Normal file
15
_test/composite11.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image/color"
|
||||
)
|
||||
|
||||
func main() {
|
||||
c := color.NRGBA64{1, 1, 1, 1}
|
||||
|
||||
fmt.Println(c)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {1 1 1 1}
|
||||
17
_test/const15.go
Normal file
17
_test/const15.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
type T1 t1
|
||||
|
||||
type t1 int8
|
||||
|
||||
const (
|
||||
P2 T1 = 2
|
||||
P3 T1 = 3
|
||||
)
|
||||
|
||||
func main() {
|
||||
println(P3)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 3
|
||||
12
_test/init1.go
Normal file
12
_test/init1.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
func init() {
|
||||
println("here")
|
||||
}
|
||||
|
||||
func main() {
|
||||
init()
|
||||
}
|
||||
|
||||
// Error:
|
||||
// _test/init1.go:8:2: undefined: init
|
||||
19
_test/interface44.go
Normal file
19
_test/interface44.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
type S struct {
|
||||
a int
|
||||
}
|
||||
|
||||
func main() {
|
||||
var i interface{} = S{a: 1}
|
||||
|
||||
s, ok := i.(S)
|
||||
if !ok {
|
||||
println("bad")
|
||||
return
|
||||
}
|
||||
println(s.a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
13
_test/interface45.go
Normal file
13
_test/interface45.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
var i interface{} = 1
|
||||
var s struct{}
|
||||
s, _ = i.(struct{})
|
||||
fmt.Println(s)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {}
|
||||
35
_test/interface46.go
Normal file
35
_test/interface46.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type I interface {
|
||||
Foo() string
|
||||
}
|
||||
|
||||
type Printer struct {
|
||||
i I
|
||||
}
|
||||
|
||||
func New(i I) *Printer {
|
||||
return &Printer{
|
||||
i: i,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Printer) Print() {
|
||||
fmt.Println(p.i.Foo())
|
||||
}
|
||||
|
||||
type T struct{}
|
||||
|
||||
func (t *T) Foo() string {
|
||||
return "test"
|
||||
}
|
||||
|
||||
func main() {
|
||||
g := New(&T{})
|
||||
g.Print()
|
||||
}
|
||||
|
||||
// Output:
|
||||
// test
|
||||
29
_test/issue-735.go
Normal file
29
_test/issue-735.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var optionsG map[string]string
|
||||
|
||||
var roundG int = 30
|
||||
|
||||
func strToInt(s string, defaultValue int) int {
|
||||
n, err := strconv.ParseInt(s, 10, 0)
|
||||
if err != nil {
|
||||
return defaultValue
|
||||
}
|
||||
return int(n)
|
||||
}
|
||||
|
||||
func main() {
|
||||
optionsG := map[string]string{"round": "12", "b": "one"}
|
||||
roundG = strToInt(optionsG["round"], 50)
|
||||
fmt.Println(roundG)
|
||||
fmt.Println(optionsG)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 12
|
||||
// map[b:one round:12]
|
||||
25
_test/issue-770.go
Normal file
25
_test/issue-770.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import "reflect"
|
||||
|
||||
type I interface {
|
||||
Foo() int
|
||||
}
|
||||
|
||||
type T struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (t T) Foo() int { return 0 }
|
||||
|
||||
func f(v reflect.Value) int {
|
||||
i := v.Interface().(I)
|
||||
return i.Foo()
|
||||
}
|
||||
|
||||
func main() {
|
||||
println("hello")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
31
_test/issue-772.go
Normal file
31
_test/issue-772.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
Data string
|
||||
}
|
||||
|
||||
func main() {
|
||||
tmpl := template.New("name")
|
||||
|
||||
_, err := tmpl.Parse("{{.Data}}")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = tmpl.Execute(os.Stdout, Message{
|
||||
Data: "Hello, World!!",
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Hello, World!!
|
||||
18
_test/issue-775.go
Normal file
18
_test/issue-775.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http/httptest"
|
||||
)
|
||||
|
||||
func main() {
|
||||
recorder := httptest.NewRecorder()
|
||||
recorder.Header().Add("Foo", "Bar")
|
||||
|
||||
for key, value := range recorder.Header() {
|
||||
fmt.Println(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Foo [Bar]
|
||||
39
_test/issue-776.go
Normal file
39
_test/issue-776.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Filter is a filter
|
||||
type Filter interface {
|
||||
Foo()
|
||||
}
|
||||
|
||||
// GIFT is a gift
|
||||
type GIFT struct {
|
||||
Filters []Filter
|
||||
}
|
||||
|
||||
// New is a new filter list
|
||||
func New(filters ...Filter) *GIFT {
|
||||
return &GIFT{
|
||||
Filters: filters,
|
||||
}
|
||||
}
|
||||
|
||||
// List lists filters
|
||||
func (g *GIFT) List() {
|
||||
fmt.Printf("Hello from List!\n")
|
||||
}
|
||||
|
||||
// MyFilter is one of the filters
|
||||
type MyFilter struct{}
|
||||
|
||||
// Foo is a foo
|
||||
func (f *MyFilter) Foo() {}
|
||||
|
||||
func main() {
|
||||
g := New(&MyFilter{})
|
||||
g.List()
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Hello from List!
|
||||
14
_test/issue-782.go
Normal file
14
_test/issue-782.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
from := uint32(2)
|
||||
to := uint32(4)
|
||||
b := a[from:to]
|
||||
fmt.Print(b)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// [3 4]
|
||||
39
_test/issue-784.go
Normal file
39
_test/issue-784.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Filter is a filter interface
|
||||
type Filter interface {
|
||||
Bounds(srcBounds string) (dstBounds string)
|
||||
}
|
||||
|
||||
// GIFT type
|
||||
type GIFT struct {
|
||||
Filters []Filter
|
||||
}
|
||||
|
||||
// New creates a new filter list and initializes it with the given slice of filters.
|
||||
func New(filters ...Filter) *GIFT {
|
||||
return &GIFT{
|
||||
Filters: filters,
|
||||
}
|
||||
}
|
||||
|
||||
// Bounds calculates the appropriate bounds for the result image after applying all the added filters.
|
||||
func (g *GIFT) Bounds(srcBounds string) (dstBounds string) {
|
||||
dstBounds = srcBounds
|
||||
for _, f := range g.Filters {
|
||||
dstBounds = f.Bounds(dstBounds)
|
||||
}
|
||||
return dstBounds
|
||||
}
|
||||
|
||||
func main() {
|
||||
var filters []Filter
|
||||
bounds := "foo"
|
||||
g := New(filters...)
|
||||
fmt.Println(g.Bounds(bounds))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// foo
|
||||
12
_test/math2.go
Normal file
12
_test/math2.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
const c uint64 = 2
|
||||
|
||||
func main() {
|
||||
if c&(1<<(uint64(1))) > 0 {
|
||||
println("yes")
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// yes
|
||||
32
_test/math3.go
Normal file
32
_test/math3.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func md5Crypt(password, salt, magic []byte) []byte {
|
||||
d := md5.New()
|
||||
d.Write(password)
|
||||
d.Write(magic)
|
||||
d.Write(salt)
|
||||
|
||||
d2 := md5.New()
|
||||
d2.Write(password)
|
||||
d2.Write(salt)
|
||||
|
||||
for i, mixin := 0, d2.Sum(nil); i < len(password); i++ {
|
||||
d.Write([]byte{mixin[i%16]})
|
||||
}
|
||||
|
||||
return d.Sum(nil)
|
||||
}
|
||||
|
||||
func main() {
|
||||
b := md5Crypt([]byte("1"), []byte("2"), []byte("3"))
|
||||
|
||||
fmt.Println(b)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// [187 141 73 89 101 229 33 106 226 63 117 234 117 149 230 21]
|
||||
@@ -7,4 +7,4 @@ func main() {
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 5:2: illegal operand types for '+=' operator
|
||||
// 5:2: invalid operation: mismatched types int and float64
|
||||
|
||||
18
_test/restricted0.go
Normal file
18
_test/restricted0.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
defer func() {
|
||||
r := recover()
|
||||
fmt.Println("recover:", r)
|
||||
}()
|
||||
log.Fatal("log.Fatal does not exit")
|
||||
println("not printed")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// recover: log.Fatal does not exit
|
||||
18
_test/restricted1.go
Normal file
18
_test/restricted1.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
defer func() {
|
||||
r := recover()
|
||||
fmt.Println("recover:", r)
|
||||
}()
|
||||
os.Exit(1)
|
||||
println("not printed")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// recover: os.Exit(1)
|
||||
14
_test/restricted2.go
Normal file
14
_test/restricted2.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
p, err := os.FindProcess(os.Getpid())
|
||||
fmt.Println(p, err)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// <nil> restricted
|
||||
23
_test/restricted3.go
Normal file
23
_test/restricted3.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
var (
|
||||
buf bytes.Buffer
|
||||
logger = log.New(&buf, "logger: ", log.Lshortfile)
|
||||
)
|
||||
|
||||
func main() {
|
||||
defer func() {
|
||||
r := recover()
|
||||
fmt.Println("recover:", r, buf.String())
|
||||
}()
|
||||
logger.Fatal("test log")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// recover: test log logger: restricted.go:39: test log
|
||||
12
_test/rune1.go
Normal file
12
_test/rune1.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
t := make([]byte, 2)
|
||||
t[0] = '$'
|
||||
fmt.Println(t)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// [36 0]
|
||||
@@ -1,15 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
)
|
||||
|
||||
type S struct {
|
||||
Child []*S
|
||||
Name string
|
||||
Child []*S
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := &S{Name: "root"}
|
||||
s.Child = append(s.Child, &S{Name: "child"})
|
||||
println(s.Child[0].Name)
|
||||
a := S{Name: "hello"}
|
||||
a.Child = append(a.Child, &S{Name: "world"})
|
||||
json.NewEncoder(os.Stdout).Encode(a)
|
||||
a.Child[0].Child = append([]*S{}, &S{Name: "sunshine"})
|
||||
json.NewEncoder(os.Stdout).Encode(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// child
|
||||
// {"Name":"hello","Child":[{"Name":"world","Child":null}]}
|
||||
// {"Name":"hello","Child":[{"Name":"world","Child":[{"Name":"sunshine","Child":null}]}]}
|
||||
|
||||
33
_test/struct49.go
Normal file
33
_test/struct49.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
type S struct {
|
||||
ts map[string][]*T
|
||||
}
|
||||
|
||||
type T struct {
|
||||
s *S
|
||||
}
|
||||
|
||||
func (c *S) getT(addr string) (t *T, ok bool) {
|
||||
cns, ok := c.ts[addr]
|
||||
if !ok || len(cns) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
t = cns[len(cns)-1]
|
||||
c.ts[addr] = cns[:len(cns)-1]
|
||||
return t, true
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := &S{
|
||||
ts: map[string][]*T{},
|
||||
}
|
||||
s.ts["test"] = append(s.ts["test"], &T{s: s})
|
||||
|
||||
t , ok:= s.getT("test")
|
||||
println(t != nil, ok)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true true
|
||||
20
_test/struct50.go
Normal file
20
_test/struct50.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Node struct {
|
||||
Name string
|
||||
Child []Node
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := Node{Name: "hello"}
|
||||
a.Child = append([]Node{}, Node{Name: "world"})
|
||||
fmt.Println(a)
|
||||
a.Child[0].Child = append([]Node{}, Node{Name: "sunshine"})
|
||||
fmt.Println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {hello [{world []}]}
|
||||
// {hello [{world [{sunshine []}]}]}
|
||||
23
_test/struct51.go
Normal file
23
_test/struct51.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
Name string
|
||||
Child [2]*Node
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := Node{Name: "hello"}
|
||||
a.Child[0] = &Node{Name: "world"}
|
||||
json.NewEncoder(os.Stdout).Encode(a)
|
||||
a.Child[0].Child[0] = &Node{Name: "sunshine"}
|
||||
json.NewEncoder(os.Stdout).Encode(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {"Name":"hello","Child":[{"Name":"world","Child":[null,null]},null]}
|
||||
// {"Name":"hello","Child":[{"Name":"world","Child":[{"Name":"sunshine","Child":[null,null]},null]},null]}
|
||||
20
_test/struct52.go
Normal file
20
_test/struct52.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Node struct {
|
||||
Name string
|
||||
Child map[string]Node
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := Node{Name: "hello", Child: map[string]Node{}}
|
||||
a.Child["1"] = Node{Name: "world", Child: map[string]Node{}}
|
||||
fmt.Println(a)
|
||||
a.Child["1"].Child["1"] = Node{Name: "sunshine", Child: map[string]Node{}}
|
||||
fmt.Println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {hello map[1:{world map[]}]}
|
||||
// {hello map[1:{world map[1:{sunshine map[]}]}]}
|
||||
23
_test/struct53.go
Normal file
23
_test/struct53.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type T1 struct {
|
||||
P []*T
|
||||
}
|
||||
|
||||
type T2 struct {
|
||||
P2 *T
|
||||
}
|
||||
|
||||
type T struct {
|
||||
*T1
|
||||
S1 *T
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println(T2{})
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {<nil>}
|
||||
26
_test/struct54.go
Normal file
26
_test/struct54.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
type S struct {
|
||||
t *T
|
||||
}
|
||||
|
||||
func newS() *S {
|
||||
return &S{
|
||||
t: &T{u: map[string]*U{}},
|
||||
}
|
||||
}
|
||||
|
||||
type T struct {
|
||||
u map[string]*U
|
||||
}
|
||||
|
||||
type U struct {
|
||||
a int
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := newS()
|
||||
_ = s
|
||||
|
||||
println("ok")
|
||||
}
|
||||
28
_test/switch38.go
Normal file
28
_test/switch38.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
func isSeparator(c byte) bool {
|
||||
switch c {
|
||||
case '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ', '\t':
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := "max-age=20"
|
||||
for _, c := range []byte(s) {
|
||||
println(string(c), isSeparator(c))
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// m false
|
||||
// a false
|
||||
// x false
|
||||
// - false
|
||||
// a false
|
||||
// g false
|
||||
// e false
|
||||
// = true
|
||||
// 2 false
|
||||
// 0 false
|
||||
@@ -1,5 +1,5 @@
|
||||
// The following comment line has the same effect as 'go run -tags=dummy'
|
||||
//yaegi:tags dummy
|
||||
// yaegi:tags dummy
|
||||
|
||||
package main
|
||||
|
||||
|
||||
18
_test/time13.go
Normal file
18
_test/time13.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
var dummy = 1
|
||||
|
||||
var t time.Time = time.Date(2007, time.November, 10, 23, 4, 5, 0, time.UTC)
|
||||
|
||||
func main() {
|
||||
t = time.Date(2009, time.November, 10, 23, 4, 5, 0, time.UTC)
|
||||
fmt.Println(t.Clock())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 23 4 5
|
||||
44
_test/type25.go
Normal file
44
_test/type25.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type wrappedError struct {
|
||||
wrapped error
|
||||
}
|
||||
|
||||
func (e wrappedError) Error() string {
|
||||
return "some outer error"
|
||||
}
|
||||
|
||||
func (e wrappedError) Unwrap() error {
|
||||
return e.wrapped
|
||||
}
|
||||
|
||||
var err atomic.Value
|
||||
|
||||
func getWrapped() *wrappedError {
|
||||
if v := err.Load(); v != nil {
|
||||
err := v.(wrappedError)
|
||||
if err.wrapped != nil {
|
||||
return &err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
err.Store(wrappedError{wrapped: errors.New("test")})
|
||||
|
||||
e := getWrapped()
|
||||
if e != nil {
|
||||
println(e.Error())
|
||||
println(e.wrapped.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// some outer error
|
||||
// test
|
||||
44
_test/type26.go
Normal file
44
_test/type26.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type wrappedError struct {
|
||||
wrapped error
|
||||
}
|
||||
|
||||
func (e *wrappedError) Error() string {
|
||||
return "some outer error"
|
||||
}
|
||||
|
||||
func (e *wrappedError) Unwrap() error {
|
||||
return e.wrapped
|
||||
}
|
||||
|
||||
var err atomic.Value
|
||||
|
||||
func getWrapped() *wrappedError {
|
||||
if v := err.Load(); v != nil {
|
||||
err := v.(*wrappedError)
|
||||
if err.wrapped != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
err.Store(&wrappedError{wrapped: errors.New("test")})
|
||||
|
||||
e := getWrapped()
|
||||
if e != nil {
|
||||
println(e.Error())
|
||||
println(e.wrapped.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// some outer error
|
||||
// test
|
||||
13
_test/var12.go
Normal file
13
_test/var12.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
var (
|
||||
a = b
|
||||
b = "hello"
|
||||
)
|
||||
|
||||
func main() {
|
||||
println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
23
_test/var13.go
Normal file
23
_test/var13.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
var (
|
||||
a = concat("hello", b)
|
||||
b = concat(" ", c, "!")
|
||||
c = d
|
||||
d = "world"
|
||||
)
|
||||
|
||||
func concat(a ...string) string {
|
||||
var s string
|
||||
for _, ss := range a {
|
||||
s += ss
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func main() {
|
||||
println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello world!
|
||||
10
_test/var14.go
Normal file
10
_test/var14.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
import "github.com/containous/yaegi/_test/vars"
|
||||
|
||||
func main() {
|
||||
println(vars.A)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello world!
|
||||
14
_test/variadic9.go
Normal file
14
_test/variadic9.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func Sprintf(format string, a ...interface{}) string {
|
||||
return fmt.Sprintf(format, a...)
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println(Sprintf("Hello %s", "World!"))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Hello World!
|
||||
14
_test/vars/first.go
Normal file
14
_test/vars/first.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package vars
|
||||
|
||||
var (
|
||||
A = concat("hello", B)
|
||||
C = D
|
||||
)
|
||||
|
||||
func concat(a ...string) string {
|
||||
var s string
|
||||
for _, ss := range a {
|
||||
s += ss
|
||||
}
|
||||
return s
|
||||
}
|
||||
6
_test/vars/second.go
Normal file
6
_test/vars/second.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package vars
|
||||
|
||||
var (
|
||||
B = concat(" ", C, "!")
|
||||
D = "world"
|
||||
)
|
||||
@@ -23,272 +23,16 @@ import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"go/format"
|
||||
"go/importer"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/containous/yaegi/extract"
|
||||
)
|
||||
|
||||
const model = `// Code generated by 'goexports {{.PkgName}}'. DO NOT EDIT.
|
||||
|
||||
{{.License}}
|
||||
|
||||
{{if .BuildTags}}// +build {{.BuildTags}}{{end}}
|
||||
|
||||
package {{.Dest}}
|
||||
|
||||
import (
|
||||
{{- range $key, $value := .Imports }}
|
||||
{{- if $value}}
|
||||
"{{$key}}"
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
"{{.PkgName}}"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Symbols["{{.PkgName}}"] = map[string]reflect.Value{
|
||||
{{- if .Val}}
|
||||
// function, constant and variable definitions
|
||||
{{range $key, $value := .Val -}}
|
||||
{{- if $value.Addr -}}
|
||||
"{{$key}}": reflect.ValueOf(&{{$value.Name}}).Elem(),
|
||||
{{else -}}
|
||||
"{{$key}}": reflect.ValueOf({{$value.Name}}),
|
||||
{{end -}}
|
||||
{{end}}
|
||||
|
||||
{{- end}}
|
||||
{{- if .Typ}}
|
||||
// type definitions
|
||||
{{range $key, $value := .Typ -}}
|
||||
"{{$key}}": reflect.ValueOf((*{{$value}})(nil)),
|
||||
{{end}}
|
||||
|
||||
{{- end}}
|
||||
{{- if .Wrap}}
|
||||
// interface wrapper definitions
|
||||
{{range $key, $value := .Wrap -}}
|
||||
"_{{$key}}": reflect.ValueOf((*{{$value.Name}})(nil)),
|
||||
{{end}}
|
||||
{{- end}}
|
||||
}
|
||||
}
|
||||
{{range $key, $value := .Wrap -}}
|
||||
// {{$value.Name}} is an interface wrapper for {{$key}} type
|
||||
type {{$value.Name}} struct {
|
||||
{{range $m := $value.Method -}}
|
||||
W{{$m.Name}} func{{$m.Param}} {{$m.Result}}
|
||||
{{end}}
|
||||
}
|
||||
{{range $m := $value.Method -}}
|
||||
func (W {{$value.Name}}) {{$m.Name}}{{$m.Param}} {{$m.Result}} { {{$m.Ret}} W.W{{$m.Name}}{{$m.Arg}} }
|
||||
{{end}}
|
||||
{{end}}
|
||||
`
|
||||
|
||||
// Val store the value name and addressable status of symbols.
|
||||
type Val struct {
|
||||
Name string // "package.name"
|
||||
Addr bool // true if symbol is a Var
|
||||
}
|
||||
|
||||
// Method store information for generating interface wrapper method.
|
||||
type Method struct {
|
||||
Name, Param, Result, Arg, Ret string
|
||||
}
|
||||
|
||||
// Wrap store information for generating interface wrapper.
|
||||
type Wrap struct {
|
||||
Name string
|
||||
Method []Method
|
||||
}
|
||||
|
||||
func genContent(dest, pkgName, license string, skip map[string]bool) ([]byte, error) {
|
||||
p, err := importer.ForCompiler(token.NewFileSet(), "source", nil).Import(pkgName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
prefix := "_" + pkgName + "_"
|
||||
prefix = strings.NewReplacer("/", "_", "-", "_", ".", "_").Replace(prefix)
|
||||
|
||||
typ := map[string]string{}
|
||||
val := map[string]Val{}
|
||||
wrap := map[string]Wrap{}
|
||||
imports := map[string]bool{}
|
||||
sc := p.Scope()
|
||||
|
||||
for _, pkg := range p.Imports() {
|
||||
imports[pkg.Path()] = false
|
||||
}
|
||||
qualify := func(pkg *types.Package) string {
|
||||
if pkg.Path() != pkgName {
|
||||
imports[pkg.Path()] = true
|
||||
}
|
||||
return pkg.Name()
|
||||
}
|
||||
|
||||
for _, name := range sc.Names() {
|
||||
o := sc.Lookup(name)
|
||||
if !o.Exported() {
|
||||
continue
|
||||
}
|
||||
|
||||
pname := path.Base(pkgName) + "." + name
|
||||
if skip[pname] {
|
||||
continue
|
||||
}
|
||||
|
||||
switch o := o.(type) {
|
||||
case *types.Const:
|
||||
if b, ok := o.Type().(*types.Basic); ok && (b.Info()&types.IsUntyped) != 0 {
|
||||
// convert untyped constant to right type to avoid overflow
|
||||
val[name] = Val{fixConst(pname, o.Val(), imports), false}
|
||||
} else {
|
||||
val[name] = Val{pname, false}
|
||||
}
|
||||
case *types.Func:
|
||||
val[name] = Val{pname, false}
|
||||
case *types.Var:
|
||||
val[name] = Val{pname, true}
|
||||
case *types.TypeName:
|
||||
typ[name] = pname
|
||||
if t, ok := o.Type().Underlying().(*types.Interface); ok {
|
||||
var methods []Method
|
||||
for i := 0; i < t.NumMethods(); i++ {
|
||||
f := t.Method(i)
|
||||
if !f.Exported() {
|
||||
continue
|
||||
}
|
||||
|
||||
sign := f.Type().(*types.Signature)
|
||||
args := make([]string, sign.Params().Len())
|
||||
params := make([]string, len(args))
|
||||
for j := range args {
|
||||
v := sign.Params().At(j)
|
||||
if args[j] = v.Name(); args[j] == "" {
|
||||
args[j] = fmt.Sprintf("a%d", j)
|
||||
}
|
||||
params[j] = args[j] + " " + types.TypeString(v.Type(), qualify)
|
||||
}
|
||||
arg := "(" + strings.Join(args, ", ") + ")"
|
||||
param := "(" + strings.Join(params, ", ") + ")"
|
||||
|
||||
results := make([]string, sign.Results().Len())
|
||||
for j := range results {
|
||||
v := sign.Results().At(j)
|
||||
results[j] = v.Name() + " " + types.TypeString(v.Type(), qualify)
|
||||
}
|
||||
result := "(" + strings.Join(results, ", ") + ")"
|
||||
|
||||
ret := ""
|
||||
if sign.Results().Len() > 0 {
|
||||
ret = "return"
|
||||
}
|
||||
|
||||
methods = append(methods, Method{f.Name(), param, result, arg, ret})
|
||||
}
|
||||
wrap[name] = Wrap{prefix + name, methods}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var buildTags string
|
||||
if runtime.Version() != "devel" {
|
||||
parts := strings.Split(runtime.Version(), ".")
|
||||
|
||||
minorRaw := getMinor(parts[1])
|
||||
|
||||
currentGoVersion := parts[0] + "." + minorRaw
|
||||
|
||||
minor, errParse := strconv.Atoi(minorRaw)
|
||||
if errParse != nil {
|
||||
return nil, fmt.Errorf("failed to parse version: %v", errParse)
|
||||
}
|
||||
|
||||
nextGoVersion := parts[0] + "." + strconv.Itoa(minor+1)
|
||||
|
||||
buildTags = currentGoVersion + ",!" + nextGoVersion
|
||||
}
|
||||
|
||||
base := template.New("goexports")
|
||||
parse, err := base.Parse(model)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("template parsing error: %v", err)
|
||||
}
|
||||
|
||||
if pkgName == "log/syslog" {
|
||||
buildTags += ",!windows,!nacl,!plan9"
|
||||
}
|
||||
|
||||
b := new(bytes.Buffer)
|
||||
data := map[string]interface{}{
|
||||
"Dest": dest,
|
||||
"Imports": imports,
|
||||
"PkgName": pkgName,
|
||||
"Val": val,
|
||||
"Typ": typ,
|
||||
"Wrap": wrap,
|
||||
"BuildTags": buildTags,
|
||||
"License": license,
|
||||
}
|
||||
err = parse.Execute(b, data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("template error: %v", err)
|
||||
}
|
||||
|
||||
// gofmt
|
||||
source, err := format.Source(b.Bytes())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to format source: %v: %s", err, b.Bytes())
|
||||
}
|
||||
return source, nil
|
||||
}
|
||||
|
||||
// fixConst checks untyped constant value, converting it if necessary to avoid overflow.
|
||||
func fixConst(name string, val constant.Value, imports map[string]bool) string {
|
||||
var (
|
||||
tok string
|
||||
str string
|
||||
)
|
||||
switch val.Kind() {
|
||||
case constant.Int:
|
||||
tok = "INT"
|
||||
str = val.ExactString()
|
||||
case constant.Float:
|
||||
v := constant.Val(val) // v is *big.Rat or *big.Float
|
||||
f, ok := v.(*big.Float)
|
||||
if !ok {
|
||||
f = new(big.Float).SetRat(v.(*big.Rat))
|
||||
}
|
||||
|
||||
tok = "FLOAT"
|
||||
str = f.Text('g', int(f.Prec()))
|
||||
case constant.Complex:
|
||||
// TODO: not sure how to parse this case
|
||||
fallthrough
|
||||
default:
|
||||
return name
|
||||
}
|
||||
|
||||
imports["go/constant"] = true
|
||||
imports["go/token"] = true
|
||||
|
||||
return fmt.Sprintf("constant.MakeFromLiteral(\"%s\", token.%s, 0)", str, tok)
|
||||
}
|
||||
|
||||
// genLicense generates the correct LICENSE header text from the provided
|
||||
// path to a LICENSE file.
|
||||
func genLicense(fname string) (string, error) {
|
||||
@@ -318,9 +62,13 @@ func genLicense(fname string) (string, error) {
|
||||
return license.String(), nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
licenseFlag := flag.String("license", "", "path to a LICENSE file")
|
||||
var (
|
||||
licenseFlag = flag.String("license", "", "path to a LICENSE file")
|
||||
// TODO: deal with a module that has several packages (so there's only one go.mod file at the root of the project).
|
||||
importPathFlag = flag.String("import_path", "", "the namespace for the symbols extracted from the argument. Not needed if the argument is from the stdlib, or if the name can be found in a go.mod")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() == 0 {
|
||||
@@ -333,55 +81,57 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
dir, err := os.Getwd()
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
dest := path.Base(dir)
|
||||
|
||||
for _, pkg := range flag.Args() {
|
||||
var oFile string
|
||||
skip := map[string]bool{}
|
||||
if pkg == "syscall" {
|
||||
goos, arch := os.Getenv("GOOS"), os.Getenv("GOARCH")
|
||||
oFile = strings.Replace(pkg, "/", "_", -1) + "_" + goos + "_" + arch + ".go"
|
||||
if goos == "solaris" {
|
||||
skip["syscall.RawSyscall6"] = true
|
||||
skip["syscall.Syscall6"] = true
|
||||
}
|
||||
} else {
|
||||
oFile = strings.Replace(pkg, "/", "_", -1) + ".go"
|
||||
}
|
||||
ext := extract.Extractor{
|
||||
Dest: path.Base(wd),
|
||||
License: license,
|
||||
}
|
||||
goos, goarch := os.Getenv("GOOS"), os.Getenv("GOARCH")
|
||||
skip := map[string]bool{}
|
||||
if goos == "solaris" {
|
||||
skip["syscall.RawSyscall6"] = true
|
||||
skip["syscall.Syscall6"] = true
|
||||
}
|
||||
ext.Skip = skip
|
||||
|
||||
content, err := genContent(dest, pkg, license, skip)
|
||||
for _, pkgIdent := range flag.Args() {
|
||||
var buf bytes.Buffer
|
||||
importPath, err := ext.Extract(pkgIdent, *importPathFlag, &buf)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
var oFile string
|
||||
if pkgIdent == "syscall" {
|
||||
oFile = strings.Replace(importPath, "/", "_", -1) + "_" + goos + "_" + goarch + ".go"
|
||||
} else {
|
||||
oFile = strings.Replace(importPath, "/", "_", -1) + ".go"
|
||||
}
|
||||
|
||||
prefix := runtime.Version()
|
||||
if runtime.Version() != "devel" {
|
||||
parts := strings.Split(runtime.Version(), ".")
|
||||
|
||||
prefix = parts[0] + "_" + getMinor(parts[1])
|
||||
prefix = parts[0] + "_" + extract.GetMinor(parts[1])
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(prefix+"_"+oFile, content, 0666)
|
||||
f, err := os.Create(prefix + "_" + oFile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := io.Copy(f, &buf); err != nil {
|
||||
_ = f.Close()
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := f.Close(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getMinor(part string) string {
|
||||
minor := part
|
||||
index := strings.Index(minor, "beta")
|
||||
if index < 0 {
|
||||
index = strings.Index(minor, "rc")
|
||||
}
|
||||
if index > 0 {
|
||||
minor = minor[:index]
|
||||
}
|
||||
|
||||
return minor
|
||||
}
|
||||
|
||||
107
cmd/yaegi/extract.go
Normal file
107
cmd/yaegi/extract.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/containous/yaegi/extract"
|
||||
)
|
||||
|
||||
func extractCmd(arg []string) error {
|
||||
var licensePath string
|
||||
var importPath string
|
||||
|
||||
eflag := flag.NewFlagSet("run", flag.ContinueOnError)
|
||||
eflag.StringVar(&licensePath, "license", "", "path to a LICENSE file")
|
||||
eflag.StringVar(&importPath, "import_path", "", "the namespace for the extracted symbols")
|
||||
eflag.Usage = func() {
|
||||
fmt.Println("Usage: yaegi extract [options] packages...")
|
||||
fmt.Println("Options:")
|
||||
eflag.PrintDefaults()
|
||||
}
|
||||
|
||||
if err := eflag.Parse(arg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args := eflag.Args()
|
||||
if len(args) == 0 {
|
||||
return fmt.Errorf("missing package")
|
||||
}
|
||||
|
||||
license, err := genLicense(licensePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ext := extract.Extractor{
|
||||
Dest: path.Base(wd),
|
||||
License: license,
|
||||
}
|
||||
|
||||
for _, pkgIdent := range args {
|
||||
var buf bytes.Buffer
|
||||
importPath, err := ext.Extract(pkgIdent, importPath, &buf)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
continue
|
||||
}
|
||||
|
||||
oFile := strings.Replace(importPath, "/", "_", -1) + ".go"
|
||||
f, err := os.Create(oFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := io.Copy(f, &buf); err != nil {
|
||||
_ = f.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
if err := f.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// genLicense generates the correct LICENSE header text from the provided
|
||||
// path to a LICENSE file.
|
||||
func genLicense(fname string) (string, error) {
|
||||
if fname == "" {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
f, err := os.Open(fname)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not open LICENSE file: %v", err)
|
||||
}
|
||||
defer func() { _ = f.Close() }()
|
||||
|
||||
license := new(strings.Builder)
|
||||
sc := bufio.NewScanner(f)
|
||||
for sc.Scan() {
|
||||
txt := sc.Text()
|
||||
if txt != "" {
|
||||
txt = " " + txt
|
||||
}
|
||||
license.WriteString("//" + txt + "\n")
|
||||
}
|
||||
if sc.Err() != nil {
|
||||
return "", fmt.Errorf("could not scan LICENSE file: %v", err)
|
||||
}
|
||||
|
||||
return license.String(), nil
|
||||
}
|
||||
43
cmd/yaegi/help.go
Normal file
43
cmd/yaegi/help.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
const usage = `Yaegi is a Go interpreter.
|
||||
|
||||
Usage:
|
||||
|
||||
yaegi [command] [arguments]
|
||||
|
||||
The commands are:
|
||||
|
||||
extract generate a wrapper file from a source package
|
||||
help print usage information
|
||||
run execute a Go program from source
|
||||
test execute test functions in a Go package
|
||||
|
||||
Use "yaegi help <command>" for more information about a command.
|
||||
|
||||
If no command is given or if the first argument is not a command, then
|
||||
the run command is assumed.
|
||||
`
|
||||
|
||||
func help(arg []string) error {
|
||||
var cmd string
|
||||
if len(arg) > 0 {
|
||||
cmd = arg[0]
|
||||
}
|
||||
|
||||
switch cmd {
|
||||
case Extract:
|
||||
return extractCmd([]string{"-h"})
|
||||
case Help, "", "-h", "--help":
|
||||
fmt.Print(usage)
|
||||
return nil
|
||||
case Run:
|
||||
return run([]string{"-h"})
|
||||
case Test:
|
||||
return fmt.Errorf("help: test not implemented")
|
||||
default:
|
||||
return fmt.Errorf("help: invalid yaegi command: %v", cmd)
|
||||
}
|
||||
}
|
||||
132
cmd/yaegi/run.go
Normal file
132
cmd/yaegi/run.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/containous/yaegi/interp"
|
||||
"github.com/containous/yaegi/stdlib"
|
||||
"github.com/containous/yaegi/stdlib/syscall"
|
||||
"github.com/containous/yaegi/stdlib/unrestricted"
|
||||
"github.com/containous/yaegi/stdlib/unsafe"
|
||||
)
|
||||
|
||||
func run(arg []string) error {
|
||||
var interactive bool
|
||||
var useSyscall bool
|
||||
var useUnrestricted bool
|
||||
var useUnsafe bool
|
||||
var tags string
|
||||
var cmd string
|
||||
var err error
|
||||
|
||||
rflag := flag.NewFlagSet("run", flag.ContinueOnError)
|
||||
rflag.BoolVar(&interactive, "i", false, "start an interactive REPL")
|
||||
rflag.BoolVar(&useSyscall, "syscall", false, "include syscall symbols")
|
||||
rflag.BoolVar(&useUnrestricted, "unrestricted", false, "include unrestricted symbols")
|
||||
rflag.StringVar(&tags, "tags", "", "set a list of build tags")
|
||||
rflag.BoolVar(&useUnsafe, "unsafe", false, "include usafe symbols")
|
||||
rflag.StringVar(&cmd, "e", "", "set the command to be executed (instead of script or/and shell)")
|
||||
rflag.Usage = func() {
|
||||
fmt.Println("Usage: yaegi run [options] [path] [args]")
|
||||
fmt.Println("Options:")
|
||||
rflag.PrintDefaults()
|
||||
}
|
||||
if err = rflag.Parse(arg); err != nil {
|
||||
return err
|
||||
}
|
||||
args := rflag.Args()
|
||||
|
||||
i := interp.New(interp.Options{GoPath: build.Default.GOPATH, BuildTags: strings.Split(tags, ",")})
|
||||
i.Use(stdlib.Symbols)
|
||||
i.Use(interp.Symbols)
|
||||
if useSyscall {
|
||||
i.Use(syscall.Symbols)
|
||||
}
|
||||
if useUnsafe {
|
||||
i.Use(unsafe.Symbols)
|
||||
}
|
||||
if useUnrestricted {
|
||||
// Use of unrestricted symbols should always follow use of stdlib symbols, to update them.
|
||||
i.Use(unrestricted.Symbols)
|
||||
}
|
||||
|
||||
if cmd != "" {
|
||||
i.REPL(strings.NewReader(cmd), os.Stderr)
|
||||
}
|
||||
|
||||
if len(args) == 0 {
|
||||
if interactive || cmd == "" {
|
||||
i.REPL(os.Stdin, os.Stdout)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Skip first os arg to set command line as expected by interpreted main
|
||||
path := args[0]
|
||||
os.Args = arg[1:]
|
||||
flag.CommandLine = flag.NewFlagSet(path, flag.ExitOnError)
|
||||
|
||||
if isPackageName(path) {
|
||||
err = runPackage(i, path)
|
||||
} else {
|
||||
if isDir(path) {
|
||||
err = runDir(i, path)
|
||||
} else {
|
||||
err = runFile(i, path)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if interactive {
|
||||
i.REPL(os.Stdin, os.Stdout)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isPackageName(path string) bool {
|
||||
return !strings.HasPrefix(path, "/") && !strings.HasPrefix(path, "./") && !strings.HasPrefix(path, "../")
|
||||
}
|
||||
|
||||
func isDir(path string) bool {
|
||||
fi, err := os.Lstat(path)
|
||||
return err == nil && fi.IsDir()
|
||||
}
|
||||
|
||||
func runPackage(i *interp.Interpreter, path string) error {
|
||||
return fmt.Errorf("runPackage not implemented")
|
||||
}
|
||||
|
||||
func runDir(i *interp.Interpreter, path string) error {
|
||||
return fmt.Errorf("runDir not implemented")
|
||||
}
|
||||
|
||||
func runFile(i *interp.Interpreter, path string) error {
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if s := string(b); strings.HasPrefix(s, "#!") {
|
||||
// Allow executable go scripts, Have the same behavior as in interactive mode.
|
||||
s = strings.Replace(s, "#!", "//", 1)
|
||||
i.REPL(strings.NewReader(s), os.Stdout)
|
||||
} else {
|
||||
// Files not starting with "#!" are supposed to be pure Go, directly Evaled.
|
||||
i.Name = path
|
||||
_, err := i.Eval(s)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
if p, ok := err.(interp.Panic); ok {
|
||||
fmt.Println(string(p.Stack))
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -85,87 +85,53 @@ Debugging support (may be removed at any time):
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
"github.com/containous/yaegi/interp"
|
||||
"github.com/containous/yaegi/stdlib"
|
||||
"github.com/containous/yaegi/stdlib/syscall"
|
||||
"github.com/containous/yaegi/stdlib/unsafe"
|
||||
const (
|
||||
Extract = "extract"
|
||||
Help = "help"
|
||||
Run = "run"
|
||||
Test = "test"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var interactive bool
|
||||
var useUnsafe bool
|
||||
var useSyscall bool
|
||||
var tags string
|
||||
var cmd string
|
||||
flag.BoolVar(&interactive, "i", false, "start an interactive REPL")
|
||||
flag.BoolVar(&useSyscall, "syscall", false, "include syscall symbols")
|
||||
flag.StringVar(&tags, "tags", "", "set a list of build tags")
|
||||
flag.BoolVar(&useUnsafe, "unsafe", false, "include usafe symbols")
|
||||
flag.StringVar(&cmd, "e", "", "set the command to be executed (instead of script or/and shell)")
|
||||
flag.Usage = func() {
|
||||
fmt.Println("Usage:", os.Args[0], "[options] [script] [args]")
|
||||
fmt.Println("Options:")
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
flag.Parse()
|
||||
args := flag.Args()
|
||||
log.SetFlags(log.Lshortfile)
|
||||
var err error
|
||||
var exitCode int
|
||||
|
||||
i := interp.New(interp.Options{GoPath: build.Default.GOPATH, BuildTags: strings.Split(tags, ",")})
|
||||
i.Use(stdlib.Symbols)
|
||||
i.Use(interp.Symbols)
|
||||
if useSyscall {
|
||||
i.Use(syscall.Symbols)
|
||||
}
|
||||
if useUnsafe {
|
||||
i.Use(unsafe.Symbols)
|
||||
log.SetFlags(log.Lshortfile) // Ease debugging.
|
||||
|
||||
if len(os.Args) > 1 {
|
||||
cmd = os.Args[1]
|
||||
}
|
||||
|
||||
if cmd != `` {
|
||||
i.REPL(strings.NewReader(cmd), os.Stderr)
|
||||
switch cmd {
|
||||
case Extract:
|
||||
err = extractCmd(os.Args[2:])
|
||||
case Help, "-h", "--help":
|
||||
err = help(os.Args[2:])
|
||||
case Run:
|
||||
err = run(os.Args[2:])
|
||||
case Test:
|
||||
err = fmt.Errorf("test not implemented")
|
||||
default:
|
||||
// If no command is given, fallback to default "run" command.
|
||||
// This allows scripts starting with "#!/usr/bin/env yaegi",
|
||||
// as passing more than 1 argument to #! executable may be not supported
|
||||
// on all platforms.
|
||||
cmd = Run
|
||||
err = run(os.Args[1:])
|
||||
}
|
||||
|
||||
if len(args) == 0 {
|
||||
if interactive || cmd == `` {
|
||||
i.REPL(os.Stdin, os.Stdout)
|
||||
}
|
||||
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])
|
||||
}
|
||||
|
||||
if s := string(b); strings.HasPrefix(s, "#!") {
|
||||
// Allow executable go scripts, Have the same behavior as in interactive mode.
|
||||
s = strings.Replace(s, "#!", "//", 1)
|
||||
i.REPL(strings.NewReader(s), os.Stdout)
|
||||
} else {
|
||||
// Files not starting with "#!" are supposed to be pure Go, directly Evaled.
|
||||
i.Name = args[0]
|
||||
_, err := i.Eval(s)
|
||||
if 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)
|
||||
if err != nil && !errors.Is(err, flag.ErrHelp) {
|
||||
err = fmt.Errorf("%s: %w", cmd, err)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
exitCode = 1
|
||||
}
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// CITimeoutMultiplier is the multiplier for all timeouts in the CI
|
||||
// CITimeoutMultiplier is the multiplier for all timeouts in the CI.
|
||||
CITimeoutMultiplier = 3
|
||||
)
|
||||
|
||||
|
||||
11
example/pkg/_pkg12/src/guthib.com/foo/main.go
Normal file
11
example/pkg/_pkg12/src/guthib.com/foo/main.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"guthib.com/foo/pkg"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Printf("%s", pkg.NewSample()())
|
||||
}
|
||||
17
example/pkg/_pkg12/src/guthib.com/foo/pkg/pkg.go
Normal file
17
example/pkg/_pkg12/src/guthib.com/foo/pkg/pkg.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"guthib.com/bar"
|
||||
)
|
||||
|
||||
func Here() string {
|
||||
return "hello"
|
||||
}
|
||||
|
||||
func NewSample() func() string {
|
||||
return func() string {
|
||||
return fmt.Sprintf("%s %s", bar.Bar(), Here())
|
||||
}
|
||||
}
|
||||
6
example/pkg/_pkg12/src/guthib.com/foo/vendor/guthib.com/bar/bar.go
generated
vendored
Normal file
6
example/pkg/_pkg12/src/guthib.com/foo/vendor/guthib.com/bar/bar.go
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
package bar
|
||||
|
||||
// Bar is bar
|
||||
func Bar() string {
|
||||
return "Yo"
|
||||
}
|
||||
@@ -83,6 +83,18 @@ func TestPackages(t *testing.T) {
|
||||
expected: "Fromage",
|
||||
evalFile: "./_pkg11/src/foo/foo.go",
|
||||
},
|
||||
{
|
||||
desc: "vendor dir is a sibling or an uncle",
|
||||
goPath: "./_pkg12/",
|
||||
expected: "Yo hello",
|
||||
topImport: "guthib.com/foo/pkg",
|
||||
},
|
||||
{
|
||||
desc: "eval main with vendor as a sibling",
|
||||
goPath: "./_pkg12/",
|
||||
expected: "Yo hello",
|
||||
evalFile: "./_pkg12/src/guthib.com/foo/main.go",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
@@ -116,7 +128,7 @@ func TestPackages(t *testing.T) {
|
||||
os.Stdout = pw
|
||||
|
||||
if _, err := i.Eval(string(data)); err != nil {
|
||||
t.Fatal(err)
|
||||
fatalStderrf(t, "%v", err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
@@ -127,10 +139,10 @@ func TestPackages(t *testing.T) {
|
||||
}()
|
||||
|
||||
if err := pw.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
fatalStderrf(t, "%v", err)
|
||||
}
|
||||
if err := <-errC; err != nil {
|
||||
t.Fatal(err)
|
||||
fatalStderrf(t, "%v", err)
|
||||
}
|
||||
msg = buf.String()
|
||||
} else {
|
||||
@@ -153,12 +165,17 @@ func TestPackages(t *testing.T) {
|
||||
}
|
||||
|
||||
if msg != test.expected {
|
||||
t.Errorf("Got %q, want %q", msg, test.expected)
|
||||
fatalStderrf(t, "Got %q, want %q", msg, test.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func fatalStderrf(t *testing.T, format string, args ...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, format+"\n", args...)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
func TestPackagesError(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
|
||||
409
extract/extract.go
Normal file
409
extract/extract.go
Normal file
@@ -0,0 +1,409 @@
|
||||
/*
|
||||
Package extract generates wrappers of package exported symbols.
|
||||
*/
|
||||
package extract
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"go/format"
|
||||
"go/importer"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
const model = `// Code generated by 'github.com/containous/yaegi/extract {{.PkgName}}'. DO NOT EDIT.
|
||||
|
||||
{{.License}}
|
||||
|
||||
{{if .BuildTags}}// +build {{.BuildTags}}{{end}}
|
||||
|
||||
package {{.Dest}}
|
||||
|
||||
import (
|
||||
{{- range $key, $value := .Imports }}
|
||||
{{- if $value}}
|
||||
"{{$key}}"
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
"{{.PkgName}}"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Symbols["{{.PkgName}}"] = map[string]reflect.Value{
|
||||
{{- if .Val}}
|
||||
// function, constant and variable definitions
|
||||
{{range $key, $value := .Val -}}
|
||||
{{- if $value.Addr -}}
|
||||
"{{$key}}": reflect.ValueOf(&{{$value.Name}}).Elem(),
|
||||
{{else -}}
|
||||
"{{$key}}": reflect.ValueOf({{$value.Name}}),
|
||||
{{end -}}
|
||||
{{end}}
|
||||
|
||||
{{- end}}
|
||||
{{- if .Typ}}
|
||||
// type definitions
|
||||
{{range $key, $value := .Typ -}}
|
||||
"{{$key}}": reflect.ValueOf((*{{$value}})(nil)),
|
||||
{{end}}
|
||||
|
||||
{{- end}}
|
||||
{{- if .Wrap}}
|
||||
// interface wrapper definitions
|
||||
{{range $key, $value := .Wrap -}}
|
||||
"_{{$key}}": reflect.ValueOf((*{{$value.Name}})(nil)),
|
||||
{{end}}
|
||||
{{- end}}
|
||||
}
|
||||
}
|
||||
{{range $key, $value := .Wrap -}}
|
||||
// {{$value.Name}} is an interface wrapper for {{$key}} type
|
||||
type {{$value.Name}} struct {
|
||||
{{range $m := $value.Method -}}
|
||||
W{{$m.Name}} func{{$m.Param}} {{$m.Result}}
|
||||
{{end}}
|
||||
}
|
||||
{{range $m := $value.Method -}}
|
||||
func (W {{$value.Name}}) {{$m.Name}}{{$m.Param}} {{$m.Result}} { {{$m.Ret}} W.W{{$m.Name}}{{$m.Arg}} }
|
||||
{{end}}
|
||||
{{end}}
|
||||
`
|
||||
|
||||
// Val stores the value name and addressable status of symbols.
|
||||
type Val struct {
|
||||
Name string // "package.name"
|
||||
Addr bool // true if symbol is a Var
|
||||
}
|
||||
|
||||
// Method stores information for generating interface wrapper method.
|
||||
type Method struct {
|
||||
Name, Param, Result, Arg, Ret string
|
||||
}
|
||||
|
||||
// Wrap stores information for generating interface wrapper.
|
||||
type Wrap struct {
|
||||
Name string
|
||||
Method []Method
|
||||
}
|
||||
|
||||
// restricted map defines symbols for which a special implementation is provided.
|
||||
var restricted = map[string]bool{
|
||||
"osExit": true,
|
||||
"osFindProcess": true,
|
||||
"logFatal": true,
|
||||
"logFatalf": true,
|
||||
"logFatalln": true,
|
||||
"logLogger": true,
|
||||
"logNew": true,
|
||||
}
|
||||
|
||||
func genContent(dest, importPath, license string, p *types.Package, skip map[string]bool) ([]byte, error) {
|
||||
prefix := "_" + importPath + "_"
|
||||
prefix = strings.NewReplacer("/", "_", "-", "_", ".", "_").Replace(prefix)
|
||||
|
||||
typ := map[string]string{}
|
||||
val := map[string]Val{}
|
||||
wrap := map[string]Wrap{}
|
||||
imports := map[string]bool{}
|
||||
sc := p.Scope()
|
||||
|
||||
for _, pkg := range p.Imports() {
|
||||
imports[pkg.Path()] = false
|
||||
}
|
||||
qualify := func(pkg *types.Package) string {
|
||||
if pkg.Path() != importPath {
|
||||
imports[pkg.Path()] = true
|
||||
}
|
||||
return pkg.Name()
|
||||
}
|
||||
|
||||
for _, name := range sc.Names() {
|
||||
o := sc.Lookup(name)
|
||||
if !o.Exported() {
|
||||
continue
|
||||
}
|
||||
|
||||
pname := path.Base(importPath) + "." + name
|
||||
if skip[pname] {
|
||||
continue
|
||||
}
|
||||
|
||||
if rname := path.Base(importPath) + name; restricted[rname] {
|
||||
// Restricted symbol, locally provided by stdlib wrapper.
|
||||
pname = rname
|
||||
}
|
||||
|
||||
switch o := o.(type) {
|
||||
case *types.Const:
|
||||
if b, ok := o.Type().(*types.Basic); ok && (b.Info()&types.IsUntyped) != 0 {
|
||||
// convert untyped constant to right type to avoid overflow
|
||||
val[name] = Val{fixConst(pname, o.Val(), imports), false}
|
||||
} else {
|
||||
val[name] = Val{pname, false}
|
||||
}
|
||||
case *types.Func:
|
||||
val[name] = Val{pname, false}
|
||||
case *types.Var:
|
||||
val[name] = Val{pname, true}
|
||||
case *types.TypeName:
|
||||
typ[name] = pname
|
||||
if t, ok := o.Type().Underlying().(*types.Interface); ok {
|
||||
var methods []Method
|
||||
for i := 0; i < t.NumMethods(); i++ {
|
||||
f := t.Method(i)
|
||||
if !f.Exported() {
|
||||
continue
|
||||
}
|
||||
|
||||
sign := f.Type().(*types.Signature)
|
||||
args := make([]string, sign.Params().Len())
|
||||
params := make([]string, len(args))
|
||||
for j := range args {
|
||||
v := sign.Params().At(j)
|
||||
if args[j] = v.Name(); args[j] == "" {
|
||||
args[j] = fmt.Sprintf("a%d", j)
|
||||
}
|
||||
params[j] = args[j] + " " + types.TypeString(v.Type(), qualify)
|
||||
}
|
||||
arg := "(" + strings.Join(args, ", ") + ")"
|
||||
param := "(" + strings.Join(params, ", ") + ")"
|
||||
|
||||
results := make([]string, sign.Results().Len())
|
||||
for j := range results {
|
||||
v := sign.Results().At(j)
|
||||
results[j] = v.Name() + " " + types.TypeString(v.Type(), qualify)
|
||||
}
|
||||
result := "(" + strings.Join(results, ", ") + ")"
|
||||
|
||||
ret := ""
|
||||
if sign.Results().Len() > 0 {
|
||||
ret = "return"
|
||||
}
|
||||
|
||||
methods = append(methods, Method{f.Name(), param, result, arg, ret})
|
||||
}
|
||||
wrap[name] = Wrap{prefix + name, methods}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTags, err := buildTags()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base := template.New("goexports")
|
||||
parse, err := base.Parse(model)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("template parsing error: %v", err)
|
||||
}
|
||||
|
||||
if importPath == "log/syslog" {
|
||||
buildTags += ",!windows,!nacl,!plan9"
|
||||
}
|
||||
|
||||
b := new(bytes.Buffer)
|
||||
data := map[string]interface{}{
|
||||
"Dest": dest,
|
||||
"Imports": imports,
|
||||
"PkgName": importPath,
|
||||
"Val": val,
|
||||
"Typ": typ,
|
||||
"Wrap": wrap,
|
||||
"BuildTags": buildTags,
|
||||
"License": license,
|
||||
}
|
||||
err = parse.Execute(b, data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("template error: %v", err)
|
||||
}
|
||||
|
||||
// gofmt
|
||||
source, err := format.Source(b.Bytes())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to format source: %v: %s", err, b.Bytes())
|
||||
}
|
||||
return source, nil
|
||||
}
|
||||
|
||||
// fixConst checks untyped constant value, converting it if necessary to avoid overflow.
|
||||
func fixConst(name string, val constant.Value, imports map[string]bool) string {
|
||||
var (
|
||||
tok string
|
||||
str string
|
||||
)
|
||||
switch val.Kind() {
|
||||
case constant.Int:
|
||||
tok = "INT"
|
||||
str = val.ExactString()
|
||||
case constant.Float:
|
||||
v := constant.Val(val) // v is *big.Rat or *big.Float
|
||||
f, ok := v.(*big.Float)
|
||||
if !ok {
|
||||
f = new(big.Float).SetRat(v.(*big.Rat))
|
||||
}
|
||||
|
||||
tok = "FLOAT"
|
||||
str = f.Text('g', int(f.Prec()))
|
||||
case constant.Complex:
|
||||
// TODO: not sure how to parse this case
|
||||
fallthrough
|
||||
default:
|
||||
return name
|
||||
}
|
||||
|
||||
imports["go/constant"] = true
|
||||
imports["go/token"] = true
|
||||
|
||||
return fmt.Sprintf("constant.MakeFromLiteral(\"%s\", token.%s, 0)", str, tok)
|
||||
}
|
||||
|
||||
// importPath checks whether pkgIdent is an existing directory relative to
|
||||
// e.WorkingDir. If yes, it returns the actual import path of the Go package
|
||||
// located in the directory. If it is definitely a relative path, but it does not
|
||||
// exist, an error is returned. Otherwise, it is assumed to be an import path, and
|
||||
// pkgIdent is returned.
|
||||
func (e Extractor) importPath(pkgIdent, importPath string) (string, error) {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
dirPath := filepath.Join(wd, pkgIdent)
|
||||
_, err = os.Stat(dirPath)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return "", err
|
||||
}
|
||||
if err != nil {
|
||||
if len(pkgIdent) > 0 && pkgIdent[0] == '.' {
|
||||
// pkgIdent is definitely a relative path, not a package name, and it does not exist
|
||||
return "", err
|
||||
}
|
||||
// pkgIdent might be a valid stdlib package name. So we leave that responsibility to the caller now.
|
||||
return pkgIdent, nil
|
||||
}
|
||||
|
||||
// local import
|
||||
if importPath != "" {
|
||||
return importPath, nil
|
||||
}
|
||||
|
||||
modPath := filepath.Join(dirPath, "go.mod")
|
||||
_, err = os.Stat(modPath)
|
||||
if os.IsNotExist(err) {
|
||||
return "", errors.New("no go.mod found, and no import path specified")
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
f, err := os.Open(modPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer func() {
|
||||
_ = f.Close()
|
||||
}()
|
||||
sc := bufio.NewScanner(f)
|
||||
var l string
|
||||
for sc.Scan() {
|
||||
l = sc.Text()
|
||||
break
|
||||
}
|
||||
if sc.Err() != nil {
|
||||
return "", err
|
||||
}
|
||||
parts := strings.Fields(l)
|
||||
if len(parts) < 2 {
|
||||
return "", errors.New(`invalid first line syntax in go.mod`)
|
||||
}
|
||||
if parts[0] != "module" {
|
||||
return "", errors.New(`invalid first line in go.mod, no "module" found`)
|
||||
}
|
||||
|
||||
return parts[1], nil
|
||||
}
|
||||
|
||||
// Extractor creates a package with all the symbols from a dependency package.
|
||||
type Extractor struct {
|
||||
Dest string // the name of the created package.
|
||||
License string // license text to be included in the created package, optional.
|
||||
Skip map[string]bool
|
||||
}
|
||||
|
||||
// Extract writes to rw a Go package with all the symbols found at pkgIdent.
|
||||
// pkgIdent can be an import path, or a local path, relative to e.WorkingDir. In
|
||||
// the latter case, Extract returns the actual import path of the package found at
|
||||
// pkgIdent, otherwise it just returns pkgIdent.
|
||||
// If pkgIdent is an import path, it is looked up in GOPATH. Vendoring is not
|
||||
// supported yet, and the behavior is only defined for GO111MODULE=off.
|
||||
func (e Extractor) Extract(pkgIdent, importPath string, rw io.Writer) (string, error) {
|
||||
ipp, err := e.importPath(pkgIdent, importPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
pkg, err := importer.ForCompiler(token.NewFileSet(), "source", nil).Import(pkgIdent)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
content, err := genContent(e.Dest, ipp, e.License, pkg, e.Skip)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if _, err := rw.Write(content); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return ipp, nil
|
||||
}
|
||||
|
||||
// GetMinor returns the minor part of the version number.
|
||||
func GetMinor(part string) string {
|
||||
minor := part
|
||||
index := strings.Index(minor, "beta")
|
||||
if index < 0 {
|
||||
index = strings.Index(minor, "rc")
|
||||
}
|
||||
if index > 0 {
|
||||
minor = minor[:index]
|
||||
}
|
||||
|
||||
return minor
|
||||
}
|
||||
|
||||
func buildTags() (string, error) {
|
||||
version := runtime.Version()
|
||||
if version == "devel" {
|
||||
return "", nil
|
||||
}
|
||||
parts := strings.Split(version, ".")
|
||||
|
||||
minorRaw := GetMinor(parts[1])
|
||||
|
||||
currentGoVersion := parts[0] + "." + minorRaw
|
||||
|
||||
minor, err := strconv.Atoi(minorRaw)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse version: %v", err)
|
||||
}
|
||||
|
||||
nextGoVersion := parts[0] + "." + strconv.Itoa(minor+1)
|
||||
|
||||
return currentGoVersion + ",!" + nextGoVersion, nil
|
||||
}
|
||||
140
extract/extract_test.go
Normal file
140
extract/extract_test.go
Normal file
@@ -0,0 +1,140 @@
|
||||
package extract
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var expectedOutput = `// Code generated by 'github.com/containous/yaegi/extract guthib.com/baz'. DO NOT EDIT.
|
||||
|
||||
// +build BUILD_TAGS
|
||||
|
||||
package bar
|
||||
|
||||
import (
|
||||
"guthib.com/baz"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Symbols["guthib.com/baz"] = map[string]reflect.Value{
|
||||
// function, constant and variable definitions
|
||||
"Hello": reflect.ValueOf(baz.Hello),
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
func init() {
|
||||
buildTags, err := buildTags()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
expectedOutput = strings.Replace(expectedOutput, "BUILD_TAGS", buildTags, 1)
|
||||
}
|
||||
|
||||
func TestPackages(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
moduleOn string
|
||||
wd string
|
||||
arg string
|
||||
importPath string
|
||||
expected string
|
||||
contains string
|
||||
dest string
|
||||
}{
|
||||
{
|
||||
desc: "stdlib math pkg, using go/importer",
|
||||
dest: "math",
|
||||
arg: "math",
|
||||
// We check this one because it shows both defects when we break it: the value
|
||||
// gets corrupted, and the type becomes token.INT
|
||||
// TODO(mpl): if the ident between key and value becomes annoying, be smarter about it.
|
||||
contains: `"MaxFloat64": reflect.ValueOf(constant.MakeFromLiteral("179769313486231570814527423731704356798100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", token.FLOAT, 0)),`,
|
||||
},
|
||||
{
|
||||
desc: "using relative path, using go.mod",
|
||||
wd: "./testdata/1/src/guthib.com/bar",
|
||||
arg: "../baz",
|
||||
expected: expectedOutput,
|
||||
},
|
||||
{
|
||||
desc: "using relative path, manual import path",
|
||||
wd: "./testdata/2/src/guthib.com/bar",
|
||||
arg: "../baz",
|
||||
importPath: "guthib.com/baz",
|
||||
expected: expectedOutput,
|
||||
},
|
||||
{
|
||||
desc: "using relative path, go.mod is ignored, because manual path",
|
||||
wd: "./testdata/3/src/guthib.com/bar",
|
||||
arg: "../baz",
|
||||
importPath: "guthib.com/baz",
|
||||
expected: expectedOutput,
|
||||
},
|
||||
{
|
||||
desc: "using relative path, dep in vendor, using go.mod",
|
||||
wd: "./testdata/4/src/guthib.com/bar",
|
||||
arg: "./vendor/guthib.com/baz",
|
||||
expected: expectedOutput,
|
||||
},
|
||||
{
|
||||
desc: "using relative path, dep in vendor, manual import path",
|
||||
wd: "./testdata/5/src/guthib.com/bar",
|
||||
arg: "./vendor/guthib.com/baz",
|
||||
importPath: "guthib.com/baz",
|
||||
expected: expectedOutput,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wd := test.wd
|
||||
if wd == "" {
|
||||
wd = cwd
|
||||
} else {
|
||||
if err := os.Chdir(wd); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
if err := os.Chdir(cwd); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
dest := path.Base(wd)
|
||||
if test.dest != "" {
|
||||
dest = test.dest
|
||||
}
|
||||
ext := Extractor{
|
||||
Dest: dest,
|
||||
}
|
||||
|
||||
var out bytes.Buffer
|
||||
if _, err := ext.Extract(test.arg, test.importPath, &out); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if test.expected != "" {
|
||||
if out.String() != test.expected {
|
||||
t.Fatalf("\nGot:\n%q\nWant: \n%q", out.String(), test.expected)
|
||||
}
|
||||
}
|
||||
|
||||
if test.contains != "" {
|
||||
if !strings.Contains(out.String(), test.contains) {
|
||||
t.Fatalf("Missing expected part: %s in %s", test.contains, out.String())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
9
extract/testdata/1/src/guthib.com/bar/main.go
vendored
Normal file
9
extract/testdata/1/src/guthib.com/bar/main.go
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"guthib.com/baz"
|
||||
)
|
||||
|
||||
func main() {
|
||||
baz.Hello()
|
||||
}
|
||||
5
extract/testdata/1/src/guthib.com/baz/baz.go
vendored
Normal file
5
extract/testdata/1/src/guthib.com/baz/baz.go
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
package baz
|
||||
|
||||
func Hello() {
|
||||
println("HELLO")
|
||||
}
|
||||
3
extract/testdata/1/src/guthib.com/baz/go.mod
vendored
Normal file
3
extract/testdata/1/src/guthib.com/baz/go.mod
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
module guthib.com/baz
|
||||
|
||||
go 1.14
|
||||
9
extract/testdata/2/src/guthib.com/bar/main.go
vendored
Normal file
9
extract/testdata/2/src/guthib.com/bar/main.go
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"guthib.com/baz"
|
||||
)
|
||||
|
||||
func main() {
|
||||
baz.Hello()
|
||||
}
|
||||
5
extract/testdata/2/src/guthib.com/baz/baz.go
vendored
Normal file
5
extract/testdata/2/src/guthib.com/baz/baz.go
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
package baz
|
||||
|
||||
func Hello() {
|
||||
println("HELLO")
|
||||
}
|
||||
9
extract/testdata/3/src/guthib.com/bar/main.go
vendored
Normal file
9
extract/testdata/3/src/guthib.com/bar/main.go
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"guthib.com/baz"
|
||||
)
|
||||
|
||||
func main() {
|
||||
baz.Hello()
|
||||
}
|
||||
5
extract/testdata/3/src/guthib.com/baz/baz.go
vendored
Normal file
5
extract/testdata/3/src/guthib.com/baz/baz.go
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
package baz
|
||||
|
||||
func Hello() {
|
||||
println("HELLO")
|
||||
}
|
||||
3
extract/testdata/3/src/guthib.com/baz/go.mod
vendored
Normal file
3
extract/testdata/3/src/guthib.com/baz/go.mod
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
module should.not/be/used
|
||||
|
||||
go 1.14
|
||||
9
extract/testdata/4/src/guthib.com/bar/main.go
vendored
Normal file
9
extract/testdata/4/src/guthib.com/bar/main.go
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"guthib.com/baz"
|
||||
)
|
||||
|
||||
func main() {
|
||||
baz.Hello()
|
||||
}
|
||||
5
extract/testdata/4/src/guthib.com/bar/vendor/guthib.com/baz/baz.go
generated
vendored
Normal file
5
extract/testdata/4/src/guthib.com/bar/vendor/guthib.com/baz/baz.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
package baz
|
||||
|
||||
func Hello() {
|
||||
println("HELLO")
|
||||
}
|
||||
3
extract/testdata/4/src/guthib.com/bar/vendor/guthib.com/baz/go.mod
generated
vendored
Normal file
3
extract/testdata/4/src/guthib.com/bar/vendor/guthib.com/baz/go.mod
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
module guthib.com/baz
|
||||
|
||||
go 1.14
|
||||
9
extract/testdata/5/src/guthib.com/bar/main.go
vendored
Normal file
9
extract/testdata/5/src/guthib.com/bar/main.go
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"guthib.com/baz"
|
||||
)
|
||||
|
||||
func main() {
|
||||
baz.Hello()
|
||||
}
|
||||
5
extract/testdata/5/src/guthib.com/bar/vendor/guthib.com/baz/baz.go
generated
vendored
Normal file
5
extract/testdata/5/src/guthib.com/bar/vendor/guthib.com/baz/baz.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
package baz
|
||||
|
||||
func Hello() {
|
||||
println("HELLO")
|
||||
}
|
||||
@@ -181,7 +181,11 @@ func {{$name}}(n *node) {
|
||||
|
||||
func {{$name}}Const(n *node) {
|
||||
v0, v1 := n.child[0].rval, n.child[1].rval
|
||||
{{- if $op.Shift}}
|
||||
isConst := (v0.IsValid() && isConstantValue(v0.Type()))
|
||||
{{- else}}
|
||||
isConst := (v0.IsValid() && isConstantValue(v0.Type())) && (v1.IsValid() && isConstantValue(v1.Type()))
|
||||
{{- end}}
|
||||
t := n.typ.rtype
|
||||
if isConst {
|
||||
t = constVal
|
||||
@@ -190,8 +194,7 @@ func {{$name}}Const(n *node) {
|
||||
switch {
|
||||
case isConst:
|
||||
{{- if $op.Shift}}
|
||||
s, _ := constant.Uint64Val(vConstantValue(v1))
|
||||
v := constant.Shift(vConstantValue(v0), token.{{tokenFromName $name}}, uint(s))
|
||||
v := constant.Shift(vConstantValue(v0), token.{{tokenFromName $name}}, uint(vUint(v1)))
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
{{- else}}
|
||||
v := constant.BinaryOp(vConstantValue(v0), token.{{tokenFromName $name}}, vConstantValue(v1))
|
||||
@@ -412,6 +415,8 @@ func {{$name}}(n *node) {
|
||||
dest := genValueOutput(n, reflect.TypeOf(true))
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
|
||||
{{- if or (eq $op.Name "==") (eq $op.Name "!=") }}
|
||||
|
||||
if c0.typ.cat == aliasT || c1.typ.cat == aliasT {
|
||||
switch {
|
||||
case c0.rval.IsValid():
|
||||
@@ -432,7 +437,7 @@ func {{$name}}(n *node) {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
dest(f).SetBool(i0 {{$op.Name}} i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
@@ -454,7 +459,7 @@ func {{$name}}(n *node) {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
dest(f).SetBool(i0 {{$op.Name}} i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
@@ -478,13 +483,14 @@ func {{$name}}(n *node) {
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
dest(f).SetBool(i0 {{$op.Name}} i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
{{- end}}
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
case isString(t0) || isString(t1):
|
||||
|
||||
@@ -190,6 +190,8 @@ type astError error
|
||||
type action uint
|
||||
|
||||
// Node actions for the go language.
|
||||
// It is important for type checking that *Assign directly
|
||||
// follows it non-assign counterpart.
|
||||
const (
|
||||
aNop action = iota
|
||||
aAddr
|
||||
@@ -250,7 +252,6 @@ const (
|
||||
aTypeAssert
|
||||
aXor
|
||||
aXorAssign
|
||||
aMax
|
||||
)
|
||||
|
||||
var actions = [...]string{
|
||||
@@ -320,6 +321,15 @@ func (a action) String() string {
|
||||
return "Action(" + strconv.Itoa(int(a)) + ")"
|
||||
}
|
||||
|
||||
func isAssignAction(a action) bool {
|
||||
switch a {
|
||||
case aAddAssign, aAndAssign, aAndNotAssign, aMulAssign, aOrAssign,
|
||||
aQuoAssign, aRemAssign, aShlAssign, aShrAssign, aSubAssign, aXorAssign:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (interp *Interpreter) firstToken(src string) token.Token {
|
||||
var s scanner.Scanner
|
||||
file := interp.fset.AddFile("", interp.fset.Base(), len(src))
|
||||
@@ -795,7 +805,7 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
|
||||
}
|
||||
|
||||
case *ast.UnaryExpr:
|
||||
var kind = unaryExpr
|
||||
kind := unaryExpr
|
||||
var act action
|
||||
switch a.Op {
|
||||
case token.ADD:
|
||||
|
||||
499
interp/cfg.go
499
interp/cfg.go
@@ -8,7 +8,6 @@ import (
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
@@ -46,12 +45,15 @@ var constBltn = map[string]func(*node){
|
||||
|
||||
var identifier = regexp.MustCompile(`([\pL_][\pL_\d]*)$`)
|
||||
|
||||
const nilIdent = "nil"
|
||||
|
||||
// cfg generates a control flow graph (CFG) from AST (wiring successors in AST)
|
||||
// and pre-compute frame sizes and indexes for all un-named (temporary) and named
|
||||
// variables. A list of nodes of init functions is returned.
|
||||
// Following this pass, the CFG is ready to run.
|
||||
func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
sc := interp.initScopePkg(pkgID)
|
||||
check := typecheck{}
|
||||
var initNodes []*node
|
||||
var err error
|
||||
|
||||
@@ -193,7 +195,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
if len(n.child) == 2 {
|
||||
// 1 type in clause: define the var with this type in the case clause scope
|
||||
switch {
|
||||
case n.child[0].ident == "nil":
|
||||
case n.child[0].ident == nilIdent:
|
||||
typ = sc.getType("interface{}")
|
||||
case !n.child[0].isType(sc):
|
||||
err = n.cfgErrorf("%s is not a type", n.child[0].ident)
|
||||
@@ -243,6 +245,8 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
if n.typ, err = nodeType(interp, sc, n.child[0]); err != nil {
|
||||
return false
|
||||
}
|
||||
// Indicate that the first child is the type
|
||||
n.nleft = 1
|
||||
} else {
|
||||
// Get type from ancestor (implicit type)
|
||||
if n.anc.kind == keyValueExpr && n == n.anc.child[0] {
|
||||
@@ -254,20 +258,29 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
err = n.cfgErrorf("undefined type")
|
||||
return false
|
||||
}
|
||||
n.typ.untyped = true
|
||||
}
|
||||
|
||||
child := n.child
|
||||
if n.nleft > 0 {
|
||||
n.child[0].typ = n.typ
|
||||
child = n.child[1:]
|
||||
}
|
||||
// Propagate type to children, to handle implicit types
|
||||
for _, c := range n.child {
|
||||
for _, c := range child {
|
||||
switch c.kind {
|
||||
case binaryExpr, unaryExpr:
|
||||
case binaryExpr, unaryExpr, compositeLitExpr:
|
||||
// Do not attempt to propagate composite type to operator expressions,
|
||||
// it breaks constant folding.
|
||||
case callExpr:
|
||||
case keyValueExpr, typeAssertExpr, indexExpr:
|
||||
c.typ = n.typ
|
||||
default:
|
||||
if c.ident == nilIdent {
|
||||
c.typ = sc.getType(nilIdent)
|
||||
continue
|
||||
}
|
||||
if c.typ, err = nodeType(interp, sc, c); err != nil {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
c.typ = n.typ
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,6 +443,12 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
switch n.kind {
|
||||
case addressExpr:
|
||||
wireChild(n)
|
||||
|
||||
err = check.addressExpr(n)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
n.typ = &itype{cat: ptrT, val: n.child[0].typ}
|
||||
n.findex = sc.add(n.typ)
|
||||
|
||||
@@ -476,6 +495,9 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
dest.typ = sc.fixType(src.typ)
|
||||
}
|
||||
}
|
||||
if dest.typ.incomplete {
|
||||
return
|
||||
}
|
||||
if dest.typ.sizedef {
|
||||
dest.typ.size = arrayTypeLen(src)
|
||||
dest.typ.rtype = nil
|
||||
@@ -495,31 +517,14 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
} else {
|
||||
sym, level, _ = sc.lookup(dest.ident)
|
||||
}
|
||||
switch t0, t1 := dest.typ.TypeOf(), src.typ.TypeOf(); n.action {
|
||||
case aAddAssign:
|
||||
if !(isNumber(t0) && isNumber(t1) || isString(t0) && isString(t1)) || isInt(t0) && isFloat(t1) {
|
||||
err = n.cfgErrorf("illegal operand types for '%v' operator", n.action)
|
||||
}
|
||||
case aSubAssign, aMulAssign, aQuoAssign:
|
||||
if !(isNumber(t0) && isNumber(t1)) || isInt(t0) && isFloat(t1) {
|
||||
err = n.cfgErrorf("illegal operand types for '%v' operator", n.action)
|
||||
}
|
||||
case aRemAssign, aAndAssign, aOrAssign, aXorAssign, aAndNotAssign:
|
||||
if !(isInt(t0) && isInt(t1)) {
|
||||
err = n.cfgErrorf("illegal operand types for '%v' operator", n.action)
|
||||
}
|
||||
case aShlAssign, aShrAssign:
|
||||
if !(dest.isInteger() && src.isNatural()) {
|
||||
err = n.cfgErrorf("illegal operand types for '%v' operator", n.action)
|
||||
}
|
||||
default:
|
||||
// Detect invalid float truncate.
|
||||
if isInt(t0) && isFloat(t1) {
|
||||
err = src.cfgErrorf("invalid float truncate")
|
||||
return
|
||||
}
|
||||
|
||||
err = check.assignExpr(n, dest, src)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
n.findex = dest.findex
|
||||
n.level = dest.level
|
||||
|
||||
// Propagate type
|
||||
// TODO: Check that existing destination type matches source type
|
||||
@@ -548,23 +553,9 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
n.gen = nop
|
||||
src.findex = dest.findex
|
||||
src.level = level
|
||||
case src.kind == basicLit:
|
||||
// TODO: perform constant folding and propagation here.
|
||||
switch {
|
||||
case dest.typ.cat == interfaceT:
|
||||
case isComplex(dest.typ.TypeOf()):
|
||||
// Value set in genValue.
|
||||
case !src.rval.IsValid():
|
||||
// Assign to nil.
|
||||
src.rval = reflect.New(dest.typ.TypeOf()).Elem()
|
||||
case n.anc.kind == constDecl:
|
||||
// Possible conversion from const to actual type will be handled later
|
||||
default:
|
||||
// Convert literal value to destination type.
|
||||
convertConstantValue(src)
|
||||
src.rval = src.rval.Convert(dest.typ.TypeOf())
|
||||
src.typ = dest.typ
|
||||
}
|
||||
case src.kind == basicLit && !src.rval.IsValid():
|
||||
// Assign to nil.
|
||||
src.rval = reflect.New(dest.typ.TypeOf()).Elem()
|
||||
}
|
||||
n.typ = dest.typ
|
||||
if sym != nil {
|
||||
@@ -576,13 +567,11 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
dest.gen = nop // skip getIndexMap
|
||||
}
|
||||
if n.anc.kind == constDecl {
|
||||
if !dest.typ.untyped {
|
||||
// If the dest is untyped, any constant rval needs to be converted
|
||||
convertConstantValue(src)
|
||||
}
|
||||
n.gen = nop
|
||||
n.findex = -1
|
||||
sc.sym[dest.ident].kind = constSym
|
||||
if sym, _, ok := sc.lookup(dest.ident); ok {
|
||||
sym.kind = constSym
|
||||
}
|
||||
if childPos(n) == len(n.anc.child)-1 {
|
||||
sc.iota = 0
|
||||
} else {
|
||||
@@ -640,82 +629,20 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
|
||||
case binaryExpr:
|
||||
wireChild(n)
|
||||
nilSym := interp.universe.sym["nil"]
|
||||
nilSym := interp.universe.sym[nilIdent]
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
|
||||
|
||||
isConstVal := func(n *node) bool {
|
||||
return n.rval.IsValid() && isConstantValue(n.rval.Type())
|
||||
}
|
||||
|
||||
// Type check the binary expression. Mimics Gos logic as closely and possible.
|
||||
c := c0
|
||||
if isConstVal(c) {
|
||||
c = c1
|
||||
}
|
||||
|
||||
if isShiftNode(n) {
|
||||
if !c1.isNatural() {
|
||||
err = n.cfgErrorf("invalid operation: shift count type %v, must be integer", strings.TrimLeft(c1.typ.id(), "."))
|
||||
break
|
||||
}
|
||||
|
||||
if !c0.isInteger() {
|
||||
err = n.cfgErrorf("invalid operation: shift of type %v", strings.TrimLeft(c0.typ.id(), "."))
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isShiftNode(n) && isComparisonNode(n) && !isConstVal(c) && !c0.typ.equals(c1.typ) {
|
||||
if isInterface(c1.typ) && !isInterface(c0.typ) && !c0.typ.comparable() {
|
||||
err = n.cfgErrorf("invalid operation: operator %v not defined on %s", n.action, strings.TrimLeft(c0.typ.id(), "."))
|
||||
break
|
||||
}
|
||||
|
||||
if isInterface(c0.typ) && !isInterface(c1.typ) && !c1.typ.comparable() {
|
||||
err = n.cfgErrorf("invalid operation: operator %v not defined on %s", n.action, strings.TrimLeft(c1.typ.id(), "."))
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isShiftNode(n) && !isConstVal(c) && !c0.typ.equals(c1.typ) && t0 != nil && t1 != nil {
|
||||
switch {
|
||||
case isConstVal(c0) && isNumber(t1) || isConstVal(c1) && isNumber(t0): // const <-> numberic case
|
||||
case t0.Kind() == reflect.Uint8 && t1.Kind() == reflect.Int32 || t1.Kind() == reflect.Uint8 && t0.Kind() == reflect.Int32: // byte <-> rune case
|
||||
case isInterface(c0.typ) && isInterface(c1.typ): // interface <-> interface case
|
||||
default:
|
||||
err = n.cfgErrorf("invalid operation: mismatched types %s and %s", strings.TrimLeft(c0.typ.id(), "."), strings.TrimLeft(c1.typ.id(), "."))
|
||||
}
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
cat := c.typ.cat
|
||||
switch {
|
||||
case isConstVal(c):
|
||||
cat = catOfConst(c.rval)
|
||||
case c.typ.cat == valueT:
|
||||
cat = catOf(c.typ.rtype)
|
||||
case c.typ.cat == aliasT:
|
||||
cat = c.typ.val.cat
|
||||
}
|
||||
if !isShiftNode(n) && !okFor[n.action][cat] {
|
||||
err = n.cfgErrorf("invalid operation: operator %v not defined on %s", n.action, strings.TrimLeft(c0.typ.id(), "."))
|
||||
err = check.binaryExpr(n)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if !isShiftNode(n) && isConstVal(c0) && isConstVal(c1) {
|
||||
// If both are constants, check the left type as well.
|
||||
if !okFor[n.action][catOfConst(c0.rval)] {
|
||||
err = n.cfgErrorf("invalid operation: operator %v not defined on %s", n.action, strings.TrimLeft(c0.typ.id(), "."))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
switch n.action {
|
||||
case aRem, aShl, aShr:
|
||||
n.typ = c0.typ
|
||||
case aEqual, aNotEqual:
|
||||
n.typ = sc.getType("bool")
|
||||
if n.child[0].sym == nilSym || n.child[1].sym == nilSym {
|
||||
if c0.sym == nilSym || c1.sym == nilSym {
|
||||
if n.action == aEqual {
|
||||
n.gen = isNil
|
||||
} else {
|
||||
@@ -749,6 +676,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
dest := n.anc.child[childPos(n)-n.anc.nright]
|
||||
n.typ = dest.typ
|
||||
n.findex = dest.findex
|
||||
n.level = dest.level
|
||||
case n.anc.kind == returnStmt:
|
||||
// To avoid a copy in frame, if the result is to be returned, store it directly
|
||||
// at the frame location reserved for output arguments.
|
||||
@@ -784,13 +712,22 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
}
|
||||
n.findex = sc.add(n.typ)
|
||||
typ := t.TypeOf()
|
||||
switch k := typ.Kind(); k {
|
||||
case reflect.Map:
|
||||
if typ.Kind() == reflect.Map {
|
||||
err = check.assignment(n.child[1], t.key, "map index")
|
||||
n.gen = getIndexMap
|
||||
case reflect.Array, reflect.Slice, reflect.String:
|
||||
break
|
||||
}
|
||||
|
||||
l := -1
|
||||
switch k := typ.Kind(); k {
|
||||
case reflect.Array:
|
||||
l = typ.Len()
|
||||
fallthrough
|
||||
case reflect.Slice, reflect.String:
|
||||
n.gen = getIndexArray
|
||||
case reflect.Ptr:
|
||||
if typ2 := typ.Elem(); typ2.Kind() == reflect.Array {
|
||||
l = typ2.Len()
|
||||
n.gen = getIndexArray
|
||||
} else {
|
||||
err = n.cfgErrorf("type %v does not support indexing", typ)
|
||||
@@ -799,11 +736,14 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
err = n.cfgErrorf("type is not an array, slice, string or map: %v", t.id())
|
||||
}
|
||||
|
||||
err = check.index(n.child[1], l)
|
||||
|
||||
case blockStmt:
|
||||
wireChild(n)
|
||||
if len(n.child) > 0 {
|
||||
l := n.lastChild()
|
||||
n.findex = l.findex
|
||||
n.level = l.level
|
||||
n.val = l.val
|
||||
n.sym = l.sym
|
||||
n.typ = l.typ
|
||||
@@ -811,13 +751,22 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
}
|
||||
sc = sc.pop()
|
||||
|
||||
case constDecl, varDecl:
|
||||
case constDecl:
|
||||
wireChild(n)
|
||||
|
||||
case varDecl:
|
||||
// Global varDecl do not need to be wired as this
|
||||
// will be handled after cfg.
|
||||
if n.anc.kind == fileStmt {
|
||||
break
|
||||
}
|
||||
wireChild(n)
|
||||
|
||||
case declStmt, exprStmt, sendStmt:
|
||||
wireChild(n)
|
||||
l := n.lastChild()
|
||||
n.findex = l.findex
|
||||
n.level = l.level
|
||||
n.val = l.val
|
||||
n.sym = l.sym
|
||||
n.typ = l.typ
|
||||
@@ -872,9 +821,11 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
// Type conversion expression
|
||||
if isInt(n.child[0].typ.TypeOf()) && n.child[1].kind == basicLit && isFloat(n.child[1].typ.TypeOf()) {
|
||||
err = n.cfgErrorf("truncated to integer")
|
||||
break
|
||||
}
|
||||
n.action = aConvert
|
||||
if isInterface(n.child[0].typ) && !n.child[1].isNil() {
|
||||
switch {
|
||||
case isInterface(n.child[0].typ) && !n.child[1].isNil():
|
||||
// Convert to interface: just check that all required methods are defined by concrete type.
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
if !c1.typ.implements(c0.typ) {
|
||||
@@ -884,9 +835,16 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
n.gen = nop
|
||||
n.typ = n.child[1].typ
|
||||
n.findex = n.child[1].findex
|
||||
n.level = n.child[1].level
|
||||
n.val = n.child[1].val
|
||||
n.rval = n.child[1].rval
|
||||
} else {
|
||||
case n.child[1].rval.IsValid() && isConstType(n.child[0].typ):
|
||||
n.gen = nop
|
||||
n.findex = -1
|
||||
n.typ = n.child[0].typ
|
||||
n.rval = n.child[1].rval
|
||||
convertConstantValue(n)
|
||||
default:
|
||||
n.gen = convert
|
||||
n.typ = n.child[0].typ
|
||||
n.findex = sc.add(n.typ)
|
||||
@@ -987,9 +945,49 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
|
||||
case compositeLitExpr:
|
||||
wireChild(n)
|
||||
|
||||
underlying := func(t *itype) *itype {
|
||||
for {
|
||||
switch t.cat {
|
||||
case ptrT, aliasT:
|
||||
t = t.val
|
||||
continue
|
||||
default:
|
||||
return t
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
child := n.child
|
||||
if n.nleft > 0 {
|
||||
child = child[1:]
|
||||
}
|
||||
|
||||
switch n.typ.cat {
|
||||
case arrayT:
|
||||
err = check.arrayLitExpr(child, underlying(n.typ.val), n.typ.size)
|
||||
case mapT:
|
||||
err = check.mapLitExpr(child, n.typ.key, underlying(n.typ.val))
|
||||
case structT:
|
||||
err = check.structLitExpr(child, n.typ)
|
||||
case valueT:
|
||||
rtype := n.typ.rtype
|
||||
switch rtype.Kind() {
|
||||
case reflect.Struct:
|
||||
err = check.structBinLitExpr(child, rtype)
|
||||
case reflect.Map:
|
||||
ktyp := &itype{cat: valueT, rtype: rtype.Key()}
|
||||
vtyp := &itype{cat: valueT, rtype: rtype.Elem()}
|
||||
err = check.mapLitExpr(child, ktyp, vtyp)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
n.findex = sc.add(n.typ)
|
||||
// TODO: Check that composite literal expr matches corresponding type
|
||||
n.gen = compositeGenerator(n, sc)
|
||||
n.gen = compositeGenerator(n)
|
||||
|
||||
case fallthroughtStmt:
|
||||
if n.anc.kind != caseBody {
|
||||
@@ -997,7 +995,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
}
|
||||
|
||||
case fileStmt:
|
||||
wireChild(n)
|
||||
wireChild(n, varDecl)
|
||||
sc = sc.pop()
|
||||
n.findex = -1
|
||||
|
||||
@@ -1109,11 +1107,11 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
n.types = sc.types
|
||||
sc = sc.pop()
|
||||
funcName := n.child[1].ident
|
||||
if !isMethod(n) {
|
||||
interp.scopes[pkgID].sym[funcName].index = -1 // to force value to n.val
|
||||
interp.scopes[pkgID].sym[funcName].typ = n.typ
|
||||
interp.scopes[pkgID].sym[funcName].kind = funcSym
|
||||
interp.scopes[pkgID].sym[funcName].node = n
|
||||
if sym := sc.sym[funcName]; !isMethod(n) && sym != nil {
|
||||
sym.index = -1 // to force value to n.val
|
||||
sym.typ = n.typ
|
||||
sym.kind = funcSym
|
||||
sym.node = n
|
||||
}
|
||||
|
||||
case funcLit:
|
||||
@@ -1128,6 +1126,10 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
if isKey(n) || isNewDefine(n, sc) {
|
||||
break
|
||||
}
|
||||
if n.anc.kind == funcDecl && n.anc.child[1] == n {
|
||||
// Dont process a function name identExpr.
|
||||
break
|
||||
}
|
||||
|
||||
sym, level, found := sc.lookup(n.ident)
|
||||
if !found {
|
||||
@@ -1150,9 +1152,9 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
n.rval = sym.rval
|
||||
n.kind = basicLit
|
||||
case n.ident == "iota":
|
||||
n.rval = reflect.ValueOf(sc.iota)
|
||||
n.rval = reflect.ValueOf(constant.MakeInt64(int64(sc.iota)))
|
||||
n.kind = basicLit
|
||||
case n.ident == "nil":
|
||||
case n.ident == nilIdent:
|
||||
n.kind = basicLit
|
||||
case sym.kind == binSym:
|
||||
n.typ = sym.typ
|
||||
@@ -1278,12 +1280,13 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
wireChild(n)
|
||||
c := n.lastChild()
|
||||
n.findex = c.findex
|
||||
n.level = c.level
|
||||
n.typ = c.typ
|
||||
n.rval = c.rval
|
||||
|
||||
case rangeStmt:
|
||||
if sc.rangeChanType(n) != nil {
|
||||
n.start = n.child[1] // Get chan
|
||||
n.start = n.child[1].start // Get chan
|
||||
n.child[1].tnext = n // then go to range function
|
||||
n.tnext = n.child[2].start // then go to range body
|
||||
n.child[2].tnext = n // then body go to range function (loop)
|
||||
@@ -1295,7 +1298,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
} else {
|
||||
k, o, body = n.child[0], n.child[1], n.child[2]
|
||||
}
|
||||
n.start = o // Get array or map object
|
||||
n.start = o.start // Get array or map object
|
||||
o.tnext = k.start // then go to iterator init
|
||||
k.tnext = n // then go to range function
|
||||
n.tnext = body.start // then go to range body
|
||||
@@ -1674,17 +1677,18 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
case typeAssertExpr:
|
||||
if len(n.child) > 1 {
|
||||
wireChild(n)
|
||||
if n.child[1].typ == nil {
|
||||
if n.child[1].typ, err = nodeType(interp, sc, n.child[1]); err != nil {
|
||||
c1 := n.child[1]
|
||||
if c1.typ == nil {
|
||||
if c1.typ, err = nodeType(interp, sc, c1); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if n.anc.action != aAssignX {
|
||||
if n.child[0].typ.cat == valueT {
|
||||
if n.child[0].typ.cat == valueT && isFunc(c1.typ) {
|
||||
// Avoid special wrapping of interfaces and func types.
|
||||
n.typ = &itype{cat: valueT, rtype: n.child[1].typ.TypeOf()}
|
||||
n.typ = &itype{cat: valueT, rtype: c1.typ.TypeOf()}
|
||||
} else {
|
||||
n.typ = n.child[1].typ
|
||||
n.typ = c1.typ
|
||||
}
|
||||
n.findex = sc.add(n.typ)
|
||||
}
|
||||
@@ -1701,36 +1705,27 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
|
||||
case unaryExpr:
|
||||
wireChild(n)
|
||||
|
||||
err = check.unaryExpr(n)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
n.typ = n.child[0].typ
|
||||
switch n.action {
|
||||
case aRecv:
|
||||
if n.action == aRecv {
|
||||
// Channel receive operation: set type to the channel data type
|
||||
if n.typ.cat == valueT {
|
||||
n.typ = &itype{cat: valueT, rtype: n.typ.rtype.Elem()}
|
||||
} else {
|
||||
n.typ = n.typ.val
|
||||
}
|
||||
case aBitNot:
|
||||
if !isInt(n.typ.TypeOf()) {
|
||||
err = n.cfgErrorf("illegal operand type for '^' operator")
|
||||
return
|
||||
}
|
||||
case aNot:
|
||||
if !isBool(n.typ) {
|
||||
err = n.cfgErrorf("illegal operand type for '!' operator")
|
||||
return
|
||||
}
|
||||
case aNeg, aPos:
|
||||
if !isNumber(n.typ.TypeOf()) {
|
||||
err = n.cfgErrorf("illegal operand type for '%v' operator", n.action)
|
||||
return
|
||||
}
|
||||
}
|
||||
if n.typ == nil {
|
||||
if n.typ, err = nodeType(interp, sc, n); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Optimisation: avoid allocation if boolean branch op (i.e. '!' in an 'if' expr)
|
||||
if n.child[0].rval.IsValid() && !isInterface(n.typ) && constOp[n.action] != nil {
|
||||
n.typ.TypeOf() // init reflect type
|
||||
@@ -1744,6 +1739,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
dest := n.anc.child[childPos(n)-n.anc.nright]
|
||||
n.typ = dest.typ
|
||||
n.findex = dest.findex
|
||||
n.level = dest.level
|
||||
case n.anc.kind == returnStmt:
|
||||
pos := childPos(n)
|
||||
n.typ = sc.def.typ.ret[pos]
|
||||
@@ -1902,6 +1898,90 @@ func genRun(nod *node) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func genGlobalVars(roots []*node, sc *scope) (*node, error) {
|
||||
var vars []*node
|
||||
for _, n := range roots {
|
||||
vars = append(vars, getVars(n)...)
|
||||
}
|
||||
|
||||
if len(vars) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
varNode, err := genGlobalVarDecl(vars, sc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
setExec(varNode.start)
|
||||
return varNode, nil
|
||||
}
|
||||
|
||||
func getVars(n *node) (vars []*node) {
|
||||
for _, child := range n.child {
|
||||
if child.kind == varDecl {
|
||||
vars = append(vars, child.child...)
|
||||
}
|
||||
}
|
||||
return vars
|
||||
}
|
||||
|
||||
func genGlobalVarDecl(nodes []*node, sc *scope) (*node, error) {
|
||||
varNode := &node{kind: varDecl, action: aNop, gen: nop}
|
||||
|
||||
deps := map[*node][]*node{}
|
||||
for _, n := range nodes {
|
||||
deps[n] = getVarDependencies(n, sc)
|
||||
}
|
||||
|
||||
inited := map[*node]bool{}
|
||||
revisit := []*node{}
|
||||
for {
|
||||
for _, n := range nodes {
|
||||
canInit := true
|
||||
for _, d := range deps[n] {
|
||||
if !inited[d] {
|
||||
canInit = false
|
||||
}
|
||||
}
|
||||
if !canInit {
|
||||
revisit = append(revisit, n)
|
||||
continue
|
||||
}
|
||||
|
||||
varNode.child = append(varNode.child, n)
|
||||
inited[n] = true
|
||||
}
|
||||
|
||||
if len(revisit) == 0 || equalNodes(nodes, revisit) {
|
||||
break
|
||||
}
|
||||
|
||||
nodes = revisit
|
||||
revisit = []*node{}
|
||||
}
|
||||
|
||||
if len(revisit) > 0 {
|
||||
return nil, revisit[0].cfgErrorf("variable definition loop")
|
||||
}
|
||||
wireChild(varNode)
|
||||
return varNode, nil
|
||||
}
|
||||
|
||||
func getVarDependencies(nod *node, sc *scope) (deps []*node) {
|
||||
nod.Walk(func(n *node) bool {
|
||||
if n.kind == identExpr {
|
||||
if sym, _, ok := sc.lookup(n.ident); ok {
|
||||
if sym.kind != varSym || !sym.global || sym.node == nod {
|
||||
return false
|
||||
}
|
||||
deps = append(deps, sym.node)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}, nil)
|
||||
return deps
|
||||
}
|
||||
|
||||
// setFnext sets the cond fnext field to next, propagates it for parenthesis blocks
|
||||
// and sets the action to branch.
|
||||
func setFNext(cond, next *node) {
|
||||
@@ -1961,47 +2041,68 @@ func (n *node) isType(sc *scope) bool {
|
||||
}
|
||||
|
||||
// wireChild wires AST nodes for CFG in subtree.
|
||||
func wireChild(n *node) {
|
||||
func wireChild(n *node, exclude ...nkind) {
|
||||
child := excludeNodeKind(n.child, exclude)
|
||||
|
||||
// Set start node, in subtree (propagated to ancestors by post-order processing)
|
||||
for _, child := range n.child {
|
||||
switch child.kind {
|
||||
for _, c := range child {
|
||||
switch c.kind {
|
||||
case arrayType, chanType, chanTypeRecv, chanTypeSend, funcDecl, importDecl, mapType, basicLit, identExpr, typeDecl:
|
||||
continue
|
||||
default:
|
||||
n.start = child.start
|
||||
n.start = c.start
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// Chain sequential operations inside a block (next is right sibling)
|
||||
for i := 1; i < len(n.child); i++ {
|
||||
switch n.child[i].kind {
|
||||
for i := 1; i < len(child); i++ {
|
||||
switch child[i].kind {
|
||||
case funcDecl:
|
||||
n.child[i-1].tnext = n.child[i]
|
||||
child[i-1].tnext = child[i]
|
||||
default:
|
||||
switch n.child[i-1].kind {
|
||||
switch child[i-1].kind {
|
||||
case breakStmt, continueStmt, gotoStmt, returnStmt:
|
||||
// tnext is already computed, no change
|
||||
default:
|
||||
n.child[i-1].tnext = n.child[i].start
|
||||
child[i-1].tnext = child[i].start
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Chain subtree next to self
|
||||
for i := len(n.child) - 1; i >= 0; i-- {
|
||||
switch n.child[i].kind {
|
||||
for i := len(child) - 1; i >= 0; i-- {
|
||||
switch child[i].kind {
|
||||
case arrayType, chanType, chanTypeRecv, chanTypeSend, importDecl, mapType, funcDecl, basicLit, identExpr, typeDecl:
|
||||
continue
|
||||
case breakStmt, continueStmt, gotoStmt, returnStmt:
|
||||
// tnext is already computed, no change
|
||||
default:
|
||||
n.child[i].tnext = n
|
||||
child[i].tnext = n
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func excludeNodeKind(child []*node, kinds []nkind) []*node {
|
||||
if len(kinds) == 0 {
|
||||
return child
|
||||
}
|
||||
var res []*node
|
||||
for _, c := range child {
|
||||
exclude := false
|
||||
for _, k := range kinds {
|
||||
if c.kind == k {
|
||||
exclude = true
|
||||
}
|
||||
}
|
||||
if !exclude {
|
||||
res = append(res, c)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (n *node) name() (s string) {
|
||||
switch {
|
||||
case n.ident != "":
|
||||
@@ -2012,43 +2113,6 @@ func (n *node) name() (s string) {
|
||||
return s
|
||||
}
|
||||
|
||||
// isInteger returns true if node type is integer, false otherwise.
|
||||
func (n *node) isInteger() bool {
|
||||
if isInt(n.typ.TypeOf()) {
|
||||
return true
|
||||
}
|
||||
if n.rval.IsValid() {
|
||||
t := n.rval.Type()
|
||||
if isInt(t) {
|
||||
return true
|
||||
}
|
||||
if isFloat(t) {
|
||||
// untyped float constant with null decimal part is ok
|
||||
f := n.rval.Float()
|
||||
if f == math.Trunc(f) {
|
||||
n.rval = reflect.ValueOf(int(f))
|
||||
n.typ.rtype = n.rval.Type()
|
||||
return true
|
||||
}
|
||||
}
|
||||
if isConstantValue(t) {
|
||||
c := n.rval.Interface().(constant.Value)
|
||||
switch c.Kind() {
|
||||
case constant.Int:
|
||||
return true
|
||||
case constant.Float:
|
||||
f, _ := constant.Float64Val(c)
|
||||
if f == math.Trunc(f) {
|
||||
n.rval = reflect.ValueOf(constant.ToInt(c))
|
||||
n.typ.rtype = n.rval.Type()
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isNatural returns true if node type is natural, false otherwise.
|
||||
func (n *node) isNatural() bool {
|
||||
if isUint(n.typ.TypeOf()) {
|
||||
@@ -2135,7 +2199,17 @@ func isField(n *node) bool {
|
||||
}
|
||||
|
||||
func isRecursiveField(n *node) bool {
|
||||
return isField(n) && (n.typ.recursive || n.typ.cat == ptrT && n.typ.val.recursive)
|
||||
if !isField(n) {
|
||||
return false
|
||||
}
|
||||
t := n.typ
|
||||
for t != nil {
|
||||
if t.recursive {
|
||||
return true
|
||||
}
|
||||
t = t.val
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isNewDefine returns true if node refers to a new definition.
|
||||
@@ -2268,12 +2342,11 @@ func gotoLabel(s *symbol) {
|
||||
}
|
||||
}
|
||||
|
||||
func compositeGenerator(n *node, sc *scope) (gen bltnGenerator) {
|
||||
func compositeGenerator(n *node) (gen bltnGenerator) {
|
||||
switch n.typ.cat {
|
||||
case aliasT, ptrT:
|
||||
n.typ.val.untyped = n.typ.untyped
|
||||
n.typ = n.typ.val
|
||||
gen = compositeGenerator(n, sc)
|
||||
gen = compositeGenerator(n)
|
||||
case arrayT:
|
||||
gen = arrayLit
|
||||
case mapT:
|
||||
@@ -2283,13 +2356,13 @@ func compositeGenerator(n *node, sc *scope) (gen bltnGenerator) {
|
||||
case len(n.child) == 0:
|
||||
gen = compositeLitNotype
|
||||
case n.lastChild().kind == keyValueExpr:
|
||||
if n.child[0].isType(sc) {
|
||||
if n.nleft == 1 {
|
||||
gen = compositeSparse
|
||||
} else {
|
||||
gen = compositeSparseNotype
|
||||
}
|
||||
default:
|
||||
if n.child[0].isType(sc) {
|
||||
if n.nleft == 1 {
|
||||
gen = compositeLit
|
||||
} else {
|
||||
gen = compositeLitNotype
|
||||
|
||||
@@ -78,8 +78,8 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
if typ.isBinMethod {
|
||||
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}
|
||||
if sc.sym[dest.ident] == nil || sc.sym[dest.ident].typ.incomplete {
|
||||
sc.sym[dest.ident] = &symbol{kind: varSym, global: true, index: sc.add(typ), typ: typ, rval: val, node: n}
|
||||
}
|
||||
if n.anc.kind == constDecl {
|
||||
sc.sym[dest.ident].kind = constSym
|
||||
@@ -112,7 +112,7 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
sym1, exists1 := sc.sym[asImportName]
|
||||
sym2, exists2 := sc.sym[c.ident]
|
||||
if !exists1 && !exists2 {
|
||||
sc.sym[c.ident] = &symbol{index: sc.add(n.typ), kind: varSym, global: true, typ: n.typ}
|
||||
sc.sym[c.ident] = &symbol{index: sc.add(n.typ), kind: varSym, global: true, typ: n.typ, node: n}
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -138,11 +138,13 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
if n.typ, err = nodeType(interp, sc, n.child[2]); err != nil {
|
||||
return false
|
||||
}
|
||||
if isMethod(n) {
|
||||
ident := n.child[1].ident
|
||||
switch {
|
||||
case isMethod(n):
|
||||
// TODO(mpl): redeclaration detection
|
||||
// Add a method symbol in the receiver type name space
|
||||
var rcvrtype *itype
|
||||
n.ident = n.child[1].ident
|
||||
n.ident = ident
|
||||
rcvr := n.child[0].child[0]
|
||||
rtn := rcvr.lastChild()
|
||||
typeName := rtn.ident
|
||||
@@ -167,33 +169,32 @@ func (interp *Interpreter) gta(root *node, rpath, pkgID string) ([]*node, error)
|
||||
}
|
||||
rcvrtype.method = append(rcvrtype.method, n)
|
||||
n.child[0].child[0].lastChild().typ = rcvrtype
|
||||
} else {
|
||||
ident := n.child[1].ident
|
||||
case ident == "init":
|
||||
// TODO(mpl): use constant instead of hardcoded string?
|
||||
if ident != "init" {
|
||||
asImportName := filepath.Join(ident, baseName)
|
||||
if _, exists := sc.sym[asImportName]; exists {
|
||||
// redeclaration error
|
||||
// TODO(mpl): improve error with position of previous declaration.
|
||||
err = n.cfgErrorf("%s redeclared in this block", ident)
|
||||
return false
|
||||
}
|
||||
sym, exists := sc.sym[ident]
|
||||
if exists {
|
||||
// Make sure the symbol we found seems to be about another node, before calling
|
||||
// it a redeclaration.
|
||||
if sym.typ.isComplete() {
|
||||
// TODO(mpl): this check might be too permissive?
|
||||
if sym.kind != funcSym || sym.typ.cat != n.typ.cat || sym.node != n || sym.index != -1 {
|
||||
// redeclaration error
|
||||
// TODO(mpl): improve error with position of previous declaration.
|
||||
err = n.cfgErrorf("%s redeclared in this block", ident)
|
||||
return false
|
||||
}
|
||||
// init functions do not get declared as per the Go spec.
|
||||
default:
|
||||
asImportName := filepath.Join(ident, baseName)
|
||||
if _, exists := sc.sym[asImportName]; exists {
|
||||
// redeclaration error
|
||||
// TODO(mpl): improve error with position of previous declaration.
|
||||
err = n.cfgErrorf("%s redeclared in this block", ident)
|
||||
return false
|
||||
}
|
||||
sym, exists := sc.sym[ident]
|
||||
if exists {
|
||||
// Make sure the symbol we found seems to be about another node, before calling
|
||||
// it a redeclaration.
|
||||
if sym.typ.isComplete() {
|
||||
// TODO(mpl): this check might be too permissive?
|
||||
if sym.kind != funcSym || sym.typ.cat != n.typ.cat || sym.node != n || sym.index != -1 {
|
||||
// redeclaration error
|
||||
// TODO(mpl): improve error with position of previous declaration.
|
||||
err = n.cfgErrorf("%s redeclared in this block", ident)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add a function symbol in the package name space
|
||||
// Add a function symbol in the package name space except for init
|
||||
sc.sym[n.child[1].ident] = &symbol{kind: funcSym, typ: n.typ, node: n, index: -1}
|
||||
}
|
||||
if !n.typ.isComplete() {
|
||||
|
||||
103
interp/interp.go
103
interp/interp.go
@@ -3,6 +3,7 @@ package interp
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"go/scanner"
|
||||
@@ -14,6 +15,7 @@ import (
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
@@ -30,7 +32,7 @@ type node struct {
|
||||
index int64 // node index (dot display)
|
||||
findex int // index of value in frame or frame size (func def, type def)
|
||||
level int // number of frame indirections to access value
|
||||
nleft int // number of children in left part (assign)
|
||||
nleft int // number of children in left part (assign) or indicates preceding type (compositeLit)
|
||||
nright int // number of children in right part (assign)
|
||||
kind nkind // kind of node
|
||||
pos token.Pos // position in source code, relative to fset
|
||||
@@ -271,9 +273,9 @@ func initUniverse() *scope {
|
||||
"uintptr": {kind: typeSym, typ: &itype{cat: uintptrT, name: "uintptr"}},
|
||||
|
||||
// predefined Go constants
|
||||
"false": {kind: constSym, typ: &itype{cat: boolT, name: "bool"}, rval: reflect.ValueOf(false)},
|
||||
"true": {kind: constSym, typ: &itype{cat: boolT, name: "bool"}, rval: reflect.ValueOf(true)},
|
||||
"iota": {kind: constSym, typ: &itype{cat: intT}},
|
||||
"false": {kind: constSym, typ: untypedBool, rval: reflect.ValueOf(false)},
|
||||
"true": {kind: constSym, typ: untypedBool, rval: reflect.ValueOf(true)},
|
||||
"iota": {kind: constSym, typ: untypedInt},
|
||||
|
||||
// predefined Go zero value
|
||||
"nil": {typ: &itype{cat: nilT, untyped: true}},
|
||||
@@ -405,6 +407,13 @@ func (interp *Interpreter) Eval(src string) (res reflect.Value, err error) {
|
||||
// Execute node closures
|
||||
interp.run(root, nil)
|
||||
|
||||
// Wire and execute global vars
|
||||
n, err := genGlobalVars([]*node{root}, interp.scopes[interp.Name])
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
interp.run(n, nil)
|
||||
|
||||
for _, n := range initNodes {
|
||||
interp.run(n, interp.frame)
|
||||
}
|
||||
@@ -474,10 +483,33 @@ func (interp *Interpreter) Use(values Exports) {
|
||||
continue
|
||||
}
|
||||
|
||||
interp.binPkg[k] = v
|
||||
if interp.binPkg[k] == nil {
|
||||
interp.binPkg[k] = v
|
||||
continue
|
||||
}
|
||||
|
||||
for s, sym := range v {
|
||||
interp.binPkg[k][s] = sym
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ignoreScannerError returns true if the error from Go scanner can be safely ignored
|
||||
// to let the caller grab one more line before retrying to parse its input.
|
||||
func ignoreScannerError(e *scanner.Error, s string) bool {
|
||||
msg := e.Msg
|
||||
if strings.HasSuffix(msg, "found 'EOF'") {
|
||||
return true
|
||||
}
|
||||
if msg == "raw string literal not terminated" {
|
||||
return true
|
||||
}
|
||||
if strings.HasPrefix(msg, "expected operand, found '}'") && !strings.HasSuffix(s, "}") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// REPL performs a Read-Eval-Print-Loop on input reader.
|
||||
// Results are printed on output writer.
|
||||
func (interp *Interpreter) REPL(in io.Reader, out io.Writer) {
|
||||
@@ -494,29 +526,38 @@ func (interp *Interpreter) REPL(in io.Reader, out io.Writer) {
|
||||
sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT, path: k, scope: sc}}
|
||||
}
|
||||
|
||||
// Set prompt.
|
||||
var v reflect.Value
|
||||
var err error
|
||||
prompt := getPrompt(in, out)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
end := make(chan struct{}) // channel to terminate signal handling goroutine
|
||||
sig := make(chan os.Signal, 1) // channel to trap interrupt signal (Ctrl-C)
|
||||
prompt := getPrompt(in, out) // prompt activated on tty like IO stream
|
||||
s := bufio.NewScanner(in) // read input stream line by line
|
||||
var v reflect.Value // result value from eval
|
||||
var err error // error from eval
|
||||
src := "" // source string to evaluate
|
||||
signal.Notify(sig, os.Interrupt)
|
||||
prompt(v)
|
||||
|
||||
// Read, Eval, Print in a Loop.
|
||||
src := ""
|
||||
s := bufio.NewScanner(in)
|
||||
for s.Scan() {
|
||||
src += s.Text() + "\n"
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
handleSignal(ctx, cancel)
|
||||
|
||||
// The following goroutine handles interrupt signal by canceling eval.
|
||||
go func() {
|
||||
select {
|
||||
case <-sig:
|
||||
cancel()
|
||||
case <-end:
|
||||
}
|
||||
}()
|
||||
|
||||
v, err = interp.EvalWithContext(ctx, src)
|
||||
signal.Reset()
|
||||
if err != nil {
|
||||
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
|
||||
if len(e) == 0 || ignoreScannerError(e[0], s.Text()) {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintln(out, e[0])
|
||||
case Panic:
|
||||
fmt.Fprintln(out, e.Value)
|
||||
fmt.Fprintln(out, string(e.Stack))
|
||||
@@ -524,9 +565,20 @@ func (interp *Interpreter) REPL(in io.Reader, out io.Writer) {
|
||||
fmt.Fprintln(out, err)
|
||||
}
|
||||
}
|
||||
|
||||
if errors.Is(err, context.Canceled) {
|
||||
// Eval has been interrupted by the above signal handling goroutine.
|
||||
ctx, cancel = context.WithCancel(context.Background())
|
||||
} else {
|
||||
// No interrupt, release the above signal handling goroutine.
|
||||
end <- struct{}{}
|
||||
}
|
||||
|
||||
src = ""
|
||||
prompt(v)
|
||||
}
|
||||
cancel() // Do not defer, as cancel func may change over time.
|
||||
// TODO(mpl): log s.Err() if not nil?
|
||||
}
|
||||
|
||||
// Repl performs a Read-Eval-Print-Loop on input file descriptor.
|
||||
@@ -553,16 +605,3 @@ func getPrompt(in io.Reader, out io.Writer) func(reflect.Value) {
|
||||
}
|
||||
return func(reflect.Value) {}
|
||||
}
|
||||
|
||||
// handleSignal wraps signal handling for eval cancellation.
|
||||
func handleSignal(ctx context.Context, cancel context.CancelFunc) {
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
go func() {
|
||||
select {
|
||||
case <-c:
|
||||
cancel()
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
if filepath.Ext(file.Name()) != ".go" ||
|
||||
file.Name() == "assign11.go" || // expect error
|
||||
file.Name() == "assign12.go" || // expect error
|
||||
file.Name() == "assign15.go" || // expect error
|
||||
file.Name() == "bad0.go" || // expect error
|
||||
file.Name() == "const9.go" || // expect error
|
||||
file.Name() == "export1.go" || // non-main package
|
||||
@@ -44,6 +45,7 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
file.Name() == "fun22.go" || // expect error
|
||||
file.Name() == "if2.go" || // expect error
|
||||
file.Name() == "import6.go" || // expect error
|
||||
file.Name() == "init1.go" || // expect error
|
||||
file.Name() == "io0.go" || // use random number
|
||||
file.Name() == "op1.go" || // expect error
|
||||
file.Name() == "op7.go" || // expect error
|
||||
@@ -74,6 +76,10 @@ func TestInterpConsistencyBuild(t *testing.T) {
|
||||
file.Name() == "redeclaration-global4.go" || // expect error
|
||||
file.Name() == "redeclaration-global5.go" || // expect error
|
||||
file.Name() == "redeclaration-global6.go" || // expect error
|
||||
file.Name() == "restricted0.go" || // expect error
|
||||
file.Name() == "restricted1.go" || // expect error
|
||||
file.Name() == "restricted2.go" || // expect error
|
||||
file.Name() == "restricted3.go" || // expect error
|
||||
file.Name() == "server6.go" || // syntax parsing
|
||||
file.Name() == "server5.go" || // syntax parsing
|
||||
file.Name() == "server4.go" || // syntax parsing
|
||||
@@ -198,7 +204,7 @@ func TestInterpErrorConsistency(t *testing.T) {
|
||||
},
|
||||
{
|
||||
fileName: "op1.go",
|
||||
expectedInterp: "5:2: illegal operand types for '+=' operator",
|
||||
expectedInterp: "5:2: invalid operation: mismatched types int and float64",
|
||||
expectedExec: "5:4: constant 1.3 truncated to integer",
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
package interp_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -42,7 +47,9 @@ func TestEvalArithmetic(t *testing.T) {
|
||||
{desc: "mul_II", src: "2 * 3", res: "6"},
|
||||
{desc: "mul_FI", src: "2.2 * 3", res: "6.6"},
|
||||
{desc: "mul_IF", src: "3 * 2.2", res: "6.6"},
|
||||
{desc: "quo_Z", src: "3 / 0", err: "1:28: invalid operation: division by zero"},
|
||||
{desc: "rem_FI", src: "8.2 % 4", err: "1:28: invalid operation: operator % not defined on float64"},
|
||||
{desc: "rem_Z", src: "8 % 0", err: "1:28: invalid operation: division by zero"},
|
||||
{desc: "shl_II", src: "1 << 8", res: "256"},
|
||||
{desc: "shl_IN", src: "1 << -1", err: "1:28: invalid operation: shift count type int, must be integer"},
|
||||
{desc: "shl_IF", src: "1 << 1.0", res: "2"},
|
||||
@@ -55,9 +62,9 @@ func TestEvalArithmetic(t *testing.T) {
|
||||
{desc: "neg_I", src: "-2", res: "-2"},
|
||||
{desc: "pos_I", src: "+2", res: "2"},
|
||||
{desc: "bitnot_I", src: "^2", res: "-3"},
|
||||
{desc: "bitnot_F", src: "^0.2", err: "1:28: illegal operand type for '^' operator"},
|
||||
{desc: "bitnot_F", src: "^0.2", err: "1:28: invalid operation: operator ^ not defined on float64"},
|
||||
{desc: "not_B", src: "!false", res: "true"},
|
||||
{desc: "not_I", src: "!0", err: "1:28: illegal operand type for '!' operator"},
|
||||
{desc: "not_I", src: "!0", err: "1:28: invalid operation: operator ! not defined on int"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -65,9 +72,9 @@ func TestEvalAssign(t *testing.T) {
|
||||
i := interp.New(interp.Options{})
|
||||
runTests(t, i, []testCase{
|
||||
{src: `a := "Hello"; a += " world"`, res: "Hello world"},
|
||||
{src: `b := "Hello"; b += 1`, err: "1:42: illegal operand types for '+=' operator"},
|
||||
{src: `c := "Hello"; c -= " world"`, err: "1:42: illegal operand types for '-=' operator"},
|
||||
{src: "e := 64.4; e %= 64", err: "1:39: illegal operand types for '%=' operator"},
|
||||
{src: `b := "Hello"; b += 1`, err: "1:42: invalid operation: mismatched types string and int"},
|
||||
{src: `c := "Hello"; c -= " world"`, err: "1:42: invalid operation: operator -= not defined on string"},
|
||||
{src: "e := 64.4; e %= 64", err: "1:39: invalid operation: operator %= not defined on float64"},
|
||||
{src: "f := int64(3.2)", err: "1:33: truncated to integer"},
|
||||
{src: "g := 1; g <<= 8", res: "256"},
|
||||
{src: "h := 1; h >>= 8", res: "0"},
|
||||
@@ -309,6 +316,40 @@ func TestEvalCompositeArray(t *testing.T) {
|
||||
i := interp.New(interp.Options{})
|
||||
runTests(t, i, []testCase{
|
||||
{src: "a := []int{1, 2, 7: 20, 30}", res: "[1 2 0 0 0 0 0 20 30]"},
|
||||
{src: `a := []int{1, 1.2}`, err: "1:42: 6/5 truncated to int"},
|
||||
{src: `a := []int{0:1, 0:1}`, err: "1:46: duplicate index 0 in array or slice literal"},
|
||||
{src: `a := []int{1.1:1, 1.2:"test"}`, err: "1:39: index float64 must be integer constant"},
|
||||
{src: `a := [2]int{1, 1.2}`, err: "1:43: 6/5 truncated to int"},
|
||||
{src: `a := [1]int{1, 2}`, err: "1:43: index 1 is out of bounds (>= 1)"},
|
||||
})
|
||||
}
|
||||
|
||||
func TestEvalCompositeMap(t *testing.T) {
|
||||
i := interp.New(interp.Options{})
|
||||
runTests(t, i, []testCase{
|
||||
{src: `a := map[string]int{"one":1, "two":2}`, res: "map[one:1 two:2]"},
|
||||
{src: `a := map[string]int{1:1, 2:2}`, err: "1:48: cannot convert 1 to string"},
|
||||
{src: `a := map[string]int{"one":1, "two":2.2}`, err: "1:63: 11/5 truncated to int"},
|
||||
{src: `a := map[string]int{1, "two":2}`, err: "1:48: missing key in map literal"},
|
||||
{src: `a := map[string]int{"one":1, "one":2}`, err: "1:57: duplicate key one in map literal"},
|
||||
})
|
||||
}
|
||||
|
||||
func TestEvalCompositeStruct(t *testing.T) {
|
||||
i := interp.New(interp.Options{})
|
||||
runTests(t, i, []testCase{
|
||||
{src: `a := struct{A,B,C int}{}`, res: "{0 0 0}"},
|
||||
{src: `a := struct{A,B,C int}{1,2,3}`, res: "{1 2 3}"},
|
||||
{src: `a := struct{A,B,C int}{1,2.2,3}`, err: "1:53: 11/5 truncated to int"},
|
||||
{src: `a := struct{A,B,C int}{1,2}`, err: "1:53: too few values in struct literal"},
|
||||
{src: `a := struct{A,B,C int}{1,2,3,4}`, err: "1:57: too many values in struct literal"},
|
||||
{src: `a := struct{A,B,C int}{1,B:2,3}`, err: "1:53: mixture of field:value and value elements in struct literal"},
|
||||
{src: `a := struct{A,B,C int}{A:1,B:2,C:3}`, res: "{1 2 3}"},
|
||||
{src: `a := struct{A,B,C int}{B:2}`, res: "{0 2 0}"},
|
||||
{src: `a := struct{A,B,C int}{A:1,D:2,C:3}`, err: "1:55: unknown field D in struct literal"},
|
||||
{src: `a := struct{A,B,C int}{A:1,A:2,C:3}`, err: "1:55: duplicate field name A in struct literal"},
|
||||
{src: `a := struct{A,B,C int}{A:1,B:2.2,C:3}`, err: "1:57: 11/5 truncated to int"},
|
||||
{src: `a := struct{A,B,C int}{A:1,2,C:3}`, err: "1:55: mixture of field:value and value elements in struct literal"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -385,6 +426,25 @@ func TestEvalChan(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestEvalFunctionCallWithFunctionParam(t *testing.T) {
|
||||
i := interp.New(interp.Options{})
|
||||
eval(t, i, `
|
||||
func Bar(s string, fn func(string)string) string { return fn(s) }
|
||||
`)
|
||||
|
||||
v := eval(t, i, "Bar")
|
||||
bar := v.Interface().(func(string, func(string) string) string)
|
||||
|
||||
got := bar("hello ", func(s string) string {
|
||||
return s + "world!"
|
||||
})
|
||||
|
||||
want := "hello world!"
|
||||
if got != want {
|
||||
t.Errorf("unexpected result of function eval: got %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvalMissingSymbol(t *testing.T) {
|
||||
defer func() {
|
||||
r := recover()
|
||||
@@ -556,3 +616,117 @@ func assertEval(t *testing.T, i *interp.Interpreter, src, expectedError, expecte
|
||||
t.Fatalf("got %v, want %s", res, expectedRes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvalEOF(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
src []string
|
||||
errorLine int
|
||||
}{
|
||||
{
|
||||
desc: "no error",
|
||||
src: []string{
|
||||
`func main() {`,
|
||||
`println("foo")`,
|
||||
`}`,
|
||||
},
|
||||
errorLine: -1,
|
||||
},
|
||||
{
|
||||
desc: "no parsing error, but block error",
|
||||
src: []string{
|
||||
`func main() {`,
|
||||
`println(foo)`,
|
||||
`}`,
|
||||
},
|
||||
errorLine: 2,
|
||||
},
|
||||
{
|
||||
desc: "parsing error",
|
||||
src: []string{
|
||||
`func main() {`,
|
||||
`println(/foo)`,
|
||||
`}`,
|
||||
},
|
||||
errorLine: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for it, test := range tests {
|
||||
i := interp.New(interp.Options{})
|
||||
var stderr bytes.Buffer
|
||||
safeStderr := &safeBuffer{buf: &stderr}
|
||||
pin, pout := io.Pipe()
|
||||
defer func() {
|
||||
// Closing the pipe also takes care of making i.REPL terminate,
|
||||
// hence freeing its goroutine.
|
||||
_ = pin.Close()
|
||||
_ = pout.Close()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
i.REPL(pin, safeStderr)
|
||||
}()
|
||||
for k, v := range test.src {
|
||||
if _, err := pout.Write([]byte(v + "\n")); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
Sleep(100 * time.Millisecond)
|
||||
|
||||
errMsg := safeStderr.String()
|
||||
if k == test.errorLine {
|
||||
if errMsg == "" {
|
||||
t.Fatalf("%d: statement %q should have produced an error", it, v)
|
||||
}
|
||||
break
|
||||
}
|
||||
if errMsg != "" {
|
||||
t.Fatalf("%d: unexpected error: %v", it, errMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type safeBuffer struct {
|
||||
mu sync.RWMutex
|
||||
buf *bytes.Buffer
|
||||
}
|
||||
|
||||
func (sb *safeBuffer) Read(p []byte) (int, error) {
|
||||
return sb.buf.Read(p)
|
||||
}
|
||||
|
||||
func (sb *safeBuffer) String() string {
|
||||
sb.mu.RLock()
|
||||
defer sb.mu.RUnlock()
|
||||
return sb.buf.String()
|
||||
}
|
||||
|
||||
func (sb *safeBuffer) Write(p []byte) (int, error) {
|
||||
sb.mu.Lock()
|
||||
defer sb.mu.Unlock()
|
||||
return sb.buf.Write(p)
|
||||
}
|
||||
|
||||
const (
|
||||
// CITimeoutMultiplier is the multiplier for all timeouts in the CI.
|
||||
CITimeoutMultiplier = 3
|
||||
)
|
||||
|
||||
// Sleep pauses the current goroutine for at least the duration d.
|
||||
func Sleep(d time.Duration) {
|
||||
d = applyCIMultiplier(d)
|
||||
time.Sleep(d)
|
||||
}
|
||||
|
||||
func applyCIMultiplier(timeout time.Duration) time.Duration {
|
||||
ci := os.Getenv("CI")
|
||||
if ci == "" {
|
||||
return timeout
|
||||
}
|
||||
b, err := strconv.ParseBool(ci)
|
||||
if err != nil || !b {
|
||||
return timeout
|
||||
}
|
||||
return time.Duration(float64(timeout) * CITimeoutMultiplier)
|
||||
}
|
||||
|
||||
@@ -35,12 +35,12 @@ func TestIsNatural(t *testing.T) {
|
||||
n: &node{
|
||||
typ: &itype{
|
||||
rtype: func() reflect.Type {
|
||||
var x = 3
|
||||
x := 3
|
||||
return reflect.TypeOf(x)
|
||||
}(),
|
||||
},
|
||||
rval: func() reflect.Value {
|
||||
var x = 3
|
||||
x := 3
|
||||
return reflect.ValueOf(x)
|
||||
}(),
|
||||
},
|
||||
|
||||
312
interp/op.go
312
interp/op.go
@@ -867,7 +867,7 @@ func shl(n *node) {
|
||||
|
||||
func shlConst(n *node) {
|
||||
v0, v1 := n.child[0].rval, n.child[1].rval
|
||||
isConst := (v0.IsValid() && isConstantValue(v0.Type())) && (v1.IsValid() && isConstantValue(v1.Type()))
|
||||
isConst := (v0.IsValid() && isConstantValue(v0.Type()))
|
||||
t := n.typ.rtype
|
||||
if isConst {
|
||||
t = constVal
|
||||
@@ -875,8 +875,7 @@ func shlConst(n *node) {
|
||||
n.rval = reflect.New(t).Elem()
|
||||
switch {
|
||||
case isConst:
|
||||
s, _ := constant.Uint64Val(vConstantValue(v1))
|
||||
v := constant.Shift(vConstantValue(v0), token.SHL, uint(s))
|
||||
v := constant.Shift(vConstantValue(v0), token.SHL, uint(vUint(v1)))
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
case isUint(t):
|
||||
n.rval.SetUint(vUint(v0) << vUint(v1))
|
||||
@@ -953,7 +952,7 @@ func shr(n *node) {
|
||||
|
||||
func shrConst(n *node) {
|
||||
v0, v1 := n.child[0].rval, n.child[1].rval
|
||||
isConst := (v0.IsValid() && isConstantValue(v0.Type())) && (v1.IsValid() && isConstantValue(v1.Type()))
|
||||
isConst := (v0.IsValid() && isConstantValue(v0.Type()))
|
||||
t := n.typ.rtype
|
||||
if isConst {
|
||||
t = constVal
|
||||
@@ -961,8 +960,7 @@ func shrConst(n *node) {
|
||||
n.rval = reflect.New(t).Elem()
|
||||
switch {
|
||||
case isConst:
|
||||
s, _ := constant.Uint64Val(vConstantValue(v1))
|
||||
v := constant.Shift(vConstantValue(v0), token.SHR, uint(s))
|
||||
v := constant.Shift(vConstantValue(v0), token.SHR, uint(vUint(v1)))
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
case isUint(t):
|
||||
n.rval.SetUint(vUint(v0) >> vUint(v1))
|
||||
@@ -2053,7 +2051,7 @@ func equal(n *node) {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
dest(f).SetBool(i0 == i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
@@ -2075,7 +2073,7 @@ func equal(n *node) {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
dest(f).SetBool(i0 == i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
@@ -2099,7 +2097,7 @@ func equal(n *node) {
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
dest(f).SetBool(i0 == i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
@@ -2536,80 +2534,6 @@ func greater(n *node) {
|
||||
dest := genValueOutput(n, reflect.TypeOf(true))
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
|
||||
if c0.typ.cat == aliasT || c1.typ.cat == aliasT {
|
||||
switch {
|
||||
case c0.rval.IsValid():
|
||||
i0 := c0.rval.Interface()
|
||||
v1 := genValue(c1)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
case c1.rval.IsValid():
|
||||
i1 := c1.rval.Interface()
|
||||
v0 := genValue(c0)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
default:
|
||||
v0 := genValue(c0)
|
||||
v1 := genValue(c1)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
case isString(t0) || isString(t1):
|
||||
switch {
|
||||
@@ -2899,80 +2823,6 @@ func greaterEqual(n *node) {
|
||||
dest := genValueOutput(n, reflect.TypeOf(true))
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
|
||||
if c0.typ.cat == aliasT || c1.typ.cat == aliasT {
|
||||
switch {
|
||||
case c0.rval.IsValid():
|
||||
i0 := c0.rval.Interface()
|
||||
v1 := genValue(c1)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
case c1.rval.IsValid():
|
||||
i1 := c1.rval.Interface()
|
||||
v0 := genValue(c0)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
default:
|
||||
v0 := genValue(c0)
|
||||
v1 := genValue(c1)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
case isString(t0) || isString(t1):
|
||||
switch {
|
||||
@@ -3262,80 +3112,6 @@ func lower(n *node) {
|
||||
dest := genValueOutput(n, reflect.TypeOf(true))
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
|
||||
if c0.typ.cat == aliasT || c1.typ.cat == aliasT {
|
||||
switch {
|
||||
case c0.rval.IsValid():
|
||||
i0 := c0.rval.Interface()
|
||||
v1 := genValue(c1)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
case c1.rval.IsValid():
|
||||
i1 := c1.rval.Interface()
|
||||
v0 := genValue(c0)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
default:
|
||||
v0 := genValue(c0)
|
||||
v1 := genValue(c1)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
case isString(t0) || isString(t1):
|
||||
switch {
|
||||
@@ -3625,80 +3401,6 @@ func lowerEqual(n *node) {
|
||||
dest := genValueOutput(n, reflect.TypeOf(true))
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
|
||||
if c0.typ.cat == aliasT || c1.typ.cat == aliasT {
|
||||
switch {
|
||||
case c0.rval.IsValid():
|
||||
i0 := c0.rval.Interface()
|
||||
v1 := genValue(c1)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
case c1.rval.IsValid():
|
||||
i1 := c1.rval.Interface()
|
||||
v0 := genValue(c0)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
default:
|
||||
v0 := genValue(c0)
|
||||
v1 := genValue(c1)
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
if i0 != i1 {
|
||||
dest(f).SetBool(true)
|
||||
return tnext
|
||||
}
|
||||
dest(f).SetBool(false)
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
dest := genValue(n)
|
||||
n.exec = func(f *frame) bltn {
|
||||
i0 := v0(f).Interface()
|
||||
i1 := v1(f).Interface()
|
||||
dest(f).SetBool(i0 != i1)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf(); {
|
||||
case isString(t0) || isString(t1):
|
||||
switch {
|
||||
|
||||
294
interp/run.go
294
interp/run.go
@@ -7,6 +7,7 @@ import (
|
||||
"go/constant"
|
||||
"log"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// bltn type defines functions which run at CFG execution.
|
||||
@@ -83,6 +84,9 @@ func init() {
|
||||
}
|
||||
|
||||
func (interp *Interpreter) run(n *node, cf *frame) {
|
||||
if n == nil {
|
||||
return
|
||||
}
|
||||
var f *frame
|
||||
if cf == nil {
|
||||
f = interp.frame
|
||||
@@ -229,10 +233,11 @@ func typeAssert(n *node) {
|
||||
|
||||
func typeAssert2(n *node) {
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
value := genValue(c0) // input value
|
||||
value0 := genValue(n.anc.child[0]) // returned result
|
||||
value1 := genValue(n.anc.child[1]) // returned status
|
||||
typ := c1.typ // type to assert or convert to
|
||||
value := genValue(c0) // input value
|
||||
value0 := genValue(n.anc.child[0]) // returned result
|
||||
value1 := genValue(n.anc.child[1]) // returned status
|
||||
setStatus := n.anc.child[1].ident != "_" // do not assign status to "_"
|
||||
typ := c1.typ // type to assert or convert to
|
||||
typID := typ.id()
|
||||
rtype := typ.rtype // type to assert
|
||||
next := getExec(n.tnext)
|
||||
@@ -246,7 +251,9 @@ func typeAssert2(n *node) {
|
||||
} else {
|
||||
ok = false
|
||||
}
|
||||
value1(f).SetBool(ok)
|
||||
if setStatus {
|
||||
value1(f).SetBool(ok)
|
||||
}
|
||||
return next
|
||||
}
|
||||
case isInterface(typ):
|
||||
@@ -256,7 +263,9 @@ func typeAssert2(n *node) {
|
||||
if ok {
|
||||
value0(f).Set(v)
|
||||
}
|
||||
value1(f).SetBool(ok)
|
||||
if setStatus {
|
||||
value1(f).SetBool(ok)
|
||||
}
|
||||
return next
|
||||
}
|
||||
case n.child[0].typ.cat == valueT:
|
||||
@@ -266,7 +275,9 @@ func typeAssert2(n *node) {
|
||||
if ok {
|
||||
value0(f).Set(v)
|
||||
}
|
||||
value1(f).SetBool(ok)
|
||||
if setStatus {
|
||||
value1(f).SetBool(ok)
|
||||
}
|
||||
return next
|
||||
}
|
||||
default:
|
||||
@@ -276,13 +287,18 @@ func typeAssert2(n *node) {
|
||||
if ok {
|
||||
value0(f).Set(v.value)
|
||||
}
|
||||
value1(f).SetBool(ok)
|
||||
if setStatus {
|
||||
value1(f).SetBool(ok)
|
||||
}
|
||||
return next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func canAssertTypes(src, dest reflect.Type) bool {
|
||||
if dest == nil {
|
||||
return false
|
||||
}
|
||||
if src == dest {
|
||||
return true
|
||||
}
|
||||
@@ -351,14 +367,16 @@ func convert(n *node) {
|
||||
}
|
||||
}
|
||||
|
||||
func isRecursiveStruct(t *itype, rtype reflect.Type) bool {
|
||||
func isRecursiveType(t *itype, rtype reflect.Type) bool {
|
||||
if t.cat == structT && rtype.Kind() == reflect.Interface {
|
||||
return true
|
||||
}
|
||||
if t.cat == ptrT && t.rtype != nil {
|
||||
return isRecursiveStruct(t.val, t.rtype.Elem())
|
||||
switch t.cat {
|
||||
case ptrT, arrayT, mapT:
|
||||
return isRecursiveType(t.val, t.val.rtype)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func assign(n *node) {
|
||||
@@ -387,8 +405,10 @@ func assign(n *node) {
|
||||
case src.kind == basicLit && src.val == nil:
|
||||
t := dest.typ.TypeOf()
|
||||
svalue[i] = func(*frame) reflect.Value { return reflect.New(t).Elem() }
|
||||
case isRecursiveStruct(dest.typ, dest.typ.rtype):
|
||||
svalue[i] = genValueInterfacePtr(src)
|
||||
case isRecursiveType(dest.typ, dest.typ.rtype):
|
||||
svalue[i] = genValueRecursiveInterface(src, dest.typ.rtype)
|
||||
case isRecursiveType(src.typ, src.typ.rtype):
|
||||
svalue[i] = genValueRecursiveInterfacePtrValue(src)
|
||||
case src.typ.untyped && isComplex(dest.typ.TypeOf()):
|
||||
svalue[i] = genValueComplex(src)
|
||||
case src.typ.untyped && !dest.typ.untyped:
|
||||
@@ -505,19 +525,21 @@ func deref(n *node) {
|
||||
value := genValue(n.child[0])
|
||||
tnext := getExec(n.tnext)
|
||||
i := n.findex
|
||||
l := n.level
|
||||
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value(f).Elem()
|
||||
if f.data[i].Bool() {
|
||||
r := value(f).Elem()
|
||||
if r.Bool() {
|
||||
getFrame(f, l).data[i] = r
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value(f).Elem()
|
||||
getFrame(f, l).data[i] = value(f).Elem()
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
@@ -672,9 +694,13 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
|
||||
// Copy function input arguments in local frame
|
||||
for i, arg := range in {
|
||||
if def.typ.arg[i].cat == interfaceT {
|
||||
typ := def.typ.arg[i]
|
||||
switch {
|
||||
case typ.cat == interfaceT:
|
||||
d[i].Set(reflect.ValueOf(valueInterface{value: arg.Elem()}))
|
||||
} else {
|
||||
case typ.cat == funcT && arg.Kind() == reflect.Func:
|
||||
d[i].Set(reflect.ValueOf(genFunctionNode(arg)))
|
||||
default:
|
||||
d[i].Set(arg)
|
||||
}
|
||||
}
|
||||
@@ -698,6 +724,10 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
}
|
||||
|
||||
func genFunctionNode(v reflect.Value) *node {
|
||||
return &node{kind: funcType, action: aNop, rval: v, typ: &itype{cat: valueT, rtype: v.Type()}}
|
||||
}
|
||||
|
||||
func genInterfaceWrapper(n *node, typ reflect.Type) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
if typ == nil || typ.Kind() != reflect.Interface || typ.NumMethod() == 0 || n.typ.cat == valueT {
|
||||
@@ -762,7 +792,7 @@ func call(n *node) {
|
||||
var values []func(*frame) reflect.Value
|
||||
if n.child[0].recv != nil {
|
||||
// Compute method receiver value.
|
||||
if isRecursiveStruct(n.child[0].recv.node.typ, n.child[0].recv.node.typ.rtype) {
|
||||
if isRecursiveType(n.child[0].recv.node.typ, n.child[0].recv.node.typ.rtype) {
|
||||
values = append(values, genValueRecvInterfacePtr(n.child[0]))
|
||||
} else {
|
||||
values = append(values, genValueRecv(n.child[0]))
|
||||
@@ -796,20 +826,21 @@ func call(n *node) {
|
||||
values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
|
||||
}
|
||||
default:
|
||||
var arg *itype
|
||||
if variadic >= 0 && i >= variadic {
|
||||
arg = n.child[0].typ.arg[variadic].val
|
||||
} else {
|
||||
arg = n.child[0].typ.arg[i]
|
||||
}
|
||||
if c.kind == basicLit || c.rval.IsValid() {
|
||||
var argType reflect.Type
|
||||
if variadic >= 0 && i >= variadic {
|
||||
argType = n.child[0].typ.arg[variadic].val.TypeOf()
|
||||
} else {
|
||||
argType = n.child[0].typ.arg[i].TypeOf()
|
||||
}
|
||||
argType := arg.TypeOf()
|
||||
convertLiteralValue(c, argType)
|
||||
}
|
||||
switch {
|
||||
case len(n.child[0].typ.arg) > i && n.child[0].typ.arg[i].cat == interfaceT:
|
||||
case arg.cat == interfaceT:
|
||||
values = append(values, genValueInterface(c))
|
||||
case isRecursiveStruct(c.typ, c.typ.rtype):
|
||||
values = append(values, genValueDerefInterfacePtr(c))
|
||||
case isRecursiveType(c.typ, c.typ.rtype):
|
||||
values = append(values, genValueRecursiveInterfacePtrValue(c))
|
||||
default:
|
||||
values = append(values, genValue(c))
|
||||
}
|
||||
@@ -852,9 +883,10 @@ func call(n *node) {
|
||||
}
|
||||
default:
|
||||
// Multiple return values frame index are indexed from the node frame index.
|
||||
l := n.level
|
||||
for i := range rtypes {
|
||||
j := n.findex + i
|
||||
rvalues[i] = func(f *frame) reflect.Value { return f.data[j] }
|
||||
rvalues[i] = func(f *frame) reflect.Value { return getFrame(f, l).data[j] }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1006,6 +1038,21 @@ func pindex(i, variadic int) int {
|
||||
return variadic
|
||||
}
|
||||
|
||||
func getFrame(f *frame, l int) *frame {
|
||||
switch l {
|
||||
case 0:
|
||||
return f
|
||||
case 1:
|
||||
return f.anc
|
||||
case 2:
|
||||
return f.anc.anc
|
||||
}
|
||||
for ; l > 0; l-- {
|
||||
f = f.anc
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// Callbin calls a function from a bin import, accessible through reflect.
|
||||
func callBin(n *node) {
|
||||
tnext := getExec(n.tnext)
|
||||
@@ -1033,7 +1080,7 @@ func callBin(n *node) {
|
||||
}
|
||||
|
||||
for i, c := range child {
|
||||
defType := funcType.In(pindex(i, variadic))
|
||||
defType := funcType.In(rcvrOffset + pindex(i, variadic))
|
||||
switch {
|
||||
case isBinCall(c):
|
||||
// Handle nested function calls: pass returned values as arguments
|
||||
@@ -1067,7 +1114,7 @@ func callBin(n *node) {
|
||||
values = append(values, genFunctionWrapper(c))
|
||||
case interfaceT:
|
||||
values = append(values, genValueInterfaceValue(c))
|
||||
case arrayT:
|
||||
case arrayT, variadicT:
|
||||
switch c.typ.val.cat {
|
||||
case interfaceT:
|
||||
values = append(values, genValueInterfaceArray(c))
|
||||
@@ -1106,6 +1153,7 @@ func callBin(n *node) {
|
||||
case fnext != nil:
|
||||
// Handle branching according to boolean result.
|
||||
index := n.findex
|
||||
level := n.level
|
||||
n.exec = func(f *frame) bltn {
|
||||
in := make([]reflect.Value, l)
|
||||
for i, v := range values {
|
||||
@@ -1113,7 +1161,7 @@ func callBin(n *node) {
|
||||
}
|
||||
res := callFn(value(f), in)
|
||||
b := res[0].Bool()
|
||||
f.data[index].SetBool(b)
|
||||
getFrame(f, level).data[index].SetBool(b)
|
||||
if b {
|
||||
return tnext
|
||||
}
|
||||
@@ -1169,7 +1217,7 @@ func callBin(n *node) {
|
||||
}
|
||||
out := callFn(value(f), in)
|
||||
for i := 0; i < len(out); i++ {
|
||||
f.data[n.findex+i].Set(out[i])
|
||||
getFrame(f, n.level).data[n.findex+i].Set(out[i])
|
||||
}
|
||||
return tnext
|
||||
}
|
||||
@@ -1180,6 +1228,7 @@ func callBin(n *node) {
|
||||
func getIndexBinMethod(n *node) {
|
||||
// dest := genValue(n)
|
||||
i := n.findex
|
||||
l := n.level
|
||||
m := n.val.(int)
|
||||
value := genValue(n.child[0])
|
||||
next := getExec(n.tnext)
|
||||
@@ -1187,20 +1236,21 @@ func getIndexBinMethod(n *node) {
|
||||
n.exec = func(f *frame) bltn {
|
||||
// Can not use .Set() because dest type contains the receiver and source not
|
||||
// dest(f).Set(value(f).Method(m))
|
||||
f.data[i] = value(f).Method(m)
|
||||
getFrame(f, l).data[i] = value(f).Method(m)
|
||||
return next
|
||||
}
|
||||
}
|
||||
|
||||
func getIndexBinPtrMethod(n *node) {
|
||||
i := n.findex
|
||||
l := n.level
|
||||
m := n.val.(int)
|
||||
value := genValue(n.child[0])
|
||||
next := getExec(n.tnext)
|
||||
|
||||
n.exec = func(f *frame) bltn {
|
||||
// Can not use .Set() because dest type contains the receiver and source not
|
||||
f.data[i] = value(f).Addr().Method(m)
|
||||
getFrame(f, l).data[i] = value(f).Addr().Method(m)
|
||||
return next
|
||||
}
|
||||
}
|
||||
@@ -1210,21 +1260,23 @@ func getIndexArray(n *node) {
|
||||
tnext := getExec(n.tnext)
|
||||
value0 := genValueArray(n.child[0]) // array
|
||||
i := n.findex
|
||||
l := n.level
|
||||
|
||||
if n.child[1].rval.IsValid() { // constant array index
|
||||
ai := int(vInt(n.child[1].rval))
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value0(f).Index(ai)
|
||||
if f.data[i].Bool() {
|
||||
r := value0(f).Index(ai)
|
||||
getFrame(f, l).data[i] = r
|
||||
if r.Bool() {
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value0(f).Index(ai)
|
||||
getFrame(f, l).data[i] = value0(f).Index(ai)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
@@ -1235,8 +1287,9 @@ func getIndexArray(n *node) {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
_, vi := value1(f)
|
||||
f.data[i] = value0(f).Index(int(vi))
|
||||
if f.data[i].Bool() {
|
||||
r := value0(f).Index(int(vi))
|
||||
getFrame(f, l).data[i] = r
|
||||
if r.Bool() {
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
@@ -1244,7 +1297,7 @@ func getIndexArray(n *node) {
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
_, vi := value1(f)
|
||||
f.data[i] = value0(f).Index(int(vi))
|
||||
getFrame(f, l).data[i] = value0(f).Index(int(vi))
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
@@ -1262,7 +1315,6 @@ func getIndexMap(n *node) {
|
||||
z := reflect.New(n.child[0].typ.frameType().Elem()).Elem()
|
||||
|
||||
if n.child[1].rval.IsValid() { // constant map index
|
||||
convertConstantValue(n.child[1])
|
||||
mi := n.child[1].rval
|
||||
|
||||
switch {
|
||||
@@ -1356,7 +1408,6 @@ func getIndexMap2(n *node) {
|
||||
return
|
||||
}
|
||||
if n.child[1].rval.IsValid() { // constant map index
|
||||
convertConstantValue(n.child[1])
|
||||
mi := n.child[1].rval
|
||||
switch {
|
||||
case !doValue:
|
||||
@@ -1447,6 +1498,7 @@ func getFunc(n *node) {
|
||||
|
||||
func getMethod(n *node) {
|
||||
i := n.findex
|
||||
l := n.level
|
||||
next := getExec(n.tnext)
|
||||
|
||||
n.exec = func(f *frame) bltn {
|
||||
@@ -1455,7 +1507,7 @@ func getMethod(n *node) {
|
||||
nod.val = &nod
|
||||
nod.recv = n.recv
|
||||
nod.frame = fr
|
||||
f.data[i] = reflect.ValueOf(&nod)
|
||||
getFrame(f, l).data[i] = reflect.ValueOf(&nod)
|
||||
return next
|
||||
}
|
||||
}
|
||||
@@ -1465,6 +1517,7 @@ func getMethodByName(n *node) {
|
||||
value0 := genValue(n.child[0])
|
||||
name := n.child[1].ident
|
||||
i := n.findex
|
||||
l := n.level
|
||||
|
||||
n.exec = func(f *frame) bltn {
|
||||
val := value0(f).Interface().(valueInterface)
|
||||
@@ -1474,7 +1527,7 @@ func getMethodByName(n *node) {
|
||||
nod.val = &nod
|
||||
nod.recv = &receiver{nil, val.value, li}
|
||||
nod.frame = fr
|
||||
f.data[i] = reflect.ValueOf(&nod)
|
||||
getFrame(f, l).data[i] = reflect.ValueOf(&nod)
|
||||
return next
|
||||
}
|
||||
}
|
||||
@@ -1484,6 +1537,7 @@ func getIndexSeq(n *node) {
|
||||
index := n.val.([]int)
|
||||
tnext := getExec(n.tnext)
|
||||
i := n.findex
|
||||
l := n.level
|
||||
|
||||
// Note:
|
||||
// Here we have to store the result using
|
||||
@@ -1498,44 +1552,63 @@ func getIndexSeq(n *node) {
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value(f).FieldByIndex(index)
|
||||
if f.data[i].Bool() {
|
||||
v := value(f)
|
||||
if v.Type().Kind() == reflect.Interface && n.child[0].typ.recursive {
|
||||
v = writableDeref(v)
|
||||
}
|
||||
r := v.FieldByIndex(index)
|
||||
getFrame(f, l).data[i] = r
|
||||
if r.Bool() {
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value(f).FieldByIndex(index)
|
||||
v := value(f)
|
||||
if v.Type().Kind() == reflect.Interface && n.child[0].typ.recursive {
|
||||
v = writableDeref(v)
|
||||
}
|
||||
getFrame(f, l).data[i] = v.FieldByIndex(index)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//go:nocheckptr
|
||||
func writableDeref(v reflect.Value) reflect.Value {
|
||||
// Here we have an interface to a struct. Any attempt to dereference it will
|
||||
// make a copy of the struct. We need to get a Value to the actual struct.
|
||||
// TODO: using unsafe is a temporary measure. Rethink this.
|
||||
return reflect.NewAt(v.Elem().Type(), unsafe.Pointer(v.InterfaceData()[1])).Elem() //nolint:govet
|
||||
}
|
||||
|
||||
func getPtrIndexSeq(n *node) {
|
||||
index := n.val.([]int)
|
||||
tnext := getExec(n.tnext)
|
||||
var value func(*frame) reflect.Value
|
||||
if isRecursiveStruct(n.child[0].typ, n.child[0].typ.rtype) {
|
||||
if isRecursiveType(n.child[0].typ, n.child[0].typ.rtype) {
|
||||
v := genValue(n.child[0])
|
||||
value = func(f *frame) reflect.Value { return v(f).Elem().Elem() }
|
||||
} else {
|
||||
value = genValue(n.child[0])
|
||||
}
|
||||
i := n.findex
|
||||
l := n.level
|
||||
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value(f).Elem().FieldByIndex(index)
|
||||
if f.data[i].Bool() {
|
||||
r := value(f).Elem().FieldByIndex(index)
|
||||
getFrame(f, l).data[i] = r
|
||||
if r.Bool() {
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value(f).Elem().FieldByIndex(index)
|
||||
getFrame(f, l).data[i] = value(f).Elem().FieldByIndex(index)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
@@ -1545,22 +1618,25 @@ func getIndexSeqField(n *node) {
|
||||
value := genValue(n.child[0])
|
||||
index := n.val.([]int)
|
||||
i := n.findex
|
||||
l := n.level
|
||||
tnext := getExec(n.tnext)
|
||||
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
if n.child[0].typ.TypeOf().Kind() == reflect.Ptr {
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value(f).Elem().FieldByIndex(index)
|
||||
if f.data[i].Bool() {
|
||||
r := value(f).Elem().FieldByIndex(index)
|
||||
getFrame(f, l).data[i] = r
|
||||
if r.Bool() {
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
}
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value(f).FieldByIndex(index)
|
||||
if f.data[i].Bool() {
|
||||
r := value(f).FieldByIndex(index)
|
||||
getFrame(f, l).data[i] = r
|
||||
if r.Bool() {
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
@@ -1569,12 +1645,12 @@ func getIndexSeqField(n *node) {
|
||||
} else {
|
||||
if n.child[0].typ.TypeOf().Kind() == reflect.Ptr {
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value(f).Elem().FieldByIndex(index)
|
||||
getFrame(f, l).data[i] = value(f).Elem().FieldByIndex(index)
|
||||
return tnext
|
||||
}
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value(f).FieldByIndex(index)
|
||||
getFrame(f, l).data[i] = value(f).FieldByIndex(index)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
@@ -1587,16 +1663,17 @@ func getIndexSeqPtrMethod(n *node) {
|
||||
fi := index[1:]
|
||||
mi := index[0]
|
||||
i := n.findex
|
||||
l := n.level
|
||||
next := getExec(n.tnext)
|
||||
|
||||
if n.child[0].typ.TypeOf().Kind() == reflect.Ptr {
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value(f).Elem().FieldByIndex(fi).Addr().Method(mi)
|
||||
getFrame(f, l).data[i] = value(f).Elem().FieldByIndex(fi).Addr().Method(mi)
|
||||
return next
|
||||
}
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value(f).FieldByIndex(fi).Addr().Method(mi)
|
||||
getFrame(f, l).data[i] = value(f).FieldByIndex(fi).Addr().Method(mi)
|
||||
return next
|
||||
}
|
||||
}
|
||||
@@ -1608,16 +1685,17 @@ func getIndexSeqMethod(n *node) {
|
||||
fi := index[1:]
|
||||
mi := index[0]
|
||||
i := n.findex
|
||||
l := n.level
|
||||
next := getExec(n.tnext)
|
||||
|
||||
if n.child[0].typ.TypeOf().Kind() == reflect.Ptr {
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value(f).Elem().FieldByIndex(fi).Method(mi)
|
||||
getFrame(f, l).data[i] = value(f).Elem().FieldByIndex(fi).Method(mi)
|
||||
return next
|
||||
}
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value(f).FieldByIndex(fi).Method(mi)
|
||||
getFrame(f, l).data[i] = value(f).FieldByIndex(fi).Method(mi)
|
||||
return next
|
||||
}
|
||||
}
|
||||
@@ -1814,7 +1892,7 @@ func arrayLit(n *node) {
|
||||
value := valueGenerator(n, n.findex)
|
||||
next := getExec(n.tnext)
|
||||
child := n.child
|
||||
if !n.typ.untyped {
|
||||
if n.nleft == 1 {
|
||||
child = n.child[1:]
|
||||
}
|
||||
|
||||
@@ -1867,7 +1945,7 @@ func mapLit(n *node) {
|
||||
value := valueGenerator(n, n.findex)
|
||||
next := getExec(n.tnext)
|
||||
child := n.child
|
||||
if !n.typ.untyped {
|
||||
if n.nleft == 1 {
|
||||
child = n.child[1:]
|
||||
}
|
||||
typ := n.typ.TypeOf()
|
||||
@@ -1902,7 +1980,7 @@ func compositeBinMap(n *node) {
|
||||
value := valueGenerator(n, n.findex)
|
||||
next := getExec(n.tnext)
|
||||
child := n.child
|
||||
if !n.typ.untyped {
|
||||
if n.nleft == 1 {
|
||||
child = n.child[1:]
|
||||
}
|
||||
typ := n.typ.TypeOf()
|
||||
@@ -1946,10 +2024,11 @@ func compositeBinStruct(n *node) {
|
||||
}
|
||||
} else {
|
||||
fieldIndex[i] = []int{i}
|
||||
convertLiteralValue(c.child[1], typ.Field(i).Type)
|
||||
if c.typ.cat == funcT {
|
||||
convertLiteralValue(c.child[1], typ.Field(i).Type)
|
||||
values[i] = genFunctionWrapper(c.child[1])
|
||||
} else {
|
||||
convertLiteralValue(c, typ.Field(i).Type)
|
||||
values[i] = genValue(c)
|
||||
}
|
||||
}
|
||||
@@ -1995,6 +2074,7 @@ func doCompositeLit(n *node, hasType bool) {
|
||||
}
|
||||
|
||||
i := n.findex
|
||||
l := n.level
|
||||
n.exec = func(f *frame) bltn {
|
||||
a := reflect.New(n.typ.TypeOf()).Elem()
|
||||
for i, v := range values {
|
||||
@@ -2006,7 +2086,7 @@ func doCompositeLit(n *node, hasType bool) {
|
||||
case destInterface:
|
||||
d.Set(reflect.ValueOf(valueInterface{n, a}))
|
||||
default:
|
||||
f.data[i] = a
|
||||
getFrame(f, l).data[i] = a
|
||||
}
|
||||
return next
|
||||
}
|
||||
@@ -2023,6 +2103,7 @@ func doCompositeSparse(n *node, hasType bool) {
|
||||
if hasType {
|
||||
child = n.child[1:]
|
||||
}
|
||||
destInterface := destType(n).cat == interfaceT
|
||||
|
||||
values := make(map[int]func(*frame) reflect.Value)
|
||||
a, _ := n.typ.zero()
|
||||
@@ -2033,8 +2114,10 @@ func doCompositeSparse(n *node, hasType bool) {
|
||||
switch {
|
||||
case c1.typ.cat == funcT:
|
||||
values[field] = genFunctionWrapper(c1)
|
||||
case isRecursiveStruct(n.typ.field[field].typ, n.typ.field[field].typ.rtype):
|
||||
values[field] = genValueInterfacePtr(c1)
|
||||
case isArray(c1.typ) && c1.typ.val != nil && c1.typ.val.cat == interfaceT:
|
||||
values[field] = genValueInterfaceArray(c1)
|
||||
case isRecursiveType(n.typ.field[field].typ, n.typ.field[field].typ.rtype):
|
||||
values[field] = genValueRecursiveInterface(c1, n.typ.field[field].typ.rtype)
|
||||
default:
|
||||
values[field] = genValue(c1)
|
||||
}
|
||||
@@ -2044,9 +2127,13 @@ func doCompositeSparse(n *node, hasType bool) {
|
||||
for i, v := range values {
|
||||
a.Field(i).Set(v(f))
|
||||
}
|
||||
if d := value(f); d.Type().Kind() == reflect.Ptr {
|
||||
d := value(f)
|
||||
switch {
|
||||
case d.Type().Kind() == reflect.Ptr:
|
||||
d.Set(a.Addr())
|
||||
} else {
|
||||
case destInterface:
|
||||
d.Set(reflect.ValueOf(valueInterface{n, a}))
|
||||
default:
|
||||
d.Set(a)
|
||||
}
|
||||
return next
|
||||
@@ -2288,8 +2375,13 @@ func _case(n *node) {
|
||||
values[i] = genValue(n.child[i])
|
||||
}
|
||||
n.exec = func(f *frame) bltn {
|
||||
v0 := value(f)
|
||||
for _, v := range values {
|
||||
if value(f).Interface() == v(f).Interface() {
|
||||
v1 := v(f)
|
||||
if !v0.Type().AssignableTo(v1.Type()) {
|
||||
v0 = v0.Convert(v1.Type())
|
||||
}
|
||||
if v0.Interface() == v1.Interface() {
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
@@ -2336,8 +2428,8 @@ func _append(n *node) {
|
||||
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 isRecursiveType(n.typ.val, n.typ.val.rtype):
|
||||
values[i] = genValueRecursiveInterface(arg, n.typ.val.rtype)
|
||||
case arg.typ.untyped:
|
||||
values[i] = genValueAs(arg, n.child[1].typ.TypeOf().Elem())
|
||||
default:
|
||||
@@ -2358,8 +2450,8 @@ func _append(n *node) {
|
||||
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 isRecursiveType(n.typ.val, n.typ.val.rtype):
|
||||
value0 = genValueRecursiveInterface(n.child[2], n.typ.val.rtype)
|
||||
case n.child[2].typ.untyped:
|
||||
value0 = genValueAs(n.child[2], n.child[1].typ.TypeOf().Elem())
|
||||
default:
|
||||
@@ -2582,6 +2674,7 @@ func recv(n *node) {
|
||||
value := genValue(n.child[0])
|
||||
tnext := getExec(n.tnext)
|
||||
i := n.findex
|
||||
l := n.level
|
||||
|
||||
if n.interp.cancelChan {
|
||||
// Cancellable channel read
|
||||
@@ -2589,10 +2682,10 @@ func recv(n *node) {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
// Fast: channel read doesn't block
|
||||
var ok bool
|
||||
ch := value(f)
|
||||
if f.data[i], ok = ch.TryRecv(); ok {
|
||||
if f.data[i].Bool() {
|
||||
if r, ok := ch.TryRecv(); ok {
|
||||
getFrame(f, l).data[i] = r
|
||||
if r.Bool() {
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
@@ -2610,14 +2703,14 @@ func recv(n *node) {
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
// Fast: channel read doesn't block
|
||||
var ok bool
|
||||
ch := value(f)
|
||||
if f.data[i], ok = ch.TryRecv(); ok {
|
||||
if r, ok := ch.TryRecv(); ok {
|
||||
getFrame(f, l).data[i] = r
|
||||
return tnext
|
||||
}
|
||||
// Slow: channel is blocked, allow cancel
|
||||
var chosen int
|
||||
chosen, f.data[i], _ = reflect.Select([]reflect.SelectCase{f.done, {Dir: reflect.SelectRecv, Chan: ch}})
|
||||
chosen, getFrame(f, l).data[i], _ = reflect.Select([]reflect.SelectCase{f.done, {Dir: reflect.SelectRecv, Chan: ch}})
|
||||
if chosen == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -2629,7 +2722,8 @@ func recv(n *node) {
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
if f.data[i], _ = value(f).Recv(); f.data[i].Bool() {
|
||||
if r, _ := value(f).Recv(); r.Bool() {
|
||||
getFrame(f, l).data[i] = r
|
||||
return tnext
|
||||
}
|
||||
return fnext
|
||||
@@ -2637,7 +2731,7 @@ func recv(n *node) {
|
||||
} else {
|
||||
i := n.findex
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i], _ = value(f).Recv()
|
||||
getFrame(f, l).data[i], _ = value(f).Recv()
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
@@ -2685,7 +2779,7 @@ func convertLiteralValue(n *node, t reflect.Type) {
|
||||
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:
|
||||
case !(n.kind == basicLit || n.rval.IsValid()) || t == nil || t.Kind() == reflect.Interface || t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Interface:
|
||||
// Skip non-constant values, undefined target type or interface target type.
|
||||
case n.rval.IsValid():
|
||||
// Convert constant value to target type.
|
||||
@@ -2697,20 +2791,6 @@ func convertLiteralValue(n *node, t reflect.Type) {
|
||||
}
|
||||
}
|
||||
|
||||
var bitlen = [...]int{
|
||||
reflect.Int: 64,
|
||||
reflect.Int8: 8,
|
||||
reflect.Int16: 16,
|
||||
reflect.Int32: 32,
|
||||
reflect.Int64: 64,
|
||||
reflect.Uint: 64,
|
||||
reflect.Uint8: 8,
|
||||
reflect.Uint16: 16,
|
||||
reflect.Uint32: 32,
|
||||
reflect.Uint64: 64,
|
||||
reflect.Uintptr: 64,
|
||||
}
|
||||
|
||||
func convertConstantValue(n *node) {
|
||||
if !n.rval.IsValid() {
|
||||
return
|
||||
@@ -2918,6 +2998,7 @@ func _select(n *node) {
|
||||
// slice expression: array[low:high:max].
|
||||
func slice(n *node) {
|
||||
i := n.findex
|
||||
l := n.level
|
||||
next := getExec(n.tnext)
|
||||
value0 := genValueArray(n.child[0]) // array
|
||||
value1 := genValue(n.child[1]) // low (if 2 or 3 args) or high (if 1 arg)
|
||||
@@ -2926,7 +3007,7 @@ func slice(n *node) {
|
||||
case 2:
|
||||
n.exec = func(f *frame) bltn {
|
||||
a := value0(f)
|
||||
f.data[i] = a.Slice(int(value1(f).Int()), a.Len())
|
||||
getFrame(f, l).data[i] = a.Slice(int(vInt(value1(f))), a.Len())
|
||||
return next
|
||||
}
|
||||
case 3:
|
||||
@@ -2934,7 +3015,7 @@ func slice(n *node) {
|
||||
|
||||
n.exec = func(f *frame) bltn {
|
||||
a := value0(f)
|
||||
f.data[i] = a.Slice(int(value1(f).Int()), int(value2(f).Int()))
|
||||
getFrame(f, l).data[i] = a.Slice(int(vInt(value1(f))), int(vInt(value2(f))))
|
||||
return next
|
||||
}
|
||||
case 4:
|
||||
@@ -2943,7 +3024,7 @@ func slice(n *node) {
|
||||
|
||||
n.exec = func(f *frame) bltn {
|
||||
a := value0(f)
|
||||
f.data[i] = a.Slice3(int(value1(f).Int()), int(value2(f).Int()), int(value3(f).Int()))
|
||||
getFrame(f, l).data[i] = a.Slice3(int(vInt(value1(f))), int(vInt(value2(f))), int(vInt(value3(f))))
|
||||
return next
|
||||
}
|
||||
}
|
||||
@@ -2952,6 +3033,7 @@ func slice(n *node) {
|
||||
// slice expression, no low value: array[:high:max].
|
||||
func slice0(n *node) {
|
||||
i := n.findex
|
||||
l := n.level
|
||||
next := getExec(n.tnext)
|
||||
value0 := genValueArray(n.child[0])
|
||||
|
||||
@@ -2959,14 +3041,14 @@ func slice0(n *node) {
|
||||
case 1:
|
||||
n.exec = func(f *frame) bltn {
|
||||
a := value0(f)
|
||||
f.data[i] = a.Slice(0, a.Len())
|
||||
getFrame(f, l).data[i] = a.Slice(0, a.Len())
|
||||
return next
|
||||
}
|
||||
case 2:
|
||||
value1 := genValue(n.child[1])
|
||||
n.exec = func(f *frame) bltn {
|
||||
a := value0(f)
|
||||
f.data[i] = a.Slice(0, int(value1(f).Int()))
|
||||
getFrame(f, l).data[i] = a.Slice(0, int(vInt(value1(f))))
|
||||
return next
|
||||
}
|
||||
case 3:
|
||||
@@ -2974,7 +3056,7 @@ func slice0(n *node) {
|
||||
value2 := genValue(n.child[2])
|
||||
n.exec = func(f *frame) bltn {
|
||||
a := value0(f)
|
||||
f.data[i] = a.Slice3(0, int(value1(f).Int()), int(value2(f).Int()))
|
||||
getFrame(f, l).data[i] = a.Slice3(0, int(vInt(value1(f))), int(vInt(value2(f))))
|
||||
return next
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,8 +53,6 @@ type symbol struct {
|
||||
rval reflect.Value // default value (used for constants)
|
||||
builtin bltnGenerator // Builtin function or nil
|
||||
global bool // true if symbol is defined in global space
|
||||
// TODO: implement constant checking
|
||||
//constant bool // true if symbol value is constant
|
||||
}
|
||||
|
||||
// scope type stores symbols in maps, and frame layout as array of types
|
||||
|
||||
@@ -22,7 +22,7 @@ func (interp *Interpreter) importSrc(rPath, path string) (string, error) {
|
||||
// In all other cases, absolute import paths are resolved from the GOPATH
|
||||
// and the nested "vendor" directories.
|
||||
if isPathRelative(path) {
|
||||
if rPath == "main" {
|
||||
if rPath == mainID {
|
||||
rPath = "."
|
||||
}
|
||||
dir = filepath.Join(filepath.Dir(interp.Name), rPath, path)
|
||||
@@ -132,6 +132,13 @@ func (interp *Interpreter) importSrc(rPath, path string) (string, error) {
|
||||
interp.run(n, nil)
|
||||
}
|
||||
|
||||
// Wire and execute global vars
|
||||
n, err := genGlobalVars(rootNodes, interp.scopes[path])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
interp.run(n, nil)
|
||||
|
||||
// Add main to list of functions to run, after all inits
|
||||
if m := interp.main(); m != nil {
|
||||
initNodes = append(initNodes, m)
|
||||
@@ -146,7 +153,7 @@ func (interp *Interpreter) importSrc(rPath, path string) (string, error) {
|
||||
|
||||
func (interp *Interpreter) rootFromSourceLocation(rPath string) (string, error) {
|
||||
sourceFile := interp.Name
|
||||
if rPath != "main" || !strings.HasSuffix(sourceFile, ".go") {
|
||||
if rPath != mainID || !strings.HasSuffix(sourceFile, ".go") {
|
||||
return rPath, nil
|
||||
}
|
||||
wd, err := os.Getwd()
|
||||
@@ -181,13 +188,62 @@ func pkgDir(goPath string, root, path string) (string, string, error) {
|
||||
return "", "", fmt.Errorf("unable to find source related to: %q", path)
|
||||
}
|
||||
|
||||
return pkgDir(goPath, previousRoot(root), path)
|
||||
rootPath := filepath.Join(goPath, "src", root)
|
||||
prevRoot, err := previousRoot(rootPath, root)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return pkgDir(goPath, prevRoot, path)
|
||||
}
|
||||
|
||||
// Find the previous source root (vendor > vendor > ... > GOPATH).
|
||||
func previousRoot(root string) string {
|
||||
splitRoot := strings.Split(root, string(filepath.Separator))
|
||||
const vendor = "vendor"
|
||||
|
||||
// Find the previous source root (vendor > vendor > ... > GOPATH).
|
||||
func previousRoot(rootPath, root string) (string, error) {
|
||||
rootPath = filepath.Clean(rootPath)
|
||||
parent, final := filepath.Split(rootPath)
|
||||
parent = filepath.Clean(parent)
|
||||
|
||||
// TODO(mpl): maybe it works for the special case main, but can't be bothered for now.
|
||||
if root != mainID && final != vendor {
|
||||
root = strings.TrimSuffix(root, string(filepath.Separator))
|
||||
prefix := strings.TrimSuffix(rootPath, root)
|
||||
|
||||
// look for the closest vendor in one of our direct ancestors, as it takes priority.
|
||||
var vendored string
|
||||
for {
|
||||
fi, err := os.Lstat(filepath.Join(parent, vendor))
|
||||
if err == nil && fi.IsDir() {
|
||||
vendored = strings.TrimPrefix(strings.TrimPrefix(parent, prefix), string(filepath.Separator))
|
||||
break
|
||||
}
|
||||
if !os.IsNotExist(err) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// stop when we reach GOPATH/src/blah
|
||||
parent = filepath.Dir(parent)
|
||||
if parent == prefix {
|
||||
break
|
||||
}
|
||||
|
||||
// just an additional failsafe, stop if we reach the filesystem root.
|
||||
// TODO(mpl): It should probably be a critical error actually,
|
||||
// as we shouldn't have gone that high up in the tree.
|
||||
if parent == string(filepath.Separator) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if vendored != "" {
|
||||
return vendored, nil
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(mpl): the algorithm below might be redundant with the one above,
|
||||
// but keeping it for now. Investigate/simplify/remove later.
|
||||
splitRoot := strings.Split(root, string(filepath.Separator))
|
||||
var index int
|
||||
for i := len(splitRoot) - 1; i >= 0; i-- {
|
||||
if splitRoot[i] == "vendor" {
|
||||
@@ -197,10 +253,10 @@ func previousRoot(root string) string {
|
||||
}
|
||||
|
||||
if index == 0 {
|
||||
return ""
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return filepath.Join(splitRoot[:index]...)
|
||||
return filepath.Join(splitRoot[:index]...), nil
|
||||
}
|
||||
|
||||
func effectivePkg(root, path string) string {
|
||||
|
||||
@@ -196,9 +196,10 @@ func Test_pkgDir(t *testing.T) {
|
||||
|
||||
func Test_previousRoot(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
root string
|
||||
expected string
|
||||
desc string
|
||||
root string
|
||||
rootPathSuffix string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "GOPATH",
|
||||
@@ -215,6 +216,18 @@ func Test_previousRoot(t *testing.T) {
|
||||
root: "github.com/foo/pkg/vendor/guthib.com/containous/fromage/vendor/guthib.com/containous/fuu",
|
||||
expected: "github.com/foo/pkg/vendor/guthib.com/containous/fromage",
|
||||
},
|
||||
{
|
||||
desc: "vendor is sibling",
|
||||
root: "github.com/foo/bar",
|
||||
rootPathSuffix: "testdata/src/github.com/foo/bar",
|
||||
expected: "github.com/foo",
|
||||
},
|
||||
{
|
||||
desc: "vendor is uncle",
|
||||
root: "github.com/foo/bar/baz",
|
||||
rootPathSuffix: "testdata/src/github.com/foo/bar/baz",
|
||||
expected: "github.com/foo",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
@@ -222,7 +235,20 @@ func Test_previousRoot(t *testing.T) {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
p := previousRoot(test.root)
|
||||
var rootPath string
|
||||
if test.rootPathSuffix != "" {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rootPath = filepath.Join(wd, test.rootPathSuffix)
|
||||
} else {
|
||||
rootPath = vendor
|
||||
}
|
||||
p, err := previousRoot(rootPath, test.root)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if p != test.expected {
|
||||
t.Errorf("got: %s, want: %s", p, test.expected)
|
||||
|
||||
1
interp/testdata/src/github.com/foo/bar/baz/baz.go
vendored
Normal file
1
interp/testdata/src/github.com/foo/bar/baz/baz.go
vendored
Normal file
@@ -0,0 +1 @@
|
||||
package baz
|
||||
1
interp/testdata/src/github.com/foo/vendor/whatever/whatever.go
vendored
Normal file
1
interp/testdata/src/github.com/foo/vendor/whatever/whatever.go
vendored
Normal file
@@ -0,0 +1 @@
|
||||
package whatever
|
||||
344
interp/type.go
344
interp/type.go
@@ -124,6 +124,15 @@ type itype struct {
|
||||
scope *scope // type declaration scope (in case of re-parse incomplete type)
|
||||
}
|
||||
|
||||
var (
|
||||
untypedBool = &itype{cat: boolT, name: "bool", untyped: true}
|
||||
untypedString = &itype{cat: stringT, name: "string", untyped: true}
|
||||
untypedRune = &itype{cat: int32T, name: "int32", untyped: true}
|
||||
untypedInt = &itype{cat: intT, name: "int", untyped: true}
|
||||
untypedFloat = &itype{cat: float64T, name: "float64", untyped: true}
|
||||
untypedComplex = &itype{cat: complex128T, name: "complex128", untyped: true}
|
||||
)
|
||||
|
||||
// nodeType returns a type definition for the corresponding AST subtree.
|
||||
func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
if n.typ != nil && !n.typ.incomplete {
|
||||
@@ -133,7 +142,7 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
return n.typ, nil
|
||||
}
|
||||
|
||||
var t = &itype{node: n, scope: sc}
|
||||
t := &itype{node: n, scope: sc}
|
||||
|
||||
if n.anc.kind == typeSpec {
|
||||
name := n.anc.child[0].ident
|
||||
@@ -207,57 +216,27 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
case basicLit:
|
||||
switch v := n.rval.Interface().(type) {
|
||||
case bool:
|
||||
t.cat = boolT
|
||||
t.name = "bool"
|
||||
case byte:
|
||||
t.cat = uint8T
|
||||
t.name = "uint8"
|
||||
t.untyped = true
|
||||
case complex64:
|
||||
t.cat = complex64T
|
||||
t.name = "complex64"
|
||||
case complex128:
|
||||
t.cat = complex128T
|
||||
t.name = "complex128"
|
||||
t.untyped = true
|
||||
case float32:
|
||||
t.cat = float32T
|
||||
t.name = "float32"
|
||||
t.untyped = true
|
||||
case float64:
|
||||
t.cat = float64T
|
||||
t.name = "float64"
|
||||
t.untyped = true
|
||||
case int:
|
||||
t.cat = intT
|
||||
t.name = "int"
|
||||
t.untyped = true
|
||||
case uint:
|
||||
t.cat = uintT
|
||||
t.name = "uint"
|
||||
t.untyped = true
|
||||
n.rval = reflect.ValueOf(constant.MakeBool(v))
|
||||
t = untypedBool
|
||||
case rune:
|
||||
t.cat = int32T
|
||||
t.name = "int32"
|
||||
t.untyped = true
|
||||
// It is impossible to work out rune const literals in AST
|
||||
// with the correct type so we must make the const type here.
|
||||
n.rval = reflect.ValueOf(constant.MakeInt64(int64(v)))
|
||||
t = untypedRune
|
||||
case string:
|
||||
t.cat = stringT
|
||||
t.name = "string"
|
||||
t.untyped = true
|
||||
t = untypedString
|
||||
case constant.Value:
|
||||
switch v.Kind() {
|
||||
case constant.Bool:
|
||||
t = untypedBool
|
||||
case constant.String:
|
||||
t = untypedString
|
||||
case constant.Int:
|
||||
t.cat = intT
|
||||
t.name = "int"
|
||||
t.untyped = true
|
||||
t = untypedInt
|
||||
case constant.Float:
|
||||
t.cat = float64T
|
||||
t.name = "float64"
|
||||
t.untyped = true
|
||||
t = untypedFloat
|
||||
case constant.Complex:
|
||||
t.cat = complex128T
|
||||
t.name = "complex128"
|
||||
t.untyped = true
|
||||
t = untypedComplex
|
||||
default:
|
||||
err = n.cfgErrorf("missing support for type %v", n.rval)
|
||||
}
|
||||
@@ -691,7 +670,6 @@ func fieldName(n *node) string {
|
||||
}
|
||||
|
||||
var zeroValues [maxT]reflect.Value
|
||||
var okFor [aMax][maxT]bool
|
||||
|
||||
func init() {
|
||||
zeroValues[boolT] = reflect.ValueOf(false)
|
||||
@@ -712,77 +690,6 @@ func init() {
|
||||
zeroValues[uint32T] = reflect.ValueOf(uint32(0))
|
||||
zeroValues[uint64T] = reflect.ValueOf(uint64(0))
|
||||
zeroValues[uintptrT] = reflect.ValueOf(uintptr(0))
|
||||
|
||||
// Calculate the action -> type allowances
|
||||
var (
|
||||
okForEq [maxT]bool
|
||||
okForCmp [maxT]bool
|
||||
okForAdd [maxT]bool
|
||||
okForAnd [maxT]bool
|
||||
okForBool [maxT]bool
|
||||
okForArith [maxT]bool
|
||||
)
|
||||
for cat := tcat(0); cat < maxT; cat++ {
|
||||
if (cat >= intT && cat <= int64T) || (cat >= uintT && cat <= uintptrT) {
|
||||
okForEq[cat] = true
|
||||
okForCmp[cat] = true
|
||||
okForAdd[cat] = true
|
||||
okForAnd[cat] = true
|
||||
okForArith[cat] = true
|
||||
}
|
||||
if cat == float32T || cat == float64T {
|
||||
okForEq[cat] = true
|
||||
okForCmp[cat] = true
|
||||
okForAdd[cat] = true
|
||||
okForArith[cat] = true
|
||||
}
|
||||
if cat == complex64T || cat == complex128T {
|
||||
okForEq[cat] = true
|
||||
okForAdd[cat] = true
|
||||
okForArith[cat] = true
|
||||
}
|
||||
}
|
||||
|
||||
okForAdd[stringT] = true
|
||||
|
||||
okForBool[boolT] = true
|
||||
|
||||
okForEq[nilT] = true
|
||||
okForEq[ptrT] = true
|
||||
okForEq[interfaceT] = true
|
||||
okForEq[errorT] = true
|
||||
okForEq[chanT] = true
|
||||
okForEq[stringT] = true
|
||||
okForEq[boolT] = true
|
||||
okForEq[mapT] = true // nil only
|
||||
okForEq[funcT] = true // nil only
|
||||
okForEq[arrayT] = true // array: only if element type is comparable slice: nil only
|
||||
okForEq[structT] = true // only if all struct fields are comparable
|
||||
|
||||
okForCmp[stringT] = true
|
||||
|
||||
okFor[aAdd] = okForAdd
|
||||
okFor[aAnd] = okForAnd
|
||||
okFor[aLand] = okForBool
|
||||
okFor[aAndNot] = okForAnd
|
||||
okFor[aQuo] = okForArith
|
||||
okFor[aEqual] = okForEq
|
||||
okFor[aGreaterEqual] = okForCmp
|
||||
okFor[aGreater] = okForCmp
|
||||
okFor[aLowerEqual] = okForCmp
|
||||
okFor[aLower] = okForCmp
|
||||
okFor[aRem] = okForAnd
|
||||
okFor[aMul] = okForArith
|
||||
okFor[aNotEqual] = okForEq
|
||||
okFor[aOr] = okForAnd
|
||||
okFor[aLor] = okForBool
|
||||
okFor[aSub] = okForArith
|
||||
okFor[aXor] = okForAnd
|
||||
okFor[aShl] = okForAnd
|
||||
okFor[aShr] = okForAnd
|
||||
okFor[aNeg] = okForArith
|
||||
okFor[aNot] = okForBool
|
||||
okFor[aPos] = okForArith
|
||||
}
|
||||
|
||||
// Finalize returns a type pointer and error. It reparses a type from the
|
||||
@@ -933,6 +840,26 @@ func (t *itype) comparable() bool {
|
||||
return t.cat == nilT || typ != nil && typ.Comparable()
|
||||
}
|
||||
|
||||
func (t *itype) assignableTo(o *itype) bool {
|
||||
if t.equals(o) {
|
||||
return true
|
||||
}
|
||||
if t.cat == aliasT && o.cat == aliasT {
|
||||
// if alias types are not identical, it is not assignable.
|
||||
return false
|
||||
}
|
||||
if t.isNil() && o.hasNil() || o.isNil() && t.hasNil() {
|
||||
return true
|
||||
}
|
||||
return t.TypeOf().AssignableTo(o.TypeOf())
|
||||
}
|
||||
|
||||
// ordered returns true if the type is ordered.
|
||||
func (t *itype) ordered() bool {
|
||||
typ := t.TypeOf()
|
||||
return isInt(typ) || isFloat(typ) || isString(typ)
|
||||
}
|
||||
|
||||
// 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); {
|
||||
@@ -1007,13 +934,22 @@ func (t *itype) methods() methodSet {
|
||||
// id returns a unique type identificator string.
|
||||
func (t *itype) id() (res string) {
|
||||
if t.name != "" {
|
||||
return t.path + "." + t.name
|
||||
if t.path != "" {
|
||||
return t.path + "." + t.name
|
||||
}
|
||||
return t.name
|
||||
}
|
||||
switch t.cat {
|
||||
case nilT:
|
||||
res = "nil"
|
||||
case arrayT:
|
||||
res = "[" + strconv.Itoa(t.size) + "]" + t.val.id()
|
||||
case chanT:
|
||||
res = "<-" + t.val.id()
|
||||
res = "chan " + t.val.id()
|
||||
case chanSendT:
|
||||
res = "chan<- " + t.val.id()
|
||||
case chanRecvT:
|
||||
res = "<-chan " + t.val.id()
|
||||
case funcT:
|
||||
res = "func("
|
||||
for _, t := range t.arg {
|
||||
@@ -1041,7 +977,11 @@ func (t *itype) id() (res string) {
|
||||
}
|
||||
res += "}"
|
||||
case valueT:
|
||||
res = t.rtype.PkgPath() + "." + t.rtype.Name()
|
||||
res = ""
|
||||
if t.rtype.PkgPath() != "" {
|
||||
res += t.rtype.PkgPath() + "."
|
||||
}
|
||||
res += t.rtype.Name()
|
||||
}
|
||||
return res
|
||||
}
|
||||
@@ -1214,8 +1154,10 @@ func exportName(s string) string {
|
||||
return "X" + s
|
||||
}
|
||||
|
||||
var interf = reflect.TypeOf((*interface{})(nil)).Elem()
|
||||
var constVal = reflect.TypeOf((*constant.Value)(nil)).Elem()
|
||||
var (
|
||||
interf = reflect.TypeOf((*interface{})(nil)).Elem()
|
||||
constVal = reflect.TypeOf((*constant.Value)(nil)).Elem()
|
||||
)
|
||||
|
||||
// RefType returns a reflect.Type representation from an interpreter type.
|
||||
// In simple cases, reflect types are directly mapped from the interpreter
|
||||
@@ -1241,6 +1183,10 @@ func (t *itype) refType(defined map[string]*itype, wrapRecursive bool) reflect.T
|
||||
// a node can still point to a previous copy.
|
||||
st.typ.recursive = st.typ.recursive || st.typ.isRecursive()
|
||||
recursive = st.typ.isRecursive()
|
||||
// It is possible that t.recursive is not inline with st.typ.recursive
|
||||
// which will break recursion detection. Set it here to make sure it
|
||||
// is correct.
|
||||
t.recursive = recursive
|
||||
}
|
||||
}
|
||||
if wrapRecursive && t.recursive {
|
||||
@@ -1252,9 +1198,11 @@ func (t *itype) refType(defined map[string]*itype, wrapRecursive bool) reflect.T
|
||||
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 {
|
||||
if t.val != nil && t.val.cat == structT && t.val.rtype == nil && hasRecursiveStruct(t.val, copyDefined(defined)) {
|
||||
// Replace reference to self (direct or indirect) by an interface{} to handle
|
||||
// recursive types with reflect.
|
||||
typ := *t.val
|
||||
t.val = &typ
|
||||
t.val.rtype = interf
|
||||
recursive = true
|
||||
}
|
||||
@@ -1357,86 +1305,64 @@ func (t *itype) implements(it *itype) bool {
|
||||
return t.methods().contains(it.methods())
|
||||
}
|
||||
|
||||
var errType = reflect.TypeOf((*error)(nil)).Elem()
|
||||
|
||||
func catOf(t reflect.Type) tcat {
|
||||
if t == nil {
|
||||
return nilT
|
||||
// defaultType returns the default type of an untyped type.
|
||||
func (t *itype) defaultType() *itype {
|
||||
if !t.untyped {
|
||||
return t
|
||||
}
|
||||
if t == errType {
|
||||
return errorT
|
||||
}
|
||||
switch t.Kind() {
|
||||
case reflect.Bool:
|
||||
return boolT
|
||||
case reflect.Int:
|
||||
return intT
|
||||
case reflect.Int8:
|
||||
return int8T
|
||||
case reflect.Int16:
|
||||
return int16T
|
||||
case reflect.Int32:
|
||||
return int32T
|
||||
case reflect.Int64:
|
||||
return int64T
|
||||
case reflect.Uint:
|
||||
return uintT
|
||||
case reflect.Uint8:
|
||||
return uint8T
|
||||
case reflect.Uint16:
|
||||
return uint16T
|
||||
case reflect.Uint32:
|
||||
return uint32T
|
||||
case reflect.Uint64:
|
||||
return uint64T
|
||||
case reflect.Uintptr:
|
||||
return uintptrT
|
||||
case reflect.Float32:
|
||||
return float32T
|
||||
case reflect.Float64:
|
||||
return float64T
|
||||
case reflect.Complex64:
|
||||
return complex64T
|
||||
case reflect.Complex128:
|
||||
return complex128T
|
||||
case reflect.Array, reflect.Slice:
|
||||
return arrayT
|
||||
case reflect.Chan:
|
||||
return chanT
|
||||
case reflect.Func:
|
||||
return funcT
|
||||
case reflect.Interface:
|
||||
return interfaceT
|
||||
case reflect.Map:
|
||||
return mapT
|
||||
case reflect.Ptr:
|
||||
return ptrT
|
||||
case reflect.String:
|
||||
return stringT
|
||||
case reflect.Struct:
|
||||
return structT
|
||||
case reflect.UnsafePointer:
|
||||
return uintptrT
|
||||
}
|
||||
return nilT
|
||||
typ := *t
|
||||
typ.untyped = false
|
||||
return &typ
|
||||
}
|
||||
|
||||
func catOfConst(v reflect.Value) tcat {
|
||||
c, ok := v.Interface().(constant.Value)
|
||||
if !ok {
|
||||
return nilT
|
||||
func (t *itype) isNil() bool { return t.cat == nilT }
|
||||
|
||||
func (t *itype) hasNil() bool {
|
||||
switch t.TypeOf().Kind() {
|
||||
case reflect.UnsafePointer:
|
||||
return true
|
||||
case reflect.Slice, reflect.Ptr, reflect.Func, reflect.Interface, reflect.Map, reflect.Chan:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func copyDefined(m map[string]*itype) map[string]*itype {
|
||||
n := make(map[string]*itype, len(m))
|
||||
for k, v := range m {
|
||||
n[k] = v
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// hasRecursiveStruct determines if a struct is a recursion or a recursion
|
||||
// intermediate. A recursion intermediate is a struct that contains a recursive
|
||||
// struct.
|
||||
func hasRecursiveStruct(t *itype, defined map[string]*itype) bool {
|
||||
if len(defined) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
switch c.Kind() {
|
||||
case constant.Int:
|
||||
return intT
|
||||
case constant.Float:
|
||||
return float64T
|
||||
case constant.Complex:
|
||||
return complex128T
|
||||
default:
|
||||
return nilT
|
||||
typ := t
|
||||
for typ != nil {
|
||||
if typ.cat != structT {
|
||||
typ = typ.val
|
||||
continue
|
||||
}
|
||||
|
||||
if defined[typ.path+"/"+typ.name] != nil {
|
||||
return true
|
||||
}
|
||||
defined[typ.path+"/"+typ.name] = typ
|
||||
|
||||
for _, f := range typ.field {
|
||||
if hasRecursiveStruct(f.typ, defined) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func constToInt(c constant.Value) int {
|
||||
@@ -1465,14 +1391,6 @@ func isShiftNode(n *node) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func isComparisonNode(n *node) bool {
|
||||
switch n.action {
|
||||
case aEqual, aNotEqual, aGreater, aGreaterEqual, aLower, aLowerEqual:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// chanElement returns the channel element type.
|
||||
func chanElement(t *itype) *itype {
|
||||
switch t.cat {
|
||||
@@ -1488,11 +1406,19 @@ func chanElement(t *itype) *itype {
|
||||
|
||||
func isBool(t *itype) bool { return t.TypeOf().Kind() == reflect.Bool }
|
||||
func isChan(t *itype) bool { return t.TypeOf().Kind() == reflect.Chan }
|
||||
func isFunc(t *itype) bool { return t.TypeOf().Kind() == reflect.Func }
|
||||
func isMap(t *itype) bool { return t.TypeOf().Kind() == reflect.Map }
|
||||
func isPtr(t *itype) bool { return t.TypeOf().Kind() == reflect.Ptr }
|
||||
|
||||
func isSendChan(t *itype) bool {
|
||||
rt := t.TypeOf()
|
||||
return rt.Kind() == reflect.Chan && rt.ChanDir() == reflect.SendDir
|
||||
}
|
||||
func isMap(t *itype) bool { return t.TypeOf().Kind() == reflect.Map }
|
||||
|
||||
func isArray(t *itype) bool {
|
||||
k := t.TypeOf().Kind()
|
||||
return k == reflect.Array || k == reflect.Slice
|
||||
}
|
||||
|
||||
func isInterfaceSrc(t *itype) bool {
|
||||
return t.cat == interfaceT || (t.cat == aliasT && isInterfaceSrc(t.val))
|
||||
@@ -1518,6 +1444,11 @@ func isStruct(t *itype) bool {
|
||||
}
|
||||
}
|
||||
|
||||
func isConstType(t *itype) bool {
|
||||
rt := t.TypeOf()
|
||||
return isBoolean(rt) || isString(rt) || isNumber(rt)
|
||||
}
|
||||
|
||||
func isInt(t reflect.Type) bool {
|
||||
if t == nil {
|
||||
return false
|
||||
@@ -1575,5 +1506,6 @@ func isFloat64(t reflect.Type) bool { return t != nil && t.Kind() == reflect.Flo
|
||||
func isNumber(t reflect.Type) bool {
|
||||
return isInt(t) || isFloat(t) || isComplex(t) || isConstantValue(t)
|
||||
}
|
||||
func isBoolean(t reflect.Type) bool { return t != nil && t.Kind() == reflect.Bool }
|
||||
func isString(t reflect.Type) bool { return t != nil && t.Kind() == reflect.String }
|
||||
func isConstantValue(t reflect.Type) bool { return t != nil && t.Implements(constVal) }
|
||||
|
||||
670
interp/typecheck.go
Normal file
670
interp/typecheck.go
Normal file
@@ -0,0 +1,670 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"go/constant"
|
||||
"math"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type opPredicates map[action]func(reflect.Type) bool
|
||||
|
||||
// typecheck handles all type checking following "go/types" logic.
|
||||
//
|
||||
// Due to variant type systems (itype vs reflect.Type) a single
|
||||
// type system should used, namely reflect.Type with exception
|
||||
// of the untyped flag on itype.
|
||||
type typecheck struct{}
|
||||
|
||||
// op type checks an expression against a set of expression predicates.
|
||||
func (check typecheck) op(p opPredicates, a action, n, c *node, t reflect.Type) error {
|
||||
if pred := p[a]; pred != nil {
|
||||
if !pred(t) {
|
||||
return n.cfgErrorf("invalid operation: operator %v not defined on %s", n.action, c.typ.id())
|
||||
}
|
||||
} else {
|
||||
return n.cfgErrorf("invalid operation: unknown operator %v", n.action)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// assignment checks if n can be assigned to typ.
|
||||
//
|
||||
// Use typ == nil to indicate assignment to an untyped blank identifier.
|
||||
func (check typecheck) assignment(n *node, typ *itype, context string) error {
|
||||
if n.typ.untyped {
|
||||
if typ == nil || isInterface(typ) {
|
||||
if typ == nil && n.typ.cat == nilT {
|
||||
return n.cfgErrorf("use of untyped nil in %s", context)
|
||||
}
|
||||
typ = n.typ.defaultType()
|
||||
}
|
||||
if err := check.convertUntyped(n, typ); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if typ == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !n.typ.assignableTo(typ) {
|
||||
return n.cfgErrorf("cannot use type %s as type %s in %s", n.typ.id(), typ.id(), context)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// assignExpr type checks an assign expression.
|
||||
//
|
||||
// This is done per pair of assignments.
|
||||
func (check typecheck) assignExpr(n, dest, src *node) error {
|
||||
if n.action == aAssign {
|
||||
isConst := n.anc.kind == constDecl
|
||||
if !isConst {
|
||||
// var operations must be typed
|
||||
dest.typ = dest.typ.defaultType()
|
||||
}
|
||||
|
||||
return check.assignment(src, dest.typ, "assignment")
|
||||
}
|
||||
|
||||
// assignment operations.
|
||||
if n.nleft > 1 || n.nright > 1 {
|
||||
return n.cfgErrorf("assignment operation %s requires single-valued expressions", n.action)
|
||||
}
|
||||
|
||||
return check.binaryExpr(n)
|
||||
}
|
||||
|
||||
// addressExpr type checks a unary address expression.
|
||||
func (check typecheck) addressExpr(n *node) error {
|
||||
c0 := n.child[0]
|
||||
found := false
|
||||
for !found {
|
||||
switch c0.kind {
|
||||
case parenExpr:
|
||||
c0 = c0.child[0]
|
||||
continue
|
||||
case selectorExpr:
|
||||
c0 = c0.child[1]
|
||||
continue
|
||||
case indexExpr:
|
||||
c := c0.child[0]
|
||||
if isArray(c.typ) || isMap(c.typ) {
|
||||
c0 = c
|
||||
continue
|
||||
}
|
||||
case compositeLitExpr, identExpr:
|
||||
found = true
|
||||
continue
|
||||
}
|
||||
return n.cfgErrorf("invalid operation: cannot take address of %s", c0.typ.id())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var unaryOpPredicates = opPredicates{
|
||||
aPos: isNumber,
|
||||
aNeg: isNumber,
|
||||
aBitNot: isInt,
|
||||
aNot: isBoolean,
|
||||
}
|
||||
|
||||
// unaryExpr type checks a unary expression.
|
||||
func (check typecheck) unaryExpr(n *node) error {
|
||||
c0 := n.child[0]
|
||||
t0 := c0.typ.TypeOf()
|
||||
|
||||
if n.action == aRecv {
|
||||
if !isChan(c0.typ) {
|
||||
return n.cfgErrorf("invalid operation: cannot receive from non-channel %s", c0.typ.id())
|
||||
}
|
||||
if isSendChan(c0.typ) {
|
||||
return n.cfgErrorf("invalid operation: cannot receive from send-only channel %s", c0.typ.id())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := check.op(unaryOpPredicates, n.action, n, c0, t0); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// shift type checks a shift binary expression.
|
||||
func (check typecheck) shift(n *node) error {
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
|
||||
|
||||
var v0 constant.Value
|
||||
if c0.typ.untyped {
|
||||
v0 = constant.ToInt(c0.rval.Interface().(constant.Value))
|
||||
c0.rval = reflect.ValueOf(v0)
|
||||
}
|
||||
|
||||
if !(c0.typ.untyped && v0 != nil && v0.Kind() == constant.Int || isInt(t0)) {
|
||||
return n.cfgErrorf("invalid operation: shift of type %v", c0.typ.id())
|
||||
}
|
||||
|
||||
switch {
|
||||
case c1.typ.untyped:
|
||||
if err := check.convertUntyped(c1, &itype{cat: uintT, name: "uint"}); err != nil {
|
||||
return n.cfgErrorf("invalid operation: shift count type %v, must be integer", c1.typ.id())
|
||||
}
|
||||
case isInt(t1):
|
||||
// nothing to do
|
||||
default:
|
||||
return n.cfgErrorf("invalid operation: shift count type %v, must be integer", c1.typ.id())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// comparison type checks a comparison binary expression.
|
||||
func (check typecheck) comparison(n *node) error {
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
|
||||
if !c0.typ.assignableTo(c1.typ) && !c1.typ.assignableTo(c0.typ) {
|
||||
return n.cfgErrorf("invalid operation: mismatched types %s and %s", c0.typ.id(), c1.typ.id())
|
||||
}
|
||||
|
||||
ok := false
|
||||
switch n.action {
|
||||
case aEqual, aNotEqual:
|
||||
ok = c0.typ.comparable() && c1.typ.comparable() || c0.typ.isNil() && c1.typ.hasNil() || c1.typ.isNil() && c0.typ.hasNil()
|
||||
case aLower, aLowerEqual, aGreater, aGreaterEqual:
|
||||
ok = c0.typ.ordered() && c1.typ.ordered()
|
||||
}
|
||||
if !ok {
|
||||
typ := c0.typ
|
||||
if typ.isNil() {
|
||||
typ = c1.typ
|
||||
}
|
||||
return n.cfgErrorf("invalid operation: operator %v not defined on %s", n.action, typ.id(), ".")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var binaryOpPredicates = opPredicates{
|
||||
aAdd: func(typ reflect.Type) bool { return isNumber(typ) || isString(typ) },
|
||||
aSub: isNumber,
|
||||
aMul: isNumber,
|
||||
aQuo: isNumber,
|
||||
aRem: isInt,
|
||||
|
||||
aAnd: isInt,
|
||||
aOr: isInt,
|
||||
aXor: isInt,
|
||||
aAndNot: isInt,
|
||||
|
||||
aLand: isBoolean,
|
||||
aLor: isBoolean,
|
||||
}
|
||||
|
||||
// binaryExpr type checks a binary expression.
|
||||
func (check typecheck) binaryExpr(n *node) error {
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
a := n.action
|
||||
if isAssignAction(a) {
|
||||
a--
|
||||
}
|
||||
|
||||
if isShiftAction(a) {
|
||||
return check.shift(n)
|
||||
}
|
||||
|
||||
_ = check.convertUntyped(c0, c1.typ)
|
||||
_ = check.convertUntyped(c1, c0.typ)
|
||||
|
||||
if isComparisonAction(a) {
|
||||
return check.comparison(n)
|
||||
}
|
||||
|
||||
if !c0.typ.equals(c1.typ) {
|
||||
return n.cfgErrorf("invalid operation: mismatched types %s and %s", c0.typ.id(), c1.typ.id())
|
||||
}
|
||||
|
||||
t0 := c0.typ.TypeOf()
|
||||
if err := check.op(binaryOpPredicates, a, n, c0, t0); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch n.action {
|
||||
case aQuo, aRem:
|
||||
if (c0.typ.untyped || isInt(t0)) && c1.typ.untyped && constant.Sign(c1.rval.Interface().(constant.Value)) == 0 {
|
||||
return n.cfgErrorf("invalid operation: division by zero")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (check typecheck) index(n *node, max int) error {
|
||||
if err := check.convertUntyped(n, &itype{cat: intT, name: "int"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !isInt(n.typ.TypeOf()) {
|
||||
return n.cfgErrorf("index %s must be integer", n.typ.id())
|
||||
}
|
||||
|
||||
if !n.rval.IsValid() || max < 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if int(vInt(n.rval)) >= max {
|
||||
return n.cfgErrorf("index %s is out of bounds", n.typ.id())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// arrayLitExpr type checks an array composite literal expression.
|
||||
func (check typecheck) arrayLitExpr(child []*node, typ *itype, length int) error {
|
||||
visited := make(map[int]bool, len(child))
|
||||
index := 0
|
||||
for _, c := range child {
|
||||
n := c
|
||||
switch {
|
||||
case c.kind == keyValueExpr:
|
||||
if err := check.index(c.child[0], length); err != nil {
|
||||
return c.cfgErrorf("index %s must be integer constant", c.child[0].typ.id())
|
||||
}
|
||||
n = c.child[1]
|
||||
index = int(vInt(c.child[0].rval))
|
||||
case length > 0 && index >= length:
|
||||
return c.cfgErrorf("index %d is out of bounds (>= %d)", index, length)
|
||||
}
|
||||
|
||||
if visited[index] {
|
||||
return n.cfgErrorf("duplicate index %d in array or slice literal", index)
|
||||
}
|
||||
visited[index] = true
|
||||
index++
|
||||
|
||||
if err := check.assignment(n, typ, "array or slice literal"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// mapLitExpr type checks an map composite literal expression.
|
||||
func (check typecheck) mapLitExpr(child []*node, ktyp, vtyp *itype) error {
|
||||
visited := make(map[interface{}]bool, len(child))
|
||||
for _, c := range child {
|
||||
if c.kind != keyValueExpr {
|
||||
return c.cfgErrorf("missing key in map literal")
|
||||
}
|
||||
|
||||
key, val := c.child[0], c.child[1]
|
||||
if err := check.assignment(key, ktyp, "map literal"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if key.rval.IsValid() {
|
||||
kval := key.rval.Interface()
|
||||
if visited[kval] {
|
||||
return c.cfgErrorf("duplicate key %s in map literal", kval)
|
||||
}
|
||||
visited[kval] = true
|
||||
}
|
||||
|
||||
if err := check.assignment(val, vtyp, "map literal"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// structLitExpr type checks an struct composite literal expression.
|
||||
func (check typecheck) structLitExpr(child []*node, typ *itype) error {
|
||||
if len(child) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if child[0].kind == keyValueExpr {
|
||||
// All children must be keyValueExpr
|
||||
visited := make([]bool, len(typ.field))
|
||||
for _, c := range child {
|
||||
if c.kind != keyValueExpr {
|
||||
return c.cfgErrorf("mixture of field:value and value elements in struct literal")
|
||||
}
|
||||
|
||||
key, val := c.child[0], c.child[1]
|
||||
name := key.ident
|
||||
if name == "" {
|
||||
return c.cfgErrorf("invalid field name %s in struct literal", key.typ.id())
|
||||
}
|
||||
i := typ.fieldIndex(name)
|
||||
if i < 0 {
|
||||
return c.cfgErrorf("unknown field %s in struct literal", name)
|
||||
}
|
||||
field := typ.field[i]
|
||||
|
||||
if err := check.assignment(val, field.typ, "struct literal"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if visited[i] {
|
||||
return c.cfgErrorf("duplicate field name %s in struct literal", name)
|
||||
}
|
||||
visited[i] = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// No children can be keyValueExpr
|
||||
for i, c := range child {
|
||||
if c.kind == keyValueExpr {
|
||||
return c.cfgErrorf("mixture of field:value and value elements in struct literal")
|
||||
}
|
||||
|
||||
if i >= len(typ.field) {
|
||||
return c.cfgErrorf("too many values in struct literal")
|
||||
}
|
||||
field := typ.field[i]
|
||||
// TODO(nick): check if this field is not exported and in a different package.
|
||||
|
||||
if err := check.assignment(c, field.typ, "struct literal"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(child) < len(typ.field) {
|
||||
return child[len(child)-1].cfgErrorf("too few values in struct literal")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// structBinLitExpr type checks an struct composite literal expression on a binary type.
|
||||
func (check typecheck) structBinLitExpr(child []*node, typ reflect.Type) error {
|
||||
if len(child) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if child[0].kind == keyValueExpr {
|
||||
// All children must be keyValueExpr
|
||||
visited := make(map[string]bool, typ.NumField())
|
||||
for _, c := range child {
|
||||
if c.kind != keyValueExpr {
|
||||
return c.cfgErrorf("mixture of field:value and value elements in struct literal")
|
||||
}
|
||||
|
||||
key, val := c.child[0], c.child[1]
|
||||
name := key.ident
|
||||
if name == "" {
|
||||
return c.cfgErrorf("invalid field name %s in struct literal", key.typ.id())
|
||||
}
|
||||
field, ok := typ.FieldByName(name)
|
||||
if !ok {
|
||||
return c.cfgErrorf("unknown field %s in struct literal", name)
|
||||
}
|
||||
|
||||
if err := check.assignment(val, &itype{cat: valueT, rtype: field.Type}, "struct literal"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if visited[field.Name] {
|
||||
return c.cfgErrorf("duplicate field name %s in struct literal", name)
|
||||
}
|
||||
visited[field.Name] = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// No children can be keyValueExpr
|
||||
for i, c := range child {
|
||||
if c.kind == keyValueExpr {
|
||||
return c.cfgErrorf("mixture of field:value and value elements in struct literal")
|
||||
}
|
||||
|
||||
if i >= typ.NumField() {
|
||||
return c.cfgErrorf("too many values in struct literal")
|
||||
}
|
||||
field := typ.Field(i)
|
||||
if !canExport(field.Name) {
|
||||
return c.cfgErrorf("implicit assignment to unexported field %s in %s literal", field.Name, typ)
|
||||
}
|
||||
|
||||
if err := check.assignment(c, &itype{cat: valueT, rtype: field.Type}, "struct literal"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(child) < typ.NumField() {
|
||||
return child[len(child)-1].cfgErrorf("too few values in struct literal")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var errCantConvert = errors.New("cannot convert")
|
||||
|
||||
func (check typecheck) convertUntyped(n *node, typ *itype) error {
|
||||
if n.typ == nil || !n.typ.untyped || typ == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
convErr := n.cfgErrorf("cannot convert %s to %s", n.typ.id(), typ.id())
|
||||
|
||||
ntyp, ttyp := n.typ.TypeOf(), typ.TypeOf()
|
||||
if typ.untyped {
|
||||
// Both n and target are untyped.
|
||||
nkind, tkind := ntyp.Kind(), ttyp.Kind()
|
||||
if isNumber(ntyp) && isNumber(ttyp) {
|
||||
if nkind < tkind {
|
||||
n.typ = typ
|
||||
}
|
||||
} else if nkind != tkind {
|
||||
return convErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
ityp *itype
|
||||
rtyp reflect.Type
|
||||
err error
|
||||
)
|
||||
switch {
|
||||
case typ.isNil() && n.typ.isNil():
|
||||
n.typ = typ
|
||||
return nil
|
||||
case isNumber(ttyp) || isString(ttyp) || isBoolean(ttyp):
|
||||
ityp = typ
|
||||
rtyp = ttyp
|
||||
case isInterface(typ):
|
||||
if n.typ.isNil() {
|
||||
return nil
|
||||
}
|
||||
if len(n.typ.methods()) > 0 { // untyped cannot be set to iface
|
||||
return convErr
|
||||
}
|
||||
ityp = n.typ.defaultType()
|
||||
rtyp = ntyp
|
||||
|
||||
case isArray(typ) || isMap(typ) || isChan(typ) || isFunc(typ) || isPtr(typ):
|
||||
// TODO(nick): above we are acting on itype, but really it is an rtype check. This is not clear which type
|
||||
// plain we are in. Fix this later.
|
||||
if !n.typ.isNil() {
|
||||
return convErr
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return convErr
|
||||
}
|
||||
|
||||
if err := check.representable(n, rtyp); err != nil {
|
||||
return err
|
||||
}
|
||||
n.rval, err = check.convertConst(n.rval, rtyp)
|
||||
if err != nil {
|
||||
if errors.Is(err, errCantConvert) {
|
||||
return convErr
|
||||
}
|
||||
return n.cfgErrorf(err.Error())
|
||||
}
|
||||
n.typ = ityp
|
||||
return nil
|
||||
}
|
||||
|
||||
func (check typecheck) representable(n *node, t reflect.Type) error {
|
||||
if !n.rval.IsValid() {
|
||||
// TODO(nick): This should be an error as the const is in the frame which is undesirable.
|
||||
return nil
|
||||
}
|
||||
c, ok := n.rval.Interface().(constant.Value)
|
||||
if !ok {
|
||||
// TODO(nick): This should be an error as untyped strings and bools should be constant.Values.
|
||||
return nil
|
||||
}
|
||||
|
||||
if !representableConst(c, t) {
|
||||
typ := n.typ.TypeOf()
|
||||
if isNumber(typ) && isNumber(t) {
|
||||
// numeric conversion : error msg
|
||||
//
|
||||
// integer -> integer : overflows
|
||||
// integer -> float : overflows (actually not possible)
|
||||
// float -> integer : truncated
|
||||
// float -> float : overflows
|
||||
//
|
||||
if !isInt(typ) && isInt(t) {
|
||||
return n.cfgErrorf("%s truncated to %s", c.ExactString(), t.Kind().String())
|
||||
}
|
||||
return n.cfgErrorf("%s overflows %s", c.ExactString(), t.Kind().String())
|
||||
}
|
||||
return n.cfgErrorf("cannot convert %s to %s", c.ExactString(), t.Kind().String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (check typecheck) convertConst(v reflect.Value, t reflect.Type) (reflect.Value, error) {
|
||||
if !v.IsValid() {
|
||||
// TODO(nick): This should be an error as the const is in the frame which is undesirable.
|
||||
return v, nil
|
||||
}
|
||||
c, ok := v.Interface().(constant.Value)
|
||||
if !ok {
|
||||
// TODO(nick): This should be an error as untyped strings and bools should be constant.Values.
|
||||
return v, nil
|
||||
}
|
||||
|
||||
kind := t.Kind()
|
||||
switch kind {
|
||||
case reflect.Bool:
|
||||
v = reflect.ValueOf(constant.BoolVal(c))
|
||||
case reflect.String:
|
||||
v = reflect.ValueOf(constant.StringVal(c))
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
i, _ := constant.Int64Val(constant.ToInt(c))
|
||||
v = reflect.ValueOf(i).Convert(t)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
i, _ := constant.Uint64Val(constant.ToInt(c))
|
||||
v = reflect.ValueOf(i).Convert(t)
|
||||
case reflect.Float32:
|
||||
f, _ := constant.Float32Val(constant.ToFloat(c))
|
||||
v = reflect.ValueOf(f)
|
||||
case reflect.Float64:
|
||||
f, _ := constant.Float64Val(constant.ToFloat(c))
|
||||
v = reflect.ValueOf(f)
|
||||
case reflect.Complex64:
|
||||
r, _ := constant.Float32Val(constant.Real(c))
|
||||
i, _ := constant.Float32Val(constant.Imag(c))
|
||||
v = reflect.ValueOf(complex(r, i)).Convert(t)
|
||||
case reflect.Complex128:
|
||||
r, _ := constant.Float64Val(constant.Real(c))
|
||||
i, _ := constant.Float64Val(constant.Imag(c))
|
||||
v = reflect.ValueOf(complex(r, i)).Convert(t)
|
||||
default:
|
||||
return v, errCantConvert
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
var bitlen = [...]int{
|
||||
reflect.Int: 64,
|
||||
reflect.Int8: 8,
|
||||
reflect.Int16: 16,
|
||||
reflect.Int32: 32,
|
||||
reflect.Int64: 64,
|
||||
reflect.Uint: 64,
|
||||
reflect.Uint8: 8,
|
||||
reflect.Uint16: 16,
|
||||
reflect.Uint32: 32,
|
||||
reflect.Uint64: 64,
|
||||
reflect.Uintptr: 64,
|
||||
}
|
||||
|
||||
func representableConst(c constant.Value, t reflect.Type) bool {
|
||||
switch {
|
||||
case isInt(t):
|
||||
x := constant.ToInt(c)
|
||||
if x.Kind() != constant.Int {
|
||||
return false
|
||||
}
|
||||
switch t.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
if _, ok := constant.Int64Val(x); !ok {
|
||||
return false
|
||||
}
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
if _, ok := constant.Uint64Val(x); !ok {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return constant.BitLen(x) <= bitlen[t.Kind()]
|
||||
case isFloat(t):
|
||||
x := constant.ToFloat(c)
|
||||
if x.Kind() != constant.Float {
|
||||
return false
|
||||
}
|
||||
switch t.Kind() {
|
||||
case reflect.Float32:
|
||||
f, _ := constant.Float32Val(x)
|
||||
return !math.IsInf(float64(f), 0)
|
||||
case reflect.Float64:
|
||||
f, _ := constant.Float64Val(x)
|
||||
return !math.IsInf(f, 0)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
case isComplex(t):
|
||||
x := constant.ToComplex(c)
|
||||
if x.Kind() != constant.Complex {
|
||||
return false
|
||||
}
|
||||
switch t.Kind() {
|
||||
case reflect.Complex64:
|
||||
r, _ := constant.Float32Val(constant.Real(x))
|
||||
i, _ := constant.Float32Val(constant.Imag(x))
|
||||
return !math.IsInf(float64(r), 0) && !math.IsInf(float64(i), 0)
|
||||
case reflect.Complex128:
|
||||
r, _ := constant.Float64Val(constant.Real(x))
|
||||
i, _ := constant.Float64Val(constant.Imag(x))
|
||||
return !math.IsInf(r, 0) && !math.IsInf(i, 0)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
case isString(t):
|
||||
return c.Kind() == constant.String
|
||||
case isBoolean(t):
|
||||
return c.Kind() == constant.Bool
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func isShiftAction(a action) bool {
|
||||
switch a {
|
||||
case aShl, aShr, aShlAssign, aShrAssign:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isComparisonAction(a action) bool {
|
||||
switch a {
|
||||
case aEqual, aNotEqual, aGreater, aGreaterEqual, aLower, aLowerEqual:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
134
interp/value.go
134
interp/value.go
@@ -155,17 +155,36 @@ func genValueArray(n *node) func(*frame) reflect.Value {
|
||||
|
||||
func genValueRangeArray(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
// dereference array pointer, to support array operations on array pointer
|
||||
if n.typ.TypeOf().Kind() == reflect.Ptr {
|
||||
|
||||
switch {
|
||||
case n.typ.TypeOf().Kind() == reflect.Ptr:
|
||||
// dereference array pointer, to support array operations on array pointer
|
||||
return func(f *frame) reflect.Value {
|
||||
return value(f).Elem()
|
||||
}
|
||||
}
|
||||
return func(f *frame) reflect.Value {
|
||||
// This is necessary to prevent changes in the returned
|
||||
// reflect.Value being reflected back to the value used
|
||||
// for the range expression.
|
||||
return reflect.ValueOf(value(f).Interface())
|
||||
case n.typ.val != nil && n.typ.val.cat == interfaceT:
|
||||
return func(f *frame) reflect.Value {
|
||||
val := value(f)
|
||||
v := []valueInterface{}
|
||||
for i := 0; i < val.Len(); i++ {
|
||||
switch av := val.Index(i).Interface().(type) {
|
||||
case []valueInterface:
|
||||
v = append(v, av...)
|
||||
case valueInterface:
|
||||
v = append(v, av)
|
||||
default:
|
||||
panic(n.cfgErrorf("invalid type %v", val.Index(i).Type()))
|
||||
}
|
||||
}
|
||||
return reflect.ValueOf(v)
|
||||
}
|
||||
default:
|
||||
return func(f *frame) reflect.Value {
|
||||
// This is necessary to prevent changes in the returned
|
||||
// reflect.Value being reflected back to the value used
|
||||
// for the range expression.
|
||||
return reflect.ValueOf(value(f).Interface())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,16 +201,6 @@ func genValueInterfaceArray(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
}
|
||||
|
||||
func genValueInterfacePtr(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
v := reflect.New(interf).Elem()
|
||||
v.Set(value(f))
|
||||
return v.Addr()
|
||||
}
|
||||
}
|
||||
|
||||
func genValueInterface(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
|
||||
@@ -211,18 +220,6 @@ func genValueInterface(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
}
|
||||
|
||||
func genValueDerefInterfacePtr(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
v := value(f)
|
||||
if v.IsZero() {
|
||||
return v
|
||||
}
|
||||
return v.Elem().Elem()
|
||||
}
|
||||
}
|
||||
|
||||
func zeroInterfaceValue() reflect.Value {
|
||||
n := &node{kind: basicLit, typ: &itype{cat: nilT, untyped: true}}
|
||||
v := reflect.New(interf).Elem()
|
||||
@@ -269,6 +266,63 @@ func genValueNode(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
}
|
||||
|
||||
func genValueRecursiveInterface(n *node, t reflect.Type) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
vv := value(f)
|
||||
v := reflect.New(t).Elem()
|
||||
toRecursive(v, vv)
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
func toRecursive(dest, src reflect.Value) {
|
||||
if !src.IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
switch dest.Kind() {
|
||||
case reflect.Map:
|
||||
v := reflect.MakeMapWithSize(dest.Type(), src.Len())
|
||||
for _, kv := range src.MapKeys() {
|
||||
vv := reflect.New(dest.Type().Elem()).Elem()
|
||||
toRecursive(vv, src.MapIndex(kv))
|
||||
vv.SetMapIndex(kv, vv)
|
||||
}
|
||||
dest.Set(v)
|
||||
case reflect.Slice:
|
||||
l := src.Len()
|
||||
v := reflect.MakeSlice(dest.Type(), l, l)
|
||||
for i := 0; i < l; i++ {
|
||||
toRecursive(v.Index(i), src.Index(i))
|
||||
}
|
||||
dest.Set(v)
|
||||
case reflect.Ptr:
|
||||
v := reflect.New(dest.Type().Elem()).Elem()
|
||||
s := src
|
||||
if s.Elem().Kind() != reflect.Struct { // In the case of *interface{}, we want *struct{}
|
||||
s = s.Elem()
|
||||
}
|
||||
toRecursive(v, s)
|
||||
dest.Set(v.Addr())
|
||||
default:
|
||||
dest.Set(src)
|
||||
}
|
||||
}
|
||||
|
||||
func genValueRecursiveInterfacePtrValue(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
v := value(f)
|
||||
if v.IsZero() {
|
||||
return v
|
||||
}
|
||||
return v.Elem().Elem()
|
||||
}
|
||||
}
|
||||
|
||||
func vInt(v reflect.Value) (i int64) {
|
||||
switch v.Type().Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
@@ -280,10 +334,6 @@ func vInt(v reflect.Value) (i int64) {
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
i = int64(real(v.Complex()))
|
||||
}
|
||||
if v.Type().Implements(constVal) {
|
||||
c := v.Interface().(constant.Value)
|
||||
i, _ = constant.Int64Val(constant.ToInt(c))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -298,11 +348,6 @@ func vUint(v reflect.Value) (i uint64) {
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
i = uint64(real(v.Complex()))
|
||||
}
|
||||
if v.Type().Implements(constVal) {
|
||||
c := v.Interface().(constant.Value)
|
||||
iv, _ := constant.Int64Val(constant.ToInt(c))
|
||||
i = uint64(iv)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -317,13 +362,6 @@ func vComplex(v reflect.Value) (c complex128) {
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
c = v.Complex()
|
||||
}
|
||||
if v.Type().Implements(constVal) {
|
||||
con := v.Interface().(constant.Value)
|
||||
con = constant.ToComplex(con)
|
||||
rel, _ := constant.Float64Val(constant.Real(con))
|
||||
img, _ := constant.Float64Val(constant.Imag(con))
|
||||
c = complex(rel, img)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -338,10 +376,6 @@ func vFloat(v reflect.Value) (i float64) {
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
i = real(v.Complex())
|
||||
}
|
||||
if v.Type().Implements(constVal) {
|
||||
c := v.Interface().(constant.Value)
|
||||
i, _ = constant.Float64Val(constant.ToFloat(c))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
// Code generated by 'goexports debug/pe'. DO NOT EDIT.
|
||||
|
||||
// +build go1.13,!go1.14
|
||||
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"debug/pe"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Symbols["debug/pe"] = map[string]reflect.Value{
|
||||
// function, constant and variable definitions
|
||||
"COFFSymbolSize": reflect.ValueOf(constant.MakeFromLiteral("18", token.INT, 0)),
|
||||
"IMAGE_DIRECTORY_ENTRY_ARCHITECTURE": reflect.ValueOf(constant.MakeFromLiteral("7", token.INT, 0)),
|
||||
"IMAGE_DIRECTORY_ENTRY_BASERELOC": reflect.ValueOf(constant.MakeFromLiteral("5", token.INT, 0)),
|
||||
"IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT": reflect.ValueOf(constant.MakeFromLiteral("11", token.INT, 0)),
|
||||
"IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR": reflect.ValueOf(constant.MakeFromLiteral("14", token.INT, 0)),
|
||||
"IMAGE_DIRECTORY_ENTRY_DEBUG": reflect.ValueOf(constant.MakeFromLiteral("6", token.INT, 0)),
|
||||
"IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT": reflect.ValueOf(constant.MakeFromLiteral("13", token.INT, 0)),
|
||||
"IMAGE_DIRECTORY_ENTRY_EXCEPTION": reflect.ValueOf(constant.MakeFromLiteral("3", token.INT, 0)),
|
||||
"IMAGE_DIRECTORY_ENTRY_EXPORT": reflect.ValueOf(constant.MakeFromLiteral("0", token.INT, 0)),
|
||||
"IMAGE_DIRECTORY_ENTRY_GLOBALPTR": reflect.ValueOf(constant.MakeFromLiteral("8", token.INT, 0)),
|
||||
"IMAGE_DIRECTORY_ENTRY_IAT": reflect.ValueOf(constant.MakeFromLiteral("12", token.INT, 0)),
|
||||
"IMAGE_DIRECTORY_ENTRY_IMPORT": reflect.ValueOf(constant.MakeFromLiteral("1", token.INT, 0)),
|
||||
"IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG": reflect.ValueOf(constant.MakeFromLiteral("10", token.INT, 0)),
|
||||
"IMAGE_DIRECTORY_ENTRY_RESOURCE": reflect.ValueOf(constant.MakeFromLiteral("2", token.INT, 0)),
|
||||
"IMAGE_DIRECTORY_ENTRY_SECURITY": reflect.ValueOf(constant.MakeFromLiteral("4", token.INT, 0)),
|
||||
"IMAGE_DIRECTORY_ENTRY_TLS": reflect.ValueOf(constant.MakeFromLiteral("9", token.INT, 0)),
|
||||
"IMAGE_FILE_MACHINE_AM33": reflect.ValueOf(constant.MakeFromLiteral("467", token.INT, 0)),
|
||||
"IMAGE_FILE_MACHINE_AMD64": reflect.ValueOf(constant.MakeFromLiteral("34404", token.INT, 0)),
|
||||
"IMAGE_FILE_MACHINE_ARM": reflect.ValueOf(constant.MakeFromLiteral("448", token.INT, 0)),
|
||||
"IMAGE_FILE_MACHINE_ARM64": reflect.ValueOf(constant.MakeFromLiteral("43620", token.INT, 0)),
|
||||
"IMAGE_FILE_MACHINE_ARMNT": reflect.ValueOf(constant.MakeFromLiteral("452", token.INT, 0)),
|
||||
"IMAGE_FILE_MACHINE_EBC": reflect.ValueOf(constant.MakeFromLiteral("3772", token.INT, 0)),
|
||||
"IMAGE_FILE_MACHINE_I386": reflect.ValueOf(constant.MakeFromLiteral("332", token.INT, 0)),
|
||||
"IMAGE_FILE_MACHINE_IA64": reflect.ValueOf(constant.MakeFromLiteral("512", token.INT, 0)),
|
||||
"IMAGE_FILE_MACHINE_M32R": reflect.ValueOf(constant.MakeFromLiteral("36929", token.INT, 0)),
|
||||
"IMAGE_FILE_MACHINE_MIPS16": reflect.ValueOf(constant.MakeFromLiteral("614", token.INT, 0)),
|
||||
"IMAGE_FILE_MACHINE_MIPSFPU": reflect.ValueOf(constant.MakeFromLiteral("870", token.INT, 0)),
|
||||
"IMAGE_FILE_MACHINE_MIPSFPU16": reflect.ValueOf(constant.MakeFromLiteral("1126", token.INT, 0)),
|
||||
"IMAGE_FILE_MACHINE_POWERPC": reflect.ValueOf(constant.MakeFromLiteral("496", token.INT, 0)),
|
||||
"IMAGE_FILE_MACHINE_POWERPCFP": reflect.ValueOf(constant.MakeFromLiteral("497", token.INT, 0)),
|
||||
"IMAGE_FILE_MACHINE_R4000": reflect.ValueOf(constant.MakeFromLiteral("358", token.INT, 0)),
|
||||
"IMAGE_FILE_MACHINE_SH3": reflect.ValueOf(constant.MakeFromLiteral("418", token.INT, 0)),
|
||||
"IMAGE_FILE_MACHINE_SH3DSP": reflect.ValueOf(constant.MakeFromLiteral("419", token.INT, 0)),
|
||||
"IMAGE_FILE_MACHINE_SH4": reflect.ValueOf(constant.MakeFromLiteral("422", token.INT, 0)),
|
||||
"IMAGE_FILE_MACHINE_SH5": reflect.ValueOf(constant.MakeFromLiteral("424", token.INT, 0)),
|
||||
"IMAGE_FILE_MACHINE_THUMB": reflect.ValueOf(constant.MakeFromLiteral("450", token.INT, 0)),
|
||||
"IMAGE_FILE_MACHINE_UNKNOWN": reflect.ValueOf(constant.MakeFromLiteral("0", token.INT, 0)),
|
||||
"IMAGE_FILE_MACHINE_WCEMIPSV2": reflect.ValueOf(constant.MakeFromLiteral("361", token.INT, 0)),
|
||||
"NewFile": reflect.ValueOf(pe.NewFile),
|
||||
"Open": reflect.ValueOf(pe.Open),
|
||||
|
||||
// type definitions
|
||||
"COFFSymbol": reflect.ValueOf((*pe.COFFSymbol)(nil)),
|
||||
"DataDirectory": reflect.ValueOf((*pe.DataDirectory)(nil)),
|
||||
"File": reflect.ValueOf((*pe.File)(nil)),
|
||||
"FileHeader": reflect.ValueOf((*pe.FileHeader)(nil)),
|
||||
"FormatError": reflect.ValueOf((*pe.FormatError)(nil)),
|
||||
"ImportDirectory": reflect.ValueOf((*pe.ImportDirectory)(nil)),
|
||||
"OptionalHeader32": reflect.ValueOf((*pe.OptionalHeader32)(nil)),
|
||||
"OptionalHeader64": reflect.ValueOf((*pe.OptionalHeader64)(nil)),
|
||||
"Reloc": reflect.ValueOf((*pe.Reloc)(nil)),
|
||||
"Section": reflect.ValueOf((*pe.Section)(nil)),
|
||||
"SectionHeader": reflect.ValueOf((*pe.SectionHeader)(nil)),
|
||||
"SectionHeader32": reflect.ValueOf((*pe.SectionHeader32)(nil)),
|
||||
"StringTable": reflect.ValueOf((*pe.StringTable)(nil)),
|
||||
"Symbol": reflect.ValueOf((*pe.Symbol)(nil)),
|
||||
}
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
// Code generated by 'goexports os'. DO NOT EDIT.
|
||||
|
||||
// +build go1.13,!go1.14
|
||||
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"os"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Symbols["os"] = map[string]reflect.Value{
|
||||
// function, constant and variable definitions
|
||||
"Args": reflect.ValueOf(&os.Args).Elem(),
|
||||
"Chdir": reflect.ValueOf(os.Chdir),
|
||||
"Chmod": reflect.ValueOf(os.Chmod),
|
||||
"Chown": reflect.ValueOf(os.Chown),
|
||||
"Chtimes": reflect.ValueOf(os.Chtimes),
|
||||
"Clearenv": reflect.ValueOf(os.Clearenv),
|
||||
"Create": reflect.ValueOf(os.Create),
|
||||
"DevNull": reflect.ValueOf(os.DevNull),
|
||||
"Environ": reflect.ValueOf(os.Environ),
|
||||
"ErrClosed": reflect.ValueOf(&os.ErrClosed).Elem(),
|
||||
"ErrExist": reflect.ValueOf(&os.ErrExist).Elem(),
|
||||
"ErrInvalid": reflect.ValueOf(&os.ErrInvalid).Elem(),
|
||||
"ErrNoDeadline": reflect.ValueOf(&os.ErrNoDeadline).Elem(),
|
||||
"ErrNotExist": reflect.ValueOf(&os.ErrNotExist).Elem(),
|
||||
"ErrPermission": reflect.ValueOf(&os.ErrPermission).Elem(),
|
||||
"Executable": reflect.ValueOf(os.Executable),
|
||||
"Exit": reflect.ValueOf(os.Exit),
|
||||
"Expand": reflect.ValueOf(os.Expand),
|
||||
"ExpandEnv": reflect.ValueOf(os.ExpandEnv),
|
||||
"FindProcess": reflect.ValueOf(os.FindProcess),
|
||||
"Getegid": reflect.ValueOf(os.Getegid),
|
||||
"Getenv": reflect.ValueOf(os.Getenv),
|
||||
"Geteuid": reflect.ValueOf(os.Geteuid),
|
||||
"Getgid": reflect.ValueOf(os.Getgid),
|
||||
"Getgroups": reflect.ValueOf(os.Getgroups),
|
||||
"Getpagesize": reflect.ValueOf(os.Getpagesize),
|
||||
"Getpid": reflect.ValueOf(os.Getpid),
|
||||
"Getppid": reflect.ValueOf(os.Getppid),
|
||||
"Getuid": reflect.ValueOf(os.Getuid),
|
||||
"Getwd": reflect.ValueOf(os.Getwd),
|
||||
"Hostname": reflect.ValueOf(os.Hostname),
|
||||
"Interrupt": reflect.ValueOf(&os.Interrupt).Elem(),
|
||||
"IsExist": reflect.ValueOf(os.IsExist),
|
||||
"IsNotExist": reflect.ValueOf(os.IsNotExist),
|
||||
"IsPathSeparator": reflect.ValueOf(os.IsPathSeparator),
|
||||
"IsPermission": reflect.ValueOf(os.IsPermission),
|
||||
"IsTimeout": reflect.ValueOf(os.IsTimeout),
|
||||
"Kill": reflect.ValueOf(&os.Kill).Elem(),
|
||||
"Lchown": reflect.ValueOf(os.Lchown),
|
||||
"Link": reflect.ValueOf(os.Link),
|
||||
"LookupEnv": reflect.ValueOf(os.LookupEnv),
|
||||
"Lstat": reflect.ValueOf(os.Lstat),
|
||||
"Mkdir": reflect.ValueOf(os.Mkdir),
|
||||
"MkdirAll": reflect.ValueOf(os.MkdirAll),
|
||||
"ModeAppend": reflect.ValueOf(os.ModeAppend),
|
||||
"ModeCharDevice": reflect.ValueOf(os.ModeCharDevice),
|
||||
"ModeDevice": reflect.ValueOf(os.ModeDevice),
|
||||
"ModeDir": reflect.ValueOf(os.ModeDir),
|
||||
"ModeExclusive": reflect.ValueOf(os.ModeExclusive),
|
||||
"ModeIrregular": reflect.ValueOf(os.ModeIrregular),
|
||||
"ModeNamedPipe": reflect.ValueOf(os.ModeNamedPipe),
|
||||
"ModePerm": reflect.ValueOf(os.ModePerm),
|
||||
"ModeSetgid": reflect.ValueOf(os.ModeSetgid),
|
||||
"ModeSetuid": reflect.ValueOf(os.ModeSetuid),
|
||||
"ModeSocket": reflect.ValueOf(os.ModeSocket),
|
||||
"ModeSticky": reflect.ValueOf(os.ModeSticky),
|
||||
"ModeSymlink": reflect.ValueOf(os.ModeSymlink),
|
||||
"ModeTemporary": reflect.ValueOf(os.ModeTemporary),
|
||||
"ModeType": reflect.ValueOf(os.ModeType),
|
||||
"NewFile": reflect.ValueOf(os.NewFile),
|
||||
"NewSyscallError": reflect.ValueOf(os.NewSyscallError),
|
||||
"O_APPEND": reflect.ValueOf(os.O_APPEND),
|
||||
"O_CREATE": reflect.ValueOf(os.O_CREATE),
|
||||
"O_EXCL": reflect.ValueOf(os.O_EXCL),
|
||||
"O_RDONLY": reflect.ValueOf(os.O_RDONLY),
|
||||
"O_RDWR": reflect.ValueOf(os.O_RDWR),
|
||||
"O_SYNC": reflect.ValueOf(os.O_SYNC),
|
||||
"O_TRUNC": reflect.ValueOf(os.O_TRUNC),
|
||||
"O_WRONLY": reflect.ValueOf(os.O_WRONLY),
|
||||
"Open": reflect.ValueOf(os.Open),
|
||||
"OpenFile": reflect.ValueOf(os.OpenFile),
|
||||
"PathListSeparator": reflect.ValueOf(constant.MakeFromLiteral("58", token.INT, 0)),
|
||||
"PathSeparator": reflect.ValueOf(constant.MakeFromLiteral("47", token.INT, 0)),
|
||||
"Pipe": reflect.ValueOf(os.Pipe),
|
||||
"Readlink": reflect.ValueOf(os.Readlink),
|
||||
"Remove": reflect.ValueOf(os.Remove),
|
||||
"RemoveAll": reflect.ValueOf(os.RemoveAll),
|
||||
"Rename": reflect.ValueOf(os.Rename),
|
||||
"SEEK_CUR": reflect.ValueOf(os.SEEK_CUR),
|
||||
"SEEK_END": reflect.ValueOf(os.SEEK_END),
|
||||
"SEEK_SET": reflect.ValueOf(os.SEEK_SET),
|
||||
"SameFile": reflect.ValueOf(os.SameFile),
|
||||
"Setenv": reflect.ValueOf(os.Setenv),
|
||||
"StartProcess": reflect.ValueOf(os.StartProcess),
|
||||
"Stat": reflect.ValueOf(os.Stat),
|
||||
"Stderr": reflect.ValueOf(&os.Stderr).Elem(),
|
||||
"Stdin": reflect.ValueOf(&os.Stdin).Elem(),
|
||||
"Stdout": reflect.ValueOf(&os.Stdout).Elem(),
|
||||
"Symlink": reflect.ValueOf(os.Symlink),
|
||||
"TempDir": reflect.ValueOf(os.TempDir),
|
||||
"Truncate": reflect.ValueOf(os.Truncate),
|
||||
"Unsetenv": reflect.ValueOf(os.Unsetenv),
|
||||
"UserCacheDir": reflect.ValueOf(os.UserCacheDir),
|
||||
"UserConfigDir": reflect.ValueOf(os.UserConfigDir),
|
||||
"UserHomeDir": reflect.ValueOf(os.UserHomeDir),
|
||||
|
||||
// type definitions
|
||||
"File": reflect.ValueOf((*os.File)(nil)),
|
||||
"FileInfo": reflect.ValueOf((*os.FileInfo)(nil)),
|
||||
"FileMode": reflect.ValueOf((*os.FileMode)(nil)),
|
||||
"LinkError": reflect.ValueOf((*os.LinkError)(nil)),
|
||||
"PathError": reflect.ValueOf((*os.PathError)(nil)),
|
||||
"ProcAttr": reflect.ValueOf((*os.ProcAttr)(nil)),
|
||||
"Process": reflect.ValueOf((*os.Process)(nil)),
|
||||
"ProcessState": reflect.ValueOf((*os.ProcessState)(nil)),
|
||||
"Signal": reflect.ValueOf((*os.Signal)(nil)),
|
||||
"SyscallError": reflect.ValueOf((*os.SyscallError)(nil)),
|
||||
|
||||
// interface wrapper definitions
|
||||
"_FileInfo": reflect.ValueOf((*_os_FileInfo)(nil)),
|
||||
"_Signal": reflect.ValueOf((*_os_Signal)(nil)),
|
||||
}
|
||||
}
|
||||
|
||||
// _os_FileInfo is an interface wrapper for FileInfo type
|
||||
type _os_FileInfo struct {
|
||||
WIsDir func() bool
|
||||
WModTime func() time.Time
|
||||
WMode func() os.FileMode
|
||||
WName func() string
|
||||
WSize func() int64
|
||||
WSys func() interface{}
|
||||
}
|
||||
|
||||
func (W _os_FileInfo) IsDir() bool { return W.WIsDir() }
|
||||
func (W _os_FileInfo) ModTime() time.Time { return W.WModTime() }
|
||||
func (W _os_FileInfo) Mode() os.FileMode { return W.WMode() }
|
||||
func (W _os_FileInfo) Name() string { return W.WName() }
|
||||
func (W _os_FileInfo) Size() int64 { return W.WSize() }
|
||||
func (W _os_FileInfo) Sys() interface{} { return W.WSys() }
|
||||
|
||||
// _os_Signal is an interface wrapper for Signal type
|
||||
type _os_Signal struct {
|
||||
WSignal func()
|
||||
WString func() string
|
||||
}
|
||||
|
||||
func (W _os_Signal) Signal() { W.WSignal() }
|
||||
func (W _os_Signal) String() string { return W.WString() }
|
||||
@@ -1,25 +0,0 @@
|
||||
// Code generated by 'goexports os/exec'. DO NOT EDIT.
|
||||
|
||||
// +build go1.13,!go1.14
|
||||
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Symbols["os/exec"] = map[string]reflect.Value{
|
||||
// function, constant and variable definitions
|
||||
"Command": reflect.ValueOf(exec.Command),
|
||||
"CommandContext": reflect.ValueOf(exec.CommandContext),
|
||||
"ErrNotFound": reflect.ValueOf(&exec.ErrNotFound).Elem(),
|
||||
"LookPath": reflect.ValueOf(exec.LookPath),
|
||||
|
||||
// type definitions
|
||||
"Cmd": reflect.ValueOf((*exec.Cmd)(nil)),
|
||||
"Error": reflect.ValueOf((*exec.Error)(nil)),
|
||||
"ExitError": reflect.ValueOf((*exec.ExitError)(nil)),
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by 'goexports archive/tar'. DO NOT EDIT.
|
||||
// Code generated by 'github.com/containous/yaegi/extract archive/tar'. DO NOT EDIT.
|
||||
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by 'goexports archive/zip'. DO NOT EDIT.
|
||||
// Code generated by 'github.com/containous/yaegi/extract archive/zip'. DO NOT EDIT.
|
||||
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by 'goexports bufio'. DO NOT EDIT.
|
||||
// Code generated by 'github.com/containous/yaegi/extract bufio'. DO NOT EDIT.
|
||||
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by 'goexports bytes'. DO NOT EDIT.
|
||||
// Code generated by 'github.com/containous/yaegi/extract bytes'. DO NOT EDIT.
|
||||
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by 'goexports compress/bzip2'. DO NOT EDIT.
|
||||
// Code generated by 'github.com/containous/yaegi/extract compress/bzip2'. DO NOT EDIT.
|
||||
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by 'goexports compress/flate'. DO NOT EDIT.
|
||||
// Code generated by 'github.com/containous/yaegi/extract compress/flate'. DO NOT EDIT.
|
||||
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by 'goexports compress/gzip'. DO NOT EDIT.
|
||||
// Code generated by 'github.com/containous/yaegi/extract compress/gzip'. DO NOT EDIT.
|
||||
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by 'goexports compress/lzw'. DO NOT EDIT.
|
||||
// Code generated by 'github.com/containous/yaegi/extract compress/lzw'. DO NOT EDIT.
|
||||
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by 'goexports compress/zlib'. DO NOT EDIT.
|
||||
// Code generated by 'github.com/containous/yaegi/extract compress/zlib'. DO NOT EDIT.
|
||||
|
||||
// +build go1.14,!go1.15
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user