Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
274eecdf18 | ||
|
|
8fa00f826c | ||
|
|
a64fe5b210 | ||
|
|
5c59dc425f | ||
|
|
8ad14d8ea4 | ||
|
|
8a1f9ef44e | ||
|
|
5cd1e11379 | ||
|
|
24b5375636 | ||
|
|
a83f492309 | ||
|
|
02c30482cc | ||
|
|
9e1da978b0 | ||
|
|
662838fd80 | ||
|
|
92d65c22f0 | ||
|
|
2db4579b6f | ||
|
|
101633c380 | ||
|
|
1e0f6ece6e | ||
|
|
662d2a6afe | ||
|
|
b25ee3f809 | ||
|
|
81e1e5f206 | ||
|
|
81d8339132 | ||
|
|
d494f9e420 | ||
|
|
6da1107c39 |
@@ -47,7 +47,7 @@ archives:
|
||||
- LICENSE
|
||||
|
||||
brews:
|
||||
- github:
|
||||
- tap:
|
||||
owner: traefik
|
||||
name: homebrew-tap
|
||||
commit_author:
|
||||
|
||||
29
_test/addr2.go
Normal file
29
_test/addr2.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Email struct {
|
||||
Where string `xml:"where,attr"`
|
||||
Addr string
|
||||
}
|
||||
|
||||
func f(s string, r interface{}) error {
|
||||
return xml.Unmarshal([]byte(s), &r)
|
||||
}
|
||||
|
||||
func main() {
|
||||
data := `
|
||||
<Email where='work'>
|
||||
<Addr>bob@work.com</Addr>
|
||||
</Email>
|
||||
`
|
||||
v := Email{}
|
||||
err := f(data, &v)
|
||||
fmt.Println(err, v)
|
||||
}
|
||||
|
||||
// Ouput:
|
||||
// <nil> {work bob@work.com}
|
||||
125
_test/assert0.go
Normal file
125
_test/assert0.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
type MyWriter interface {
|
||||
Write(p []byte) (i int, err error)
|
||||
}
|
||||
|
||||
type TestStruct struct{}
|
||||
|
||||
func (t TestStruct) Write(p []byte) (n int, err error) {
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func usesWriter(w MyWriter) {
|
||||
n, _ := w.Write([]byte("hello world"))
|
||||
fmt.Println(n)
|
||||
}
|
||||
|
||||
type MyStringer interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
func usesStringer(s MyStringer) {
|
||||
fmt.Println(s.String())
|
||||
}
|
||||
|
||||
func main() {
|
||||
aType := reflect.TypeOf((*MyWriter)(nil)).Elem()
|
||||
|
||||
var t interface{}
|
||||
t = TestStruct{}
|
||||
var tw MyWriter
|
||||
var ok bool
|
||||
tw, ok = t.(MyWriter)
|
||||
if !ok {
|
||||
fmt.Println("TestStruct does not implement MyWriter")
|
||||
} else {
|
||||
fmt.Println("TestStruct implements MyWriter")
|
||||
usesWriter(tw)
|
||||
}
|
||||
n, _ := t.(MyWriter).Write([]byte("hello world"))
|
||||
fmt.Println(n)
|
||||
bType := reflect.TypeOf(TestStruct{})
|
||||
fmt.Println(bType.Implements(aType))
|
||||
|
||||
// not redundant with the above, because it goes through a slightly different code path.
|
||||
if _, ok := t.(MyWriter); !ok {
|
||||
fmt.Println("TestStruct does not implement MyWriter")
|
||||
return
|
||||
} else {
|
||||
fmt.Println("TestStruct implements MyWriter")
|
||||
}
|
||||
|
||||
t = 42
|
||||
foo, ok := t.(MyWriter)
|
||||
if !ok {
|
||||
fmt.Println("42 does not implement MyWriter")
|
||||
} else {
|
||||
fmt.Println("42 implements MyWriter")
|
||||
}
|
||||
_ = foo
|
||||
|
||||
if _, ok := t.(MyWriter); !ok {
|
||||
fmt.Println("42 does not implement MyWriter")
|
||||
} else {
|
||||
fmt.Println("42 implements MyWriter")
|
||||
}
|
||||
|
||||
var tt interface{}
|
||||
tt = time.Nanosecond
|
||||
var myD MyStringer
|
||||
myD, ok = tt.(MyStringer)
|
||||
if !ok {
|
||||
fmt.Println("time.Nanosecond does not implement MyStringer")
|
||||
} else {
|
||||
fmt.Println("time.Nanosecond implements MyStringer")
|
||||
usesStringer(myD)
|
||||
}
|
||||
fmt.Println(tt.(MyStringer).String())
|
||||
cType := reflect.TypeOf((*MyStringer)(nil)).Elem()
|
||||
dType := reflect.TypeOf(time.Nanosecond)
|
||||
fmt.Println(dType.Implements(cType))
|
||||
|
||||
if _, ok := tt.(MyStringer); !ok {
|
||||
fmt.Println("time.Nanosecond does not implement MyStringer")
|
||||
} else {
|
||||
fmt.Println("time.Nanosecond implements MyStringer")
|
||||
}
|
||||
|
||||
tt = 42
|
||||
bar, ok := tt.(MyStringer)
|
||||
if !ok {
|
||||
fmt.Println("42 does not implement MyStringer")
|
||||
} else {
|
||||
fmt.Println("42 implements MyStringer")
|
||||
}
|
||||
_ = bar
|
||||
|
||||
if _, ok := tt.(MyStringer); !ok {
|
||||
fmt.Println("42 does not implement MyStringer")
|
||||
} else {
|
||||
fmt.Println("42 implements MyStringer")
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// TestStruct implements MyWriter
|
||||
// 11
|
||||
// 11
|
||||
// true
|
||||
// TestStruct implements MyWriter
|
||||
// 42 does not implement MyWriter
|
||||
// 42 does not implement MyWriter
|
||||
// time.Nanosecond implements MyStringer
|
||||
// 1ns
|
||||
// 1ns
|
||||
// true
|
||||
// time.Nanosecond implements MyStringer
|
||||
// 42 does not implement MyStringer
|
||||
// 42 does not implement MyStringer
|
||||
85
_test/assert1.go
Normal file
85
_test/assert1.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TestStruct struct{}
|
||||
|
||||
func (t TestStruct) String() string {
|
||||
return "hello world"
|
||||
}
|
||||
|
||||
func main() {
|
||||
aType := reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
|
||||
|
||||
var t interface{}
|
||||
t = time.Nanosecond
|
||||
s, ok := t.(fmt.Stringer)
|
||||
if !ok {
|
||||
fmt.Println("time.Nanosecond does not implement fmt.Stringer")
|
||||
return
|
||||
}
|
||||
fmt.Println(s.String())
|
||||
fmt.Println(t.(fmt.Stringer).String())
|
||||
bType := reflect.TypeOf(time.Nanosecond)
|
||||
fmt.Println(bType.Implements(aType))
|
||||
|
||||
// not redundant with the above, because it goes through a slightly different code path.
|
||||
if _, ok := t.(fmt.Stringer); !ok {
|
||||
fmt.Println("time.Nanosecond does not implement fmt.Stringer")
|
||||
return
|
||||
} else {
|
||||
fmt.Println("time.Nanosecond implements fmt.Stringer")
|
||||
}
|
||||
|
||||
t = 42
|
||||
foo, ok := t.(fmt.Stringer)
|
||||
if !ok {
|
||||
fmt.Println("42 does not implement fmt.Stringer")
|
||||
} else {
|
||||
fmt.Println("42 implements fmt.Stringer")
|
||||
return
|
||||
}
|
||||
_ = foo
|
||||
|
||||
if _, ok := t.(fmt.Stringer); !ok {
|
||||
fmt.Println("42 does not implement fmt.Stringer")
|
||||
} else {
|
||||
fmt.Println("42 implements fmt.Stringer")
|
||||
return
|
||||
}
|
||||
|
||||
var tt interface{}
|
||||
tt = TestStruct{}
|
||||
ss, ok := tt.(fmt.Stringer)
|
||||
if !ok {
|
||||
fmt.Println("TestStuct does not implement fmt.Stringer")
|
||||
return
|
||||
}
|
||||
fmt.Println(ss.String())
|
||||
fmt.Println(tt.(fmt.Stringer).String())
|
||||
// TODO(mpl): uncomment when fixed
|
||||
// cType := reflect.TypeOf(TestStruct{})
|
||||
// fmt.Println(cType.Implements(aType))
|
||||
|
||||
if _, ok := tt.(fmt.Stringer); !ok {
|
||||
fmt.Println("TestStuct does not implement fmt.Stringer")
|
||||
return
|
||||
} else {
|
||||
fmt.Println("TestStuct implements fmt.Stringer")
|
||||
}
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1ns
|
||||
// 1ns
|
||||
// true
|
||||
// time.Nanosecond implements fmt.Stringer
|
||||
// 42 does not implement fmt.Stringer
|
||||
// 42 does not implement fmt.Stringer
|
||||
// hello world
|
||||
// hello world
|
||||
// TestStuct implements fmt.Stringer
|
||||
50
_test/composite15.go
Normal file
50
_test/composite15.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func interfaceAsInts() {
|
||||
var a interface{}
|
||||
b := 2
|
||||
c := 3
|
||||
a = []int{b, c}
|
||||
|
||||
d, ok := a.([]int)
|
||||
if !ok {
|
||||
println("nope")
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range d {
|
||||
fmt.Println(v)
|
||||
}
|
||||
}
|
||||
|
||||
func interfaceAsInterfaces() {
|
||||
var a, b, c interface{}
|
||||
b = 2
|
||||
c = 3
|
||||
a = []interface{}{b, c}
|
||||
|
||||
d, ok := a.([]interface{})
|
||||
if !ok {
|
||||
println("nope")
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range d {
|
||||
fmt.Println(v)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
interfaceAsInts()
|
||||
interfaceAsInterfaces()
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
// 3
|
||||
// 2
|
||||
// 3
|
||||
16
_test/composite16.go
Normal file
16
_test/composite16.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func main() {
|
||||
body := url.Values{
|
||||
"Action": {"none"},
|
||||
}
|
||||
fmt.Println(body)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// map[Action:[none]]
|
||||
15
_test/const22.go
Normal file
15
_test/const22.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
const (
|
||||
numDec uint8 = (1 << iota) / 2
|
||||
numHex
|
||||
numOct
|
||||
numFloat
|
||||
)
|
||||
|
||||
func main() {
|
||||
println(13 & (numHex | numOct))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
17
_test/fun26.go
Normal file
17
_test/fun26.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
type F func() (int, error)
|
||||
|
||||
func f1() (int, error) { return 3, nil }
|
||||
|
||||
func f2(a string, f F) {
|
||||
c, _ := f()
|
||||
println(a, c)
|
||||
}
|
||||
|
||||
func main() {
|
||||
f2("hello", F(f1))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello 3
|
||||
12
_test/goto1.go
Normal file
12
_test/goto1.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
if true {
|
||||
goto here
|
||||
}
|
||||
here:
|
||||
println("ok")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ok
|
||||
32
_test/interface47.go
Normal file
32
_test/interface47.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
type Doer interface {
|
||||
Do() error
|
||||
}
|
||||
|
||||
type T struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (t *T) Do() error { println("in do"); return nil }
|
||||
|
||||
func f() (Doer, error) { return &T{"truc"}, nil }
|
||||
|
||||
type Ev struct {
|
||||
doer func() (Doer, error)
|
||||
}
|
||||
|
||||
func (e *Ev) do() {
|
||||
d, _ := e.doer()
|
||||
d.Do()
|
||||
}
|
||||
|
||||
func main() {
|
||||
e := &Ev{f}
|
||||
println(e != nil)
|
||||
e.do()
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// in do
|
||||
17
_test/interface48.go
Normal file
17
_test/interface48.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type I1 interface{ A }
|
||||
|
||||
type A = I2
|
||||
|
||||
type I2 interface{ F() I1 }
|
||||
|
||||
func main() {
|
||||
var i I1
|
||||
fmt.Println(i)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// <nil>
|
||||
45
_test/interface49.go
Normal file
45
_test/interface49.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package main
|
||||
|
||||
type Descriptor interface {
|
||||
ParentFile() FileDescriptor
|
||||
}
|
||||
|
||||
type FileDescriptor interface {
|
||||
Enums() EnumDescriptors
|
||||
Services() ServiceDescriptors
|
||||
}
|
||||
|
||||
type EnumDescriptors interface {
|
||||
Get(i int) EnumDescriptor
|
||||
}
|
||||
|
||||
type EnumDescriptor interface {
|
||||
Values() EnumValueDescriptors
|
||||
}
|
||||
|
||||
type EnumValueDescriptors interface {
|
||||
Get(i int) EnumValueDescriptor
|
||||
}
|
||||
|
||||
type EnumValueDescriptor interface {
|
||||
Descriptor
|
||||
}
|
||||
|
||||
type ServiceDescriptors interface {
|
||||
Get(i int) ServiceDescriptor
|
||||
}
|
||||
|
||||
type ServiceDescriptor interface {
|
||||
Descriptor
|
||||
isServiceDescriptor
|
||||
}
|
||||
|
||||
type isServiceDescriptor interface{ ProtoType(ServiceDescriptor) }
|
||||
|
||||
func main() {
|
||||
var d Descriptor
|
||||
println(d == nil)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true
|
||||
40
_test/issue-1007.go
Normal file
40
_test/issue-1007.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package main
|
||||
|
||||
type TypeA struct {
|
||||
B TypeB
|
||||
}
|
||||
|
||||
type TypeB struct {
|
||||
C1 *TypeC
|
||||
C2 *TypeC
|
||||
}
|
||||
|
||||
type TypeC struct {
|
||||
Val string
|
||||
D *TypeD
|
||||
D2 *TypeD
|
||||
}
|
||||
|
||||
type TypeD struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func build() *TypeA {
|
||||
return &TypeA{
|
||||
B: TypeB{
|
||||
C2: &TypeC{Val: "22"},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func Bar(s string) string {
|
||||
a := build()
|
||||
return s + "-" + a.B.C2.Val
|
||||
}
|
||||
|
||||
func main() {
|
||||
println(Bar("test"))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// test-22
|
||||
15
_test/issue-981.go
Normal file
15
_test/issue-981.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
dp := make(map[int]int)
|
||||
dp[0] = 1
|
||||
for i := 1; i < 10; i++ {
|
||||
dp[i] = dp[i-1] + dp[i-2]
|
||||
}
|
||||
fmt.Printf("%v\n", dp)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// map[0:1 1:1 2:2 3:3 4:5 5:8 6:13 7:21 8:34 9:55]
|
||||
15
_test/issue-993.go
Normal file
15
_test/issue-993.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
var m map[string]int64
|
||||
|
||||
func initVar() {
|
||||
m = make(map[string]int64)
|
||||
}
|
||||
|
||||
func main() {
|
||||
initVar()
|
||||
println(len(m))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 0
|
||||
41
_test/struct59.go
Normal file
41
_test/struct59.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type A struct {
|
||||
B map[string]*B
|
||||
C map[string]*C
|
||||
}
|
||||
|
||||
type C struct {
|
||||
D *D
|
||||
E *E
|
||||
}
|
||||
|
||||
type D struct {
|
||||
F *F
|
||||
G []G
|
||||
}
|
||||
|
||||
type E struct {
|
||||
H []H
|
||||
F *F
|
||||
}
|
||||
|
||||
type B struct{}
|
||||
type F struct{}
|
||||
type G struct{}
|
||||
type H struct{}
|
||||
|
||||
func main() {
|
||||
conf := &A{
|
||||
B: make(map[string]*B),
|
||||
C: make(map[string]*C),
|
||||
}
|
||||
fmt.Println(conf)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// &{map[] map[]}
|
||||
15
_test/time15.go
Normal file
15
_test/time15.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import "time"
|
||||
|
||||
type TimeValue time.Time
|
||||
|
||||
func (v *TimeValue) decode() { println("in decode") }
|
||||
|
||||
func main() {
|
||||
var tv TimeValue
|
||||
tv.decode()
|
||||
}
|
||||
|
||||
// Output:
|
||||
// in decode
|
||||
15
_test/var15.go
Normal file
15
_test/var15.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
var a int = 2
|
||||
|
||||
func inca() {
|
||||
a = a + 1
|
||||
}
|
||||
|
||||
func main() {
|
||||
inca()
|
||||
println(a)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 3
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"go/build"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -102,6 +103,18 @@ func test(arg []string) (err error) {
|
||||
testing.Init()
|
||||
os.Args = tf
|
||||
flag.Parse()
|
||||
path += string(filepath.Separator)
|
||||
var dir string
|
||||
|
||||
switch strings.Split(path, string(filepath.Separator))[0] {
|
||||
case ".", "..", string(filepath.Separator):
|
||||
dir = path
|
||||
default:
|
||||
dir = filepath.Join(build.Default.GOPATH, "src", path)
|
||||
}
|
||||
if err = os.Chdir(dir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
i := interp.New(interp.Options{GoPath: build.Default.GOPATH, BuildTags: strings.Split(tags, ",")})
|
||||
i.Use(stdlib.Symbols)
|
||||
|
||||
@@ -209,7 +209,11 @@ func {{$name}}Const(n *node) {
|
||||
v := constant.BinaryOp(vConstantValue(v0), operator, vConstantValue(v1))
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
{{- else}}
|
||||
{{- if $op.Int}}
|
||||
v := constant.BinaryOp(constant.ToInt(vConstantValue(v0)), token.{{tokenFromName $name}}, constant.ToInt(vConstantValue(v1)))
|
||||
{{- else}}
|
||||
v := constant.BinaryOp(vConstantValue(v0), token.{{tokenFromName $name}}, vConstantValue(v1))
|
||||
{{- end}}
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
{{- end}}
|
||||
{{- if $op.Str}}
|
||||
@@ -940,6 +944,7 @@ type Op struct {
|
||||
Complex bool // true if operator applies to complex
|
||||
Shift bool // true if operator is a shift operation
|
||||
Bool bool // true if operator applies to bool
|
||||
Int bool // true if operator applies to int only
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -968,17 +973,17 @@ func main() {
|
||||
b := &bytes.Buffer{}
|
||||
data := map[string]interface{}{
|
||||
"Arithmetic": map[string]Op{
|
||||
"add": {"+", true, true, true, false, false},
|
||||
"sub": {"-", false, true, true, false, false},
|
||||
"mul": {"*", false, true, true, false, false},
|
||||
"quo": {"/", false, true, true, false, false},
|
||||
"rem": {"%", false, false, false, false, false},
|
||||
"shl": {"<<", false, false, false, true, false},
|
||||
"shr": {">>", false, false, false, true, false},
|
||||
"and": {"&", false, false, false, false, false},
|
||||
"or": {"|", false, false, false, false, false},
|
||||
"xor": {"^", false, false, false, false, false},
|
||||
"andNot": {"&^", false, false, false, false, false},
|
||||
"add": {"+", true, true, true, false, false, false},
|
||||
"sub": {"-", false, true, true, false, false, false},
|
||||
"mul": {"*", false, true, true, false, false, false},
|
||||
"quo": {"/", false, true, true, false, false, false},
|
||||
"rem": {"%", false, false, false, false, false, true},
|
||||
"shl": {"<<", false, false, false, true, false, true},
|
||||
"shr": {">>", false, false, false, true, false, true},
|
||||
"and": {"&", false, false, false, false, false, true},
|
||||
"or": {"|", false, false, false, false, false, true},
|
||||
"xor": {"^", false, false, false, false, false, true},
|
||||
"andNot": {"&^", false, false, false, false, false, true},
|
||||
},
|
||||
"IncDec": map[string]Op{
|
||||
"inc": {Name: "+"},
|
||||
@@ -996,7 +1001,7 @@ func main() {
|
||||
"not": {Name: "!", Float: false, Bool: true},
|
||||
"neg": {Name: "-", Float: true, Bool: false},
|
||||
"pos": {Name: "+", Float: true, Bool: false},
|
||||
"bitNot": {Name: "^", Float: false, Bool: false},
|
||||
"bitNot": {Name: "^", Float: false, Bool: false, Int: true},
|
||||
},
|
||||
}
|
||||
if err = parse.Execute(b, data); err != nil {
|
||||
|
||||
122
interp/cfg.go
122
interp/cfg.go
@@ -75,6 +75,9 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
case assignStmt, defineStmt:
|
||||
a := n.anc
|
||||
i := childPos(n) - a.nright
|
||||
if i < 0 {
|
||||
break
|
||||
}
|
||||
if len(a.child) > a.nright+a.nleft {
|
||||
i--
|
||||
}
|
||||
@@ -194,7 +197,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
|
||||
case breakStmt, continueStmt, gotoStmt:
|
||||
if len(n.child) > 0 {
|
||||
// Handle labeled statements
|
||||
// Handle labeled statements.
|
||||
label := n.child[0].ident
|
||||
if sym, _, ok := sc.lookup(label); ok {
|
||||
if sym.kind != labelSym {
|
||||
@@ -211,25 +214,23 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
|
||||
case labeledStmt:
|
||||
label := n.child[0].ident
|
||||
if sym, _, ok := sc.lookup(label); ok {
|
||||
if sym.kind != labelSym {
|
||||
err = n.child[0].cfgErrorf("label %s not defined", label)
|
||||
break
|
||||
}
|
||||
// TODO(marc): labels must be stored outside of symbols to avoid collisions
|
||||
// Used labels are searched in current and sub scopes, not upper ones.
|
||||
if sym, ok := sc.lookdown(label); ok {
|
||||
sym.node = n
|
||||
n.sym = sym
|
||||
} else {
|
||||
n.sym = &symbol{kind: labelSym, node: n, index: -1}
|
||||
sc.sym[label] = n.sym
|
||||
}
|
||||
sc.sym[label] = n.sym
|
||||
|
||||
case caseClause:
|
||||
sc = sc.pushBloc()
|
||||
if sn := n.anc.anc; sn.kind == typeSwitch && sn.child[1].action == aAssign {
|
||||
// Type switch clause with a var defined in switch guard
|
||||
// Type switch clause with a var defined in switch guard.
|
||||
var typ *itype
|
||||
if len(n.child) == 2 {
|
||||
// 1 type in clause: define the var with this type in the case clause scope
|
||||
// 1 type in clause: define the var with this type in the case clause scope.
|
||||
switch {
|
||||
case n.child[0].ident == nilIdent:
|
||||
typ = sc.getType("interface{}")
|
||||
@@ -239,7 +240,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
typ, err = nodeType(interp, sc, n.child[0])
|
||||
}
|
||||
} else {
|
||||
// define the var with the type in the switch guard expression
|
||||
// Define the var with the type in the switch guard expression.
|
||||
typ = sn.child[1].child[1].child[0].typ
|
||||
}
|
||||
if err != nil {
|
||||
@@ -287,8 +288,12 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
// Get type from ancestor (implicit type)
|
||||
if n.anc.kind == keyValueExpr && n == n.anc.child[0] {
|
||||
n.typ = n.anc.typ.key
|
||||
} else if n.anc.typ != nil {
|
||||
n.typ = n.anc.typ.val
|
||||
} else if atyp := n.anc.typ; atyp != nil {
|
||||
if atyp.cat == valueT {
|
||||
n.typ = &itype{cat: valueT, rtype: atyp.rtype.Elem()}
|
||||
} else {
|
||||
n.typ = atyp.val
|
||||
}
|
||||
}
|
||||
if n.typ == nil {
|
||||
err = n.cfgErrorf("undefined type")
|
||||
@@ -417,9 +422,10 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
return false
|
||||
}
|
||||
|
||||
if n.child[1].kind == identExpr {
|
||||
switch n.child[1].kind {
|
||||
case identExpr, selectorExpr:
|
||||
n.typ = &itype{cat: aliasT, val: typ, name: typeName}
|
||||
} else {
|
||||
default:
|
||||
n.typ = typ
|
||||
n.typ.name = typeName
|
||||
}
|
||||
@@ -549,10 +555,21 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
n.findex = dest.findex
|
||||
n.level = dest.level
|
||||
|
||||
// Propagate type
|
||||
// TODO: Check that existing destination type matches source type
|
||||
// Propagate type.
|
||||
// TODO: Check that existing destination type matches source type.
|
||||
|
||||
// In the following, we attempt to optimize by skipping the assign
|
||||
// operation and setting the source location directly to the destination
|
||||
// location in the frame.
|
||||
//
|
||||
switch {
|
||||
case n.action == aAssign && isCall(src) && dest.typ.cat != interfaceT && !isMapEntry(dest) && !isRecursiveField(dest):
|
||||
case n.action != aAssign:
|
||||
// Do not optimize assign combined with another operator.
|
||||
case isMapEntry(dest):
|
||||
// Setting a map entry needs an additional step, do not optimize.
|
||||
// As we only write, skip the default useless getIndexMap dest action.
|
||||
dest.gen = nop
|
||||
case isCall(src) && dest.typ.cat != interfaceT && !isRecursiveField(dest):
|
||||
// Call action may perform the assignment directly.
|
||||
n.gen = nop
|
||||
src.level = level
|
||||
@@ -560,32 +577,25 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
if src.typ.untyped && !dest.typ.untyped {
|
||||
src.typ = dest.typ
|
||||
}
|
||||
case n.action == aAssign && src.action == aRecv:
|
||||
case src.action == aRecv:
|
||||
// Assign by reading from a receiving channel.
|
||||
n.gen = nop
|
||||
src.findex = dest.findex // Set recv address to LHS
|
||||
src.findex = dest.findex // Set recv address to LHS.
|
||||
dest.typ = src.typ
|
||||
case n.action == aAssign && src.action == aCompositeLit && !isMapEntry(dest):
|
||||
case src.action == aCompositeLit:
|
||||
if dest.typ.cat == valueT && dest.typ.rtype.Kind() == reflect.Interface {
|
||||
// Skip optimisation for assigned binary interface or map entry
|
||||
// which require and additional operation to set the value
|
||||
// Skip optimisation for assigned interface.
|
||||
break
|
||||
}
|
||||
if dest.action == aGetIndex {
|
||||
// optimization does not work when assigning to a struct field. Maybe we're not
|
||||
// setting the right frame index or something, and we would end up not writing at
|
||||
// the right place. So disabling it for now.
|
||||
// Optimization does not work when assigning to a struct field.
|
||||
break
|
||||
}
|
||||
// Skip the assign operation entirely, the source frame index is set
|
||||
// to destination index, avoiding extra memory alloc and duplication.
|
||||
n.gen = nop
|
||||
src.findex = dest.findex
|
||||
src.level = level
|
||||
case n.action == aAssign && len(n.child) < 4 && !src.rval.IsValid() && isArithmeticAction(src):
|
||||
case len(n.child) < 4 && !src.rval.IsValid() && isArithmeticAction(src):
|
||||
// Optimize single assignments from some arithmetic operations.
|
||||
// Skip the assign operation entirely, the source frame index is set
|
||||
// to destination index, avoiding extra memory alloc and duplication.
|
||||
src.typ = dest.typ
|
||||
src.findex = dest.findex
|
||||
src.level = level
|
||||
@@ -594,18 +604,17 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
// Assign to nil.
|
||||
src.rval = reflect.New(dest.typ.TypeOf()).Elem()
|
||||
}
|
||||
|
||||
n.typ = dest.typ
|
||||
if sym != nil {
|
||||
sym.typ = n.typ
|
||||
sym.recv = src.recv
|
||||
}
|
||||
n.level = level
|
||||
if isMapEntry(dest) {
|
||||
dest.gen = nop // skip getIndexMap
|
||||
}
|
||||
|
||||
if n.anc.kind == constDecl {
|
||||
n.gen = nop
|
||||
n.findex = -1
|
||||
n.findex = notInFrame
|
||||
if sym, _, ok := sc.lookup(dest.ident); ok {
|
||||
sym.kind = constSym
|
||||
}
|
||||
@@ -646,7 +655,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
if n.child[0].ident == "_" {
|
||||
lc.gen = typeAssertStatus
|
||||
} else {
|
||||
lc.gen = typeAssert2
|
||||
lc.gen = typeAssertLong
|
||||
}
|
||||
n.gen = nop
|
||||
case unaryExpr:
|
||||
@@ -711,7 +720,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
// This operation involved constants, and the result is already computed
|
||||
// by constOp and available in n.rval. Nothing else to do at execution.
|
||||
n.gen = nop
|
||||
n.findex = -1
|
||||
n.findex = notInFrame
|
||||
case n.anc.kind == assignStmt && n.anc.action == aAssign:
|
||||
// To avoid a copy in frame, if the result is to be assigned, store it directly
|
||||
// at the frame location of destination.
|
||||
@@ -734,7 +743,13 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
wireChild(n)
|
||||
t := n.child[0].typ
|
||||
switch t.cat {
|
||||
case aliasT, ptrT:
|
||||
case aliasT:
|
||||
if isString(t.val.TypeOf()) {
|
||||
n.typ = sc.getType("byte")
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
case ptrT:
|
||||
n.typ = t.val
|
||||
if t.val.cat == valueT {
|
||||
n.typ = &itype{cat: valueT, rtype: t.val.rtype.Elem()}
|
||||
@@ -852,7 +867,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
}
|
||||
switch {
|
||||
case n.typ.cat == builtinT:
|
||||
n.findex = -1
|
||||
n.findex = notInFrame
|
||||
n.val = nil
|
||||
case n.anc.kind == returnStmt:
|
||||
// Store result directly to frame output location, to avoid a frame copy.
|
||||
@@ -894,7 +909,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
n.rval = c1.rval
|
||||
case c1.rval.IsValid() && isConstType(c0.typ):
|
||||
n.gen = nop
|
||||
n.findex = -1
|
||||
n.findex = notInFrame
|
||||
n.typ = c0.typ
|
||||
if c, ok := c1.rval.Interface().(constant.Value); ok {
|
||||
i, _ := constant.Int64Val(constant.ToInt(c))
|
||||
@@ -957,7 +972,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
n.findex = -1
|
||||
n.findex = notInFrame
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1038,7 +1053,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
case fileStmt:
|
||||
wireChild(n, varDecl)
|
||||
sc = sc.pop()
|
||||
n.findex = -1
|
||||
n.findex = notInFrame
|
||||
|
||||
case forStmt0: // for {}
|
||||
body := n.child[0]
|
||||
@@ -1496,6 +1511,9 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
// Resolve source package symbol
|
||||
if sym, ok := interp.srcPkg[pkg][name]; ok {
|
||||
n.findex = sym.index
|
||||
if sym.global {
|
||||
n.level = globalFrame
|
||||
}
|
||||
n.val = sym.node
|
||||
n.gen = nop
|
||||
n.action = aGetSym
|
||||
@@ -1510,7 +1528,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
if n.child[0].isType(sc) {
|
||||
// Handle method as a function with receiver in 1st argument
|
||||
n.val = m
|
||||
n.findex = -1
|
||||
n.findex = notInFrame
|
||||
n.gen = nop
|
||||
n.typ = &itype{}
|
||||
*n.typ = *m.typ
|
||||
@@ -1823,7 +1841,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
switch {
|
||||
case n.rval.IsValid():
|
||||
n.gen = nop
|
||||
n.findex = -1
|
||||
n.findex = notInFrame
|
||||
case n.anc.kind == assignStmt && n.anc.action == aAssign:
|
||||
dest := n.anc.child[childPos(n)-n.anc.nright]
|
||||
n.typ = dest.typ
|
||||
@@ -1850,6 +1868,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) {
|
||||
if sc.global {
|
||||
// Global object allocation is already performed in GTA.
|
||||
index = sc.sym[c.ident].index
|
||||
c.level = globalFrame
|
||||
} else {
|
||||
index = sc.add(n.typ)
|
||||
sc.sym[c.ident] = &symbol{index: index, kind: varSym, typ: n.typ}
|
||||
@@ -1876,8 +1895,15 @@ func compDefineX(sc *scope, n *node) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for funtype.cat == valueT && funtype.val != nil {
|
||||
// Retrieve original interpreter type from a wrapped function.
|
||||
// Struct fields of function types are always wrapped in valueT to ensure
|
||||
// their possible use in runtime. In that case, the val field retains the
|
||||
// original interpreter type, which is used now.
|
||||
funtype = funtype.val
|
||||
}
|
||||
if funtype.cat == valueT {
|
||||
// Handle functions imported from runtime
|
||||
// Handle functions imported from runtime.
|
||||
for i := 0; i < funtype.rtype.NumOut(); i++ {
|
||||
types = append(types, &itype{cat: valueT, rtype: funtype.rtype.Out(i)})
|
||||
}
|
||||
@@ -1901,7 +1927,7 @@ func compDefineX(sc *scope, n *node) error {
|
||||
if n.child[0].ident == "_" {
|
||||
n.child[l].gen = typeAssertStatus
|
||||
} else {
|
||||
n.child[l].gen = typeAssert2
|
||||
n.child[l].gen = typeAssertLong
|
||||
}
|
||||
types = append(types, n.child[l].child[1].typ, sc.getType("bool"))
|
||||
n.gen = nop
|
||||
@@ -2333,7 +2359,7 @@ func isCall(n *node) bool {
|
||||
}
|
||||
|
||||
func isBinCall(n *node) bool {
|
||||
return n.kind == callExpr && n.child[0].typ.cat == valueT && n.child[0].typ.rtype.Kind() == reflect.Func
|
||||
return isCall(n) && n.child[0].typ.cat == valueT && n.child[0].typ.rtype.Kind() == reflect.Func
|
||||
}
|
||||
|
||||
func mustReturnValue(n *node) bool {
|
||||
@@ -2349,7 +2375,7 @@ func mustReturnValue(n *node) bool {
|
||||
}
|
||||
|
||||
func isRegularCall(n *node) bool {
|
||||
return n.kind == callExpr && n.child[0].typ.cat == funcT
|
||||
return isCall(n) && n.child[0].typ.cat == funcT
|
||||
}
|
||||
|
||||
func variadicPos(n *node) int {
|
||||
@@ -2471,6 +2497,8 @@ func compositeGenerator(n *node, typ *itype, rtyp reflect.Type) (gen bltnGenerat
|
||||
gen = compositeBinMap
|
||||
case reflect.Ptr:
|
||||
gen = compositeGenerator(n, typ, n.typ.val.rtype)
|
||||
case reflect.Slice:
|
||||
gen = compositeBinSlice
|
||||
default:
|
||||
log.Panic(n.cfgErrorf("compositeGenerator not implemented for type kind: %s", k))
|
||||
}
|
||||
|
||||
@@ -115,6 +115,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
|
||||
sc.sym[c.ident] = &symbol{index: sc.add(n.typ), kind: varSym, global: true, typ: n.typ, node: n}
|
||||
continue
|
||||
}
|
||||
c.level = globalFrame
|
||||
|
||||
// redeclaration error
|
||||
if sym.typ.node != nil && sym.typ.node.anc != nil {
|
||||
@@ -253,10 +254,11 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e
|
||||
return false
|
||||
}
|
||||
|
||||
if n.child[1].kind == identExpr {
|
||||
switch n.child[1].kind {
|
||||
case identExpr, selectorExpr:
|
||||
n.typ = &itype{cat: aliasT, val: typ, name: typeName, path: importPath, field: typ.field, incomplete: typ.incomplete, scope: sc, node: n.child[0]}
|
||||
copy(n.typ.method, typ.method)
|
||||
} else {
|
||||
default:
|
||||
n.typ = typ
|
||||
n.typ.name = typeName
|
||||
n.typ.path = importPath
|
||||
@@ -317,7 +319,50 @@ func (interp *Interpreter) gtaRetry(nodes []*node, importPath string) error {
|
||||
}
|
||||
|
||||
if len(revisit) > 0 {
|
||||
return revisit[0].cfgErrorf("constant definition loop")
|
||||
n := revisit[0]
|
||||
if n.kind == typeSpec {
|
||||
if err := definedType(n.typ); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return n.cfgErrorf("constant definition loop")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func definedType(typ *itype) error {
|
||||
if !typ.incomplete {
|
||||
return nil
|
||||
}
|
||||
switch typ.cat {
|
||||
case interfaceT, structT:
|
||||
for _, f := range typ.field {
|
||||
if err := definedType(f.typ); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case funcT:
|
||||
for _, t := range typ.arg {
|
||||
if err := definedType(t); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, t := range typ.ret {
|
||||
if err := definedType(t); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case mapT:
|
||||
if err := definedType(typ.key); err != nil {
|
||||
return err
|
||||
}
|
||||
fallthrough
|
||||
case aliasT, arrayT, chanT, chanSendT, chanRecvT, ptrT, variadicT:
|
||||
if err := definedType(typ.val); err != nil {
|
||||
return err
|
||||
}
|
||||
case nilT:
|
||||
return typ.node.cfgErrorf("undefined: %s", typ.node.ident)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -65,7 +65,8 @@ type frame struct {
|
||||
// Located at start of struct to ensure proper aligment.
|
||||
id uint64
|
||||
|
||||
anc *frame // ancestor frame (global space)
|
||||
root *frame // global space
|
||||
anc *frame // ancestor frame (caller space)
|
||||
data []reflect.Value // values
|
||||
|
||||
mutex sync.RWMutex
|
||||
@@ -80,8 +81,11 @@ func newFrame(anc *frame, len int, id uint64) *frame {
|
||||
data: make([]reflect.Value, len),
|
||||
id: id,
|
||||
}
|
||||
if anc != nil {
|
||||
if anc == nil {
|
||||
f.root = f
|
||||
} else {
|
||||
f.done = anc.done
|
||||
f.root = anc.root
|
||||
}
|
||||
return f
|
||||
}
|
||||
@@ -93,6 +97,7 @@ func (f *frame) clone() *frame {
|
||||
defer f.mutex.RUnlock()
|
||||
return &frame{
|
||||
anc: f.anc,
|
||||
root: f.root,
|
||||
data: f.data,
|
||||
deferred: f.deferred,
|
||||
recovered: f.recovered,
|
||||
@@ -239,7 +244,7 @@ type Options struct {
|
||||
func New(options Options) *Interpreter {
|
||||
i := Interpreter{
|
||||
opt: opt{context: build.Default},
|
||||
frame: &frame{data: []reflect.Value{}},
|
||||
frame: newFrame(nil, 0, 0),
|
||||
fset: token.NewFileSet(),
|
||||
universe: initUniverse(),
|
||||
scopes: map[string]*scope{},
|
||||
|
||||
@@ -110,6 +110,7 @@ func TestEvalAssign(t *testing.T) {
|
||||
{src: "f := int64(3.2)", err: "1:39: cannot convert expression of type float64 to type int64"},
|
||||
{src: "g := 1; g <<= 8", res: "256"},
|
||||
{src: "h := 1; h >>= 8", res: "0"},
|
||||
{src: "i := 1; j := &i; (*j) = 2", res: "2"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -444,22 +445,19 @@ func TestEvalSliceExpression(t *testing.T) {
|
||||
{src: `a := []int{0,1,2}[:]`, res: "[0 1 2]"},
|
||||
{src: `a := []int{0,1,2,3}[1:3:4]`, res: "[1 2]"},
|
||||
{src: `a := []int{0,1,2,3}[:3:4]`, res: "[0 1 2]"},
|
||||
{src: `ar := [3]int{0,1,2}
|
||||
a := ar[1:3]`, res: "[1 2]"},
|
||||
{src: `ar := [3]int{0,1,2}; a := ar[1:3]`, res: "[1 2]"},
|
||||
{src: `a := (&[3]int{0,1,2})[1:3]`, res: "[1 2]"},
|
||||
{src: `a := (&[3]int{0,1,2})[1:3]`, res: "[1 2]"},
|
||||
{src: `s := "hello"[1:3]`, res: "el"},
|
||||
{src: `str := "hello"
|
||||
s := str[1:3]`, res: "el"},
|
||||
{src: `str := "hello"; s := str[1:3]`, res: "el"},
|
||||
{src: `a := int(1)[0:1]`, err: "1:33: cannot slice type int"},
|
||||
{src: `a := ([3]int{0,1,2})[1:3]`, err: "1:33: cannot slice type [3]int"},
|
||||
{src: `a := (&[]int{0,1,2,3})[1:3]`, err: "1:33: cannot slice type *[]int"},
|
||||
{src: `a := "hello"[1:3:4]`, err: "1:45: invalid operation: 3-index slice of string"},
|
||||
{src: `ar := [3]int{0,1,2}
|
||||
a := ar[:4]`, err: "2:16: index int is out of bounds"},
|
||||
{src: `ar := [3]int{0,1,2}; a := ar[:4]`, err: "1:58: index int is out of bounds"},
|
||||
{src: `a := []int{0,1,2,3}[1::4]`, err: "1:49: 2nd index required in 3-index slice"},
|
||||
{src: `a := []int{0,1,2,3}[1:3:]`, err: "1:51: 3rd index required in 3-index slice"},
|
||||
{src: `a := []int{0,1,2}[3:1]`, err: "invalid index values, must be low <= high <= max"},
|
||||
{pre: func() { eval(t, i, `type Str = string; var r Str = "truc"`) }, src: `r[1]`, res: "114"},
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
10
interp/op.go
10
interp/op.go
@@ -253,7 +253,7 @@ func andConst(n *node) {
|
||||
n.rval = reflect.New(t).Elem()
|
||||
switch {
|
||||
case isConst:
|
||||
v := constant.BinaryOp(vConstantValue(v0), token.AND, vConstantValue(v1))
|
||||
v := constant.BinaryOp(constant.ToInt(vConstantValue(v0)), token.AND, constant.ToInt(vConstantValue(v1)))
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
case isUint(t):
|
||||
n.rval.SetUint(vUint(v0) & vUint(v1))
|
||||
@@ -338,7 +338,7 @@ func andNotConst(n *node) {
|
||||
n.rval = reflect.New(t).Elem()
|
||||
switch {
|
||||
case isConst:
|
||||
v := constant.BinaryOp(vConstantValue(v0), token.AND_NOT, vConstantValue(v1))
|
||||
v := constant.BinaryOp(constant.ToInt(vConstantValue(v0)), token.AND_NOT, constant.ToInt(vConstantValue(v1)))
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
case isUint(t):
|
||||
n.rval.SetUint(vUint(v0) &^ vUint(v1))
|
||||
@@ -564,7 +564,7 @@ func orConst(n *node) {
|
||||
n.rval = reflect.New(t).Elem()
|
||||
switch {
|
||||
case isConst:
|
||||
v := constant.BinaryOp(vConstantValue(v0), token.OR, vConstantValue(v1))
|
||||
v := constant.BinaryOp(constant.ToInt(vConstantValue(v0)), token.OR, constant.ToInt(vConstantValue(v1)))
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
case isUint(t):
|
||||
n.rval.SetUint(vUint(v0) | vUint(v1))
|
||||
@@ -799,7 +799,7 @@ func remConst(n *node) {
|
||||
n.rval = reflect.New(t).Elem()
|
||||
switch {
|
||||
case isConst:
|
||||
v := constant.BinaryOp(vConstantValue(v0), token.REM, vConstantValue(v1))
|
||||
v := constant.BinaryOp(constant.ToInt(vConstantValue(v0)), token.REM, constant.ToInt(vConstantValue(v1)))
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
case isUint(t):
|
||||
n.rval.SetUint(vUint(v0) % vUint(v1))
|
||||
@@ -1195,7 +1195,7 @@ func xorConst(n *node) {
|
||||
n.rval = reflect.New(t).Elem()
|
||||
switch {
|
||||
case isConst:
|
||||
v := constant.BinaryOp(vConstantValue(v0), token.XOR, vConstantValue(v1))
|
||||
v := constant.BinaryOp(constant.ToInt(vConstantValue(v0)), token.XOR, constant.ToInt(vConstantValue(v1)))
|
||||
n.rval.Set(reflect.ValueOf(v))
|
||||
case isUint(t):
|
||||
n.rval.SetUint(vUint(v0) ^ vUint(v1))
|
||||
|
||||
482
interp/run.go
482
interp/run.go
@@ -3,10 +3,12 @@ package interp
|
||||
//go:generate go run ../internal/cmd/genop/genop.go
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"log"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
@@ -67,11 +69,22 @@ var builtin = [...]bltnGenerator{
|
||||
aStar: deref,
|
||||
aSub: sub,
|
||||
aSubAssign: subAssign,
|
||||
aTypeAssert: typeAssert,
|
||||
aTypeAssert: typeAssertShort,
|
||||
aXor: xor,
|
||||
aXorAssign: xorAssign,
|
||||
}
|
||||
|
||||
var receiverStripperRxp *regexp.Regexp
|
||||
|
||||
func init() {
|
||||
re := `func\(((.*?(, |\)))(.*))`
|
||||
var err error
|
||||
receiverStripperRxp, err = regexp.Compile(re)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
type valueInterface struct {
|
||||
node *node
|
||||
value reflect.Value
|
||||
@@ -178,118 +191,47 @@ func runCfg(n *node, f *frame) {
|
||||
}
|
||||
}
|
||||
|
||||
func typeAssertStatus(n *node) {
|
||||
c0, c1 := n.child[0], n.child[1] // cO contains the input value, c1 the type to assert
|
||||
value := genValue(c0) // input value
|
||||
value1 := genValue(n.anc.child[1]) // returned status
|
||||
rtype := c1.typ.rtype // type to assert
|
||||
next := getExec(n.tnext)
|
||||
|
||||
switch {
|
||||
case isInterfaceSrc(c1.typ):
|
||||
typ := c1.typ
|
||||
n.exec = func(f *frame) bltn {
|
||||
v, ok := value(f).Interface().(valueInterface)
|
||||
value1(f).SetBool(ok && v.node.typ.implements(typ))
|
||||
return next
|
||||
}
|
||||
case isInterface(c1.typ):
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value(f)
|
||||
ok := v.IsValid() && canAssertTypes(v.Elem().Type(), rtype)
|
||||
value1(f).SetBool(ok)
|
||||
return next
|
||||
}
|
||||
case c0.typ.cat == valueT || c0.typ.cat == errorT:
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value(f)
|
||||
ok := v.IsValid() && canAssertTypes(v.Elem().Type(), rtype)
|
||||
value1(f).SetBool(ok)
|
||||
return next
|
||||
}
|
||||
default:
|
||||
n.exec = func(f *frame) bltn {
|
||||
v, ok := value(f).Interface().(valueInterface)
|
||||
ok = ok && v.value.IsValid() && canAssertTypes(v.value.Type(), rtype)
|
||||
value1(f).SetBool(ok)
|
||||
return next
|
||||
}
|
||||
func stripReceiverFromArgs(signature string) (string, error) {
|
||||
fields := receiverStripperRxp.FindStringSubmatch(signature)
|
||||
if len(fields) < 5 {
|
||||
return "", errors.New("error while matching method signature")
|
||||
}
|
||||
if fields[3] == ")" {
|
||||
return fmt.Sprintf("func()%s", fields[4]), nil
|
||||
}
|
||||
return fmt.Sprintf("func(%s", fields[4]), nil
|
||||
}
|
||||
|
||||
func typeAssert(n *node) {
|
||||
func typeAssertShort(n *node) {
|
||||
typeAssert(n, true, false)
|
||||
}
|
||||
|
||||
func typeAssertLong(n *node) {
|
||||
typeAssert(n, true, true)
|
||||
}
|
||||
|
||||
func typeAssertStatus(n *node) {
|
||||
typeAssert(n, false, true)
|
||||
}
|
||||
|
||||
func typeAssert(n *node, withResult, withOk bool) {
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
value := genValue(c0) // input value
|
||||
value0 := genValue(n) // returned result
|
||||
next := getExec(n.tnext)
|
||||
|
||||
var value0, value1 func(*frame) reflect.Value
|
||||
setStatus := false
|
||||
switch {
|
||||
case isInterfaceSrc(c1.typ):
|
||||
typ := n.child[1].typ
|
||||
typID := n.child[1].typ.id()
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value(f)
|
||||
vi, ok := v.Interface().(valueInterface)
|
||||
if !ok {
|
||||
panic(n.cfgErrorf("interface conversion: nil is not %v", typID))
|
||||
}
|
||||
if !vi.node.typ.implements(typ) {
|
||||
panic(n.cfgErrorf("interface conversion: %v is not %v", vi.node.typ.id(), typID))
|
||||
}
|
||||
value0(f).Set(v)
|
||||
return next
|
||||
}
|
||||
case isInterface(c1.typ):
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value(f).Elem()
|
||||
typ := value0(f).Type()
|
||||
if !v.IsValid() {
|
||||
panic(fmt.Sprintf("interface conversion: interface {} is nil, not %s", typ.String()))
|
||||
}
|
||||
if !canAssertTypes(v.Type(), typ) {
|
||||
method := firstMissingMethod(v.Type(), typ)
|
||||
panic(fmt.Sprintf("interface conversion: %s is not %s: missing method %s", v.Type().String(), typ.String(), method))
|
||||
}
|
||||
value0(f).Set(v)
|
||||
return next
|
||||
}
|
||||
case c0.typ.cat == valueT || c0.typ.cat == errorT:
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value(f).Elem()
|
||||
typ := value0(f).Type()
|
||||
if !v.IsValid() {
|
||||
panic(fmt.Sprintf("interface conversion: interface {} is nil, not %s", typ.String()))
|
||||
}
|
||||
if !canAssertTypes(v.Type(), typ) {
|
||||
method := firstMissingMethod(v.Type(), typ)
|
||||
panic(fmt.Sprintf("interface conversion: %s is not %s: missing method %s", v.Type().String(), typ.String(), method))
|
||||
}
|
||||
value0(f).Set(v)
|
||||
return next
|
||||
}
|
||||
default:
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value(f).Interface().(valueInterface)
|
||||
typ := value0(f).Type()
|
||||
if !v.value.IsValid() {
|
||||
panic(fmt.Sprintf("interface conversion: interface {} is nil, not %s", typ.String()))
|
||||
}
|
||||
if !canAssertTypes(v.value.Type(), typ) {
|
||||
panic(fmt.Sprintf("interface conversion: interface {} is %s, not %s", v.value.Type().String(), typ.String()))
|
||||
}
|
||||
value0(f).Set(v.value)
|
||||
return next
|
||||
}
|
||||
case withResult && withOk:
|
||||
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 "_"
|
||||
case withResult && !withOk:
|
||||
value0 = genValue(n) // returned result
|
||||
case !withResult && withOk:
|
||||
value1 = genValue(n.anc.child[1]) // returned status
|
||||
setStatus = n.anc.child[1].ident != "_" // do not assign status to "_"
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
setStatus := n.anc.child[1].ident != "_" // do not assign status to "_"
|
||||
typ := c1.typ // type to assert or convert to
|
||||
typ := c1.typ // type to assert or convert to
|
||||
typID := typ.id()
|
||||
rtype := typ.rtype // type to assert
|
||||
next := getExec(n.tnext)
|
||||
@@ -297,50 +239,198 @@ func typeAssert2(n *node) {
|
||||
switch {
|
||||
case isInterfaceSrc(typ):
|
||||
n.exec = func(f *frame) bltn {
|
||||
v, ok := value(f).Interface().(valueInterface)
|
||||
if ok && v.node.typ.id() == typID {
|
||||
value0(f).Set(value(f))
|
||||
} else {
|
||||
ok = false
|
||||
}
|
||||
valf := value(f)
|
||||
v, ok := valf.Interface().(valueInterface)
|
||||
if setStatus {
|
||||
value1(f).SetBool(ok)
|
||||
defer func() {
|
||||
value1(f).SetBool(ok)
|
||||
}()
|
||||
}
|
||||
if !ok {
|
||||
if !withOk {
|
||||
panic(n.cfgErrorf("interface conversion: nil is not %v", typID))
|
||||
}
|
||||
return next
|
||||
}
|
||||
if v.node.typ.id() == typID {
|
||||
if withResult {
|
||||
value0(f).Set(valf)
|
||||
}
|
||||
return next
|
||||
}
|
||||
m0 := v.node.typ.methods()
|
||||
m1 := typ.methods()
|
||||
if len(m0) < len(m1) {
|
||||
ok = false
|
||||
if !withOk {
|
||||
panic(n.cfgErrorf("interface conversion: %v is not %v", v.node.typ.id(), typID))
|
||||
}
|
||||
return next
|
||||
}
|
||||
|
||||
for k, meth1 := range m1 {
|
||||
var meth0 string
|
||||
meth0, ok = m0[k]
|
||||
if !ok {
|
||||
return next
|
||||
}
|
||||
// As far as we know this equality check can fail because they are two ways to
|
||||
// represent the signature of a method: one where the receiver appears before the
|
||||
// func keyword, and one where it is just a func signature, and the receiver is
|
||||
// seen as the first argument. That's why if that equality fails, we try harder to
|
||||
// compare them afterwards. Hopefully that is the only reason this equality can fail.
|
||||
if meth0 == meth1 {
|
||||
continue
|
||||
}
|
||||
tm := lookupFieldOrMethod(v.node.typ, k)
|
||||
if tm == nil {
|
||||
ok = false
|
||||
return next
|
||||
}
|
||||
|
||||
var err error
|
||||
meth0, err = stripReceiverFromArgs(meth0)
|
||||
if err != nil {
|
||||
ok = false
|
||||
return next
|
||||
}
|
||||
|
||||
if meth0 != meth1 {
|
||||
ok = false
|
||||
return next
|
||||
}
|
||||
}
|
||||
|
||||
if withResult {
|
||||
value0(f).Set(valf)
|
||||
}
|
||||
return next
|
||||
}
|
||||
case isInterface(typ):
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value(f).Elem()
|
||||
ok := v.IsValid() && canAssertTypes(v.Type(), rtype)
|
||||
if ok {
|
||||
value0(f).Set(v)
|
||||
}
|
||||
var leftType reflect.Type
|
||||
v := value(f)
|
||||
val, ok := v.Interface().(valueInterface)
|
||||
if setStatus {
|
||||
value1(f).SetBool(ok)
|
||||
defer func() {
|
||||
value1(f).SetBool(ok)
|
||||
}()
|
||||
}
|
||||
if ok && val.node.typ.cat != valueT {
|
||||
m0 := val.node.typ.methods()
|
||||
m1 := typ.methods()
|
||||
if len(m0) < len(m1) {
|
||||
ok = false
|
||||
return next
|
||||
}
|
||||
|
||||
for k, meth1 := range m1 {
|
||||
var meth0 string
|
||||
meth0, ok = m0[k]
|
||||
if !ok {
|
||||
return next
|
||||
}
|
||||
if meth0 != meth1 {
|
||||
ok = false
|
||||
return next
|
||||
}
|
||||
}
|
||||
|
||||
if withResult {
|
||||
value0(f).Set(genInterfaceWrapper(val.node, rtype)(f))
|
||||
}
|
||||
ok = true
|
||||
return next
|
||||
}
|
||||
|
||||
if ok {
|
||||
v = val.value
|
||||
leftType = val.node.typ.rtype
|
||||
} else {
|
||||
v = v.Elem()
|
||||
leftType = v.Type()
|
||||
ok = true
|
||||
}
|
||||
ok = v.IsValid()
|
||||
if !ok {
|
||||
if !withOk {
|
||||
panic(fmt.Sprintf("interface conversion: interface {} is nil, not %s", rtype.String()))
|
||||
}
|
||||
return next
|
||||
}
|
||||
ok = canAssertTypes(leftType, rtype)
|
||||
if !ok {
|
||||
if !withOk {
|
||||
method := firstMissingMethod(leftType, rtype)
|
||||
panic(fmt.Sprintf("interface conversion: %s is not %s: missing method %s", leftType.String(), rtype.String(), method))
|
||||
}
|
||||
return next
|
||||
}
|
||||
if withResult {
|
||||
value0(f).Set(v)
|
||||
}
|
||||
return next
|
||||
}
|
||||
case n.child[0].typ.cat == valueT || n.child[0].typ.cat == errorT:
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value(f).Elem()
|
||||
ok := v.IsValid() && canAssertTypes(v.Type(), rtype)
|
||||
if ok {
|
||||
value0(f).Set(v)
|
||||
}
|
||||
ok := v.IsValid()
|
||||
if setStatus {
|
||||
value1(f).SetBool(ok)
|
||||
defer func() {
|
||||
value1(f).SetBool(ok)
|
||||
}()
|
||||
}
|
||||
if !ok {
|
||||
if !withOk {
|
||||
panic(fmt.Sprintf("interface conversion: interface {} is nil, not %s", rtype.String()))
|
||||
}
|
||||
return next
|
||||
}
|
||||
v = valueInterfaceValue(v)
|
||||
ok = canAssertTypes(v.Type(), rtype)
|
||||
if !ok {
|
||||
if !withOk {
|
||||
method := firstMissingMethod(v.Type(), rtype)
|
||||
panic(fmt.Sprintf("interface conversion: %s is not %s: missing method %s", v.Type().String(), rtype.String(), method))
|
||||
}
|
||||
return next
|
||||
}
|
||||
if withResult {
|
||||
value0(f).Set(v)
|
||||
}
|
||||
return next
|
||||
}
|
||||
default:
|
||||
n.exec = func(f *frame) bltn {
|
||||
v, ok := value(f).Interface().(valueInterface)
|
||||
ok = ok && v.value.IsValid() && canAssertTypes(v.value.Type(), rtype)
|
||||
if ok {
|
||||
value0(f).Set(v.value)
|
||||
}
|
||||
if setStatus {
|
||||
value1(f).SetBool(ok)
|
||||
defer func() {
|
||||
value1(f).SetBool(ok)
|
||||
}()
|
||||
}
|
||||
if !ok || !v.value.IsValid() {
|
||||
ok = false
|
||||
if !withOk {
|
||||
panic(fmt.Sprintf("interface conversion: interface {} is nil, not %s", rtype.String()))
|
||||
}
|
||||
return next
|
||||
}
|
||||
|
||||
styp := v.value.Type()
|
||||
// TODO(mpl): probably also maps and others. and might have to recurse too.
|
||||
if styp.String() == "[]interp.valueInterface" {
|
||||
styp = v.node.typ.rtype
|
||||
}
|
||||
|
||||
ok = canAssertTypes(styp, rtype)
|
||||
if !ok {
|
||||
if !withOk {
|
||||
panic(fmt.Sprintf("interface conversion: interface {} is %s, not %s", v.value.Type().String(), rtype.String()))
|
||||
}
|
||||
return next
|
||||
}
|
||||
if withResult {
|
||||
value0(f).Set(v.value)
|
||||
}
|
||||
return next
|
||||
}
|
||||
@@ -588,12 +678,23 @@ func not(n *node) {
|
||||
|
||||
func addr(n *node) {
|
||||
dest := genValue(n)
|
||||
value := genValue(n.child[0])
|
||||
next := getExec(n.tnext)
|
||||
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).Set(value(f).Addr())
|
||||
return next
|
||||
c0 := n.child[0]
|
||||
value := genValue(c0)
|
||||
switch c0.typ.cat {
|
||||
case interfaceT:
|
||||
i := n.findex
|
||||
l := n.level
|
||||
n.exec = func(f *frame) bltn {
|
||||
v := value(f).Interface().(valueInterface).value
|
||||
getFrame(f, l).data[i] = reflect.ValueOf(v.Interface())
|
||||
return next
|
||||
}
|
||||
default:
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).Set(value(f).Addr())
|
||||
return next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -763,6 +864,9 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
if src.Type().Kind() != dest.Type().Kind() {
|
||||
dest.Set(src.Addr())
|
||||
} else {
|
||||
if wrappedSrc, ok := src.Interface().(valueInterface); ok {
|
||||
src = wrappedSrc.value
|
||||
}
|
||||
dest.Set(src)
|
||||
}
|
||||
d = d[numRet+1:]
|
||||
@@ -791,11 +895,6 @@ func genFunctionWrapper(n *node) func(*frame) reflect.Value {
|
||||
if v, ok := r.Interface().(*node); ok {
|
||||
result[i] = genFunctionWrapper(v)(f)
|
||||
}
|
||||
if def.typ.ret[i].cat == interfaceT {
|
||||
x := result[i].Interface().(valueInterface).value
|
||||
result[i] = reflect.New(reflect.TypeOf((*interface{})(nil)).Elem()).Elem()
|
||||
result[i].Set(x)
|
||||
}
|
||||
}
|
||||
return result
|
||||
})
|
||||
@@ -868,7 +967,10 @@ func call(n *node) {
|
||||
var method bool
|
||||
value := genValue(n.child[0])
|
||||
var values []func(*frame) reflect.Value
|
||||
if n.child[0].recv != nil {
|
||||
|
||||
recvIndexLater := false
|
||||
switch {
|
||||
case n.child[0].recv != nil:
|
||||
// Compute method receiver value.
|
||||
if isRecursiveType(n.child[0].recv.node.typ, n.child[0].recv.node.typ.rtype) {
|
||||
values = append(values, genValueRecvInterfacePtr(n.child[0]))
|
||||
@@ -876,11 +978,17 @@ func call(n *node) {
|
||||
values = append(values, genValueRecv(n.child[0]))
|
||||
}
|
||||
method = true
|
||||
} else if n.child[0].action == aMethod {
|
||||
case len(n.child[0].child) > 0 && n.child[0].child[0].typ != nil && n.child[0].child[0].typ.cat == interfaceT:
|
||||
recvIndexLater = true
|
||||
values = append(values, genValueBinRecv(n.child[0], &receiver{node: n.child[0].child[0]}))
|
||||
value = genValueBinMethodOnInterface(n, value)
|
||||
method = true
|
||||
case n.child[0].action == aMethod:
|
||||
// Add a place holder for interface method receiver.
|
||||
values = append(values, nil)
|
||||
method = true
|
||||
}
|
||||
|
||||
numRet := len(n.child[0].typ.ret)
|
||||
variadic := variadicPos(n)
|
||||
child := n.child[1:]
|
||||
@@ -990,6 +1098,7 @@ func call(n *node) {
|
||||
n.exec = func(f *frame) bltn {
|
||||
var def *node
|
||||
var ok bool
|
||||
|
||||
bf := value(f)
|
||||
if def, ok = bf.Interface().(*node); ok {
|
||||
bf = def.rval
|
||||
@@ -1059,16 +1168,16 @@ func call(n *node) {
|
||||
var src reflect.Value
|
||||
if v == nil {
|
||||
src = def.recv.val
|
||||
if len(def.recv.index) > 0 {
|
||||
if src.Kind() == reflect.Ptr {
|
||||
src = src.Elem().FieldByIndex(def.recv.index)
|
||||
} else {
|
||||
src = src.FieldByIndex(def.recv.index)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
src = v(f)
|
||||
}
|
||||
if recvIndexLater && def.recv != nil && len(def.recv.index) > 0 {
|
||||
if src.Kind() == reflect.Ptr {
|
||||
src = src.Elem().FieldByIndex(def.recv.index)
|
||||
} else {
|
||||
src = src.FieldByIndex(def.recv.index)
|
||||
}
|
||||
}
|
||||
// Accommodate to receiver type
|
||||
d := dest[0]
|
||||
if ks, kd := src.Kind(), d.Kind(); ks != kd {
|
||||
@@ -1112,6 +1221,8 @@ func call(n *node) {
|
||||
|
||||
func getFrame(f *frame, l int) *frame {
|
||||
switch l {
|
||||
case globalFrame:
|
||||
return f.root
|
||||
case 0:
|
||||
return f
|
||||
case 1:
|
||||
@@ -1165,13 +1276,13 @@ func callBin(n *node) {
|
||||
numOut := c.child[0].typ.rtype.NumOut()
|
||||
for j := 0; j < numOut; j++ {
|
||||
ind := c.findex + j
|
||||
values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
|
||||
values = append(values, func(f *frame) reflect.Value { return valueInterfaceValue(f.data[ind]) })
|
||||
}
|
||||
case isRegularCall(c):
|
||||
// Handle nested function calls: pass returned values as arguments
|
||||
for j := range c.child[0].typ.ret {
|
||||
ind := c.findex + j
|
||||
values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
|
||||
values = append(values, func(f *frame) reflect.Value { return valueInterfaceValue(f.data[ind]) })
|
||||
}
|
||||
default:
|
||||
if c.kind == basicLit || c.rval.IsValid() {
|
||||
@@ -1265,7 +1376,11 @@ func callBin(n *node) {
|
||||
for i := range rvalues {
|
||||
c := n.anc.child[i]
|
||||
if c.ident != "_" {
|
||||
rvalues[i] = genValue(c)
|
||||
if c.typ.cat == interfaceT {
|
||||
rvalues[i] = genValueInterfaceValue(c)
|
||||
} else {
|
||||
rvalues[i] = genValue(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
n.exec = func(f *frame) bltn {
|
||||
@@ -1608,6 +1723,22 @@ func getMethodByName(n *node) {
|
||||
|
||||
n.exec = func(f *frame) bltn {
|
||||
val := value0(f).Interface().(valueInterface)
|
||||
for {
|
||||
v, ok := val.value.Interface().(valueInterface)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
val = v
|
||||
}
|
||||
typ := val.node.typ
|
||||
if typ.node == nil && typ.cat == valueT {
|
||||
// happens with a var of empty interface type, that has value of concrete type
|
||||
// from runtime, being asserted to "user-defined" interface.
|
||||
if _, ok := typ.rtype.MethodByName(name); !ok {
|
||||
panic(fmt.Sprintf("method %s not found", name))
|
||||
}
|
||||
return next
|
||||
}
|
||||
m, li := val.node.typ.lookupMethod(name)
|
||||
fr := f.clone()
|
||||
nod := *m
|
||||
@@ -2031,7 +2162,11 @@ func arrayLit(n *node) {
|
||||
for i, v := range values {
|
||||
a.Index(index[i]).Set(v(f))
|
||||
}
|
||||
value(f).Set(a)
|
||||
dest := value(f)
|
||||
if _, ok := dest.Interface().(valueInterface); ok {
|
||||
a = reflect.ValueOf(valueInterface{n, a})
|
||||
}
|
||||
dest.Set(a)
|
||||
return next
|
||||
}
|
||||
}
|
||||
@@ -2098,6 +2233,51 @@ func compositeBinMap(n *node) {
|
||||
}
|
||||
}
|
||||
|
||||
func compositeBinSlice(n *node) {
|
||||
value := valueGenerator(n, n.findex)
|
||||
next := getExec(n.tnext)
|
||||
child := n.child
|
||||
if n.nleft == 1 {
|
||||
child = n.child[1:]
|
||||
}
|
||||
|
||||
values := make([]func(*frame) reflect.Value, len(child))
|
||||
index := make([]int, len(child))
|
||||
rtype := n.typ.rtype.Elem()
|
||||
var max, prev int
|
||||
|
||||
for i, c := range child {
|
||||
if c.kind == keyValueExpr {
|
||||
convertLiteralValue(c.child[1], rtype)
|
||||
values[i] = genValue(c.child[1])
|
||||
index[i] = int(vInt(c.child[0].rval))
|
||||
} else {
|
||||
convertLiteralValue(c, rtype)
|
||||
values[i] = genValue(c)
|
||||
index[i] = prev
|
||||
}
|
||||
prev = index[i] + 1
|
||||
if prev > max {
|
||||
max = prev
|
||||
}
|
||||
}
|
||||
|
||||
typ := n.typ.frameType()
|
||||
n.exec = func(f *frame) bltn {
|
||||
var a reflect.Value
|
||||
if n.typ.sizedef {
|
||||
a, _ = n.typ.zero()
|
||||
} else {
|
||||
a = reflect.MakeSlice(typ, max, max)
|
||||
}
|
||||
for i, v := range values {
|
||||
a.Index(index[i]).Set(v(f))
|
||||
}
|
||||
value(f).Set(a)
|
||||
return next
|
||||
}
|
||||
}
|
||||
|
||||
// doCompositeBinStruct creates and populates a struct object from a binary type.
|
||||
func doCompositeBinStruct(n *node, hasType bool) {
|
||||
next := getExec(n.tnext)
|
||||
|
||||
@@ -72,26 +72,28 @@ type symbol struct {
|
||||
// execution to the index in frame, created exactly from the types layout.
|
||||
//
|
||||
type scope struct {
|
||||
anc *scope // Ancestor upper scope
|
||||
anc *scope // ancestor upper scope
|
||||
child []*scope // included scopes
|
||||
def *node // function definition node this scope belongs to, or nil
|
||||
loop *node // loop exit node for break statement
|
||||
loopRestart *node // loop restart node for continue statement
|
||||
pkgID string // unique id of package in which scope is defined
|
||||
types []reflect.Type // Frame layout, may be shared by same level scopes
|
||||
level int // Frame level: number of frame indirections to access var during execution
|
||||
sym map[string]*symbol // Map of symbols defined in this current scope
|
||||
types []reflect.Type // frame layout, may be shared by same level scopes
|
||||
level int // frame level: number of frame indirections to access var during execution
|
||||
sym map[string]*symbol // map of symbols defined in this current scope
|
||||
global bool // true if scope refers to global space (single frame for universe and package level scopes)
|
||||
iota int // iota value in this scope
|
||||
}
|
||||
|
||||
// push creates a new scope and chain it to the current one.
|
||||
// push creates a new child scope and chain it to the current one.
|
||||
func (s *scope) push(indirect bool) *scope {
|
||||
sc := scope{anc: s, level: s.level, sym: map[string]*symbol{}}
|
||||
sc := &scope{anc: s, level: s.level, sym: map[string]*symbol{}}
|
||||
s.child = append(s.child, sc)
|
||||
if indirect {
|
||||
sc.types = []reflect.Type{}
|
||||
sc.level = s.level + 1
|
||||
} else {
|
||||
// propagate size, types, def and global as scopes at same level share the same frame
|
||||
// Propagate size, types, def and global as scopes at same level share the same frame.
|
||||
sc.types = s.types
|
||||
sc.def = s.def
|
||||
sc.global = s.global
|
||||
@@ -99,7 +101,7 @@ func (s *scope) push(indirect bool) *scope {
|
||||
}
|
||||
// inherit loop state and pkgID from ancestor
|
||||
sc.loop, sc.loopRestart, sc.pkgID = s.loop, s.loopRestart, s.pkgID
|
||||
return &sc
|
||||
return sc
|
||||
}
|
||||
|
||||
func (s *scope) pushBloc() *scope { return s.push(false) }
|
||||
@@ -107,7 +109,7 @@ func (s *scope) pushFunc() *scope { return s.push(true) }
|
||||
|
||||
func (s *scope) pop() *scope {
|
||||
if s.level == s.anc.level {
|
||||
// propagate size and types, as scopes at same level share the same frame
|
||||
// Propagate size and types, as scopes at same level share the same frame.
|
||||
s.anc.types = s.types
|
||||
}
|
||||
return s.anc
|
||||
@@ -128,6 +130,9 @@ func (s *scope) lookup(ident string) (*symbol, int, bool) {
|
||||
level := s.level
|
||||
for {
|
||||
if sym, ok := s.sym[ident]; ok {
|
||||
if sym.global {
|
||||
return sym, globalFrame, true
|
||||
}
|
||||
return sym, level - s.level, true
|
||||
}
|
||||
if s.anc == nil {
|
||||
@@ -138,6 +143,20 @@ func (s *scope) lookup(ident string) (*symbol, int, bool) {
|
||||
return nil, 0, false
|
||||
}
|
||||
|
||||
// lookdown searches for a symbol in the current scope and included ones, recursively.
|
||||
// It returns the first found symbol and true, or nil and false.
|
||||
func (s *scope) lookdown(ident string) (*symbol, bool) {
|
||||
if sym, ok := s.sym[ident]; ok {
|
||||
return sym, true
|
||||
}
|
||||
for _, c := range s.child {
|
||||
if sym, ok := c.lookdown(ident); ok {
|
||||
return sym, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (s *scope) rangeChanType(n *node) *itype {
|
||||
if sym, _, found := s.lookup(n.child[1].ident); found {
|
||||
if t := sym.typ; len(n.child) == 3 && t != nil && (t.cat == chanT || t.cat == chanRecvT) {
|
||||
|
||||
@@ -109,7 +109,7 @@ type itype struct {
|
||||
cat tcat // Type category
|
||||
field []structField // Array of struct fields if structT or interfaceT
|
||||
key *itype // Type of key element if MapT or nil
|
||||
val *itype // Type of value element if chanT,chanSendT, chanRecvT, mapT, ptrT, aliasT, arrayT or variadicT
|
||||
val *itype // Type of value element if chanT, chanSendT, chanRecvT, mapT, ptrT, aliasT, arrayT or variadicT
|
||||
recv *itype // Receiver type for funcT or nil
|
||||
arg []*itype // Argument types if funcT or nil
|
||||
ret []*itype // Return types if funcT or nil
|
||||
@@ -449,6 +449,9 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
}
|
||||
}
|
||||
t = sym.typ
|
||||
if t.incomplete && t.cat == aliasT && t.val != nil && t.val.cat != nilT {
|
||||
t.incomplete = false
|
||||
}
|
||||
if t.incomplete && t.node != n {
|
||||
m := t.method
|
||||
if t, err = nodeType(interp, sc, t.node); err != nil {
|
||||
@@ -557,7 +560,8 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
|
||||
if v, ok := pkg[name]; ok {
|
||||
t.cat = valueT
|
||||
t.rtype = v.Type()
|
||||
if isBinType(v) { // a bin type is encoded as a pointer on nil value
|
||||
if isBinType(v) {
|
||||
// A bin type is encoded as a pointer on a typed nil value.
|
||||
t.rtype = t.rtype.Elem()
|
||||
}
|
||||
} else {
|
||||
@@ -736,7 +740,7 @@ func (t *itype) finalize() (*itype, error) {
|
||||
}
|
||||
|
||||
// ReferTo returns true if the type contains a reference to a
|
||||
// full type name. It allows to asses a type recursive status.
|
||||
// full type name. It allows to assess a type recursive status.
|
||||
func (t *itype) referTo(name string, seen map[*itype]bool) bool {
|
||||
if t.path+"/"+t.name == name {
|
||||
return true
|
||||
@@ -878,13 +882,19 @@ func isComplete(t *itype, visited map[string]bool) bool {
|
||||
}
|
||||
name := t.path + "/" + t.name
|
||||
if visited[name] {
|
||||
return !t.incomplete
|
||||
return true
|
||||
}
|
||||
if t.name != "" {
|
||||
visited[name] = true
|
||||
}
|
||||
switch t.cat {
|
||||
case aliasT, arrayT, chanT, chanRecvT, chanSendT, ptrT:
|
||||
case aliasT:
|
||||
if t.val != nil && t.val.cat != nilT {
|
||||
// A type aliased to a partially defined type is considered complete, to allow recursivity.
|
||||
return true
|
||||
}
|
||||
fallthrough
|
||||
case arrayT, chanT, chanRecvT, chanSendT, ptrT:
|
||||
return isComplete(t.val, visited)
|
||||
case funcT:
|
||||
complete := true
|
||||
@@ -898,6 +908,8 @@ func isComplete(t *itype, visited map[string]bool) bool {
|
||||
case interfaceT, structT:
|
||||
complete := true
|
||||
for _, f := range t.field {
|
||||
// Field implicit type names must be marked as visited, to break false circles.
|
||||
visited[f.typ.path+"/"+f.typ.name] = true
|
||||
complete = complete && isComplete(f.typ, visited)
|
||||
}
|
||||
return complete
|
||||
@@ -1402,7 +1414,7 @@ func (t *itype) refType(defined map[string]*itype, wrapRecursive bool) reflect.T
|
||||
t.rtype = reflect.TypeOf(new(error)).Elem()
|
||||
case funcT:
|
||||
if t.name != "" {
|
||||
defined[name] = t
|
||||
defined[name] = t // TODO(marc): make sure that key is name and not t.name.
|
||||
}
|
||||
variadic := false
|
||||
in := make([]reflect.Type, len(t.arg))
|
||||
@@ -1423,10 +1435,11 @@ func (t *itype) refType(defined map[string]*itype, wrapRecursive bool) reflect.T
|
||||
t.rtype = reflect.PtrTo(t.val.refType(defined, wrapRecursive))
|
||||
case structT:
|
||||
if t.name != "" {
|
||||
if defined[name] != nil {
|
||||
// Check against local t.name and not name to catch recursive type definitions.
|
||||
if defined[t.name] != nil {
|
||||
recursive = true
|
||||
}
|
||||
defined[name] = t
|
||||
defined[t.name] = t
|
||||
}
|
||||
var fields []reflect.StructField
|
||||
// TODO(mpl): make Anonymous work for recursive types too. Maybe not worth the
|
||||
@@ -1539,7 +1552,7 @@ func hasRecursiveStruct(t *itype, defined map[string]*itype) bool {
|
||||
defined[typ.path+"/"+typ.name] = typ
|
||||
|
||||
for _, f := range typ.field {
|
||||
if hasRecursiveStruct(f.typ, defined) {
|
||||
if hasRecursiveStruct(f.typ, copyDefined(defined)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -499,9 +499,7 @@ func (check typecheck) sliceExpr(n *node) error {
|
||||
case reflect.Array:
|
||||
valid = true
|
||||
l = t.Len()
|
||||
if c.kind != indexExpr && c.kind != selectorExpr && (c.sym == nil || c.sym.kind != varSym) {
|
||||
return c.cfgErrorf("cannot slice type %s", c.typ.id())
|
||||
}
|
||||
// TODO(marc): check addressable status of array object (i.e. composite arrays are not).
|
||||
case reflect.Slice:
|
||||
valid = true
|
||||
case reflect.Ptr:
|
||||
|
||||
@@ -5,8 +5,15 @@ import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
const (
|
||||
notInFrame = -1 // value of node.findex for literal values (not in frame)
|
||||
globalFrame = -1 // value of node.level for global symbols
|
||||
)
|
||||
|
||||
func valueGenerator(n *node, i int) func(*frame) reflect.Value {
|
||||
switch n.level {
|
||||
case globalFrame:
|
||||
return func(f *frame) reflect.Value { return valueOf(f.root.data, i) }
|
||||
case 0:
|
||||
return func(f *frame) reflect.Value { return valueOf(f.data, i) }
|
||||
case 1:
|
||||
@@ -33,6 +40,30 @@ func valueOf(data []reflect.Value, i int) reflect.Value {
|
||||
return reflect.Value{}
|
||||
}
|
||||
|
||||
func genValueBinMethodOnInterface(n *node, defaultGen func(*frame) reflect.Value) func(*frame) reflect.Value {
|
||||
if n == nil || n.child == nil || n.child[0] == nil ||
|
||||
n.child[0].child == nil || n.child[0].child[0] == nil {
|
||||
return defaultGen
|
||||
}
|
||||
if n.child[0].child[1] == nil || n.child[0].child[1].ident == "" {
|
||||
return defaultGen
|
||||
}
|
||||
value0 := genValue(n.child[0].child[0])
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
val, ok := value0(f).Interface().(valueInterface)
|
||||
if !ok {
|
||||
return defaultGen(f)
|
||||
}
|
||||
typ := val.node.typ
|
||||
if typ.node != nil || typ.cat != valueT {
|
||||
return defaultGen(f)
|
||||
}
|
||||
meth, _ := typ.rtype.MethodByName(n.child[0].child[1].ident)
|
||||
return meth.Func
|
||||
}
|
||||
}
|
||||
|
||||
func genValueRecvIndirect(n *node) func(*frame) reflect.Value {
|
||||
v := genValueRecv(n)
|
||||
return func(f *frame) reflect.Value { return v(f).Elem() }
|
||||
@@ -55,6 +86,35 @@ func genValueRecv(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
}
|
||||
|
||||
func genValueBinRecv(n *node, recv *receiver) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
binValue := genValue(recv.node)
|
||||
|
||||
v := func(f *frame) reflect.Value {
|
||||
if def, ok := value(f).Interface().(*node); ok {
|
||||
if def != nil && def.recv != nil && def.recv.val.IsValid() {
|
||||
return def.recv.val
|
||||
}
|
||||
}
|
||||
|
||||
ival, _ := binValue(f).Interface().(valueInterface)
|
||||
return ival.value
|
||||
}
|
||||
|
||||
fi := recv.index
|
||||
if len(fi) == 0 {
|
||||
return v
|
||||
}
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
r := v(f)
|
||||
if r.Kind() == reflect.Ptr {
|
||||
r = r.Elem()
|
||||
}
|
||||
return r.FieldByIndex(fi)
|
||||
}
|
||||
}
|
||||
|
||||
func genValueRecvInterfacePtr(n *node) func(*frame) reflect.Value {
|
||||
v := genValue(n.recv.node)
|
||||
fi := n.recv.index
|
||||
@@ -118,18 +178,16 @@ func genValue(n *node) func(*frame) reflect.Value {
|
||||
return func(f *frame) reflect.Value { return v }
|
||||
}
|
||||
if n.sym != nil {
|
||||
if n.sym.index < 0 {
|
||||
i := n.sym.index
|
||||
if i < 0 {
|
||||
return genValue(n.sym.node)
|
||||
}
|
||||
i := n.sym.index
|
||||
if n.sym.global {
|
||||
return func(f *frame) reflect.Value {
|
||||
return n.interp.frame.data[i]
|
||||
}
|
||||
return func(f *frame) reflect.Value { return f.root.data[i] }
|
||||
}
|
||||
return valueGenerator(n, i)
|
||||
}
|
||||
if n.findex < 0 {
|
||||
if n.findex == notInFrame {
|
||||
var v reflect.Value
|
||||
if w, ok := n.val.(reflect.Value); ok {
|
||||
v = w
|
||||
@@ -244,6 +302,17 @@ func genValueOutput(n *node, t reflect.Type) func(*frame) reflect.Value {
|
||||
return value
|
||||
}
|
||||
|
||||
func valueInterfaceValue(v reflect.Value) reflect.Value {
|
||||
for {
|
||||
vv, ok := v.Interface().(valueInterface)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
v = vv.value
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func genValueInterfaceValue(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
|
||||
@@ -254,7 +323,7 @@ func genValueInterfaceValue(n *node) func(*frame) reflect.Value {
|
||||
v.Set(zeroInterfaceValue())
|
||||
v = value(f)
|
||||
}
|
||||
return v.Interface().(valueInterface).value
|
||||
return valueInterfaceValue(v)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user